欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

Android 笔记&面试

程序员文章站 2022-03-15 20:25:21
1.dvm的进程和Linux的进程DVM指dalivk的虚拟机,每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例,而每一个DVM都是在Linux中的一个进程,所以说可以认为是同一个概念。2.dp和px计算公式px=dp*(dpi/160)px(像素):屏幕上的点。in(英寸):长度单位。mm(毫米):长度单位。pt(磅):1/72英寸。dp(与密度无关的像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。dip:与d...

1.dvm的进程和Linux的进程

DVM指dalivk的虚拟机,每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例,而每一个DVM都是在Linux中的一个进程,所以说可以认为是同一个概念。

2.dp和px

计算公式px=dp*(dpi/160)
px(像素):屏幕上的点。
in(英寸):长度单位。
mm(毫米):长度单位。
pt(磅):1/72英寸。
dp(与密度无关的像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。
dip:与dp相同,多用于android/ophone示例中。
sp(与刻度无关的像素):与dp类似,但是可以根据用户的字体大小首选项进行缩放。
dpi:像素密度
dpi算法,像素为480 X 800,即长800宽480,先用勾股定理求斜边长度,再除以尺寸求得dpi的值

3.Activty和Task的启动模式

task:task是一个具有栈结构的容器,可以放置多个Activity实例。启动一个应用,系统就会为之创建一个task,来放置根Activity;默认情况下,一个Activity启动另一个Activity时,两个Activity是放置在同一个task中的,后者被压入前者所在的task栈,当用户按下后退键,后者从task被弹出,前者又显示在幕前,特别是启动其他应用中的Activity时,两个Activity对用户来说就好像是属于同一个应用;系统task和task之间是互相独立的,当我们运行一个应用时,按下Home键回到主屏,启动另一个应用,这个过程中,之前的task被转移到后台,新的task被转移到前台,其根Activity也会显示到幕前,过了一会之后,在此按下Home键回到主屏,再选择之前的应用,之前的task会被转移到前台,系统仍然保留着task内的所有Activity实例,而那个新的task会被转移到后台,如果这时用户再做后退等动作,就是针对该task内部进行操作了。

在android里,有4种activity的启动模式,分别为:

  • standard: 标准模式,也是默认模式,一调用startActivity()方法就会产生一个新的实例。
  • singleTop: 如果已经有一个实例位于Activity栈的顶部时,就不产生新的实例,而只是调用Activity中的newInstance()方法。如果不位于栈顶,会产生一个新的实例。
  • singleTask: 会在一个新的task中产生这个实例,以后每次调用都会使用这个,不会去产生新的实例了。
  • singleInstance: 这个跟singleTask基本上是一样,只有一个区别:在这个模式下的Activity实例所处的task中,只能有这个activity实例,不能有其他的实例。

4.startService()和bindService()

通过startservice开启的服务.一旦服务开启, 这个服务和开启他的调用者之间就没有任何的关系了.
调用者不可以访问 service里面的方法. 调用者如果被系统回收了或者调用了ondestroy方法, service还会继续存在 。
通过bindService开启的服务,服务开启之后,调用者和服务之间 还存在着联系 , 一旦调用者挂掉了.service也会跟着挂掉 .

5.Intent.ACTION_VIEW

用于显示用户的数据。
比较通用,会根据用户的数据类型打开相应的Activity。
比如 tel:13400010001打开拨号程序,http://www.g.cn则会打开浏览器等。

6.Android 架构

Linux Kernel(Linux内核)、Hardware Abstraction Layer(硬件抽象层)、Libraries(系统运行库或者是c/c++ 核心库)、Application Framework(开发框架包 )、Applications(核心应用程序)

7.前台进程,可见进程,服务进程,后台进程和空进程

  • 前台进程(foreground): 目前正在屏幕上显示的进程和一些系统进程。举例来说,Dialer Storage,Google Search等系统进程就是前台进程;再举例来说,当你运行一个程序,如浏览器,当浏览器界面在前台显示时,浏览器属于前台进程(foreground),但一旦你按home回到主界面,浏览器就变成了后台程序(background)。 我们最不希望终止的进程就是前台进程。
  • 可见进程(visible): 可见进程是一些不在前台,但用户依然可见的进程,举个例来说:widget、输入法等,都属于visible。这部分进程虽然不在前台,但与我们的使用也密切相关,我们也不希望它们被终止(你肯定不希望时钟、天气,新闻等widget被终止,那它们将无法同步,你也不希望输入法被终止,否则你每次输入时都需要重新启动输入法)
  • 服务进程(secondary server): 目前正在运行的一些服务(主要服务,如拨号等,是不可能被进程管理终止的,故这里只谈次要服务),举例来说:谷歌企业套件,Gmail内部存储,联系人内部存储等。这部分服务虽然属于次要服务,但很一些系统功能依然息息相关,我们时常需要用到它们,所以也太希望他们被终止
  • 后台进程(hidden): 虽然用了hidden这个词,但实际即是后台进程(background),就是我们通常意义上理解的启动后被切换到后台的进程,如浏览器,阅读器等。当程序显示在屏幕上时,他所运行的进程即为前台进程(foreground),一旦我们按home返回主界面(注意是按home,不是按back),程序就驻留在后台,成为后台进程(background)。后台进程的管理策略有多种:有较为积极的方式,一旦程序到达后台立即终止,这种方式会提高程序的运行速度,但无法加速程序的再次启动;也有较消极的方式,尽可能多的保留后台程序,虽然可能会影响到单个程序的运行速度,但在再次启动已启动的程序时,速度会有所提升。
  • 空进程(empty): 没有任何东西在内运行的进程,有些程序,比如BTE,在程序退出后,依然会在进程中驻留一个空进程,这个进程里没有任何数据在运行,作用往往是提高该程序下次的启动速度或者记录程序的一些历史信息。
  • 重要性依次是:前台进程,可见进程,服务进程,后台进程和空进程;所以销毁的顺序是逆方向。

8. Android 数字签名

  • Android通过数字签名来标识应用程序的作者和在应用程序之间建立信任关系,不是用来决定最终用户可以安装哪些应用程序。
    这个数字签名由应用程序的作者完成,并不需要权威的数字证书签名机构认证,它只是用来让应用程序包自我认证的。
  • Android系统不会安装运行任何一款未经数字签名的apk程序,无论是在模拟器上还是在实际的物理设备上。
  • 所以我们会有一个疑问,为何在日常开发过程中我没有进行任何签名的操作,程序都会在模拟器和真机上运行?
    调试模式(debug mode) : 在调试模式下, ADT会自动的使用debug密钥为应用程序签名,因此我们可以直接运行程序发布模式(release mode) : 当要发布程序时,开发者就需要使用自己的数字证书给apk包签名

9.使用Toast提示时,关于提示时长

toast只能设置为 2s和3.5s ,其它的值都无效,API的文档虽然写的第三个参数是时间,但是Framework里作了重定义,限定了 2s和3.5s 这两个值 ,对应 Toast.LENGTH_SHORT和Toast.LENGTH_LONG,实现方式在NotificationManagerService.java的scheduleTimeoutLocked()这个函数里。
Toast的默认显示时间有两个,分别为Toast.LENGTH_SHORT和Toast.LENGTH_LONG

10.IntentService与Service

Android中的Service是用于后台服务的,当应用程序被挂到后台的时候,问了保证应用某些组件仍然可以工作而引入了Service这个概念,那么这里面要强调的是Service不是独立的进程,也不是独立的线程,它是依赖于应用程序的主线程的,也就是说,在更多时候不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。

那么我们当我们编写的耗时逻辑,不得不被service来管理的时候,就需要引入IntentService,IntentService是继承Service的,那么它包含了Service的全部特性,当然也包含service的生命周期,那么与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去你执行你的耗时操作。

11.android:screenOrientation="landscape"设置

android:screenOrientation="landscape"是限制此页面横屏显示,

android:screenOrientation="portrait"是限制此页面数竖屏显示。
在我们用Android开发过程中,会碰到Activity在切换到后台或布局从横屏LANDSCAPE切换到PORTRAIT,会重新切换Activity会触发一次onCreate方法。
在Android开发中这种情况视可以避免的,我们可以在androidmanifest.xml中的activit元素加入这个属性 android:configChanges=“orientation|keyboardHidden” 就能有效避免oncreat方法的重复加载,

12.style和theme

相同:
Theme和Style位于values文件夹下styles.xml下,格式相同。
不同:
Theme在AndroidManifest.xml中德Application节点或者Activity节点设置android:theme,或者在对应Activity中通过代码设置setTheme(),影响整个应用或者一个Activity.
Style一般是在布局中的View中设置。影响单个View,如EditText,TextView等等。
style和theme本质上就是同一个东西,xml格式完全相同,只是我用在activity上我就叫他theme,我用在view上我就叫他style。唯一的区别就是style里头控制的各个属性了,某些属性是只有针对Activity才能生效的(你view有标题栏嘛有状态栏有导航栏吗嘛?但是反过来Activity内部却有一个继承自View的Decorview) 。所以style可以作用在activity上,但是theme却不能反过来作用在view上,并且theme是被final标识了,不能被继承。

13.GLSurfaceView

  • 1.管理一个平面, 这个平面是一个特殊的内存块 , 它可以和 android 视图系统混合 .
  • 2.管理一个EGL 显示 , 它能够让 OpenGL 渲染到一个平面 .
  • 3.接受一个用户提供的实际显示的Renderer 对象 .
  • 4.使用一个专用线程去渲染从而和UI 线程解耦 .
  • 5.支持on-demand 和连续的渲染.
  • 6.可选的包, 追踪 和 / 或者错误检查这个渲染的 OpenGL 调用 .

14.Aidl

编写Aidl文件时,需要注意下面几点:

  • 1.接口名和aidl文件名相同。
  • 2.接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static。
  • 3.Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、 CharSequence),使用这些类型时不需要import声明。对于List和Map中的元素类型必须是Aidl支持的类型。如果使用自定义类型作 为参数或返回值,自定义类型必须实现Parcelable接口。
  • 4.自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。
  • 5.在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。
  • 6.Java原始类型默认的标记为in,不能为其它标记。

15.RemoteView

RemoteView描述一个view,而这个view是在另外一个进程显示的。它inflate于layout资源文件。并且提供了可以修改过view内容的一些简单基础的操作。

从这个定义我们就知道RemoteView是用来描述一个垮进程显示的view。
到目前为止,我发现RemoteView会用在两个地方: 一个是在AppWidget , 另外一个是在Notification.

16.Android 动画分类

在Android3.0之前有两种动画,一种方式是补间动画 Tween Animation、另一种叫逐帧动画 Frame Animation(也称Drawable Animation )。Android3.0以后增加了属性动画 Property Animation。
补间动画与逐帧动画在本质上是不同的,逐帧动画通过连续播放图片来模拟动画的效果,而补间动画则是通过在两个关键帧之间补充渐变的动画效果来实现的。补间动画的优点是可以节省空间。目前Android应用框架支持的补间动画效果有以下5种。具体实现在android.view.animation类库中。

  • AlphaAnimation:透明度(alpha)渐变效果,对应标签。

  • TranslateAnimation:位移渐变,需要指定移动点的开始和结束坐标,对应标签。

  • ScaleAnimation:缩放渐变,可以指定缩放的参考点,对应标签。

  • RotateAnimation:旋转渐变,可以指定旋转的参考点,对应标签。

  • AnimationSet:组合渐变,支持组合多种渐变效果,对应标签。

补间动画的效果同样可以使用XML语言来定义,这些动画模板文件通常会被放在Android项目的res/anim/目录下。

17. ContentResolver和 ContentProvider

ContentResolver和 ContentProvider是一对。一个运行在提供数据端(provider),一个运行在调用端(resolver)。使用的时候利用resolver来调用provider的方法(query,insert,update等),然后provider再进行数据查询

18.SAX、DOM、PULL的比较

  • 内存占用

这是一个根本性问题。由于Android手机性能相对于现在的应用操作还是有限的,程序对内存的占用直接影响到了解析XML的速度。在这点上,SAX、Pull以它们比DOM占用更少的内存的解析方式,更适合于Android手机开发。

  • 编程方式

SAX采用事件驱动,在相应事件触发的时候,会调用用户编写好的方法。也就是说,每解析一类XML,就要编写一个新的适合该类XML的处理类。这显然不是一个好的解决办法,尽管其在解析速度上是那么优秀。而这点,DOM因为是W3C的规范。所以被更多程序员所知道和使用。所以在开发过程中,没有太大困难。Pull虽然属于一个小众的,甚至是不为人知的解析器,但是通过上面对其介绍和示例,我们应该能看出它的简洁性。

  • 访问与修改

由于采用的是流式解析,这就说明它们不能像DOM那样随机访问,XML的其中任意一个节点。并且,SAX并没有提供对文档中加节点的API,更没有删除,修改文档内容的方法。

  • 访问方式

这是产生它们解析快慢的根本原因。如果把SAX和Pull比喻成一目十行,很快但是是走马观花的阅读方式的话,那么DOM就是逐字逐句的阅读,很慢,但是是过目不忘。这里还要需要注意的是,SAX,Pull解析的方式是同步的,即解析器读到哪里,就对哪里进行处理。而DOM是已经将文件解析好后,供用户提取XML中感兴趣的信息。

19.ListView的优化方案

  • 复用convertView

  • 给contentView设置tag(setTag()),传入一个viewHolder对象,用于缓存要显示的数据,可以达到图像数据异步加载的效果。

  • 设置ListView的高度为 确定值或match_parent

*如果listview需要显示的item很多,就要考虑分页加载。

20.Message,Handler,MessageQueue,Looper

  • Looper:是一个消息分发器,在主线程创建的时候就会创建一个Looper对象;
  • MessageQueue :消息队列,是由Message组成的一个队列;
  • Handler :获取到Message,然后执行动作,可以在主线程和子线程中互相传递数据;
    先大概说一下他们之间的关系:

在主线程创建之后会创建一个Looper对象,创建Looper对象的时候会去创建一个MessageQueue,而Looper是一个轮询器,会不停的轮询MessageQueue中的消息,在获取到消息之后就会把这个消息交给相应的handler来进行处理,在主线程中创建一个handler对象,这个handler对象把一个Message放到消息队列中,然后获取到消息进行处理。

举例:根据安卓系统的机制,UI只能在主线程中更新,如果我们想在子线程中更新UI,就要用到这一套机制了,在主线程和子线程之间传递信息。做法就是,现在主线程中创建一个handler对象,Looper对象和MessageQueue对象是不用创建的,因为在主线程创建的时候就创建好了。然后重写handlerMessage方法,这个方法是在handler接收到message之后执行的方法,我们把更新UI的代码放到这个方法中。然后再子线程中用sendEmptyMessage方法来发送一个消息到消息队里。在发送完这个消息之后,Looper这个轮询器因为一直在轮询消息队列。获得到这个消息之后就会执行handlerMessage方法,因为这个方法是在主线程中实现的。所以UI就可以更新了。

21.为什么要用Service不用Thread

Thread的运行是独立于 Activity 的,也就是说当一个Activity 被 finish 之后,如果你没有主动停止Thread 或者Thread 里的 run方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当Activity 被 finish 之后,你不再持有该Thread 的引用。另一方面,你没有办法在不同的Activity 中对同一Thread 进行控制。

22.如何将SQLite数据库(dictionary.db文件)与apk文件一起发布

可以将dictionary.db文件复制到 Android工程中的res/raw目录中。所有在res/raw目录中的文件不会被压缩,这样可以直接提取该目录中的文件。

复制的基本方法是使用getResources().openRawResource方法获得res/raw目录中资源的 InputStream对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。在Android SDK中可以使用SQLiteDatabase.openOrCreateDatabase方法来打开任意目录中的SQLite数据库文件。

23.DDMS和TraceView

DDMS是一个程序执行查看器,在里面可以看见线程和堆栈等信息,TraceView是程序性能分析器.

24.Picasso和Glide的区别

2个相似的图片加载库

  • Picasso加载了全尺寸的图片到内存,然后让GPU来实时重绘大小。而Glide加载的大小和ImageView的大小是一致的,因此更小。当然,Picasso也可以指定加载的图片大小的:
    * Picasso和Glide在磁盘缓存策略上有很大的不同。Picasso缓存的是全尺寸的,而Glide缓存的是跟ImageView尺寸相同的。

25.assets目录和raw目录

共同点:
它们会被原封不动的拷贝到APK中,而不会像其它资源文件那样被编译成二进制的形式。
要说区别,当然最直观的就是获取它们的InputStream的API不一样了。
assets:InputStream assets = getAssets().open(“xxxx”);
raw:InputStream raw = getResources().openRawResource(R.raw.xxxx)
由 于raw是Resources (res)的子目录,Android会自动的为这目录中的所有资源文件生成一个ID,这个ID会被存储在R类当中,作为一个文件的引用。这意味着这个资源 文件可以很容易的被Android的类和方法访问到,甚至在Android XML文件中你也可以@raw/的形式引用到它。在Android中,使用ID是访问一个文件最快捷的方式。MP3和Ogg文件放在这个目录下是比较合适 的。
assets目录更像一个附录类型的目录,Android不会为这个目录中的文件生成ID并保存在R类当中,因此它与 Android中的一些类和方法兼容度更低。同时,由于你需要一个字符串路径来获取这个目录下的文件描述符,访问的速度会更慢。但是把一些文件放在这个目 录下会使一些操作更加方便,比方说拷贝一个数据库文件到系统内存中。要注意的是,你无法在Android XML文件中引用到assets目录下的文件,只能通过AssetManager来访问这些文件。数据库文件和游戏数据等放在这个目录下是比较合适的。

26.Activity、Window 和 View 三者的区别

一个 Activity 构造的时候一定会构造一个 Window(PhoneWindow),并且只有一个。
这个Window会有一个ViewRoot(View、ViewGroup)。
通过addView()加载布局。
WindowMangerService 接收消息,并且回到 Activity 函数,比如onKeyDown()。
Activity 是控制单元,Window 是承载模型,View 是显示视图

27.本地广播和全局广播有什么差别

  • 全局广播 BroadcastReceiver 是针对应用间、应用与系统间、应用内部进行通信的一种方式。

  • 本地广播 LocalBroadcastReceiver 仅在自己的应用内发送接收广播,也就是只有自己的应用能收到。因广播数据在本应用范围内传播,不用担心隐私数据泄露的问题。 不用担心别的应用伪造广播,造成安全隐患。 相比在系统内发送全局广播,它更高效。

28.Broadcast、Content Provider 和 AIDL的区别和联系

这3种都可以实现跨进程的通信

  • Broadcast:用于发送和接收广播!实现信息的发送和接收!

  • AIDL:用于不同程序间服务的相互调用!实现了一个程序为另一个程序服务的功能!
    Content Provider:用于将程序的数据库人为地暴露出来!实现一个程序可以对另个程序的数据库进行相对用的操作!

  • Broadcast,既然是广播,那么它的优点是:注册了这个广播接收器的应用都能够收到广播,范围广。缺点是:速度慢点,而且必须在一定时间内把事情处理完(onReceive执行必须在几秒之内),否则的话系统给出ANR。

AIDL,是进程间通信用的,类似一种协议吧。优点是:速度快(系统底层直接是共享内存),性能稳,效率高,一般进程间通信就用它。

Content Provider,因为只是把自己的数据库暴露出去,其他程序都可以来获取数据,数据本身不是实时的,不像前两者,只是起个数据供应作用。一般是某个成熟的应用来暴露自己的数据用的。 你要是为了进程间通信,还是别用这个了,这个又不是实时数据。

29.View 的触摸事件分发机制

基础知识

  • 所有Touch事件都被封装成了MotionEvent对象,包括Touch的位置、时间、历史记录以及第几个手指(多指触摸)等。

  • 事件类型分为ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_POINTER_DOWN, ACTION_POINTER_UP, ACTION_CANCEL,每个事件都是以ACTION_DOWN开始ACTION_UP结束。

  • 对事件的处理包括三类,分别为传递——dispatchTouchEvent()函数、拦截——onInterceptTouchEvent()函数、消费——onTouchEvent()函数和OnTouchListener
    传递流程

  • 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。

  • 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。

  • 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。

  • 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来。
    OnTouchListener优先于onTouchEvent()对事件进行消费。
    上面的消费即表示相应函数返回值为true。

30.View 绘制流程

当 Activity 接收到焦点的时候,它会被请求绘制布局,该请求由 Android framework 处理。绘制是从根节点开始,对布局树进行 measure 和 draw。整个 View 树的绘图流程在ViewRoot.java类的 performTraversals() 函数展开,该函数所做 的工作可简单概况为是否需要重新计算视图大小(measure)、是否需要重新安置视图的位置(layout)、以及是否需要重绘(draw)

31.requertlayout onlayout onDraw drawChild 的区别和联系

  • requestLayout()方法 :会导致调用measure()过程 和 layout()过程 。 将会根据标志位判断是否需要ondraw。
  • onLayout()方法:如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局。
  • 调用onDraw()方法绘制视图本身,每个View都需要重载该方法,ViewGroup不需要实现该方法。
  • drawChild()去重新回调每个子视图的draw()方法。

32.View 刷新机制

  • 在Android的布局体系中,父View负责刷新、布局显示子View;而当子View需要刷新时,则是通知父View来完成。

  • 子View调用invalidate时,首先找到自己父View(View的成员变量mParent记录自己的父View),然后将AttachInfo中保存的信息告诉父View刷新自己。

  • 在invalidate中,调用父View的invalidateChild,这是一个从第向上回溯的过程,每一层的父View都将自己的显示区域与传入的刷新Rect做交集。

  • 这个向上回溯的过程直到ViewRoot那里结束,由ViewRoot对这个最终的刷新区域做刷新。

33.invalidata() 和 postInvalidata() 的区别及使用

invalidata() 必须在 UI 线程中调用,所以一般都是配合 Handler 使用。
postInvalidata() 可以在其他线程直接调用。

34.notifyDataSetChanged和notifyDataSetInvalidated的区别

notifyDataSetInvalidated(),会重绘整个控件(还原到初始状态)
notifyDataSetChanged(),重绘当前可见区域

35.SurfaceView和View的区别是什么?

SurfaceView中采用了双缓存技术,在单独的线程中更新界面。而View在UI线程中更新界面。

36.自定义View相关方法

  • 1.明确需求,确定你想实现的效果
  • 2.确定是使用组合控件的形式还是全新自定义的形式,组合控件即使用多个系统控件来合成一个新控件,你比如titilebar,这种形式相对简单,参考
  • 3.如果是完全自定义一个view的话,你首先需要考虑继承哪个类,是View呢,还是ImageView等子类。
  • 4.根据需要去复写View#onDraw、View#onMeasure、View#onLayout方法
  • 5.根据需要去复写dispatchTouchEvent、onTouchEvent方法
  • 6.根据需要为你的自定义view提供自定义属性,即编写attr.xml,然后在代码中通过TypedArray等类获取到自定义属性值 7.需要处理滑动冲突、像素转换等问题

37.RecyclerView和ListView的异同

  • RecyclerView 自带 ViewHolder;而 ListView 则需要自定义。
  • RecyclerView 支持水平和垂直滚动;而 ListView 只支持垂直滚动。
  • RecyclerView 提供默认的列表项动画实现,例如:添加、删除和移动列表项动画。
  • ListView通过AdapterView.OnItemClickListener接口来监听点击事件。而RecyclerView则通过RecyclerView.OnItemTouchListener接口来监听触摸事件。它虽然增加了实现的难度,但是却给予开发人员拦截触摸事件更多的控制权限。
  • ListView可以设置选择模式,并添加MultiChoiceModeListener;而 RecyclerView 没有该功能。

38.设备横竖屏切换的时候,接下来会发生什么

  • 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

  • 设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

  • 设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

39.ANR是什么?怎样避免和解决ANR

  • 1:KeyDispatchTimeout(5 seconds) --主要类型

按键或触摸事件在特定时间内无响应

  • 2:BroadcastTimeout(10 seconds)

BroadcastReceiver在特定时间内无法处理完成

  • 3:ServiceTimeout(20 seconds) --小概率类型

Service在特定的时间内无法处理完成

超时的原因一般有两种:

(1)当前的事件没有机会得到处理(UI线程正在处理前一个事件没有及时完成或者looper被某种原因阻塞住)

(2)当前的事件正在处理,但没有及时完成

UI线程尽量只做跟UI相关的工作,耗时的工作(数据库操作,I/O,连接网络或者其他可能阻碍UI线程的操作)放入单独的线程处理,尽量用Handler来处理UI thread和thread之间的交互。
查找ANR的方式: 1. 导出/data/data/anr/traces.txt,找出函数和调用过程,分析代码 2. 通过性能LOG人肉查找

40.使用Android studio分析内存泄露

检测: 1、DDMS Heap发现内存泄露 dataObject totalSize的大小,是否稳定在一个范围内,如果操作程序,不断增加,说明内存泄露 2、使用Heap Tool进行内存快照前后对比 BlankActivity手动触发GC进行前后对比,对象是否被及时回收

定位: 1、MAT插件打开.hprof具体定位内存泄露: 查看histogram项,选中某一个对象,查看它的GC引用链,因为存在GC引用链的,说明无法回收 2、AndroidStudio的Allocation Tracker: 观测到期间的内存分配,哪些对象被创建,什么时候创建,从而准确定位

41Manifest.xml文件中主要包括哪些信息?

  • manifest:根节点,描述了package中所有的内容。
  • uses-permission:请求你的package正常运作所需赋予的安全许可。
  • permission: 声明了安全许可来限制哪些程序能你package中的组件和功能。
  • instrumentation:声明了用来测试此package或其他package指令组件的代码。
  • application:包含package中application级别组件声明的根节点。
  • activity:Activity是用来与用户交互的主要工具。
  • receiver:IntentReceiver能使的application获得数据的改变或者发生的操作,即使它当前不在运行。
  • service:Service是能在后台运行任意时间的组件。
  • provider:ContentProvider是用来管理持久化数据并发布给其他应用程序使用的组件。

42横竖屏切换时 Activity 的生命周期

此时的生命周期跟清单文件里的配置有关系。
1.不设置 Activity 的 android:configChanges 时,切屏会重新调用各个生命周期默认首先销毁当前 activity,然后重新加载。
2.设置 Activity
android:configChanges=”orientation|keyboardHidden|screenSize”时,切 屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法。
通常在游戏开发, 屏幕的朝向都是写死的。

43Handler,Thread和HandlerThread的差别

  • Handler会关联一个单独的线程和消息队列。Handler默认关联主线程,虽然要提供Runnable参数,但默认是直接调用Runnable中的run()方法。也就是默认下会在主线程执行,如果在这里面的操作会有阻塞,界面也会卡住。如果要在其他线程执行,可以使用HandlerThread。
  • HandlerThread继承于Thread,所以它本质就是个Thread。与普通Thread的差别就在于,主要的作用是建立了一个线程,并且创立了消息队列,有来自己的looper,可以让我们在自己的线程中分发和处理消息。

44LaunchMode应用场景

  • singleTop适合接收通知启动的内容显示页面。
    例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。
  • singleTask适合作为程序入口点。
    例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。
  • singleInstance应用场景:
    闹铃的响铃界面。 你以前设置了一个闹铃:上午6点。在上午5点58分,你启动了闹铃设置界面,并按 Home 键回桌面;在上午5点59分时,你在微信和朋友聊天;在6点时,闹铃响了,并且弹出了一个对话框形式的 Activity(名为 AlarmAlertActivity) 提示你到6点了(这个 Activity 就是以 SingleInstance 加载模式打开的),你按返回键,回到的是微信的聊天界面,这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了。如果是以 SingleTask 打开 AlarmAlertActivity,那么当闹铃响了的时候,按返回键应该进入闹铃设置界面。

45单例

public class Singleton{
private volatile static Singleton mSingleton;
private Singleton(){
}
public static Singleton getInstance(){
  if(mSingleton == null){\\A
    synchronized(Singleton.class){\\C
     if(mSingleton == null)
      mSingleton = new Singleton();\\B
      }
    }
    return mSingleton;
  }
}

46什么情况导致内存泄漏

  • 1.资源对象没关闭造成的内存泄漏
  • 2.构造Adapter时,没有使用缓存的convertView
  • 3.Bitmap对象不在使用时调用recycle()释放内存
  • 4.试着使用关于application的context来替代和activity相关的context
  • 5.注册没取消造成的内存泄漏
  • 6.集合中对象没清理造成的内存泄漏

47如何保证service在后台不被Kill

  • onStartCommand方法,返回START_STICKY

  • 提升service优先级
    在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。

  • 提升service进程优先级
    Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级.
    当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground 将service放到前台状态。这样在低内存时被kill的几率会低一些。

  • onDestroy方法里重启service

service +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service;

  • Application加上Persistent属性

  • 监听系统广播判断Service状态

通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活,别忘记加权限啊。

48Requestlayout,onlayout,onDraw,DrawChild区别与联系

  • requestLayout()方法 :会导致调用measure()过程 和 layout()过程 。 将会根据标志位判断是否需要ondraw

  • onLayout()方法(如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局)

  • 调用onDraw()方法绘制视图本身 (每个View都需要重载该方法,ViewGroup不需要实现该方法)

  • drawChild()去重新回调每个子视图的draw()方法

49invalidate()和postInvalidate()的区别及使用

  • Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
    invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。
  • 使用postInvalidate则比较简单,不需要handler,直接在线程中调用postInvalidate即可。

50进程间通信方式

  • 通过Intent在Activity、Service或BroadcastReceiver间进行进程间通信,可通过*Intent传递数据
  • AIDL方式
  • Messenger方式
  • 利用ContentProvider
  • Socket方式
  • 基于文件共享的方式

51UriMatcher、ContentUrist和ContentResolver

因为Uri代表了要操作的数据,所以我们很经常需要解析Uri,并从 Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。掌握它们的使用,会便于我们的开发工作。

  • UriMatcher:用于匹配Uri,它的用法如下:
    1.首先把你需要匹配Uri路径全部给注册上,如下:
    //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
    UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    //如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider /contact路径,返回匹配码为1
    uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
    //如果match()方法匹配 content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2
    uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符
    2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用 addURI()方法传入的第三个参数,假设匹配 content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回的匹配码为1。
  • ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
    withAppendedId(uri, id)用于为路径加上ID部分
    parseId(uri)方法用于从路径中获取ID部分
  • ContentResolver:当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用 ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver使用insert、delete、update、query方法,来操作数据。
52:Android WebView与 JS 的交互方式

因为一直做的是原生APP的开发,说实话,对这方面了解太少了,而现在很多公司都开始使用H5加原生的方式来开发APP了,这方面有必要补习一下!
针对这个问题,可以分为两个方面来解答:
Android调用JS代码的方法:

  • 通过WebView的loadUrl()

先在网页中创建Js方法

<script type="text/javascript">
function Test(){
     //具体实现
}

然后在webview中调用

webView.loadUrl("javascript:Test()");
  • 通过WebView的evaluateJavascript()

1.该方法执行不会使页面刷新,而第一种方法(loadUrl )的执行则会。所以该方法比第一种方法效率更高、使用更简洁。
2.版本大于Android 4.4

主要代码:

webView.evaluateJavascript("javascript:Test()", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            //此处为 js 返回的结果
        }
    });

