Android消息机制之一基础简介(1)

Android 3xcn.com@站长2018年5月13日 pm4:26发布3年前 (2021)更新
1
导航号,我的单页导航
目录

在项目中,使用Handler是比较多的,延迟处理信息啊,或者跨线程刷新UI界面啊等.用大家都会用,但要用好,或许只能多看看源码和跟大牛们学习学习了.

Handler.java,Looper.java,Message.java,MessageQueue.java这几个类主要是在/frameworks/base/core/java/android/os目录中.

各类的功能简介

  1. Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage)
  2. Looper:不断循环执行(Looper.loop),按分发机制将消息分发给目标处理者
  3. Message:消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息
  4. MessageQueue:消息队列的主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)

记住以上几个类都是相关联的,总有"千丝万缕"的关系!

项目中常用实例

实例一: 主线程中使用Handler(部分代码)

    private Handler mHandler= new Handler(){

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //处理消息
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //移除所有callback和message,并把mHandler设置为null
        if(null!=mHandler){
            mHandler.removeCallbacksAndMessages(null);
            mHandler= null;
        }
    }

只要写过Android代码都朋友,都是很熟悉以上代码的.

上面我们说过Handler,Looper,Message都是有着"千丝万缕"的关系,但眼睛雪亮的朋友会发现,上面代码都没有发现Looper和MessageQueue呢.

恩,别急,看完所有实例,我们再解释.

实例二:子线程中使用Handler(部分代码)

    //代码片段1:我们直接定义一个Handler
    private class MyThread extends Thread{

        private Handler myHandler = null;

        @Override
        public void run() {
            super.run();
            myHandler =new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    //to do something
                }
            };

        }
    }

以上代码会报如下错误:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

我们继续

//代码片段2,多次调用Looper.prepare()
    private class MyThread extends Thread{

        @Override
        public void run() {
            super.run();

            //多次调用会抛出异常
            Looper.prepare();
            Looper.prepare();
        }
    }

运行代码片段2,就会抛出如下异常:java.lang.RuntimeException: Only one Looper may be created per thread

也就是说,一个线程中只运行运行一次Looper.prepare(),否则抛出异常,至于原因我们后面分析.

我们继续

//代码片段3,加Looper.prepare()和Looper.loop()
    private class MyThread extends Thread{

        @Override
        public void run() {
            super.run();

            //初始化
            Looper.prepare();

            myHandler =new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    //to do something
                    Log.d(TAG, "----handleMessage-----what:"+ msg.what);
                }
            };

            myHandler.sendEmptyMessageDelayed(MSG_NOTIFY_END, 1000);

            //循环
            Looper.loop();
            //这下面的代码永远不会执行
        }
    }

如果加上了Looper.prepare()和Looper.loop(),程序不会报错,同时myHandler可以接收并处理发送来的消息!

如果我们只是加Looper.prepare(),此时myHandler是无法接收到消息的(有兴趣的朋友可以把Looper.loop注释了试试看)

可以说Looper.prepare()和Looper.loop()是必须配套使用的,缺一不可!

接下来揭开迷雾,请继续

我们在主线程使用Handler时并没有发现Looper.prepare()和Looper.loop(),这个怎么回事?

其实Android在创建主线程时就把Looper.prepare()和Looper.loop()初始化好了,这个需要看源码,如下:

base\core\java\android\app\ActivityThread.java

ActivityThread.java 的main()

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        AndroidKeyStoreProvider.install();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper(); //[这里调用的是Looper.prepareMainLooper()]

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop(); //[Looper开始循环,下面代码一般会不执行的]

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

至于Android什么时候会执行ActivityThread.java中的main(),我们以后专门分析!

base\core\java\android\os\Looper.java

1.Looper.java中的prepareMainLooper()

    public static void prepareMainLooper() {
        prepare(false); //[这里最终调用的还是Looper.prepare()]
        //[这里进行了sMainLooper是否为null判断,如果多次调用,这里会抛出异常]
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

从上面"throw new IllegalStateException("The main Looper has already been prepared.");"告诉我们Looper.prepareMainLooper()只允许调用一次!

2.Looper.java中的prepare()

     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

Looper中有两个prepare方法,一个公开的,一个私有的,最终执行的都是prepare(boolean quitAllowed)

我们会发现,prepare中会判断sThreadLocal中是否存在同一个Looper,如果存在,就抛出"Only one Looper may be created per thread"异常.

3.Looper.java出示Looper(boolean quitAllowed)

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper在初始化时创建了MessageQueue,同时获取当前的线程.

小结一会儿

  1. 从上面中我们可以只带Looper.prepare()就是对Looper()的初始化,同时保存在sThreadLocal(至于此人是谁,我们后续讲解)中.
  2. 主线程和子线程中初始化的Looper时的区别是,主线程中的MessageQueue(boolean quitAllowed)是为false,既不允许退出,而子线程是允许退出的.

好了,目前就分析到这,预知后事如何,请听下回讲解.谢谢.

在这里还是推荐我的读书导航网址大全,读书是一种生活方式:http://www.91es.com/index.html

版权声明 1、 本站名称 91易搜
2、 本站网址 https://www.91es.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权请留言
4、 本站禁止发布或转载任何违法的相关信息,如有发现请向站长举报
导航号,我的单页导航

1 条评论

评论审核已启用。您的评论可能需要一段时间后才能被显示。

暂无评论...