指南
Activity
Service
Broadcast
ContentProvider
Fragment
View
View绘制
View事件分发
自定义View
View滑动
控件
RecyclerView
ListView
ViewPager
动画
Bitmap && Drawable
Android线程与进程
Handler/Looper
Binder
序列化
进程保活
AsyncTask
HandlerThread
IntentService
IPC
线程池
Android优化
ANR
1. ANR是什么?如何避免和解决ANR?
ANR:Application Not Responding,即应用无响应。一般有以下三种:
- KeyDispatchTimeout:5s
- BroadcatTimeout:10s
- ServiceTimeout:20s
造成Timeout的原因一般有两种:
-
主线程阻塞在了某个消息中
-
当前事件正在被处理但是没有被处理完
操作阻塞的原因可能是:
- 耗时的网络操作
- 大量的数据读写
- 数据库操作
- 硬件操作
- 线程等待
- 发生了死锁
- 动画耗时
- Cpu负载高
问题定位:
- ANR产生时,系统会生成一个tarces.txt的文本文件放在/data/anr下,最新的ANR信息在最开始的位置。通过ADB命令
adb pull data/anr/traces.txt .
可以导出到本地分析。 - 使用Android Studio中的Profile工具可以方便的得到每个方法的运行时间
- 使用Android SDK中提供的Debug类:
- 在开始的记录的点写上代码Debug.startMethodTracing("TAG");
- 在终止记录的点写上代码Debug.stopMethodTracing();
- 通过adb pull /data/tracePath.trace导出文件并使用Android Studio打开分析
解决方法:
- 不要在主线程中去做耗时操作,将耗时操作放在子线程中去。通过Thread、HandlerThread、IntentService、AsyncTask
- 同样由于Service/Broadcast运行在主线程,Service中也不能做耗时操作
- 在使用Thread或者HandlerThread时,可以尝试调用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置较低优先级,否则依然会降低程序响应,因为默认Thread的优先级和主线程相同。
- 任何UI线程中的方法都应该简短快捷,类似调整Bitmap大小这种需要长时间计算的操作,都应该执行在工作线程中。
- BroadcastReceiver中的onReceive()方法如果必须执行耗时操作,建议在这个方法中开启IntentService来执行耗时操作。
内存泄漏memory leak
1. 内存泄漏的原因、场景、解决方法?
根本原因:长生命周期的对象持有短生命周期的对象,导致短生命周期对象就无法及时释放。
泄漏场景:
-
非静态内部类的静态实例
非静态内部类会持有外部类的引用,如果这个非静态内部类的实例恰好是个静态实例,那么这个实例就会长时间的存在,从而导致外部类长时间被引用,就无法被回收,从而造成外部类的内存泄漏。 -
匿名内部类
匿名内部类同样会持有外部类的引用,如果这个匿名内部类中做了耗时操作,就会导致外部类长时间被引用从而无法回收。 -
Handler内存泄漏
Handler内存泄漏也可以归纳为非静态内部类导致的,Handler发送的消息Message首先会将自己的target指向当前Handler对象,然后这个Message会被加入到Looper中的MessageQueue中等待被处理。如果消息长时间得不到处理,Handler就会被一直引用,从而外部类也就一直被引用,导致内存泄漏。这种情况下建议:
- 使用静态Handler
- 在退出时移除消息队列中的消息
- 外部类引用使用弱引用处理
-
Context内存泄漏
根据场景确定使用Activity的Context还是ApplicationContext,因为两者生命周期不同,对于不必须使用Activity的Context的场景(Dialog),一律采用ApplicationContext。单例模式是最常见的发生此泄漏的场景。比如传入一个Activity的Context被静态类引用,导致无法回收。 -
静态View内存泄漏
使用静态View可以避免每次启动Activity都去读取并渲染View,但是View会持有Activity的引用,导致无法回收。解决办法时在Activity销毁的时候将静态View设置为null(View一旦被加载到界面上会持有一个Context对象的引用,这里就是Activity。)
-
WebView导致的内存泄漏
WebView只要使用一次,内存就不会被释放,所以WebView都存在内存泄漏的问题。通常的解决办法时为WebView单独打开一个进程,使用AIDL进行通信,根据业务需求在合适的时机释放掉。 -
资源对象未关闭
-
集合中的对象未清理
-
Bitmap内存泄漏
避免静态变量持有大的Bitmap对象。
-
监听器未关闭
很多需要register和unregister的系统服务要在合适的时候进行unregister,手动添加的listener也要及时移除。
检测方法:
- LeakCanary
OOM (out of memory)
冷启动&&热启动
性能优化
UI优化
其它
Context
1. 谈谈你对Android中Context的理解?
Context:包含上下文信息的一个参数。Android 中的 Context 分三种:
-
Application Context
-
Activity Context
-
Service Context
它描述的是一个应用程序环境的信息, 通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作, 例如: 启动一个 Activity, 发送广播, 接受 Intent 信息等。
Application
权限
Android中的强、弱、软、虚应用
LruCache && DiskLruCache
第三方库
OkHttp
Glide
1. 说说View的绘制流程
- Measure过程:View的绘制其实就是一个深度遍历的过程。这里有一个MeasureSpec的概念,每一个View都有一个MeasureSpec,它由父View根据自己的MeasureSpec和子View的LayoutParam共同决定。MeasureSpec有三个取值:
- MeasureSpec. UNSPECIFIED:不限制View大小
- MeasureSpec.EXACTLY:
- MeasureSpec.AT_MOST:
- Layout过程:
- Draw过程:
- Draw the background
- If necessary, save the canvas' layers to prepare for fading
- Draw view's content
- Draw children
- If necessary, draw the fading edges and restore layers
- Draw decorations (scrollbars for instance)
2. 说说Activity的启动模式
启动模式涉及到一个Task的概念,Task说的是为了完成一个任务的一系列的Activity的集合,这些Activity可以是来自不同的应用的。比如一个应用需要写一封邮件,它用Intent去打开了邮件这个应用的写邮件的MailActivity,这个Activity就可以和其它Activity同属一个Task。
启动模式有四种:
- standard:
- singleTask:
- singleTop:
- singleInstance:
标记位有三个:
- FLAG_ACTIVITY_NEW_TASK:
- FLAG_ACTIVITY_SINGLE_TOP:
- FLAG_ACTIVITY_CLEAR_TOP:
标记位优先级>启动模式
3. 说说Activity的生命周期
- onCreate-->onStart()-->onResume()-->onPause()-->onStop()-->onDestrory()
-->onRestart() - 正常关闭情况下onSaveInstanceState() 以及 onRestoreInstanceState()这两个方法是不会被调用的。它们只会在Activity不正常关闭下调用。我们说的视图恢复就是指的不正常关闭时候,Activity再次打开的时候要让用户感觉到Activity并没有被关闭。
- 建议onStop()中保存数据
4. 说说View事件分发
手指在屏幕的动作被抽象成了MotionEvent对象,根据不同情境分成了MotionEvent.ACTION_DOWN、MotionEvent.ACTION_POINTER_DOWN、MotionEvent.ACTION_MOVE、MotionEvent.ACTION_UP、MotionEvent.ACTON_CANCLE。事件分发主要就是当ACTION_DOWN以及ACTION_POINTER_DOWN两个事件到来时寻找新的TouchTarget的过程。主要有三个大的方法参与这个过程:
- dispatchTouchEvent()
- onInterceptTouchEvent() (ViewGroup独有)
- onTouchEvent()
其中dispatchTouchEvent负责寻找到一个新的TouchTarget,,并把事件交给TouchTarget。onInterceptTouchEvent()指示当前ViewGroup是否拦截此次事件。onTouchEvent()处理事件,它的返回代表当前事件是否被这个View消耗。
onTouch()--> onTouchEvent()-->onClick()