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

Android  2018年5月13日 pm4:26发布7年前 (2018)更新 91es.com站长
63 0 0

在项目中,使用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. 2024: Android刷新媒体库的方法(0条评论)
  2. 2024: AudioTrack简单使用(1条评论)
  3. 2023: Android截图命令介绍(0条评论)
  4. 2022: 视频播放中,拖动进度条可以seek到相应视频帧(0条评论)
  5. 2021: Android应用启动时出现白屏或者黑屏问题的简介(0条评论)
版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站内容: 部分来源于网络,仅供学习和参考,若侵权请留言
3、 本站申明: 个人流水账日记,内容并不保证有效

暂无评论

暂无评论...

随机推荐

linux禁止root远程登录

推荐使用 读书导航 极客导航 :125啦极客导航(http://www.91es.com/jike.html)一、添加新用户    adduser admin    passwd  admin (修改密码)系统提示输入确认密码后再输入一次。OK添加成功。二、禁止root远程登录...

龙应台:我为什么要求你读书

那天我问你,“你将来想做什么”,我注意到,你很不屑于回答我这个问题,所以跟我胡诌一通。是因为你们这个时代的人,对未来太自信,所以不屑与像我这一代人年轻时一样,讲究勤勤恳恳、如履薄冰,还是其实你们对于未来太没信心,所以假装出一种嘲讽和狂妄的姿态,来闪避我的追问?我几乎要相信,你是在假装潇洒了。今天的...

《阿里Android手册》笔记

前言下载了《阿里Android手册》,看了一下,很多规定还是很合理的。尤其是一些命名的规定,虽然繁琐,但对于后续查阅代码还是很爽的。今天有空,记录一些觉得很不错的内容,方便自己查阅。正文layout 文件的命名Activity 的 layout 以 module_activity 开头...

麦芒:生活与危机

梦见的还是十年前的危机生活,却仍然是眼前的生活两种因素似被姻缘强拧在一起我记得,一个冬夜危机宛如积雪休闲的火山正对着度蜜月假的一对夫妻旅馆二层房间敞开的窗口不知是为了让屋内的人向外眺望还是让屋外的火山朝里窥视哎,没有马车驰过的空旷街道就像被声音遗弃的微潮的黎明让我替你说出...

周国平:事故

火车轮子下一团污秽 是男人还是女人叫什么名字这有什么重要 为什么开来的是警察而不是一大块肥皂 假如一个人消失了不要去寻找人应该像声音一样死去只留下寂静

Android自定义水平Seekbar简介

前言Android默认的Seekbar就是水平的,为啥这里需要自定义呢?在项目中我们大多数Seekbar是可以用原生的解决,但是个别客户对Seekbar效果做了定制化,如果用Android原生的达不到客户需求。正文直入正题,效果图第一个是Android原生的SeekBar第二个是自定...