1.线程资源尽量通过线程池提供,不建议在应用中自行显式创建线程。
说明:这样的处理方式更加明确线程池的运行规则,规避资源耗尽的风险。
使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量 同类线程而导致消耗完内存或者“过度切换”的问题。
2.system_server进程中尽量不要启动新的Thread
说明:
公司手机上system_server中现有Thread已经很多,进程变的越来越庞大。如果自有业务都去增加Thread,system_server的负载会变的越来越重, 其实Google针对这个问题进行了很好的设计和思考,Android System _server引入了分类Thread(android.fg,android.bg,android.ui,androud.io)。
建议方案:
当你在system_server进程中计划启动HandlerThread或者Thread时候,看看能否复用已有的Thread进行。
参考代码:
PrintManagerService.java: BackgroundThread.getHandler().post(new Runnable())... LocationManagerService.java: mLocationHandler =new LocationWorkerHandler(BackgroundThread.get().getLooper());
检测工具: 暂无,后续可以用UT冒烟测试用例点检。
3.获取单例对象要线程安全,在单例对象里面做操作也要保证线程安全。
说明:
资源驱动类、工具类、单例工厂类都需要注意。
4.多线程中的资源需保持同步。
说明:
多线程读写全局变量,需要加锁;如果都是读,可以不加锁;对于多线程导致的空指针、数组越界问题, 建议将全局变量深度拷贝(clone为浅拷贝)到局部变量,然后判断使用该局部变量(对于集合类型不可以简单地使用“=”局部化,需要这样局局部化a = new set(b))。
案例:
(1)在应用的生命周期onPause里面有个子线程发送一个250ms的延时消息执行屏幕色彩相关的操作setDefaultMode(),其中包括对象 ColorManager的使用。在生命周期onDestroy的主线程release()里面释放了对象ColorManager。 从代码设计逻辑上看,是有较 明显的缺陷的:release()很可能比setDefaultMode先执行,从而报空指针,应用程序崩溃。 反思:1)对象的申请以及其他相关的操作跟对象的释放不在同一条线程中(初始化与释放逻辑不在同一线程,可能会出现释放出错情况)。 2)延时消息具有不稳定性,应慎用。 改进建议: 1)对象的申请以及其他相关的操作跟对象的释放需要在同一线程或者采用同步,以保证时序正确。 2)如果非得加延时,那么对象的释放操作也应该放在该延时里面进行。 (2)http://192.168.2.91:8000/rdms/qm/bug/bugAction.do?action=getBugDetail&id=1070294&popup=true 对关键的资源闪光灯进行操作没有做同步处理,导致重复操作闪光灯引起ANR。
5.避免子线程和主线程持有同一把锁执行耗时操作。
说明:
案例:系统用户界面死锁崩溃问题
1)原因分析:系统用户界面应用的RecentHelper中mRecentTaskItems的列表更新与主线程使用了同步锁。 2)解决方案:取消同步锁,在子线程中使用局部变量承载数据库列表,然后把局部变量值转至主线程更新全局变量列表。
6.避免主线程的耗时操作。
说明:
通常的耗时操作有:网络操作、IO操作、数据库操作、Binder调用、反射调用、Bitmap操作及频繁循环查询等; Application的onCreate、Service的onCreate&onBind&...、Receiver的onReceive等,都默认运行在主线程; 在主线程通过Settings.System.putInt写数据应减少调用次数,如加判断在需要时才执行,建议putInt异步执行; 避免应用主线程调用getPhoneStorageState()等可能由于系统原因引起阻塞的接口。
案例:VoLTE导致ANR
(1)原因分析:一方面,SIM卡状态变化、切换数据卡事件非常频繁;另一方面,在主线程查询短信中心号码,需要从modem中查询数据,较慢。 (2)反思:为什么发现不了这个问题? 1)开发阶段——人员、需求变更频繁,新的开发人员对模块不够熟悉,代码风格和可读性差,影响新的人员尽快熟悉代码。 2)DR阶段——评审时,主要关注需求是否满足,不够重视非功能特性(如性能等),没有充分评估广播的频繁度,代码执行时间。 3)测试阶段——BG ANR问题的隐蔽性。 4)跟踪阶段——云诊断数据突变很明显,但组内没有关注ANR的数据。
7.线程泄露
说明:
反例:相机线程泄露
1)原因分析:new AsyncTask<...>() {}.executeOnExecutor(Executors.newCachedThreadPool()); 因为newCachedThreadPool会一直创建不同的对象导致线程泄露。 2)解决方案:Executors.newCachedThreadPool()用静态常量代替,即换成AsyncTask.THREAD_POOL_EXECUTOR或者 定义一个static类型的对象Executors.newCachedThreadPool()作为参数传入进去: private static ExecutorService Cached_TASK_EXECUTOR = (ExecutorService) Executors.newCachedThreadPool();
8.CountDownLatch
说明:
使用CountDownLatch进行异步转同步操作,每个线程退出前必须调用countDown方法; 线程执行代码注意catch异常,确保countDown方法可以执行,避免主线程无法执行至countDown方法,直到超时才返回结果。 注意,子线程抛出异常堆栈,不能在主线程try-catch到。
9.HandlerThread在onDestroy时,要调用quit()退出
说明:
HandlerThread会创建一个线程,除非主动调用quit(),否则是不会退出的。
案例:相机中使用的HandleThread没有主动quit,引发内存泄漏。
CameraActivity.mInitializeThread,多次进入相机然后按返回键,CameraActivity来回多次执行onCreate--onDestoty可复现。