处理耗时操作的几种方法 ① AsyncTask 1. 定义
1 2 3 public abstract class AsyncTask <Params , Progress , Result > { ... }
2. 作用
3. 优点
方便实现异步通信 不需使用 “任务线程(如继承Thread
类) + Handler
”的复杂组合
节省资源 采用线程池的缓存线程 + 复用线程,避免了频繁创建 & 销毁线程所带来的系统资源开销
4. 使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public abstract class AsyncTask <Params , Progress , Result > { ... } }
核心方法
执行顺序
使用步骤
创建 AsyncTask
子类 & 根据需求实现核心方法
创建 AsyncTask
子类的实例对象(即 任务实例)
手动调用execute()
从而执行异步线程任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 private class MyTask extends AsyncTask <Params , Progress , Result > { .... @Override protected void onPreExecute () { ... } @Override protected String doInBackground (String... params) { ... publishProgress(count); } @Override protected void onProgressUpdate (Integer... progresses) { ... } @Override protected void onPostExecute (String result) { ... } @Override protected void onCancelled () { ... } } MyTask mTask = new MyTask(); mTask.execute();
示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 public class MainActivity extends AppCompatActivity { MyTask mTask; Button button,cancel; TextView text; ProgressBar progressBar; private class MyTask extends AsyncTask <String , Integer , String > { @Override protected void onPreExecute () { text.setText("加载中" ); } @Override protected String doInBackground (String... params) { try { int count = 0 ; int length = 1 ; while (count<99 ) { count += length; publishProgress(count); Thread.sleep(50 ); } }catch (InterruptedException e) { e.printStackTrace(); } return null ; } @Override protected void onProgressUpdate (Integer... progresses) { progressBar.setProgress(progresses[0 ]); text.setText("loading..." + progresses[0 ] + "%" ); } @Override protected void onPostExecute (String result) { text.setText("加载完毕" ); } @Override protected void onCancelled () { text.setText("已取消" ); progressBar.setProgress(0 ); } } @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.button); cancel = (Button) findViewById(R.id.cancel); text = (TextView) findViewById(R.id.text); progressBar = (ProgressBar) findViewById(R.id.progress_bar); mTask = new MyTask(); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { mTask.execute(); } }); cancel = (Button) findViewById(R.id.cancel); cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { mTask.cancel(true ); } }); } }
② Handle 1. 作用 传递信息Message、子线程通知主线程更新UI
2. 常用api 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Message message = Message.obtain(); new Handler().sendMessage(message); new Handler().sendMessageDelayed(message, 1000 ); new Handler().sendEmptyMessage(0x1 ); new Handler().sendEmptyMessageDelayed(0x1 , 1000 ); new Handler().sendMessageAtTime(message, SystemClock.uptimeMillis() + 1000 ); new Handler().removeMessages(0x1 ); new Handler().removeCallbacks(Runnable); new Handler().removeCallbacksAndMessages(null );
3. 简单使用 主线程中:首先是创建一个Handler对象,并重写handleMessage方法,然后需要消息通信的地方,通过Handler的sendMessage方法发送消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private Handler mHandler;..onCreate(..){ mHandler = new Handler() { @Override public void handleMessage (Message msg) { super .handleMessage(msg); } } }; btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { new Thread(new Runnable() { @Override public void run () { Message msg = Message.obtain(); msg.what = MSG_SUB_TO_MAIN; msg.obj = "这是一个来自子线程的消息" ; mHandler.sendMessage(msg); } }).start(); } }); }
子线程中:
调用Looper.prepare()
创建Handler对象
调用Looper.loop()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 new Thread(new Runnable() { @Override public void run () { Looper.prepare(); subHandler = new Handler() { @Override public void handleMessage (Message msg) { super .handleMessage(msg); } }; Looper.loop(); } }).start();
4. handle机制
Handler机制,主要牵涉到的类有如下四个,它们分工明确,但又相互作用
Message:消息
Hanlder:消息的发起者
Looper:消息的遍历者
MessageQueue:消息队列
4.1 Looper.prepare() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void prepare () { prepare(true ); } private static void prepare (boolean quitAllowed) { if (sThreadLocal.get() != null ) { throw new RuntimeException("Only one Looper may be created per thread" ); } sThreadLocal.set(new Looper(quitAllowed)); } private Looper (boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
Looper.prepare()的作用主要有以下三点
创建Looper对象
创建MessageQueue对象,并让Looper对象持有
让Looper对象持有当前线程
4.2 new Handler() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public Handler () { this (null , false ); } public Handler (Callback callback, boolean async) { ...... mLooper = Looper.myLooper(); if (mLooper == null ) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()" ); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
默认的Handler的创建过程主要有以下几点
创建Handler对象
得到当前线程的Looper对象,并判断是否为空
让创建的Handler对象持有Looper、MessageQueue、Callback的引用
4.3 Looper.loop() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static void loop () { final Looper me = myLooper(); if (me == null ) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread." ); } final MessageQueue queue = me.mQueue; ...... for (;;) { Message msg = queue.next(); if (msg == null ) { return ; } msg.target.dispatchMessage(msg); msg.recycleUnchecked(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void dispatchMessage (Message msg) { if (msg.callback != null ) { handleCallback(msg); } else { if (mCallback != null ) { if (mCallback.handleMessage(msg)) { return ; } } handleMessage(msg); } }
可以看出,这个方法就是从MessageQueue中取出Message以后,进行分发处理。
首先,判断msg.callback是不是空,其实msg.callback是一个Runnable对象,是Handler.post方式传递进来的参数,后面会讲到。而hanldeCallback就是调用的Runnable的run方法。
然后,判断mCallback是否为空,这是一个Handler.Callback的接口类型,之前说了Handler有多个构造方法,可以提供设置Callback,如果这里不为空,则调用它的hanldeMessage方法,注意,这个方法有返回值,如果返回了true,表示已经处理 ,不再调用Handler的handleMessage方法;如果mCallback为空,或者不为空但是它的handleMessage返回了false,则会继续调用Handler的handleMessage方法,该方法就是我们经常重写的那个方法。
所以Looper.loop的作用就是:从当前线程的MessageQueue从不断取出Message,并调用其相关的回调方法。
4.4 发送信息 使用Handler发送消息主要有两种,一种是sendXXXMessage方式,还有一个postXXX方式,不过两种方式最后都会调用到sendMessageDelayed方法,所以我们就以最简单的sendMessage方法来分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public final boolean sendMessage (Message msg) { return sendMessageDelayed(msg, 0 ); } public final boolean sendMessageDelayed (Message msg, long delayMillis) { if (delayMillis < 0 ) { delayMillis = 0 ; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime (Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null ) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue" ); Log.w("Looper" , e.getMessage(), e); return false ; } return enqueueMessage(queue, msg, uptimeMillis); }
1 2 3 4 5 6 7 8 9 private boolean enqueueMessage (MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this ; if (mAsynchronous) { msg.setAsynchronous(true ); } return queue.enqueueMessage(msg, uptimeMillis); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 boolean enqueueMessage (Message msg, long when) { if (msg.target == null ) { throw new IllegalArgumentException("Message must have a target." ); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use." ); } synchronized (this ) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread" ); Log.w("MessageQueue" , e.getMessage(), e); msg.recycle(); return false ; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break ; } if (needWake && p.isAsynchronous()) { needWake = false ; } } msg.next = p; prev.next = msg; } if (needWake) { nativeWake(mPtr); } } return true ; }
以上是sendMessage的全部过程,其实就是把Message加入到MessageQueue的合适位置。那我们来简单看看post系列方法:
1 2 3 4 5 6 7 8 9 10 11 public final boolean post (Runnable r) { return sendMessageDelayed(getPostMessage(r), 0 ); } private static Message getPostMessage (Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
所以使用handler发送消息的本质都是:把Message加入到Handler中的MessageQueue中去。
4.5 总结 我们再来总结下Handler消息机制主要的四个类的功能
Message:信息的携带者,持有了Handler,存在MessageQueue中,一个线程可以有多个
Hanlder:消息的发起者,发送Message以及消息处理的回调实现,一个线程可以有多个Handler对象
Looper:消息的遍历者,从MessageQueue中循环取出Message进行处理,一个线程最多只有一个
MessageQueue:消息队列,存放了Handler发送的消息,供Looper循环取消息,一个线程最多只有一个
5. handle的一些问题
Android中,有哪些是基于Handler来实现通信的? 答:App的运行、更新UI、AsyncTask、Glide、RxJava等
处理Handler消息,是在哪个线程?一定是创建Handler的线程么? 答:创建Handler所使用的Looper所在的线程
消息是如何插入到MessageQueue中的? 答: 是根据when在MessageQueue中升序排序的,when=开机到现在的毫秒数+延时毫秒数
当MessageQueue没有消息时,它的next方法是阻塞的,会导致App ANR么? 答:不会导致App的ANR,是Linux的pipe机制保证的,阻塞时,线程挂起;需要时,唤醒线程
子线程中可以使用Toast么? 答:可以使用,但是Toast的显示是基于Handler实现的,所以需要先创建Looper,然后调用Looper.loop。
Looper.loop()是死循环,可以停止么? 答:可以停止,Looper提供了quit和quitSafely方法
Handler内存泄露怎么解决? 答: 静态内部类+弱引用 、Handler的removeCallbacksAndMessages等方法移除MessageQueue中的消息
6. handle内存泄露的处理 6.1 静态内部类+弱引用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class HandlerActivity extends AppCompatActivity { private static final String TAG = "HandlerActivity" ; private Handler mHandler; private Button btn; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_handler); mHandler = new MyHandler(HandlerActivity.this ); btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { mHandler.sendEmptyMessageDelayed(100 , 100 * 1000 ); } }); } static class MyHandler extends Handler { private WeakReference<Activity> activityWeakReference; public MyHandler (Activity activity) { activityWeakReference = new WeakReference<Activity>(activity); } @Override public void handleMessage (Message msg) { super .handleMessage(msg); if (activityWeakReference != null ) { Activity activity = activityWeakReference.get(); if (activity != null ) { } } } } }
首先,我们自定义了一个静态内部类MyHandler,然后创建MyHandler对象时传入当前Activity的对象,供Hander以弱应用的方式持有,这个时候Activity就被强引用和弱引用两种方式引用了,我们继续发起一个延时100s的消息,然后退出当前Activity,这个时候Activity的强引用就不存在了,只存在弱引用,gc运行时会回收掉只有弱引用的Activity,这样就不会造成内存泄漏了。
但这个延时消息还是存在于MessageQueue中,得到这个Message被取出时,还是会进行分发处理,只是这时候Activity被回收掉了,activity为null,不能再继续调用Activity的方法了。所以,其实这是Activity可以被回收了,而Handler、Message都不能被回收。
至于为什么使用弱引用而没有使用软引用,其实很简单,对比下两者回收前提条件就清楚了
弱引用(WeakReference): gc运行时,无论内存是否充足,只有弱引用的对象就会被回收
软引用(SoftReference): gc运行时,只有内存不足时,只有软引用的对象就会被回收
很明显,当我们Activity退出时,我们希望不管内存是否足够,都应该回收Activity对象,所以使用弱引用合适。
6.2 移除MessageQueue中的消息 我们知道,内存泄漏的源头是MessageQueue持有的Message持有了Handler持有了Activity,那我们在合适的地方把Message从MessageQueue中移除,不就可以解决内存泄漏了么?
Handler为我们提供了removeCallbacksAndMessages等方法用于移除消息,比如,在Activity的onDestroy中调用Handler的removeCallbacksAndMessages,代码如下:
1 2 3 4 5 6 @Override protected void onDestroy () { super .onDestroy(); mHandler.removeCallbacksAndMessages(null ); }
其实就是在Activity的onDestroy方法中调用mHandler.removeCallbacksAndMessages(null),这样就移除了MessageQueue中target为该mHandler的Message,因为MessageQueue没有引用该Handler发送的Message了,所以当Activity退出时,Message、Handler、Activity都是可回收的了,这样就能解决内存泄漏的问题了。
③ Thread 实现原理:
创建一个Thread对象,然后在其run方法中调用runOnUiThread方法,在run中执行耗时操作,在runOnUiThread方法执行耗时操作完成后需要更新的UI,不要忘记调用Thread的start方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 new Thread(new Runnable() { @Override public void run () { try { Thread.sleep(5000 ); } catch (InterruptedException e) { e.printStackTrace(); } TestActivity.this .runOnUiThread(new Runnable() { @Override public void run () { } }); } }).start();