JS调用Android代码的方法:

  • 通过WebView的addJavascriptInterface()进行对象映射
    先定义接口,在需要调用的方法处用@JavascriptInterface注解
//接口名称自定义
public class WebAppInterface {
    Context mContext;

    WebAppInterface(Context c) {
        mContext = c;
    }
    @JavascriptInterface
    public void test() {
    }
}

在初始化的时候设置

webview.addJavascriptInterface(new WebAppInterface(this), "testEntity");//WebAppInterface 类对象映射到js的testEntity对象

在html文件里面调用:

<a onClick="window.testEntity.test()" />
  • 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
  • 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息(后面两种使用起来比较复杂,就不展开了)
53JNI的调用流程

JNI是什么?说实话,像我这种不做底层的开发人员跟这个东西是不会有交集的,但是还是要了解一下的。网上是这样说的:JNI是Java Native Interface的缩写,它提供了若干的api实现了Java和其他语言的通信(主要是C,C++)。
就这个问题来说,对于没用过JNI的人来说,个人觉得能答得上来当然很好,答不上来应该也是没事的,毕竟大部分Android开发,都只是单纯的做Java开发,很少与底层有交集。下面附上网上找的问题答案:

(1)安装和下载Cygwin,下载 Android NDK
(2)在ndk项目中JNI接口的设计
(3)使用C/C++实现本地方法
(4)JNI生成动态链接库.so文件
(5)将动态链接库复制到java工程,在java工程中调用,运行java工程即可

本文地址:https://blog.csdn.net/hwj3747/article/details/108980382