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

Android  2018年5月13日 pm4:26发布7年前 (2018)更新 城堡大人
89 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.biumall.com/index.html

 历史上的今天

  1. 2024: Android刷新媒体库的方法(0条评论)
  2. 2024: AudioTrack简单使用(1条评论)
  3. 2023: Android截图命令介绍(0条评论)
  4. 2022: 视频播放中,拖动进度条可以seek到相应视频帧(0条评论)
  5. 2021: Android应用启动时出现白屏或者黑屏问题的简介(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

Android静态换肤-日夜主题切换之不继承Activity

前言记录一下,有Activity换肤之日夜主题无缝切换。一般来说,换肤分为静态换肤和动态换肤,Android的日夜模式可以看做静态换肤的一种。是以资源存放位置来说的,其实不是很严谨,但换肤的本质都是一样的。正文Android高版本都支持日夜模式切换,资源放在对应日夜目录,比如//有...

[摘]Android IO流读写文件实例2

一、超类:字节流: InputStream(读入流) OutputStream(写出流)字符流: Reader(字符 读入流) Writer (字符写出流)二、文件操作流字节流: FileInputStream ,FileOutputStream字符流: FileReader, File...

WordPress页面中阻止插入额外换行

众所周知WordPress会对编辑器里的内容再格式化一遍,比如自动分段。但有些时候这些添加的格式反而也会让人很头疼。最近遇到WordPress在input,select,pre前会插入额外的换行即<br>,从而破坏页面样式的问题。方法一:去除所有页面的额外换行符在对应主题f...

朱自清:背影

那年冬天,祖母死了,父亲的差使也交卸了,正是祸不单行的日子。我从北京到徐州打算跟着父亲奔丧回家。到徐州见着父亲,看见满院狼藉的东西,又想起祖母,不禁簌簌地流下眼泪。父亲说:“事已如此,不必难过,好在天无绝人之路!”回家变卖典质,父亲还了亏空;又借钱办了丧事。这些日子,家中光景很是惨淡,一半为了丧事...

SharedPreferences数据存储介绍

前言Android中的SharedPreferences经常使用,这里就对着存储的数据类型进行简单的记录,方便自己回顾。正文进入SharedPreferences.java看一下主要的方法。写入方法常用的写入数据的方法如下Editor putStringSet(String key, ...

OkHttp简单介绍之一

前言okhttp很常用的一个网络请求框架,因此记录一下如何使用,然后走一下源码流程。这篇只是简单记录一下okhttp的同步和异步请求。好记性不让烂笔头正文同步和异步初始化都一样,只是最后调佣的方法不一样。这里只是简单记录,因此直接上代码。在Module的build.gradle中引入...