《Android高性能编程》-多线程

《Android高性能编程》-多线程

2018, Apr 22    

预览

线程基础

Thread.run()方法并不会创建一个新线程,start()才能开启新线程
Thread.join()方法可以在它的子线程完成时恢复执行状态,在不知道要执行多久时可以调用。(一般情况不会恢复么,或者有没有回调什么的)
线程安全:一个多线程应用程序再共享对象上没有什么并发操作。
每个对象都有一个监视器,线程可以锁定或者解锁它,监视器确保同一时间只会被锁住一次。

Android多线程环境

进程的生命周期:Foreground, visible(非前台但可见), service, background, empty(不包含任何组件的进程,它被用作缓存的目的,以加快应用程序未来的恢复)
进程名以 开始代表为该应用的私有进程,以小写字母开始为可共享进程。

Android应用程序线程

UI线程:唯一的可以管理用户界面的线程,原因:1.因为ui不是线程安全的; 2. 加快ui,加锁和解锁操作消耗太大。
工作线程
binder线程:线程间通信。不需要直接处理这个线程,Android接口定义语言(AIDL)

Android线程消息

Message或Runnable;MessageQueue; looper;Handler
只有主线程有自己的Looper,如果需要让两个线程进行通信,必须指定looper对象(Looper.prepare())并创建MQ。
空的Handler构造器会与改Hander创建时所在线程的Looper关联起来,所以new Handler() 的实例化方法只能再主线程中进行(只有主线程有自己的Looper) 线程之间的消息传递,照书画的添加了说明

最佳实践

线程

在使用线程时,要避免在循环中使用同步,因为获取或释放锁的操作代价高昂

HandlerThread

handlerThread=Handle+Looper+MessageQueue onLooperPrepare方法 与传统线程不同,handlerthread是可以被重用的,一直处于活跃状态,知道handerthread.quit方法被调用。 当我们需要一个可供一直使用的线程时可以使用

AsyncTask

并不是一个线程框架,只是一个用于工作线程和ui线程之间通信的帮助类。 回调方法
onPreExecute() 启动后台之前
onProgressUpdate() ui线程,处理ui更新
onPostExecute() 处理从工作线程返回的结果 -> FINISHED
onCancelled()
状态:PENDING, RUNNING, FINISHED
Executor对象,执行类型
SERIAL_EXECUTOR(顺序依次执行一个)——> execute() THREAD_POOL_EXECUTOR(并发) executeOnExecutor() Executor对象,创建: newCachedThreadPool:数量不固定,检查当前有没有,没有创建并缓存一个
newFixedThreadPool:数量固定,同上
newScheduledThreadPool:按预定时间调度,用于执行定时任务和具有固定周期的重复任务
newSingleThreadExecutor:只有一个线程。用于将外界任务统一到一个线程池中,线程之间不需要处理线程同步问题。
newSingleThreadScheduledExecutor
用处:用来处理工作线程与ui的同步,如果不需要通知用户或处理ui,没必要使用。一般的Thread足够并且性能比它要好。如果参数全为空可以考虑不用。
注意:有时候存在ui不存在的情况,但仍然再执行,此时要考虑缓存结果。

Loader

适合异步操作,它是生命周期独立的,与activity或fragment的配置导致的销毁没有关系,仍然缓存结果通知最新创建的activity或fragment,使用的是application的context因此减少了activity泄漏风险。
LoaderManager:由id确认
**LoaderCallBack**:create,finish,reset 平台提供的Loader: **AsyncTaskLoader**:使用包装过的AsycnTask做后台工作,在任务完成后传数据到ui线程。在获取数据时使用 **CursorLoader**:AsyncTaskLoader的一个实现,专门用于从ContentProvider检索数据的工具 **何时使用**:不用担心activity或fragment的生命周期,并且为用户缓存数据

Service

它默认在ui线程中执行,所以永远不要启动一个长时间运行的操作
何时使用
不需要用户交互的操作可以用Service
处理多个并发请求时可以用Started Service,但用户需要设计自己的多线程策略
如果希望某个组件与Service直接通信,可以用BoundService,维持了两个绑定组件之间的独立性
当需要在单独线程中,顺序执行一系列后台操作时,可以用IntentService,不需要处理Service的生命周期,不影响UI

Started Service

startService或intent启动,直到调用stop的时候才会停止
startId
进程被销毁后告诉系统如何处理,返回值:
START_STICKY; 重建,此时发送的intent为空,因此在start时需要判断intent
START_NOT_STICK; 不重建
START_REDELIVER_INTENT; 被非stop的情况下被终止时,在被重启时,会受到销毁前的最后一个intent。需要知道什么操作导致中断时,可以用这个作为参数0 START_FLAG_REDELIVERY : 按返回策略重新递送,已经递送过
START_FLAG_READY: 在递送时被终止

BoundService

通过bind启动,只要绑定的外部组件存在,它就活跃,否则被销毁
持有一个对客户端的引用.
onBind只在绑定第一个客户端时被调用
unBind在最后一个客户端解绑时被调用

IntentService

包装了一个单独的后台线程,处理不同的请求,与intent相关,传入的放入一个队列,执行完Service会被自动销毁。只有后台线程在执行操作时它才是活跃状态。 用handlerInent来处理intent(不是startcommond),执行在后台线程中 默认为START_NOT_STICKY
不能被绑定

进程间通信

远程过程调用RPC

使得本地线程中的客户点成,能像调用本地方法一样,调用远程方法。
客户端(marshalling)-> Binder -> 服务端 (demarshaling)—> 客户端 数据要实现parcelable接口

AIDL

用的时候再说吧,标注在书里了

先进的技术

BroadcastReceiver异步技术

只在onreceive方法中活跃,用于接受消息,生命周期断,适合用于启动一个后台线程
延长生命周期:goAsync方法,返回pendingResult 会持续到pendingResult的finish方法调用。

ContentProvider 异步技术

用于再主要组件之间、进程之间、应用之间共享数据。目的是持有一个共享信息的数据库。不能被直接访问,要用contentresolver对象进行数据处理。
SQLite是带锁的
AsyncQueryHandler:包装了contentresolver,以便再ContentProvider上开启异步操作。它能将ui线程从不必要的操作中释放出来,让一个工作线程来处理contentresolver,提升ui性能。

重复性任务

Timer:周期性任务,适用于短时间周期性任务
ScheduledExecutorService:定时重复任务。executor框架的特殊实现,更强大。
AlarmManger:在某个特殊事件启动一个新组建,比其他类更高效,因为依赖于系统的闹铃服务,不适合短期任务。

调试工具

StrictMode