Looper、MessageQueue、Message、Handler、ThreadLocal 之间的关系

  • 1.发生ANR(Application No Responding)的原因?
    答:发生ANR的原因基本上是在主线程进行了耗时操作,并且此时界面有其他需要处理的请求。因为耗时操作还未完成此时执行的更新界面请求得不到响应所以才会ANR,并不是因为占用了cpu使得cpu满负荷。 如果在主线程进行了耗时操作但是此时并没有需要处理的请求则不会发生ANR。

    注:需要处理的请求,不一定只是用户的手动触摸,也有可能是其他线程需要在主线程进行UI更新的请求,这个时候UI线程正在被耗时操作占用,根本没有办法处理请求,这就符合了ANR的条件,所以会出现ANR(比如说在耗时操作未完成时,点击了 返回按钮,就会出现 ANR)

  • 2.为什么主线程的looper.loop()不会造成ANR?
    答:首先解释looper.loop()方法是什么,是一个无限循环轮询MessageQueue中是否有需要处理的数据的一个方法。解释1:为什么要无限循环?如果不无限循环,主线程执行完成之后就直接结束了,APP根本不可能一直运行。解释2:为什么不会造成ANR?因为造成ANR的原因是  在looper.loop()中获取存在MessageQueue中的消息并进行处理时,进行了耗时操作导致不能响应后续需要处理的消息。MessageQueue中处理的不止是在线程中 创建的handler发送的消息,还有activity的oncreate、onstart。onresume方法的调用,所以在activity中这些方法也尽量不要进行耗时操作否则可能会造成ANR。解释3:为什么无限循环不会造成线程阻塞?因为用到了Linux的epoll机制

   Linux的epoll机制:非阻塞式I/O多路复用,当主线程没有事情处理时,线程为阻塞状态(暂时放弃cpu使用权),有消息来时,唤醒当前线程,线程从阻塞状态到就绪状态再到运行状态;处理完成之后线程又处于阻塞状态。

  • 3.Looper(final) MessageQueue(final) Message(final) Handler之间有什么联系?

    答:handler所在的线程必须依赖looper才能工作,不然会报异常。handler对象持有当前线程的looper对象和MessageQueue对象looper中有一个ThreadLocal<Looper>确保每个线程有独立的唯一的looper对象。主线程因为在ActivityThread类的main方法中自己已经调用了looper.prepareMainLooper()和looper.loop()方法所以不需要手动调用。

    在子线程中如果需要新建handler必须先调用looper.prepare()和looper.loop()否则会报异常。looper的构造方法中自己新建了MessageQueue对象,在looper.loop()方法中通过for循环不断轮询MessageQueue中是否有需要处理的消息; 如果没有继续轮询,如果有交给Message中的target对象也就是发送该消息的handler的dispatchMessage方法处理,如果handler中的callback为空则回调handleMessage方法,否则callback自己处理。handler通过sendMessage,在enqueueMessage中通过持有的messageQueue对象调用messageQueue的enqueueMessage方法,将当前需要发送的消息加入MessageQueue队列中。由looper轮询到消息之后再发送到handler的方法中(callback或handMessage)

ThreadLocal:确保多个线程中持有同一个变量但是值不同。

发送该消息的handler:一个线程中可以创建多个handler,怎么区分发送的Message属于哪个handler呢?就是通过该Message的target变量也就是发送这个Message的handler。

MessageQueue中怎么处理及时消息和延时消息?通过与当前时间比对,不断的把触发事件排后的放到队列后面,每次轮询时都判断是否到达触发该事件的时间。

  •  4.在子线程可以通过handler发送消息给主线程,在主线程是否能通过handler发送消息给子线程?
    答:可以,以下是错误示范。因为MyThread是异步执行,所以执行到new Handler(myThread.looper)时,myThread.looper还未赋值。
  • public class TestActivity extends Activity {
        private Handler handler;
    
        private MyThread myThread;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
            myThread = new MyThread();
            myThread.start();
            handler = new Handler(myThread.looper) {
                @Override
                public void handleMessage(Message msg) {
                    System.out.println(msg.toString() + "'XX");
                }
            };
            handler.sendEmptyMessage(1);
            System.out.println("thread name " + myThread.getId() + ";" + myThread.getName());
            System.out.println("main thread name " + Thread.currentThread().getId() + ";" + Thread.currentThread().getName());
    
        }
    
        class MyThread extends Thread {
            private Looper looper;
    
            @Override
            public void run() {
                Looper.prepare();
                looper = Looper.myLooper();
                Looper.loop();
            }
        }
    }

    可以直接使用android提供的HandlerThread类

public class TestActivity extends Activity {
    private Handler handler;

    private HandlerThread myThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        myThread = new HandlerThread("1");
        myThread.start();
        handler = new Handler(myThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                System.out.println(msg.toString() + "'XX");
            }
        };
        handler.sendEmptyMessage(1);
        System.out.println("thread name " + myThread.getId() + ";" + myThread.getName());
        System.out.println("main thread name " + Thread.currentThread().getId() + ";" + Thread.currentThread().getName());

    }
}
  • 5.更新UI的几种方式
    其内部都是通过handler来发送更新ui的消息
handler.post(new Runnable() {
           @Override
           public void run() {

           }
       });

        view.post(new Runnable() {
            @Override
            public void run() {
                
            }
        });
        
        handler.sendEmptyMessage(1);
        
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                
            }
        });
  • 6.子线程真的不能更新ui吗?
    ViewRootImpl

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

展开阅读全文

没有更多推荐了,返回首页