Android面试相关

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

2019-04-16  本文已影响6人  门心叼龙

本文为自己多年来在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.异步消息处理线程都会涉及到哪些类?架构图?

Hanlder中有Looper
Looper中有MessageQueue
MessageQueue中有Message

image image.gif

3.Handler实现消息的异步收发流程?

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;
}
image.gif
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;
}
image.gif
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();
        }
    }
}
image.gif
Message msg = queue.next();// might block
image.gif
publicvoid dispatchMessage(Message msg){
    if(msg.callback != null){
        handleCallback(msg);
    }else{
        if(mCallback != null){
            if(mCallback.handleMessage(msg)){
                return;
            }
        }
        handleMessage(msg);
    }
}
image.gif
mHandler = new Handler(){
      publicvoid handleMessage(Message msg){
     // process incoming messages here
  }
};
image.gif image image.gif

4.Handler使用场景?

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();
    }
}
image.gif

程序崩溃了,原因如下:

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;
}
image.gif

为什么在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");
}
image.gif

6.子线程中进行UI操作的其他方法

另外除了发送消息之外,我们还有以下几种方法可以在子线程中进行UI操作,本质都是调用了Handler Post方法

7.什么是消息发送所导致的内存泄漏?如果解决?

在Activity还没有收到消息的情况下就已经关闭了Activity,此时Message中携带了Handler的引用,而Hanlder中又隐式的持有了Activity的引用,垃圾回收机制发现Activity还在被引用着,此时该Activity就不会被销毁,Activity所占用内存了就成了垃圾内存,此时就造成了内存泄漏

@Override
publicvoid onDestroy(){
        mHandler.removeMessages(MESSAGE_1);
        mHandler.removeCallbacks(mRunnable);
        Handler.removeCallbacksAndMessages(null);
}   
image.gif
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();
        }
    }
image.gif

8. 一个标准的异步消息处理线程应该怎么写?

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();
      }
}
image.gif
  // 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());
        }
    });
image.gif

9.Android UI主线程为什么要用Loop.loop死循环?

这个死循环会不会卡死UI主线程?既然是死循环又怎么去处理其他事务呢?

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,流程如下?

image image.gif

13. 消息分发的优先级?

上一篇 下一篇

猜你喜欢

热点阅读