android 重要核心知识点,怎么深刻理解都不为过,本篇博客从常用api ,Looper Hanldery以及HanlderTread源码角度解读
1 Handler使用
public class MainActivity extends Activity {
private final String TAG = "HandlerActivity.class";
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
bt.setText("正在下载...");
break;
case 2:
Bitmap bm = (Bitmap) msg.obj;
iv.setImageBitmap(bm);
break;
case 3:
Bundle bundle = msg.getData();
String data = bundle.getString("text");
bt.setText(data);
break;
case 4:
Log.d(TAG, "arg1------" + msg.arg1);
Log.d(TAG, "arg2------" + msg.arg2);
Person person = (Person) msg.obj;
if (person != null) {
Log.d(TAG, "person.age------" + person.getAge());
Log.d(TAG, "person.name------" + person.getName());
}
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
/**
* 传递简单Integer类型消息
*/
Message message = new Message();
message.arg1 = 1;
message.arg2 = 2;
/**
* 传递复杂消息
*/
Person person = new Person();
person.setAge(555);
person.setName("Liu");
message.obj = person;
mHandler.sendMessage(message);
Message msg = new Message();
msg.what = 1;//魔鬼数字,避免
handler.sendMessage(msg);
//或者
handler.sendMessageDelay(what);
//或者
handler.sendEmptyMessage(what);
//如果要带一个obj
Message msg = new Message();
msg.what = 2;
msg.obj = bm;
handler.sendMessage(msg);
//带一个bundle
Message msg = new Message();
Bundle data = new Bundle();
data.putString("text", "正在下载...");
msg.what = 3;
msg.setData(data);
handler.sendMessage(msg);
//通过obtainMessage能避免重复Message创建对象
handler.sendEmptyMessage(1);
handler.sendEmptyMessageDelayed(1, 1000);
handler.sendEmptyMessageAtTime(1, 1000);
handler.sendMessage(Message.obtain());
handler.sendMessageAtTime(Message.obtain(), 100);
handler.sendMessageDelayed(Message.obtain(), 100);
//获取Message的另一种方式,看下源码就好,很简单
Message message = handler.obtainMessage();
message.sendToTarget();
}
}).start();
}
}
post发送
mHandler.post(new Runnable()
{
@Override
public void run()
{
Log.e("TAG", Thread.currentThread().getName());
mTxt.setText("yoxi");
}
}); //并未创建线程,与handler同一个线程当然还有postDelayed方法
各种发送消息图解
2 原理分析
我们知道android UI线程(主线程)更新ui信息,为什么要这么做呢?
解决多线程并发问题,
- 加入一个activity有多个线程去更新ui,都没有枷锁,会导致 更新界面错乱
- 如果对更新ui的操作都进行加锁处理,会导致 性能下降
基于以上考虑 android给我们提供了一套更新ui的机制,我们只需要遵守这样的机制就可以了。不用关系多线程问题,因为所有更新ui的操作, 都是主线程的消息队列当中通过轮询处理的。
2.1 Handler 机制工作原理
handler要想起作用有三个步骤
- 创建Looper
- 创建Handler
- 调用Looper的loop方法,循环消息
先普及一个知识,整个应用是通过AcitivityThread类启动的,在ActivityThread类当中,负责创建所有的Activity,并回调Acitivity中的生命周期方法,在ActivityThread类中,默认会去创建一个线程,整个线程叫做main线程,主线程,ui线程,
主线程的创建Looper和调用loop方法的工作,android sdk已经为我们做好了,平时用的时候创建Hander并发送消息即可。
从ActivityThread的main方法开始看起。
sThreadLocal是一个ThreadLocal对象,**可以在一个线程中存储变量。**可以看到,将一个Looper的实例放入了ThreadLocal,并且判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例.
Looper构造器
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
在构造方法中,创建了一个MessageQueue(消息队列)。也就是说默认的情况下,android为我们自动创建了主线程的Looper和MessageQuene。
那么Handler怎么和我们的MessageQuene消息队列联系在一起的那?因为之前不是说handler发出的消息是发送到消息队列中了吗?
原因还要看我们在创建Handler的时候做了那些事情,跟进Handler初始化源码发现最终调用的是下面这个构造器创建实例的
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;
}
跟进Looper的myLooper方法
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
看到了什么?sThreadLocal是不是很熟悉,没错它就是ThreadLocal对象。默认情况下android为我们创建了主线程Looper对象并存储在sThreadLocal中,所以此处返回的就是主线程的Looper对象,也就是说我们在创建Handler的时候,它就和消息队列关联起来了。
那么当我们使用handler发送消息的时候,不管使用哪一种方法,一步一步跟进源码发现最终调用的都是Handler的sendMessageAtTime方法
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);
}
看代码可知,在发送消息的时候消息队列不能为null,继续跟进enqueueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,消息最终发送到了消息队列当中。那么消息是怎么轮训的那?前面已经提过,是通过Looper的loop方法,那么来看看吧!!!
可以看到loop方法里面的机制就是一个死循环,不断的从消息队列中取出消息,然后发送给target(handler对象)的dispatchMessage方法,跟进去!!!
一般情况下我们发送消息的时候没有给Message的callback赋值,所以第一个if条件不满足。下面的mCallback是在我们初始化Handler的时候才被初始化,Handler初始化有一种方法Handler(Callback callback),此处的参数就是给mCallback赋值的。我们一般初始化Handler的时候使用的是空参数的构造器,所以导致mCallback未被初始化,所以会直接走handleMessage(msg)方法,也就是我们初始化Handler时重写的handleMessage方法。至此,Handler工作的机制就开始工作了,你、了解了吗?
下面让我们看看如果我们选择的是带Callback参数的初始化方式逻辑又会是什么样那,请看初始化代码:
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.d(TAG,"callback参数-------handleMessage");
return true;//此处的返回值会影响下面的handleMessage方法是否调用
//false 调用
//true 不调用
} }){
@Override
public void handleMessage(Message msg) {
Log.d(TAG,"重写handler的-------handleMessage方法");
super.handleMessage(msg);
}
};
根据上面的源码分析我们知道此处Callback参数中的handleMessage方法的返回值会影响到下面第二个handleMessage方法是否调用。经过验证,return true 则不调用 ,return false则调用。
最会通过一张图,看一下Handler的原理:
3 handler处理消息一定要在主线程吗
new Thread() {
private Handler handler;
public void run() {
Looper.prepare();//注意此行代码
handler = new Handler() {
public void handleMessage(android.os.Message msg) {
Log.e("TAG", Thread.currentThread().getName());
}
};
}
Looper.loop();
}.start();
直接new Handler()默认为当前线程,如果用于刷新ui,Handler handler = new Handler(Looper.getMainLooper());也是可以的,不过毫无意义。
上一章介绍了Hander机制的工作原理,默认情况下Activity为我们创建了主线程的Looper和消息队列,所以在主线程创建Handler,发送消息的时候,消息的轮询和handle都是在主线程进行的。这种情况属于子线程给主线程发消息,通知主线程更新ui,那么反过来,怎么才能让主线程给子线程发消息,通知子线程做一些耗时操作。
之前学习,android消息机制三个步骤
- 创建当前线程Looper
- 创建当前线程的Handler
- 调用Looper的loop方法。
之所以强调当前线程,只因为上一章都是android为我们做好的,譬如创建主线程Looper,主线程消息队列。如果非主线程发送、处理消息应该怎么办?
public class ChildThreadHandlerActivity extends Activity {
private MyThread childThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
childThread = new MyThread();
childThread.start();
Handler childHandler = new Handler(childThread.childLooper){//这样之后,childHandler和childLooper就关联起来了。
public void handleMessage(Message msg) {
};
};
}
private class MyThread extends Thread{
public Looper childLooper;
@Override
public void run() {
Looper.prepare();//创建与当前线程相关的Looper
childLooper = Looper.myLooper();//获取当前线程的Looper对象
Looper.loop();//调用此方法,消息才会循环处理
}
}
}
代码如上,我们依然循序Android的三步走战略,完成了子线程Handler的创建,难道这样创建完了,就可以发消息了么?发的消息在什么线程处理?一系列的问题,怎么办?看代码!!!运行上述代码,我们发现一个问题,就是此代码一会崩溃、一会不崩溃,通过查看日志我们看到崩溃的原因是空指针。谁为空???查到是我们的Looper对象,怎么会那?我不是在子线程的run方法中初始化Looper对象了么?话是没错,但是你要知道,当你statr子线程的时候,虽然子线程的run方法得到执行,但是主线程中代码依然会向下执行,造成空指针的原因是当我们new Handler(childThread.childLooper)的时候,run方法中的Looper对象还没初始化。当然这种情况是随机的,所以造成偶现的崩溃。
那怎么办?难道我们不能创建子线程Handler ???No!!!No!!!No!!!,你能想到的Android早就为我们实现好了,HandlerThread类就是解决这个问题的关键所在,看代码!!!
public class HandlerThreadActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
TextView textView = (TextView) findViewById(R.id.tv);
textView.setText("HandlerThreadActivity.class");
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler mHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("HandlerThreadActivity.class","uiThread2------"+Thread.currentThread());//子线程
}
};
Log.d("HandlerThreadActivity.class","uiThread1------"+Thread.currentThread());//主线程
mHandler.sendEmptyMessage(1);
}
}
创建HandlerThread对象的时候,有个参数,是指定线程名字的。上面的代码不管运行多少次都不会奔溃!!!并且这种方法创建的handler的handleMessage方法运行在子线程中。所以我们可以在这里处理一些耗时的逻辑。到此我们完成了主线程给子线程发通知,在子线程做耗时逻辑的操作。
下面我们去看看源码,看看为什么使用HandlerThread就可以避免空指针那?
HandlerThread的getLooper方法
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
HandlerThread类的getLooper方法如上,我们看到当我们获取当前线程Looper对象的时候,会先判断当前线程是否存活,然后还要判断Looper对象是否为空,都满足之后才会返回给我Looper对象,否则处于等待状态!!既然有等待,那就有唤醒的时候,在那里那???我们发现HandlerThread的run方法中,有如下代码:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
HandlerThead常用场景,定时更新ui
public class HandlerThreadActivity extends Activity {
private TextView mTvServiceInfo;
private HandlerThread mCheckMsgThread;
private Handler mCheckMsgHandler;
private boolean isUpdateInfo;
private static final int MSG_UPDATE_INFO = 0x110;
//与UI线程管理的handler
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread_handler);
mTvServiceInfo = (TextView) findViewById(R.id.id_textview);
mCheckMsgThread = new HandlerThread("check-message-coming");
mCheckMsgThread.start();
mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
try {
//模拟耗时
Thread.sleep(1000);
mHandler.post(new Runnable() {
@Override
public void run() {
String result = "实时更新中,当前大盘指数:<font color='red'>%d</font>";
result = String.format(result, (int) (Math.random() * 3000 + 1000));
mTvServiceInfo.setText(Html.fromHtml(result));
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
if (isUpdateInfo) {
mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);
}
}
};
}
@Override
protected void onResume() {
super.onResume();
//开始查询
isUpdateInfo = true;
mCheckMsgHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
@Override
protected void onPause() {
super.onPause();
//停止查询
isUpdateInfo = false;
mCheckMsgHandler.removeMessages(MSG_UPDATE_INFO);
}
@Override
protected void onDestroy() {
super.onDestroy();
//释放资源
mCheckMsgThread.quit();
}
}
4 handler造成内存泄漏
public class MemoryLeakActivity extends Activity {
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
mHandler = new MyHandler();
mHandler.sendEmptyMessage(1);
}
private class MyHandler extends Handler{
public MyHandler(){
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
}
handler对象作为成员变量保存在发送的消息msg中,即msg持有handler的引用,而handler是activity的内部类实例,即handler持有acitivity的引用,那么可以理解为msg间接持有acitivyt的引用,msg被发送到下线队列msgqueue中,然后等待Looper的轮询处理,(msgqueue和looper都是线程相关联的,msgqueue是looper的成员变量,looper保存在ThreadLocal中)acitivity退出后 ,msg可能仍然存在消息队列中未处理,或者正在处理,导致内存泄漏。
public abstract class WeakHandler<T> extends Handler {
protected WeakReference<T> reference;
//创建子线程Handler使用的构造器
public WeakHandler(Looper looper, T reference) {
super(looper);
this.reference = new WeakReference<>(reference);
}
//创建主线程Handler使用的构造器
public WeakHandler(T reference) {
this.reference = new WeakReference<>(reference);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
T t = reference.get();
if (t == null)
return;
handleMessage(t, msg);
}
protected abstract void handleMessage(T t, Message message);
}