BroadcastReceiver
hasn't finished executing within 10 seconds.如果输入事件(点击或触屏)在5秒之内没有响应,BroadcastReceiver在10秒之内没有执行完毕,应用程序就会报ANR错误,用户只能选择等待或强制关闭,这种用户体验是非常差的。
主线程(main thread/UI thread),它有一个消息队列(message queue),如果屏幕上发生了点击/触屏事件就会把它转化为一个消息(message)放到消息队列里,由looper不断地从message queue里取出消息派送给相应的handler进行处理。而如果某个消息执行时间非常耗时,就会阻碍其他消息的处理,如果该条消息在5秒之内没有得到响应的话,就会报ANR。
避免ANR的方法是创建子线程,由子线程来完成耗时任务。例如点击某条新闻查看新闻详情这一事件,它的实现过程是用户点击了某条新闻,这个点击事件会通过网络请求去获取服务器端的手机接口数据(耗时操作),获取到的新闻详情的数据内容会显示到新闻详情页面中(更新用户界面)。由子线程完成耗时任务,再由子线程处理返回结果来更新界面,这种做法是错误的,因为应用只允许在主线程里对UI做修改,子线程没有权利对用户界面做任何修改。因此子线程处理完耗时操作,其获取到修改UI的内容需要在主线程里做处理。
之前已经提到创建子线程处理耗时任务,那么怎样将子线程获取到的数据放到主线程中处理呢?这里就需要Handler+Message来做主线程和子线程之间的沟通。整个的异步消息处理的步骤如下:
1. 在main thread里创建worker thread来异步执行耗时任务
2.在main thread里创建Handler,并让worker thread持有handler的引用
3.将worker thread执行结束后,创建Message,将获取到的数据结果存放到message中
4.由worker thread持有的的handler引用将message发送出去
5.main thread中的handler接受到message并处理消息,UI更新
/**
* 点击按钮,创建子线程
*/
private void excuteLongTimeOperation() {
Thread workerThread = new Thread(new MyNewThread());
workerThread.start();
}
class MyNewThread implements Runnable{
@Override
public void run() {
//执行耗时操作
ThreadUtil.logThreadSignature();
}
}
/**
* 点击按钮,创建子线程
*/
private void excuteLongTimeOperation() {
Thread workerThread = new MyNewThread();
workerThread.start();
}
public class MyNewThread extends Thread{
@Override
public void run() {
super.run();
}
}
/**
* 点击按钮,创建子线程
*/
private void excuteLongTimeOperation() {
new Thread(){
@Override
public void run() {
super.run();
//执行耗时操作
}
}.start();
}
package com.aliao.myandroiddemo.view.handler;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.aliao.myandroiddemo.R;
import com.aliao.myandroiddemo.utils.ThreadUtil;
/**
* Created by liaolishuang on 14-4-9.
*/
public class TestHandlerActivity extends Activity implements View.OnClickListener{
private TextView didSthTxt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
//打印当前线程的部分信息
ThreadUtil.logThreadSignature();
Button anrBtn = (Button) findViewById(R.id.btn_createthread);
anrBtn.setOnClickListener(this);
didSthTxt = (TextView) findViewById(R.id.tv_showsth);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_createthread:
excuteLongTimeOperation();
break;
}
}
/**
* 点击按钮,创建子线程
*/
private void excuteLongTimeOperation() {
Thread workerThread = new Thread(new MyNewThread());
workerThread.start();
}
class MyNewThread implements Runnable{
@Override
public void run() {
//打印子线程的部分信息
ThreadUtil.logThreadSignature();
//执行耗时操作
}
}
}
我们在onCreate()中调用ThreadUtil.logThreadSignature();执行上面的代码后会打印出当前线程的信息:04-11 18:40:55.529 21410-21410/com.aliao.myandroiddemo D/ThreadUtils﹕ main:(id)1:(priority)5:(group)main
04-11 18:41:02.019 21410-22024/com.aliao.myandroiddemo D/ThreadUtils﹕ Thread-74777:(id)74777:(priority)5:(group)main
子线程名是Thread-74777,id是74777
Handler handler = new Handler();
Handler类中的构造函数public Handler()会做一些准备工作,先看下源码:
package android.os;
import android.util.Log;
import android.util.Printer;
import java.lang.reflect.Modifier;
public class Handler {
private static final boolean FIND_POTENTIAL_LEAKS = false;
private static final String TAG = "Handler";
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//省略其他代码....
}
构造函数handler(Callback callback, boolean async){}中该句代码:
mLooper = Looper.myLooper();
继续查看Looper.myLooper() /**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
myLooper返回的是与当前线程关联的looper。Handler与当前线程的Looper相关联。
package android.os;
import android.util.Log;
import android.util.Printer;
import android.util.PrefixPrinter;
public final class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;//在looper内部维护了一个消息队列
final Thread mThread;//当前线程
private Printer mLogging;
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//每个线程只能创建一个Looper,如果视图再次创建,报异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建looper,并把该looper和当前线程关联在一起。取出looper用对应sThreadLocal.get();方法
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
//创建了一个Looper,也就创建了一个消息队列
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
/**
* 主线程所关联的looper(application's main looper)由应用程序自动创建
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/** 返回运行在主线程中的looper—— the application's main looper
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
/**
* 返回当前线程关联的looper对象,如果调用的thread没有关联一个looper,返回null
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();//得到当前looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//得到当前lopper关联的MessageQueue
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {//循环体
//从消息队列中取出消息
Message msg = queue.next(); // might block
//如果消息为空,就退出循环
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//非常关键的一句代码:将处理消息的工作交给msg.target即handler
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
/**
* Quits the looper.
*/
public void quit() {
mQueue.quit(false);
}
/**
* Quits the looper safely.
*/
public void quitSafely() {
mQueue.quit(true);
}
//省略其他代码
}
在一个线程中,looper用来跑一个消息循环。默认情况线程并没有关联一个消息循环。为一个消息创建消息循环必须在线程中调用prepare()方法去创建一个looper来运行消息循环,然后调用loop()方法来启动循环,直到loop被停止。并且消息循环的大部分交互都是通过Handler类来进行。源码中给了一个例子,自己查看吧。应用程序会为主线程自动创建关联的looper。
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage(msg)这个方式是用来分工的。handler通过以下两种方式发送消息:handler.post(runnable)以及handler.sendMessage(msg)。其中参数一个是runnable类型的,一个是Message类型的,所以如果msg.callback != null(在Message类中定义了Runnable callback;变量)即如果该消息是Runnable类型的话就交给handleCallback(msg)去处理。否则消息类型是一个纯粹的Message类型,就交给handleMessage(msg)去处理。之所以说纯粹是因为,post(runnable),最终还是会把runnable对象"包装"成一个Message对象。
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调用sendMessage(msg):
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
两个方法均调用了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);
}
回头看在Handler的构造函数中有这样一句代码:mQueue = mLooper.mQueue;在sendMessageAtTime()中的引用的queue正式Looper中的message queue。作为参数传给了enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
之前留下一个问题:looper类中当消息被取出,怎么分发给"相应"的Handler?Message类中定义了target变量,其类型正式Handler。在该方法的源码中可以看出msg.target = this;是把当前handler与该handler发送的消息一 一对应起来,那么在取出消息的时候,也就可以通过msg.target获取到相对应的handler了。
private static void handleCallback(Message message) {
message.callback.run();
}
回调在代码中创建的Runnable类中的run方法。该实现Runnable的类作为Message的参数。 /**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
子类必须覆写handleMessage(msg)方法。
package com.aliao.myandroiddemo.view.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.aliao.myandroiddemo.R;
import com.aliao.myandroiddemo.utils.ThreadUtil;
/**
* Created by liaolishuang on 14-4-9.
*/
public class TestHandlerActivity extends Activity implements View.OnClickListener{
private TextView didSthTxt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
//打印当前线程的部分信息
ThreadUtil.logThreadSignature();
Button anrBtn = (Button) findViewById(R.id.btn_createthread);
anrBtn.setOnClickListener(this);
didSthTxt = (TextView) findViewById(R.id.tv_showsth);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_createthread:
excuteLongTimeOperation();
break;
}
}
/**
* 点击按钮,创建子线程
*/
private void excuteLongTimeOperation() {
Thread workerThread = new Thread(new MyNewThread());
workerThread.start();
}
class MyNewThread extends Thread{
@Override
public void run() {
//打印子线程的部分信息
ThreadUtil.logThreadSignature();
//模拟执行耗时操作
ThreadUtil.sleepForInSecs(5);
Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("message","界面内容已更新");
message.setData(bundle);
handler.sendMessage(message);
}
}
/**
* 以匿名类的形式创建handler
*/
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//修改界面中TextView中的内容
didSthTxt.setText(msg.getData().getString("message"));
}
};
}
public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new MyThread().start();
}
private class MyThread extends Thread{
@Override
public void run() {
super.run();
//do somthing
}
}
}
这段代码很平常也很简单,是我们经常使用的形式。我们思考一个问题:假设MyThread的run函数是一个很费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此。
由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时,MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。
有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重,Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。
这种线程导致的内存泄露问题应该如何解决呢?
第一、将线程的内部类,改为静态内部类。
第二、在线程内部采用弱引用保存Context引用。
解决的模型如下:
public abstract class WeakAsyncTask extends
AsyncTask {
protected WeakReference mTarget;
public WeakAsyncTask(WeakTarget target) {
mTarget = new WeakReference(target);
}
/** {@inheritDoc} */
@Override
protected final void onPreExecute() {
final WeakTarget target = mTarget.get();
if (target != null) {
this.onPreExecute(target);
}
}
/** {@inheritDoc} */
@Override
protected final Result doInBackground(Params... params) {
final WeakTarget target = mTarget.get();
if (target != null) {
return this.doInBackground(target, params);
} else {
return null;
}
}
/** {@inheritDoc} */
@Override
protected final void onPostExecute(Result result) {
final WeakTarget target = mTarget.get();
if (target != null) {
this.onPostExecute(target, result);
}
}
protected void onPreExecute(WeakTarget target) {
// No default action
}
protected abstract Result doInBackground(WeakTarget target, Params... params);
protected void onPostExecute(WeakTarget target, Result result) {
// No default action
}
}