本文为自己多年来在Android实战开发过程中总结归纳的一些常见问题,现在分享出来希望对初学者有所帮助。
本文出自门心叼龙的博客,转载请注明出处: https://blog.csdn.net/geduo_83/article/details/86560330
目录
[1.异步消息处理线程存在的意义?]
[2.异步消息处理线程都会涉及到哪些类?架构图?]
[3.Handler实现消息的异步收发流程?]
[4.Handler使用场景?]
[5.为什么在子线程中直接实例化一个Hanlder会导致程序崩溃?而再UI主线程则不会?]
[6.子线程中进行UI操作的其他方法]
[7.什么是消息发送所导致的内存泄漏?如果解决?]
[8. 一个标准的异步消息处理线程应该怎么写?]
[9.Android UI主线程为什么要用Loop.loop死循环?]
[10. ActivityThread是线程吗?]
[11.主线程的死循环一直运行是不是特别消耗CPU资源呢?]
[12. 结合图说说Activity生命周期,比如暂停Activity,流程如下?]
[13. 消息分发的优先级?]
1.异步消息处理线程存在的意义?
通过异步消息处理机制实现同一进程间不同线程间的通信,Android有大量的通过消息驱动方式来进行交互,比如Android的四剑客Activity, Service, Broadcast, ContentProvider 的启动过程的交互,都离不开消息机制,Android某种意义上也可以说成是一个以消息驱动的系统
2.异步消息处理线程都会涉及到哪些类?架构图?
- 2.1 Message:消息实体
- 2.2 MessageQueue:消息队列存储消息
- 2.3 Looper:消息循环器,处理消息
- 2.4 Handler:消息收发器
Hanlder中有Looper
Looper中有MessageQueue
MessageQueue中有Message
3.Handler实现消息的异步收发流程?
- 3.1 消息发送【Handler.sendMessage】
public boolean sendMessageAtTime(Message msg,long uptimeMillis){
boolean sent =false;
MessageQueuequeue= mQueue;
if(queue!= null){
msg.target =this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else{
RuntimeException e =new RuntimeException(
this+" sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
- 3.2 消息入列【MessageQueue.enqueueMessage】
final boolean enqueueMessage(Message msg,long when){
if(msg.when !=0){
throw new AndroidRuntimeException(msg +" This message is already in use.");
}
if(msg.target == null &&!mQuitAllowed){
throw new RuntimeException("Main thread not allowed to quit");
}
synchronized (this){
if(mQuiting){
RuntimeException e = new RuntimeException(msg.target +" sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
returnfalse;
}elseif(msg.target == null){
mQuiting =true;
}
msg.when = when;
Message p = mMessages;
if(p == null || when ==0|| when < p.when){
msg.next = p;
mMessages = msg;
this.notify();
}else{
Message prev = null;
while(p != null && p.when <= when){
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
returntrue;
}
- 3.3 消息循环器取消息【Loop.loop】
publicstatic final void loop(){
Looper me = myLooper();
MessageQueue queue= me.mQueue;
while(true){
Message msg =queue.next();// might block
if(msg != null){
if(msg.target == null){
return;
}
if(me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to "+ msg.target +" "
+ msg.callback +": "+ msg.what
);
msg.target.dispatchMessage(msg);
if(me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to "+ msg.target +" "
+ msg.callback);
msg.recycle();
}
}
}
- 3.4 消息出列【MessageQueue.next】
Message msg = queue.next();// might block
- 3.5 消息分发【Handler.dispatchMessage】
publicvoid dispatchMessage(Message msg){
if(msg.callback != null){
handleCallback(msg);
}else{
if(mCallback != null){
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg);
}
}
- 3.6 消息处理【Handler.handlerMessage】
mHandler = new Handler(){
publicvoid handleMessage(Message msg){
// process incoming messages here
}
};
4.Handler使用场景?
- 4.1 定时器
- 4.2 子线程向主线程发消息
- 4.3 主线程向子线程发消息
- 4.4 子线程向子线程发消息
5.为什么在子线程中直接实例化一个Hanlder会导致程序崩溃?而再UI主线程则不会?
因为在子线程实例化Hanlder的时候会去判断Loop为不为空,如果有空就直接抛出异常了,UI主线程中直接直接实例化Hanlder是因为,在ActivityThread的main函数中已经创建了Loop对象
public class MainActivity extends Activity {
private Handler handler1;
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler1 = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
handler2 = new Handler();
}
}).start();
}
}
程序崩溃了,原因如下:
public Handler(){
if(FIND_POTENTIAL_LEAKS){
...
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 = null;
}
为什么在activity中创建的Handler时,并没有创建Looper对象,而程序没有崩溃,原因如下:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(newEventLoggingReporter());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
MainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
6.子线程中进行UI操作的其他方法
另外除了发送消息之外,我们还有以下几种方法可以在子线程中进行UI操作,本质都是调用了Handler Post方法
- 6.1 Handler的post()方法
- 6.2 View的post()方法
- 6.3 Activity的runOnUiThread()方法
7.什么是消息发送所导致的内存泄漏?如果解决?
在Activity还没有收到消息的情况下就已经关闭了Activity,此时Message中携带了Handler的引用,而Hanlder中又隐式的持有了Activity的引用,垃圾回收机制发现Activity还在被引用着,此时该Activity就不会被销毁,Activity所占用内存了就成了垃圾内存,此时就造成了内存泄漏
- 7.1 使用清除消息进行解决
@Override
publicvoid onDestroy(){
mHandler.removeMessages(MESSAGE_1);
mHandler.removeCallbacks(mRunnable);
Handler.removeCallbacksAndMessages(null);
}
- 7.2 使用弱引用解决
public class HandlerActivity2 extends Activity{
private final Handler mHandler = new MyHandler(this);
@Override
publicvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendMessageDelayed(Message.obtain(),60000);
// just finish this activity
finish();
}
publicvoid todo(){
};
private static class MyHandler extends Handler{
private final WeakReference<HandlerActivity2> mActivity;
public MyHandler(HandlerActivity2 activity){
mActivity = new WeakReference<HandlerActivity2>(activity);
}
@Override
publicvoid handleMessage(Message msg){
System.out.println(msg);
if(mActivity.get()== null){
return;
}
mActivity.get().todo();
}
}
8. 一个标准的异步消息处理线程应该怎么写?
- 方法1:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
- 方法2:
// Step 1: 创建并启动HandlerThread线程,内部包含Looper
HandlerThread handlerThread = new HandlerThread("gityuan.com");
handlerThread.start();
// Step 2: 创建Handler
Handler handler = new Handler(handlerThread.getLooper()) {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
// Step 3: 发送消息
handler.post(new Runnable() {
@Override
public void run() {
System.out.println("thread id="+Thread.currentThread().getId());
}
});
9.Android UI主线程为什么要用Loop.loop死循环?
这个死循环会不会卡死UI主线程?既然是死循环又怎么去处理其他事务呢?
- 9.1 对于UI主线程绝对不希望运行一段时间就自动退出,那就需要可执行代码一直执行下去,最简单的方法就是死循环
- 9.2 当然它不是简单的死循环,无消息会处于休眠状态,在onCreate、onStart,onResume里面有比较耗时的操作有可能会导致主线程卡死,Loop.loop并不会导致应用卡死
- 9.3 在UI主线程会创建Binder子线程去处理其他事务,thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程
10. ActivityThread是线程吗?
ActivityThread实际上并非线程,不像HandlerThread类,ActivityThread并没有真正继承Thread类,只是往往运行在主线程,该人以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程
11.主线程的死循环一直运行是不是特别消耗CPU资源呢?
其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,详情见Android消息机制1-Handler(Java层),此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
12. 结合图说说Activity生命周期,比如暂停Activity,流程如下?
- 12.1 线程1的AMS中调用线程2的ATP;(由于同一个进程的线程间资源共享,可以相互直接调用,但需要注意多线程并发问题)
- 12.2 线程2通过binder传输到App进程的线程4;
- 12.3 线程4通过handler消息机制,将暂停Activity的消息发送给主线程;
- 12.4 主线程在looper.loop()中循环遍历消息,当收到暂停Activity的消息时,便将消息分发给ActivityThread.H.handleMessage()方法,再经过方法的调用,最后便会调用到Activity.onPause(),当onPause()处理完后,继续循环loop下去。
13. 消息分发的优先级?
- Message的回调方法:message.callback.run(),优先级最高;
- Handler的回调方法:Handler.mCallback.handleMessage(msg),优先级仅次于1;
- Handler的默认方法:Handler.handleMessage(msg),优先级最低。