Android实战开发Handler机制深度解析

本文为自己多年来在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),优先级最低。

  

©️2020 CSDN 皮肤主题: 撸撸猫 设计师: 设计师小姐姐 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值