Android多线程 Handler、Looper线程
AsyncTask异步任务抽象类android.os.AsyncTask相对来说更轻量级一些适用于简单的异步处理。特点轻量级异步任务工具类提供任务执行进度回调给UI线程。适用场景需要知晓任务执行进度多个任务串行执行。缺点生命周期与宿主不同步可能造成内存泄漏默认串行执行任务。三种执行方式子类复写方法可计算进度时AsyncTask.execute静态方法串行执行不需要进度。executeOnExecutor中使用THREAD_POOL_EXECUTOR并发线程池来并发执行或使用AsyncTask.THREAD_POOL_EXECUTOR.execute静态方法并发执行。使用AsyncTask时必须遵守如下规则必须在UI线程中创建AsyncTask的实例以及调用AsyncTask的execute()方法。AsyncTask 的 onPreExecute()、onPostExecute(Result)、doInBackground (Params...params)、onProgressUpdate(Progress...values)方法不应该主动调用而是由Android系统调用。每个AsyncTask只能被执行一次多次调用将会引发异常。HandlerHandler是Android操作系统中的线程间通信工具它主要由两个作用(1)安排消息或Runnable 在主线程中某个地方执行。(2)安排一个动作在另外的线程中执行。每个Handler对象维护两个队列(FIFO)消息队列和Runnable队列 都是由Android操作系统提供的。Handler可以通过这两个队列来分别发送、接受、处理消息–消息队列启动、结束、休眠线程–Runnable队列Handler的使用方法大体分为3个步骤1.创建Handler对象。2.创建Runnable和消息。3.调用post以及sendMessage方法将Runnable和消息添加到队列。Android的线程异步处理机制Handler对象维护一个线程队列有新的Runnable送来post()的时候,把它放在队尾而处理 Runnable的时候从队头取出Runnable执行。当向队列发送一个Runnable后立即就返回并不理会Runnable是否被执行执行 是否成功等。而具体的执行则是当排队排到该Runnable后系统拿来执行的。1. Handler与UI同线程时可用于在子线程中通知主线程更新。诞生一个主线程的Handler物件当做Listener去让子线程能将讯息Push到主线程的Message Quene里以便触发主线程的handlerMessage函数让主线程知道子线程的状态并在主线程更新UI。例如在子线程的状态发生变化时我们需要更新UI。如果在子线程中直接更新UI,通常会抛出下面的异常ERROR/JavaBinder(1029):android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.意思是无法在子线程中更新UI。为此需要通过Handler通知主线程Ui Thread来更新界面。2. 在Android的开发中在非UI线程中不能操作UI线程中的控件即UI是非线程安全的例如在非UI线程中调用setBackGround(int color)或者setVisibility(int v)时程序运行后出现异常。但是在工作线程非UI线程中调用android.widget.ProgressBar控件的setProgress(int count)方法时程序运行并不会出现异常在大多数应用程序中我们通常会有一个UI线程即Android的UI Looper线程还有一个通讯线程负责和服务器端的交互。因为ProgressBar控件属于非UI线程的变量它为非UI线程所有不存在多线程共享数据的问题而在非UI线程中调用setBackGround(int color)等方法就涉及到调用UI线程的Context等变量的问题这样就存在线程安全的隐患。异步不一定要切换线程关键是要切换函数调用栈handler.post、handler.send也是异步调用虽然没有切换线程但它同样因为调用栈的切换而一定程度上影响了我们对于逻辑执行的把控。也就是说handler的执行体执行会晚于handler调用语句之后的语句并且执行于handler对应的looper线程中looper线程也可能就是调用所在的线程。异步不一定要切换线程关键是要切换函数调用栈。异步与切换线程的关系一旦线程发生切换那么函数调用栈必然切换自然就产生了异步操作而异步不一定需要切换线程因此切换线程是异步的充分不必要条件。Looper一个简单的 main 函数执行完毕后整个进程也就结束了为了让一个进程长时间的运行下去就需要无限循环加事件通知的机制。一个典型的Looper线程示例// 典型的关于Handler/Looper的线程 class LooperThread extends Thread { public Handler mHandler; public void run() { //为当前线程初始化Looper Looper.prepare(); mHandler new Handler() { public void handleMessage(Message msg) { //定义消息处理逻辑. Message msg Message.obtain(); } }; //启动Looper Looper.loop(); } } // 其他线程向此looper线程发送一个消息 looperThread.mHandler.sendEmptyMessage(0);使用上来说在Looper 线程中调用Looper.prepare()初始化 Looper初始化一个 Handler 对象Looper .loop() 进入循环休眠状态在其他线程中获取到 Looper 线程中的 Handler 对象构建一个 Message 对象调用 Handler 对象的 sendMessage 发送消息给 LooperHandlerThread特点android.os.HandlerThread线程类具有消息处理能力内部创建了Looper消息循环实现线程间通信适用于持续性任务如轮询。场景适用于子线程持续性任务并且需要和主线程之间通信。使用方式创建HandlerThread并start它会轮询内部的消息队列。创建静态Handler绑定HandlerThread的Looper实现消息处理。通过Handler发送消息。缺点因为持续性运行所以不会自动销毁资源需手动调用quit/quitSafely可能造成内存泄漏。Android 线程优先级Android优先级范围通过android.os.Process.setThreadPriority()可设置更精细的优先级(−20∼19)值越小优先级越高UI线程默认为-10。通过setThreadPriority设置的优先级和通过thread.setPriority设置的优先级是隔离的。JDK限制Thread.setPriority()设置效果不明显且超出[1∼10]范围会抛出IllegalArgumentException。Android优化机制Process.setThreadPriority()对线程调度有明显影响设计目的是让UI线程获取更多CPU时间片提高响应速度。作用域隔离两种设置方式互不影响Process.setThreadPriority()不会改变Thread.getPriority()的返回值。