handler 订阅
Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。 展开全文
Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。
信息
领    域
计算机编程
通常用途
处理相对耗时比较长的操作
应    用
异步消息处理
外文名
handler
handler特点
1. 传递Message。用于接受子线程发送的数据, 并用此数据配合主线程更新UI。在Android中,对于UI的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,那么就需要把数据消息作为一个Message对象发送到消息队列中,然后,由Handler中的handlerMessage方法处理传过来的数据信息,并操作UI。当然,Handler对象是在主线程中初始化的,因为它需要绑定在主线程的消息队列中。类sendMessage(Message msg)方法实现发送消息的操作。 在初始化Handler对象时重写的handleMessage方法来接收Message并进行相关操作。2. 传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。Handler对象在进行初始化的时候,会默认的自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法。 [1] 
收起全文
精华内容
下载资源
问答
  • handler.post和handler.sendMessage本质上是没有区别的,都是发送一个消息到消息队列中,而且消息队列和handler都是依赖于同一个线程的。接下来通过本文给大家分享android handler.post和handler.sendMessage的区别...
  • 主要介绍了Flutter permission_handler 权限插件的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • 一个Android handler机制的Demo
  • NULL 博文链接:https://byandby.iteye.com/blog/832467
  • Handler耗时操作

    2016-11-15 13:59:08
    一个简单的handler下载做耗时操作的demo
  • jobHandler调度器

    2017-04-11 15:15:44
    资源使用说明:http://blog.csdn.net/qq5132834/article/details/70053723
  • Handlerjava代码实现

    2017-01-12 16:45:25
    这是我用java代码实现的Handler
  • 分享一份teamcenter流程设计常用handler处理程序的使用总结及参数设置示例,希望能帮到大家!
  • Handler

    千次阅读 2018-07-30 19:24:10
    Handler:作用就是发送与处理信息,如果希望Handle正常工作,在当前线程中要有一个Looper对象 Message:Handle接收与处理的消息对象 MessageQueue:消息队列,先进先出管理Message,在初始化Looper...

    这里写图片描述

    子线程与UI主线程的通信在android中使用了消息机制来完成
    消息处理机制本质:一个线程开启循环模式持续监听并依次处理其他线程给它发的消息

    Handler类的主要作用有两个:

    1.在新启动的线程中发送消息
    2.在主线程中获取,处理消息

    即Handler的作用是把消息加入特定的Looper所管理的MessageQueue中,并处理该MessageQueue中Looper分发的Message。

    一个线程只能有一个Looper,对应一个MessageQueue

    相关名词:
    UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue
    Handler:作用就是发送与处理信息,如果希望Handle正常工作,在当前线程中要有一个Looper对象
    Message:Handle接收与处理的消息对象
    MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue
    Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理
    这里写图片描述

    在Android应用启动时,会自动创建一个线程,即程序的主线程,主线程负责UI的展示、UI事件消息的派发处理等等,因此主线程也叫做UI线程

    如果有多个线程并发操作UI组件,就会出现线程安全问题,所以Android中制定了一个规则:在Android中只允许主线程(UI线程)修改Activity中的UI组件(子线程不允许操作主线程内的UI组件
    很多时候我们做界面刷新都需要通过Handler来通知UI组件更新
    这里写图片描述

    每个Handler都绑定了一个线程,Handler是Thread的代言人,是多线程之间通信的桥梁,通过Handler,我们可以在一个线程中控制另一个线程去做某事。

    当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而我们发送的信息会先到主线程的MessageQueue进行等待。由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理

    这里写图片描述

    展开全文
  • 资源包括如何在一个activity A里激活另一个activity B。 然后在两个activity之间用handler传输数据。在activity A里定义全局变量handler,用sendMessage来发数据,在handler里数据以消息队列的方式发送。
  • 这是Handler的介绍和使用方法Demo,介绍请参考:http://write.blog.csdn.net/postlist
  • handler发送和接收message的各种方式 (1)Handler.SendEmptyMessage(what); (2)Handler.obtainMessage(int what, int arg1, int arg2, Object obj).sendYToTarget(); (3)Message.obtain(handler, MESSAGE_MSG, 0, 0, ...
  • Fragment与Activity使用Handler进行交互

    千次下载 热门讨论 2014-06-27 12:26:19
    这里简单介绍了Fragment和Activity使用Handler方式进行交互。
  • Handler完全解读——Handler的使用

    千次阅读 2018-09-18 15:52:57
    Handler完全解读——Handler的使用 Handler是什么 Handler是Android给我们提供用于更新UI的一套机制,也是一套消息处理机制。我们用它可以发送消息,也可以用它处理消息。在Android开发中有着非常重要的地位。 为...

    个人博客:https://blog.N0tExpectErr0r.cn

    小专栏:https://xiaozhuanlan.com/N0tExpectErr0r

    Handler完全解读——Handler的使用

    Handler是什么

    Handler是Android给我们提供用于更新UI的一套机制,也是一套消息处理机制。我们用它可以发送消息,也可以用它处理消息。在Android开发中有着非常重要的地位。

    为什么要使用Handler

    当一个应用程序运行时,它会创建一个进程。这个进程就是我们的主线程(UI线程&Activity Thread) 。在主线程中,会默认为我们在系统中默认创建一个Looper,这个Looper会与我们的Message Queue 和 主线程有一定联系。 在main线程中,主要是运行一个Message Queue,管理着顶级的应用程序(Activity,Boardcast Receiver…)这些顶级应用程序在默认情况下都会在主线程中创建。这就是为什么我们需要在主线程中更新UI。

    Android在设计的过程中,就封装了一套消息创建、传递、处理的机制。如果不遵循这样的机制,是没有办法更新UI信息的,会抛出异常信息。

    非主线程更新UI的后果

    我们可以尝试在一个新的线程中更新UI,会发现程序崩溃了。查看Logcat可以看到这样的一句提示

    Only the original thread that created a view hierarchy can touch its views.

    这告诉我们,在实际开发中,我们需要遵循Google为我们设定的这样的机制。

    那么如何在其他线程达到更新UI的目的呢?使用Handler就是其中一种办法。

    Handler的作用

    根据Android Developer网站上的描述,Handler主要有两个用途

    1. 定时地去发送一个Message或Runnable对象
    2. 可以跳转到另一个线程中去执行一些操作

    Handler使用方式

    关于Message

    参数

    • public int arg1(arg2):如果只需要存储几个整型数据,arg1 和 arg2是setData()的低成本替代品。
    • public Object obj: 发送给接收者的任意对象。当使用Message对象在线程间传递消息时,如果它包含一个Parcelable的结构类(不是由应用程序实现的类),此字段必须为非空(non-null)。其他的数据传输则使用setData(Bundle)方法。
    • public Messenger replyTo:用户自定义的消息代码,这样接受者可以了解这个消息的信息。每个handler各自包含自己的消息代码,所以不用担心自定义的消息跟其他handlers有冲突。

    方法

    另外 ,用Message来传递还可通过Message的**setData(Bundle)方法来传递。获取时调用getData()**方法。与sendData相似的还有peekData。

    • public void setData(*Bundle data):设置一个任意数据值的Bundle对象。如果可以,使用arg1和arg2域发送一些整型值以减少消耗。
    • public Bundle peekData():与getData()相似,但是并不延迟创建Bundle。如果Bundle对象不存在返回null。
    • public Bundle getData():获取附加在此事件上的任意数据的Bundle对象,需要时延迟创建。通过调用setData(Bundle)来设置Bundle的值。需要注意的是,如果通过Messenger对象在进程间传递数据时,需要调用Bundle类的Bundle.setClassLoader()方法来设置ClassLoader,这样当接收到消息时可以实例化Bundle里的对象。
    • public static Message obtain(): 从全局池中返回一个新的Message实例。在大多数情况下这样可以避免分配新的对象。

    handleMessage方法

    handleMessage方法用于接收Message对象并进行相应的处理,对应Handler的sendMessage方法。

    使用时应在handler中重写此方法。当在其他线程调用sendMessage方法时,handleMessage方法便会被回调,并携带sendMessage方法调用者在Message中存入的信息。当我们想要在其他线程更新UI时,就可以用主线程中创建的Handler调用sendMessage方法,然后在该Handler重写的handleMessage方法中做相应的处理。

    比如此处,我们在handleMessage方法中进行更新TextView的操作,并把Message的arg1作为文本的内容。

    private Handler mHandler = new Handler(){
        public void handleMessage(Message msg){
            mTextView.setText(""+msg.arg1+"-"+msg.arg2);
        };
    };
    
    new Thread(){
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
                Message message = new Message();
                message.arg1 = 88;
                mHandler.sendMessage(message);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }.start();
    

    obtainMessage方法

    有的时候,我们不需要创建一个新的Message对象,可以去复用系统的Message对象。这时可以调用obtainMessage()方法,就会为我们返回一个Message对象。然后就可以直接用其来发送消息

    private TextView mTextView;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Person person = (Person)msg.obj;
            mTextView.setText("name:" + person.getName()+" age:"+person.getAge());
        }
    };
    private int index = 0;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.tv_text);
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    Message message = handler.obtainMessage();
                    Person person = new Person();
                    person.setName("梁文俊");
                    person.setAge(18);
                    message.obj = person;
                    mHandler.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
    

    那么在obtainMessage方法中,究竟是做了哪些操作呢?

    我们进入obtainMessage的源码,会发现它实际上是调用了Message的obtain方法,将调用的Handler作为了参数。

    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }
    

    这里实际上是obtain()方法的一个重载方法,与之不同的是设置了target的值。

    我们可以进入obtain方法的源码中研究一下。

    可以看到,它其实就是用obtain()方法来创建Message,然后为其设置一个target。

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }
    

    那么obtain()方法又是如何创建Message的呢?

    进入它的源码,可以看到,它其实就是先从系统中取出Message对象。如果没有多余的Message对象,则创建一个新的Message并返回,然后将sPool指向该Message的下一个Message。

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    

    一些小猜测

    这里发现Message居然有next参数,这里感觉有点像链表了…因此跳到next定义处,发现它也是一个Message类型。结合前面的注释。在这里猜测,从系统中获取的Message都是以链表的形式来连接的,然后按顺序取出。

    emmm…跑题了…不再往内研究了…不知道这个猜测对不对…

    // sometimes we store linked lists of these things
    /*package*/ Message next;
    

    当我们为Message制定了target后,就可以不再调用handler的sendMessage发送消息,直接通过该Message的sendToTarget方法即可。

    那么它具体是如何实现的呢,我们可以查看一下源码。

    这里其实很简单。它实际上就是调用了target的sendMessage方法,将自己作为参数而实现的而已。本质上仍然是调用Handler的sendMessage方法

    /**
     * Sends this Message to the Handler specified by {@link #getTarget}.
     * Throws a null pointer exception if this field has not been set.
     */
    public void sendToTarget() {
        target.sendMessage(this);
    }
    

    post方法

    刚刚的异常我们已经看到,那如何才能使用Handler在这个新建的线程更新UI呢?

    我们可以这样做:在主线程中创建一个Handler。然后在子线程中,我们可以调用Handler的post方法,并向其中传递一个Runnable为参数,在Runnable中更新UI即可。

    private TextView mTextView;
    private Handler mHandler = new Handler();
    
    mTextView = findViewById(R.id.tv_text);
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mTextView.setText("update");
                    }
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
    

    postDelayed方法

    postDelayed与post方法非常接近,仅仅是参数多了一个long类型的参数delayMills。它与post的区别就是它会在delayMills这段时间之后再去执行Runnable的方法,也就是延迟执行。

    有时候我们需要定时的完成一些事情(比如定时更换TextView的文字)时,就可以利用它延迟执行的这一特点来实现。做法是分别在主函数中以及它所执行的Runnable中postDelayed一段时间。这样就可以达到定时地完成某件任务的工作。

    比如下例就简单地实现了每隔一秒切换一次TextView文字的作用。

    private TextView mTextView;
    private Handler mHandler = new Handler();
    private String[] mTexts = {"苟利国家生死以","岂因祸福避趋之"};
    private ChangeRunnable mRunnable = new ChangeRunnable();
    private int index=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.tv_text);
        mHandler.postDelayed(mRunnable,1000);
    }
    class ChangeRunnable implements Runnable{
        @Override
        public void run() {
            index = index%2;
            mTextView.setText(mTexts[index++]);
            mHandler.postDelayed(mRunnable,1000);
        }
    }
    

    removeCallbacks方法

    比如我们这里有个定时更新TextView的文本的代码,如果想要按下按钮,停止定时更换文本,就可以通过removeCallbacks方法,传入该Runnable来中止消息。

    private Button mBtnRemove;
    private ChangeRunnable mRunnable = new ChangeRunnable();
    
    class ChangeRunnable implements Runnable{
        @Override
        public void run() {
            mIndex = mIndex%3;
            mTextView.setText(mTexts[mIndex++]);
            mHandler.postDelayed(mRunnable,1000);
        }
    }
    ...
    mBtnRemove.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            mHandler.removeCallbacks(mRunnable);
        }
    });
    

    Handler的另一种构造方法

    Handler(Callback)

    Handler的构造方法中,有一个这样的形式的构造方法。在我们创建Handler时,可以指定一个Callback。

    public Handler(Callback callback);
    

    我们可以试着在创建Handler过程中传入一个new出的Callback。会发现它有一个handleMessage方法。这个handleMessage方法和我们之前用到的有一个不同点:它有一个boolean类型的返回值。

    我们分别创建两个handleMessage方法,通过Log可以发现是Callback中的handleMessage方法先被调用。这时,如果Callback的handleMessage返回false,则后面的可以正常执行。但当返回true时,Message就被截获了,后面的handleMessage将不会被执行。

    我们可以通过Callback来截获Message。

    Handler(Looper)

    使用Handler时,还可以指定与之关联的Looper。这样handler发送消息时,消息就会进入指定的Looper的消息队列中。

    比如我们可以尝试这样新建一个Handler:

    Handler handler = new Handler(Looper.getMainLooper());
    

    HandlerThread的使用

    HandlerThread可以解决一些多线程并发的问题,达到线程的同步的效果。我们可以用它的Looper来新建一个Handler,在Handler的handleMessage中再做操作。这时的handleMessage是在这个HandlerThread中执行的

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        HandlerThread thread = new HandlerThread("handler thread");
        Handler handler = new Handler(thread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                Log.d("test",""+msg.arg1);
            }
        };
        Message message = handler.obtainMessage();
        message.arg1 = 123;
        handler.sendMessage(message);
    }
    

    Handler导致的内存泄漏(学习自民神博客)

    问题描述

    Handler使用过程中,我们需要特别注意一个问题,那就是Handler可能会导致内存泄漏。

    具体原因如下:

    • Handler的生命周期与Activity不同,Handler会关联Looper来管理Message Queue。这个队列在整个Application的生命周期中存在,因此Handler不会因Activity的finish()方法而被销毁。
    • 非静态(匿名)内部类会持有外部对象,当我们这样重写Handler时它就成为了一个匿名内部类,这样如果调用finish方法时Handler有Message未处理的话,就会导致Activity不能被销毁。

    解决方法

    1. 可以在外部新建一个类,这样便可解决这个问题。但这样无疑过于麻烦了,内部类更方便些。
    2. 可以同时使用静态内部类和弱引用,当一个对象只被弱引用依赖时它便可以被GC回收。

    **注意,要static和弱引用要同时使用,否则由于非静态内部类隐式持有了外部类Activity的引用,而导致Activity无法被释放 **

    展开全文
  • Handler在子线程中的使用以及注意事项
  • 所谓的Native Handler,只是我自己臆想出来的名词(或许也有前人创造了,但我没发现也说不定),用来对Android媒体框架中消息通信部分进行描述。之所以这么命名,主要基于如下几点: Android 媒体框架涉及到消息...

    AOSP Version: Oreo 8.0.0_r4

    所谓的Native Handler,只是我自己臆想出来的名词(或许也有前人创造了,但我没发现也说不定),用来对Android媒体框架中消息通信部分进行描述。之所以这么命名,主要基于如下几点:

    1. Android 媒体框架涉及到消息通信部分,主要由ALooper、AHandler、AMessage三个类组成,但并没有一个官方的名称,这对于写作来说,很不方便,总不能提到相关部分内容,就三个名词一起上吧。
    2. 之所以是Native,是因为这个框架涉及到的类,都位于Native层,实际上ALooper、AHandler、AMessage三个都是C++代码编写,起名Native名副其实。
    3. 至于Handler,借鉴Android 上层代码中的Handler机制名称,因为它们之间的逻辑,区别真的很小。

    该系列文章,会分为三个部分:

    简介

    来张图说明一下handler-looper-message之间的关系。
    在这里插入图片描述

    AHandler

    源码路径参见文末最后一小节

    AHandler,顾名思义,在这个框架中,是消息处理者的角色。

    代码片段如下:

    namespace android {
    struct AMessage;
    struct AHandler : public RefBase {
        AHandler()
            : mID(0),
              mVerboseStats(false),
              mMessageCounter(0) {
        }
        
        sp<ALooper> looper() const {
            return mLooper.promote();
        }
    	//......
    protected:
        // 纯虚函数,由每个继承了AHandler接口的类实现,是消息处理的关键
        virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
    
    private:
        friend struct AMessage;      // deliverMessage()
        friend struct ALooperRoster; // setID()
    	// 每一个handler都有自己的唯一id,可以通过id()函数别获取
        ALooper::handler_id mID;
        // handler需要注册在looper中才会起作用,可以通过get方法获取,通过setID初始化
        wp<ALooper> mLooper; 
        
        inline void setID(ALooper::handler_id id, const wp<ALooper> &looper) {
            mID = id;
            mLooper = looper;
        }
    
        bool mVerboseStats;
        uint32_t mMessageCounter; // 当前handler处理消息的计数器
        KeyedVector<uint32_t, uint32_t> mMessages; // 和message相关的消息计数器
    	// 当需要发送消息时,Looper线程会调用该函数,进而触发子类实现的onMessageReceived函数回调
        void deliverMessage(const sp<AMessage> &msg);
    
        DISALLOW_EVIL_CONSTRUCTORS(AHandler);
    };
    }  // namespace android
    

    很简单,来看一下具体实现,也就是AHandler.cpp。

    deliverMessage

    namespace android {
    
    void AHandler::deliverMessage(const sp<AMessage> &msg) {
        onMessageReceived(msg); // 直接调用纯虚函数(子类实现)onMessageReceived
        mMessageCounter++; // 消息计数器+1
    	  // 如果mVerboseStats为true,则会在mMessages中记录通过Handler处理的每一个Message的数量。
        // 这功能对阅读代码来说,基本没什么用。构造函数默认初始化列表直接把mVerboseStats设置成了false
        if (mVerboseStats) { 
            uint32_t what = msg->what();
            ssize_t idx = mMessages.indexOfKey(what);
            if (idx < 0) {
                mMessages.add(what, 1); // 添加新的消息
            } else {
                mMessages.editValueAt(idx)++; // 消息计数+1
            }
        }
    }
    
    }  // namespace android
    

    可以看到,AHandler的实现非常简单,头文件中,除了一个虚函数外,就只有deliverMessage函数没有实现。所以,在cpp文件中,只需要实现deliverMessage函数即可。

    KeyedVector<key, value> 相关函数:

    • indexOfKey(key):获取key对应的value值
    • add(key, value):添加一个键值对
    • editValueAt(value):返回value的地址

    小结一下

    简单总结一下deliverMessage函数:

    1. 函数体第一行就直接调用了子类的onMessageReceived函数,处理消息去了。
    2. 为mMessageCounter消息计数器加1。
    3. mVerboseStats控制的if代码块,无关紧要,不说也罢。

    小结一下AHandler:

    • 拥有一个mID,区别其它AHandler。

    • 持有一个ALooper的引用,这个引用的主要作用是,在AMmessage构建时,可以通过AHandler获取ALooper的引用,进而调用ALooper的post接口,AMessage对象自己加入到消息队列中去。

    • 维护了一个mMessageCounter计数器,记录ahandler已经处理过的Message数。

    • deliverMessage函数直接调用子类的onMessageReceived函数,交给子类处理。

    ALooper

    源码路径参见文末最后一小节

    继续顾名思义,是一个循环器,如果是循环的话,多半有线程了。先看一下它的头文件:

    namespace android {
    
    struct AHandler;
    struct AMessage;
    struct AReplyToken;
    
    struct ALooper : public RefBase {
        typedef int32_t event_id;
        typedef int32_t handler_id;
    
        ALooper();
    
    	// 注册handler
        handler_id registerHandler(const sp<AHandler> &handler);
        void unregisterHandler(handler_id handlerID); // 根据handlerID注销handler
    	// 启动looper的循环线程,开始工作
        status_t start(
                bool runOnCallingThread = false,
                bool canCallJava = false,
                int32_t priority = PRIORITY_DEFAULT
                );
    	// 停止线程
        status_t stop();
    	
        static int64_t GetNowUs();
    
    protected:
        virtual ~ALooper(); // 需析构函数,子类可以在复写函数中释放资源
    
    private:
        friend struct AMessage;       // post()消息本身,下一个小结会讲
     
        struct Event { // 将发送时间和Message封装在一个结构体重
            int64_t mWhenUs;
            sp<AMessage> mMessage;
        };
     
        Mutex mLock;
        Condition mQueueChangedCondition;
     	// Looper的名字,可以通过对应的set/get函数设置/获取该字段值
        AString mName; 
     
        List<Event> mEventQueue; // 事件列表,所有的消息都会放进来,这就是所谓的消息队列
     
        struct LooperThread; // 循环线程
        sp<LooperThread> mThread;
        bool mRunningLocally;
     
        // use a separate lock for reply handling, as it is always on another thread
        // use a central lock, however, to avoid creating a mutex for each reply
        Mutex mRepliesLock;
        Condition mRepliesCondition;
     
        // START --- 函数只会被AMessage使用
     
        // 在给定的超时时间内从当前looper上发送一个消息
        void post(const sp<AMessage> &msg, int64_t delayUs);
        // 创建一个和当前looper一起使用的回复令牌
        sp<AReplyToken> createReplyToken();
        // 等待消息发送后的回复令牌。如果状态OK,回复信息会被存储于参数提供的变量中。否则,参数无变化
        status_t awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response);
        // 发送回复令牌的响应。假如该响应发送成功,返回OK。否则,返回一个错误码
        status_t postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &msg);
    
        // END --- 函数只会被AMessage使用
    	// 从消息队列中取消息,执行
        bool loop();
    
        DISALLOW_EVIL_CONSTRUCTORS(ALooper);
    };
    
    } // namespace android
    

    简单了解了ALooper的定义,接下来看看一些函数的具体实现。

    post()

    void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) { 
        // 参数接收一个Message,和一个消息发送的时延
        int64_t whenUs; // 消息发送的真实时间:根据时延值,判断是延迟发送还是立即发送。
        if (delayUs > 0) {
            whenUs = GetNowUs() + delayUs;
        } else {
            whenUs = GetNowUs();
        }
    	// 遍历消息队列,找到一个队列中Event的时延大于whenUs的位置
        List<Event>::iterator it = mEventQueue.begin();
        while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
            ++it;
        }
    	// 根据入参和计算得到的时延,封装消息事件(Event)
        Event event;
        event.mWhenUs = whenUs;
        event.mMessage = msg;
    
        if (it == mEventQueue.begin()) {
            mQueueChangedCondition.signal();
        }
    	// 将封装好的消息事件,插到消息队列中。
        mEventQueue.insert(it, event);
    }
    

    一句话总结post函数的功能:post()函数将Message和时延值封装成Event后,插入到消息队列mEventQueue中

    从函数中的消息队列的遍历算法来推断,消息队列是按照时延值的大小,从小到大排列的。

    AReplyToken::createReplyToken

    // 只有AMessage::postAndAwaitResponse函数会调用
    sp<AReplyToken> ALooper::createReplyToken() {
        return new AReplyToken(this); 
    }
    

    createReplyToken函数直接创建了 一个AReplyToken对象返回,来看看是怎么的定义:

    AReplyToken是定义在AMessage.h中的,具体代码路径请查看本文最后一小节

    struct AReplyToken : public RefBase {
        explicit AReplyToken(const sp<ALooper> &looper)
            : mLooper(looper), // 将looper保存在mLooper成员中
              mReplied(false) { // 将mReplied初始化为false,刚刚创建回复令牌时状态当然是尚未回复的
        }
    
    private:
        friend struct AMessage;
        friend struct ALooper;
        // 回复令牌中,保存了消息来源于那个looper,一个程序中looper可不止一个。它们彼此应该是靠mName区别
        wp<ALooper> mLooper; 
        sp<AMessage> mReply; // 记录回复消息
        bool mReplied; // 本回复令牌的状态
    	
        // 如果未设置回复,则返回false; 否则,它检索回复并返回true
        bool retrieveReply(sp<AMessage> *reply) {
            if (mReplied) {
                *reply = mReply;
                mReply.clear();
            }
            return mReplied;
        }
        // 设置此令牌的回复。 返回OK或错误
        status_t setReply(const sp<AMessage> &reply);
    };
    

    只有一个未实现的函数

    status_t AReplyToken::setReply(const sp<AMessage> &reply) {
        if (mReplied) { // 已经设置过的回复不可再设置
            ALOGE("trying to post a duplicate reply");
            return -EBUSY;
        }
        CHECK(mReply == NULL);
        mReply = reply;
        mReplied = true;
        return OK;
    }
    

    setReply函数将回复的Message设置到回复令牌的mReply字段中,并将回复状态改为true。

    小结AReplyToken

    1. AReplyToken:意味消息的回复令牌
    2. AReplyToken中包含消息是否已经被处理过的字段mReplied,如果处理过,mReplied字段被置为true。
    3. AReplyToken中包含了回复消息本身,体现在mReply字段。

    awaitResponse

    // 只会被AMessage::postAndAwaitResponse函数调用
    status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> response) {
       // return status in case we want to handle an interrupted wait
       Mutex::Autolock autoLock(mRepliesLock);
       CHECK(replyToken != NULL);
       while (!replyToken->retrieveReply(response)) {
           {
               Mutex::Autolock autoLock(mLock);
               if (mThread == NULL) {
                   return -ENOENT;
               }
           }
           mRepliesCondition.wait(mRepliesLock); // 等待mRepliesLock,相关知识,请查看Condition小结
       }
       return OK;
    }
    

    awaitResponse函数,通过无限循环+同步锁的方式,让当前线程检索出一个可回复的回复令牌后通过入参,将回复消息反馈给调用线程。

    在ALooper中,mRepliesCondition.wait等待后,只有当ALooper::stop()和ALooper::postReply函数调用后,才会结束等待,继续下一次循环。

    Condition

    这是一个用于同步的对象,为Android中特有。它的函数有:

    函数名功能
    wait(Mutex& mutex)当前线程等待唤醒
    waitRelative(Mutex& mutex, nsecs_t reltime)当前线程等待唤醒,如果等待时间超过reltime,退出等待
    signal()触发唤醒通知,但只能唤醒一个等待的线程
    broadcast()发送唤醒广播,唤醒所有等待线程

    postReply

    status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) {
        Mutex::Autolock autoLock(mRepliesLock);
        status_t err = replyToken->setReply(reply); // 将回复消息设置到回复令牌中
        if (err == OK) {
            mRepliesCondition.broadcast(); // 通过Condition唤醒awaitResponse函数
        }
        return err;
    }
    

    postReply的主要作用,就是将回复令牌和回复消息绑定,并唤醒awaitResponse函数,处理回复消息。

    loop

    bool ALooper::loop() {
        Event event;
        {
            Mutex::Autolock autoLock(mLock);
            if (mThread == NULL && !mRunningLocally) { 
                // 如果没有初始化线程,或者不在本地运行,返回false
                return false;
            }
            if (mEventQueue.empty()) { // 消息队列是空的,当前线程等待,直到被唤醒发回true
                mQueueChangedCondition.wait(mLock);
                return true;
            }
            int64_t whenUs = (*mEventQueue.begin()).mWhenUs; // 获取消息队列第一条消息的发送时间
            int64_t nowUs = GetNowUs();
    
            if (whenUs > nowUs) { 
                // 如果第一条消息还没有到发送时间,则等待whenUs - nowUs后唤醒线程返回true
                int64_t delayUs = whenUs - nowUs;
                mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
                return true;
            }
    		// 如果发现消息的发送时间过期,做两件事情:1. 删掉该条消息。2. 发送该条消息
            event = *mEventQueue.begin();
            mEventQueue.erase(mEventQueue.begin()); // 1. 删掉该条消息。
        }
        event.mMessage->deliver(); // 2. 发送该条消息
        return true; // 返回true
    }
    

    loop函数,总共做了以下几件事情:

    • 条件判断:判断是否初始化线程,并且线程是否在本地运行,如果否则返回false,使可能存在的循环停止。
    • 消息队列判断:判断消息队列中是否有消息,没有的话,让线程进入等待,直到有消息入队后被唤醒。
    • 消息发送判断:判断队列中,第一条小时发送时间是否满足,满足则发送消息,并将消息移出队列。否则让线程等待,一定时间(当前时间和发送时间的时间差)后,自动唤醒线程。

    可以看到,loop函数会根据实际情况,判断是否让线程等待。防止函数不断执行的无意义死循环,造成CPU资源的浪费。

    start

    status_t ALooper::start(bool runOnCallingThread, bool canCallJava, int32_t priority) {
       if (runOnCallingThread) {
           {
                Mutex::Autolock autoLock(mLock);
                if (mThread != NULL || mRunningLocally) {
                    return INVALID_OPERATION;
                }
                mRunningLocally = true; // mRunningLocally 为true意味着当前线程陷入loop的死循环
            }
            do {
            } while (loop());
            return OK;
        }
        Mutex::Autolock autoLock(mLock);
        if (mThread != NULL || mRunningLocally) {
            return INVALID_OPERATION;
        }
        mThread = new LooperThread(this, canCallJava);
        status_t err = mThread->run(
                mName.empty() ? "ALooper" : mName.c_str(), priority);
        if (err != OK) {
            mThread.clear();
        }
        return err;
    }
    

    ALooper::start函数算是ALooper中的核心,称得上是NativeHandler机制中的发动机。那么,接下来就详细分析一下吧:

    runOnCallingThread

    这个入参决定了当调用线程调用Alooper::start函数后,取消息,发送消息的工作在当前线程执行,还是子线程执行。区别在于:

    • 如果runOnCallingThread = true:那么当前线程不会再做其它工作,陷入一个死循环。用于循环执行loop()函数。
    • 如果runOnCallingThread = false:会创建一个子线程,并将loop()逻辑放到这个特定子线程中处理。

    在继续往下分析之前,先喝瓶Dali牌西北……额,就,补充点基础知识。

    LooperThread&Thread

    LopperThread继承于Android的Thread,这个Thread其实是Android对Linux线程API的一个封装。

    Android Thread源码路径:/system/core/libutils/Threads.cpp

    在这里,我们需要知道的是,Thread中有一个很重要的函数int Thread::_threadLoop(void* user):

    int Thread::_threadLoop(void* user) // user 是Thread的子类
    {
        Thread* const self = static_cast<Thread*>(user);
        // ......
        bool first = true;
        do {
            bool result;
            if (first) { // 如果是第一次运行,执行readyToRun函数
                first = false;
                self->mStatus = self->readyToRun();
                result = (self->mStatus == NO_ERROR);
    
                if (result && !self->exitPending()) {
                    result = self->threadLoop();
                }
            } else { // 不是第一次运行
                result = self->threadLoop();
            }
    	// ......
          
        } while(strong != 0);
    
        return 0;
    }
    

    Thread::_threadLoop:是Android Thread的主要工作函数,和java层线程类的void run()函数类似。实际上,Thread::_threadLoop的调用函数正是Thread::run()。run函数也是真正调用的Linux线程相关API,创建线程的地方。

    接下来看看_threadLoop函数做了什么事情:

    1. 如果是第一次执行,则执行子类的readyToRun()函数,最终根据返回值判断是否执行子类的threadLoop();
    2. 如果不是第一次执行,本身已经在流程中了,就不断地循环调用子类的threadLoop();函数。

    所以,Thread不管如何,都会执行子类的threadLoop();函数,执行具体的业务逻辑。

    struct ALooper::LooperThread : public Thread {
        LooperThread(ALooper *looper, bool canCallJava)
            : Thread(canCallJava),
              mLooper(looper),
              mThreadId(NULL) {
        }
    
        virtual status_t readyToRun() {
            mThreadId = androidGetThreadId();
            return Thread::readyToRun();
        }
        virtual bool threadLoop() {
            return mLooper->loop();
        }
        bool isCurrentThread() const {
            return mThreadId == androidGetThreadId();
        }
        protected:
        virtual ~LooperThread() {}
    private:
        ALooper *mLooper;
        android_thread_id_t mThreadId;
    
        DISALLOW_EVIL_CONSTRUCTORS(LooperThread);
    };
    

    再来看LooperThread,主要实现了两个虚函数:

    • readyToRun(): 该函数是线程第一次进入循环前会执行的函数,子类可以做一些线程循环开启前的准备工作。LooperThread在这里做了一个初始化mThreadId的动作。
    • threadLoop(): 这是处理业务逻辑的函数,整个线程循环中会不断调用。LooperThread在这里调用了mLooper->loop();,开启ALooper的轮询。当消息符合条件时发送消息,当没有符合条件的消息是就等待。

    好了,基础知识补充完了,让我们把目光移到start函数中剩下的部分:

    mThread = new LooperThread(this, canCallJava);
    status_t err = mThread->run(
        mName.empty() ? "ALooper" : mName.c_str(), priority);
    if (err != OK) {
        mThread.clear();
    }
    

    这段代码中,有哪些重点(或者不重点)呢?

    • canCallJava:这个boolean值是从start函数的入参传进来的,一直也没说。这个之最终会被传递到Android底层的Thread中,如果是true的话,表示线程是否使用JNI函数。默认值为true。

    • new LooperThread:创建一个LooperThread对象。

    • mThread->run:通过上一步创建的LooperThread对象,创建一个Android底层的Thread,并让这个线程运行起来,进入一个do—while循环,通过LooperThread的threadLoop函数,执行mLooper->loop();,于是循环队列的动作开始跑起来了。

    • priority:这涉及到了Android线程优先级的概念,优先级高的线程,在与优先级低的线程争夺CPU时,占有优势。优先级从011,从低高。

      enum {
          PRIORITY_LOWEST         = ANDROID_PRIORITY_LOWEST,
          PRIORITY_BACKGROUND     = ANDROID_PRIORITY_BACKGROUND,
          PRIORITY_NORMAL         = ANDROID_PRIORITY_NORMAL,
          PRIORITY_FOREGROUND     = ANDROID_PRIORITY_FOREGROUND,
          PRIORITY_DISPLAY        = ANDROID_PRIORITY_DISPLAY,
          PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY,
          PRIORITY_AUDIO          = ANDROID_PRIORITY_AUDIO,
          PRIORITY_URGENT_AUDIO   = ANDROID_PRIORITY_URGENT_AUDIO,
          PRIORITY_HIGHEST        = ANDROID_PRIORITY_HIGHEST,
          PRIORITY_DEFAULT        = ANDROID_PRIORITY_DEFAULT,
          PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE,
          PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE,
      };
      

      以上是线程优先级的全部定义。比如,我们的UI线程的优先级是:ANDROID_PRIORITY_DISPLAY

    小结start函数

    • 决定消息轮询工作,是否放在当前线程执行。
    • 不管是否放在当前线程执行,都死循环执行loop函数,轮询消息队列,处理到时的消息。

    registerHandler&unregisterHandler

    ALooper还有两个函数,用来注册AHandler和取消注册AHandler的。有什么用呢?先看看代码吧:

    ALooperRoster gLooperRoster;
    ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
        return gLooperRoster.registerHandler(this, handler);
    }
    void ALooper::unregisterHandler(handler_id handlerID) {
        gLooperRoster.unregisterHandler(handlerID);
    }
    

    不管是注册,还是取消注册,都掉了gLooperRoster的接口,它是个目前为止,不多见的类。结合命名和功能来看的话,作用应该是:专门管理注册的AHandler信息的“花名册”。

    ALooperRoster

    namespace android {
        struct ALooperRoster {
            ALooperRoster();
            ALooper::handler_id registerHandler( 
                const sp<ALooper> &looper, const sp<AHandler> &handler); // 注册handler
            void unregisterHandler(ALooper::handler_id handlerID); // 取消注册handler
            void unregisterStaleHandlers(); // 取消注册陈旧的handler
            void dump(int fd, const Vector<String16>& args);
            private:
            struct HandlerInfo { // handler信息的结构体,包含了ALooper和AHandler的引用
                wp<ALooper> mLooper;
                wp<AHandler> mHandler;
            };
            Mutex mLock;
            KeyedVector<ALooper::handler_id, HandlerInfo> mHandlers; // 保存了HandlerInfo的vector
            ALooper::handler_id mNextHandlerID;
            DISALLOW_EVIL_CONSTRUCTORS(ALooperRoster);
        };
    }  // namespace android
    

    通过ALooperRoster的头文件可以看出:

    • ALooperRoster中,mHandlers是一个用户保存HandlerInfo的KeyedVector结构。以handler_id为key,HandlerInfo为value。
    • 而HandlerInfo中保存了在registerHandler函数中注册的AHandler指针和ALooper指针。

    接下来看看几个AHandler注册相关函数。

    registerHandler
    ALooper::handler_id ALooperRoster::registerHandler(
            const sp<ALooper> &looper, const sp<AHandler> &handler) {
        Mutex::Autolock autoLock(mLock);
    
        if (handler->id() != 0) { // handler的id初始化值为0。0表示没有注册过,非0表示已注册,不能再注册了
            CHECK(!"A handler must only be registered once.");
            return INVALID_OPERATION;
        }
    
        HandlerInfo info; // 封装 ALooper和AHandler
        info.mLooper = looper;
        info.mHandler = handler;
        ALooper::handler_id handlerID = mNextHandlerID++; // mNextHandlerID + 1
        mHandlers.add(handlerID, info); // 将封装好的HandlerInfo放到mHandlers中
        handler->setID(handlerID, looper); // 已经注册好的handler的id设置为mNextHandlerID
    
        return handlerID;
    }
    

    函数依旧不复杂,AHandler有一个id,AHandler对象创建之初,id值默认为0。非0表示已经注册过了,不能继续注册。0表示没有被注册,没有被注册过的AHandler对象,和入参looper一起,被封装在一个HandlerInfo的对象中后,添加到了一个mHandlers的KeyedVector中,以handler_id为key,以HandlerInfo为value。最后将AHandler的id值改为加入时的mNextHandlerID值,并将looper设置到AHandler对象的mLooper字段中去,绑定起来。

    unregisterHandler

    void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) {
        Mutex::Autolock autoLock(mLock);
        ssize_t index = mHandlers.indexOfKey(handlerID);
    	// ......
        const HandlerInfo &info = mHandlers.valueAt(index);
        sp<AHandler> handler = info.mHandler.promote();
        if (handler != NULL) {
            handler->setID(0, NULL);
        }
        mHandlers.removeItemsAt(index);
    }
    

    registerHandler是将ALooper和AHandler封装后,以handlerId值为key,存到mHandlers中。

    unregisterHandler的操作,基本上与之相反,通过入参传入的handler_id值,从mHandlers取出AHandler,将它的id值设为0后,将注册时绑定的ALooper解绑。

    小结registerHandler&unregisterHandler

    • registerHandler,直接调用ALooperRoster的registerHandler函数,将AHandler和ALooper对象封装一下,存入到一个名为mHandlers的Vector结构中,并是AHandler对象的id+1,AHandler和ALooper对象相互绑定。
    • unregisterHandler,直接调用ALooperRoster的unregisterHandler函数,从mHandlers取出指定id的AHandler,并将id置0,将绑定的ALooper对象解绑。

    源码相关路径

    Android底层代码,一般*.h文件和.cpp*文件都存放在不同路径下。

    头文件

    /frameworks/av/include/media/stagefright/foundation/

    AMessage.h

    AHandler.h

    ALooper.h

    .cpp文件

    /frameworks/av/media/libstagefright/foundation/

    AMessage.cpp

    AHandler.cpp

    ALooper.cpp

    展开全文
  • 发现handler有内存泄漏,但是度娘的那些静态类自定义handler并没有解决问题,并且那种方式局限性很严重。然后我用现在的这种方式完全解决内存泄漏问题。人格担保有用,不行就喷我。 前言 因为Android采取了单线程...

     发现handler有内存泄漏,但是度娘的那些静态类自定义handler并没有解决问题,并且那种方式局限性很严重。然后我用现在的这种方式完全解决内存泄漏问题。人格担保有用,不行就喷我。

    前言

    因为Android采取了单线程UI模型,开发者无法在子线程中更新UI,为此Android为我们提供了Handler这个类,实现ui线程的队列式更新,防止出现界面更新错乱。

    问题来了:

    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_bind_url);
            mHandler.sendEmptyMessageDelayed(0, 1000 * 2);
        }
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                intenMain();
            }
        };
        public void intenMain(){
            Intent intent = new Intent(LogoActivity.this, MineDeviceActivity.class);
            startActivity(intent);
            overridePendingTransition(R.anim.fade_out, R.anim.fade_in);
            LogoActivity.this.finish();
        }

    当handler延迟发送信息时,就会持续占用资源,而在activity跳转之后就出现内存没有回收,导致内存泄漏。项目中集成 Square 的开源库 LeakCanary,有关这个库的介绍及使用请看:Github.LeakCanary

    Handler对象隐性地持有了Activity的对象,当发生GC是以为 message – handler – acitivity 的引用链导致Activity无法被回收,所以发生了内存泄露的问题。

    完美解决方式:

    首先要感谢Badoo Trading Limited提供的开源Weak Handler;

    WeakHandler.java
    /*
     * Copyright (c) 2014 Badoo Trading Limited
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     *
     * Portions of documentation in this code are modifications based on work created and
     * shared by Android Open Source Project and used according to terms described in the
     * Apache License, Version 2.0
     */
    package com.librarypo.utilall;
    
    import android.os.Handler;
    import android.os.Looper;
    import android.os.Message;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.support.annotation.VisibleForTesting;
    
    import java.lang.ref.WeakReference;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * Memory safer implementation of android.os.Handler
     * <p/>
     * Original implementation of Handlers always keeps hard reference to handler in queue of execution.
     * If you create anonymous handler and post delayed message into it, it will keep all parent class
     * for that time in memory even if it could be cleaned.
     * <p/>
     * This implementation is trickier, it will keep WeakReferences to runnables and messages,
     * and GC could collect them once WeakHandler instance is not referenced any more
     * <p/>
     *
     * @see Handler
     *
     * Created by Dmytro Voronkevych on 17/06/2014.
     */
    @SuppressWarnings("unused")
    public class WeakHandler {
        private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory
        private final ExecHandler mExec;
        private Lock mLock = new ReentrantLock();
        @SuppressWarnings("ConstantConditions")
        @VisibleForTesting
        final ChainedRef mRunnables = new ChainedRef(mLock, null);
    
        /**
         * Default constructor associates this handler with the {@link Looper} for the
         * current thread.
         *
         * If this thread does not have a looper, this handler won't be able to receive messages
         * so an exception is thrown.
         */
        public WeakHandler() {
            mCallback = null;
            mExec = new ExecHandler();
        }
    
        /**
         * Constructor associates this handler with the {@link Looper} for the
         * current thread and takes a callback interface in which you can handle
         * messages.
         *
         * If this thread does not have a looper, this handler won't be able to receive messages
         * so an exception is thrown.
         *
         * @param callback The callback interface in which to handle messages, or null.
         */
        public WeakHandler(@Nullable Handler.Callback callback) {
            mCallback = callback; // Hard referencing body
            mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler
        }
    
        /**
         * Use the provided {@link Looper} instead of the default one.
         *
         * @param looper The looper, must not be null.
         */
        public WeakHandler(@NonNull Looper looper) {
            mCallback = null;
            mExec = new ExecHandler(looper);
        }
    
        /**
         * Use the provided {@link Looper} instead of the default one and take a callback
         * interface in which to handle messages.
         *
         * @param looper The looper, must not be null.
         * @param callback The callback interface in which to handle messages, or null.
         */
        public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) {
            mCallback = callback;
            mExec = new ExecHandler(looper, new WeakReference<>(callback));
        }
    
        /**
         * Causes the Runnable r to be added to the message queue.
         * The runnable will be run on the thread to which this handler is
         * attached.
         *
         * @param r The Runnable that will be executed.
         *
         * @return Returns true if the Runnable was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.
         */
        public final boolean post(@NonNull Runnable r) {
            return mExec.post(wrapRunnable(r));
        }
    
        /**
         * Causes the Runnable r to be added to the message queue, to be run
         * at a specific time given by <var>uptimeMillis</var>.
         * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
         * The runnable will be run on the thread to which this handler is attached.
         *
         * @param r The Runnable that will be executed.
         * @param uptimeMillis The absolute time at which the callback should run,
         *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
         *
         * @return Returns true if the Runnable was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.  Note that a
         *         result of true does not mean the Runnable will be processed -- if
         *         the looper is quit before the delivery time of the message
         *         occurs then the message will be dropped.
         */
        public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
            return mExec.postAtTime(wrapRunnable(r), uptimeMillis);
        }
    
        /**
         * Causes the Runnable r to be added to the message queue, to be run
         * at a specific time given by <var>uptimeMillis</var>.
         * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
         * The runnable will be run on the thread to which this handler is attached.
         *
         * @param r The Runnable that will be executed.
         * @param uptimeMillis The absolute time at which the callback should run,
         *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
         *
         * @return Returns true if the Runnable was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.  Note that a
         *         result of true does not mean the Runnable will be processed -- if
         *         the looper is quit before the delivery time of the message
         *         occurs then the message will be dropped.
         *
         * @see android.os.SystemClock#uptimeMillis
         */
        public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
            return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis);
        }
    
        /**
         * Causes the Runnable r to be added to the message queue, to be run
         * after the specified amount of time elapses.
         * The runnable will be run on the thread to which this handler
         * is attached.
         *
         * @param r The Runnable that will be executed.
         * @param delayMillis The delay (in milliseconds) until the Runnable
         *        will be executed.
         *
         * @return Returns true if the Runnable was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.  Note that a
         *         result of true does not mean the Runnable will be processed --
         *         if the looper is quit before the delivery time of the message
         *         occurs then the message will be dropped.
         */
        public final boolean postDelayed(Runnable r, long delayMillis) {
            return mExec.postDelayed(wrapRunnable(r), delayMillis);
        }
    
        /**
         * Posts a message to an object that implements Runnable.
         * Causes the Runnable r to executed on the next iteration through the
         * message queue. The runnable will be run on the thread to which this
         * handler is attached.
         * <b>This method is only for use in very special circumstances -- it
         * can easily starve the message queue, cause ordering problems, or have
         * other unexpected side-effects.</b>
         *
         * @param r The Runnable that will be executed.
         *
         * @return Returns true if the message was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.
         */
        public final boolean postAtFrontOfQueue(Runnable r) {
            return mExec.postAtFrontOfQueue(wrapRunnable(r));
        }
    
        /**
         * Remove any pending posts of Runnable r that are in the message queue.
         */
        public final void removeCallbacks(Runnable r) {
            final WeakRunnable runnable = mRunnables.remove(r);
            if (runnable != null) {
                mExec.removeCallbacks(runnable);
            }
        }
    
        /**
         * Remove any pending posts of Runnable <var>r</var> with Object
         * <var>token</var> that are in the message queue.  If <var>token</var> is null,
         * all callbacks will be removed.
         */
        public final void removeCallbacks(Runnable r, Object token) {
            final WeakRunnable runnable = mRunnables.remove(r);
            if (runnable != null) {
                mExec.removeCallbacks(runnable, token);
            }
        }
    
        /**
         * Pushes a message onto the end of the message queue after all pending messages
         * before the current time. It will be received in callback,
         * in the thread attached to this handler.
         *
         * @return Returns true if the message was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.
         */
        public final boolean sendMessage(Message msg) {
            return mExec.sendMessage(msg);
        }
    
        /**
         * Sends a Message containing only the what value.
         *
         * @return Returns true if the message was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.
         */
        public final boolean sendEmptyMessage(int what) {
            return mExec.sendEmptyMessage(what);
        }
    
        /**
         * Sends a Message containing only the what value, to be delivered
         * after the specified amount of time elapses.
         * @see #sendMessageDelayed(Message, long)
         *
         * @return Returns true if the message was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.
         */
        public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
            return mExec.sendEmptyMessageDelayed(what, delayMillis);
        }
    
        /**
         * Sends a Message containing only the what value, to be delivered
         * at a specific time.
         * @see #sendMessageAtTime(Message, long)
         *
         * @return Returns true if the message was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.
         */
        public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
            return mExec.sendEmptyMessageAtTime(what, uptimeMillis);
        }
    
        /**
         * Enqueue a message into the message queue after all pending messages
         * before (current time + delayMillis). You will receive it in
         * callback, in the thread attached to this handler.
         *
         * @return Returns true if the message was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.  Note that a
         *         result of true does not mean the message will be processed -- if
         *         the looper is quit before the delivery time of the message
         *         occurs then the message will be dropped.
         */
        public final boolean sendMessageDelayed(Message msg, long delayMillis) {
            return mExec.sendMessageDelayed(msg, delayMillis);
        }
    
        /**
         * Enqueue a message into the message queue after all pending messages
         * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
         * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
         * You will receive it in callback, in the thread attached
         * to this handler.
         *
         * @param uptimeMillis The absolute time at which the message should be
         *         delivered, using the
         *         {@link android.os.SystemClock#uptimeMillis} time-base.
         *
         * @return Returns true if the message was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.  Note that a
         *         result of true does not mean the message will be processed -- if
         *         the looper is quit before the delivery time of the message
         *         occurs then the message will be dropped.
         */
        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            return mExec.sendMessageAtTime(msg, uptimeMillis);
        }
    
        /**
         * Enqueue a message at the front of the message queue, to be processed on
         * the next iteration of the message loop.  You will receive it in
         * callback, in the thread attached to this handler.
         * <b>This method is only for use in very special circumstances -- it
         * can easily starve the message queue, cause ordering problems, or have
         * other unexpected side-effects.</b>
         *
         * @return Returns true if the message was successfully placed in to the
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.
         */
        public final boolean sendMessageAtFrontOfQueue(Message msg) {
            return mExec.sendMessageAtFrontOfQueue(msg);
        }
    
        /**
         * Remove any pending posts of messages with code 'what' that are in the
         * message queue.
         */
        public final void removeMessages(int what) {
            mExec.removeMessages(what);
        }
    
        /**
         * Remove any pending posts of messages with code 'what' and whose obj is
         * 'object' that are in the message queue.  If <var>object</var> is null,
         * all messages will be removed.
         */
        public final void removeMessages(int what, Object object) {
            mExec.removeMessages(what, object);
        }
    
        /**
         * Remove any pending posts of callbacks and sent messages whose
         * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
         * all callbacks and messages will be removed.
         */
        public final void removeCallbacksAndMessages(Object token) {
            mExec.removeCallbacksAndMessages(token);
        }
    
        /**
         * Check if there are any pending posts of messages with code 'what' in
         * the message queue.
         */
        public final boolean hasMessages(int what) {
            return mExec.hasMessages(what);
        }
    
        /**
         * Check if there are any pending posts of messages with code 'what' and
         * whose obj is 'object' in the message queue.
         */
        public final boolean hasMessages(int what, Object object) {
            return mExec.hasMessages(what, object);
        }
    
        public final Looper getLooper() {
            return mExec.getLooper();
        }
    
        private WeakRunnable wrapRunnable(@NonNull Runnable r) {
            //noinspection ConstantConditions
            if (r == null) {
                throw new NullPointerException("Runnable can't be null");
            }
            final ChainedRef hardRef = new ChainedRef(mLock, r);
            mRunnables.insertAfter(hardRef);
            return hardRef.wrapper;
        }
    
        private static class ExecHandler extends Handler {
            private final WeakReference<Callback> mCallback;
    
            ExecHandler() {
                mCallback = null;
            }
    
            ExecHandler(WeakReference<Callback> callback) {
                mCallback = callback;
            }
    
            ExecHandler(Looper looper) {
                super(looper);
                mCallback = null;
            }
    
            ExecHandler(Looper looper, WeakReference<Callback> callback) {
                super(looper);
                mCallback = callback;
            }
    
            @Override
            public void handleMessage(@NonNull Message msg) {
                if (mCallback == null) {
                    return;
                }
                final Callback callback = mCallback.get();
                if (callback == null) { // Already disposed
                    return;
                }
                callback.handleMessage(msg);
            }
        }
    
        static class WeakRunnable implements Runnable {
            private final WeakReference<Runnable> mDelegate;
            private final WeakReference<ChainedRef> mReference;
    
            WeakRunnable(WeakReference<Runnable> delegate, WeakReference<ChainedRef> reference) {
                mDelegate = delegate;
                mReference = reference;
            }
    
            @Override
            public void run() {
                final Runnable delegate = mDelegate.get();
                final ChainedRef reference = mReference.get();
                if (reference != null) {
                    reference.remove();
                }
                if (delegate != null) {
                    delegate.run();
                }
            }
        }
    
        static class ChainedRef {
            @Nullable
            ChainedRef next;
            @Nullable
            ChainedRef prev;
            @NonNull
            final Runnable runnable;
            @NonNull
            final WeakRunnable wrapper;
    
            @NonNull
            Lock lock;
    
            public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) {
                this.runnable = r;
                this.lock = lock;
                this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this));
            }
    
            public WeakRunnable remove() {
                lock.lock();
                try {
                    if (prev != null) {
                        prev.next = next;
                    }
                    if (next != null) {
                        next.prev = prev;
                    }
                    prev = null;
                    next = null;
                } finally {
                    lock.unlock();
                }
                return wrapper;
            }
    
            public void insertAfter(@NonNull ChainedRef candidate) {
                lock.lock();
                try {
                    if (this.next != null) {
                        this.next.prev = candidate;
                    }
    
                    candidate.next = this.next;
                    this.next = candidate;
                    candidate.prev = this;
                } finally {
                    lock.unlock();
                }
            }
    
            @Nullable
            public WeakRunnable remove(Runnable obj) {
                lock.lock();
                try {
                    ChainedRef curr = this.next; // Skipping head
                    while (curr != null) {
                        if (curr.runnable == obj) { // We do comparison exactly how Handler does inside
                            return curr.remove();
                        }
                        curr = curr.next;
                    }
                } finally {
                    lock.unlock();
                }
                return null;
            }
        }
    }
    

    自定义无内存泄漏handler,完全替代原生handler用法和原生差不多:

    消息接收:

        private WeakHandler mWeakHandler=new WeakHandler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            System.out.println("返回消息:"+msg.obj);
            intenMain();
            return false;
        }
    });

    消息发送:

    mWeakHandler.sendEmptyMessageDelayed(0, 1000 * 2);

    发送的方法和原生是一样,比原生的方便使用多了。

    总结:使用自定义的WeakHandler,基本使用方法和原生差不多,只不过不再使用handler。在这里只能吐槽Android技术并不是原生的一定是最好的,有时候我们开发者也是无所不能的。

    有用请点赞或者评论,谢谢咯!!

     

     

    展开全文
  • Android之Handler简单介绍

    千次阅读 2020-07-15 08:40:56
    今天我们的主角:Android消息机制——Handler Handler是什么: Android系统中线程间传递消息的一种机制 Handler作为这么重要的一种机制,虽然我们在使用的时候基本上只用到这一个类,但是还是有必要了解一下与之...
  • # 添加图例,通过handler_map参数添加自定义映射 lg = plt.legend( [s1, s2], ["类型1", "类型2"], handler_map={s1: custom_handler1, s2: custom_handler2}, labelspacing=2, fontsize=10, frameon=False, ) # ...
  • Handler源码分析(超详细的)

    千次阅读 2018-09-25 22:01:53
    Handler程序源码分析 这篇博客是一种入门级的但讲的很细,本人能力有限,希望看到大神,发现有不对的地方请联系我,也希望可以和大家在讨论区互动。 本文不管是从哪得到得信息,本人都认真的研究过和测试。包括...
  • Android多线程(Handler篇)

    万次阅读 多人点赞 2018-06-13 23:09:01
    今天的主角是Handler,本文将从使用及源码来分析探索其奥秘。 使用 步骤: 创建Handler对象,实现handlMessage()方法 创建Runnable线程 此时产生一个Looper,并自动创建一个消息队列MessageQueue()...
  • Android面试常客之Handler全解

    千次阅读 多人点赞 2018-03-11 18:51:14
    Android面试中经常被问到的知识——Android消息机制即Handler有关的问题你都能解释的清楚吗?如果你对Android消息机制比较模糊或者能够回答与Handler有关的问题但是不清楚其中的原理,那么你将会在本文得到你想要的...
  • Handler用法及解析

    万次阅读 多人点赞 2018-08-10 17:01:12
    1.handler作用:  1)传递消息Message 2)子线程通知主线程更新ui 2.常用api 3.handler使用避免内存泄露  1)handler怎么使用会产生内存泄露?  2)如何避免handler的内存泄露?  3) 雷区 4.handlerThread...
  • Mybatis优雅存取json字段的解决方案 - TypeHandler (一)

    万次阅读 多人点赞 2019-11-30 23:37:23
    register(TypeReference javaTypeReference, TypeHandler<? extends T> handler) Mybatis 并没有直接使用到,内部是从 javaTypeReference 获取到 rawType 之后,调用 register(javaType, typeHandler) 进行注册 ...
  • Android Handler消息机制原理最全解读(持续补充中)

    万次阅读 多人点赞 2018-05-13 19:22:57
    Handler  在Android开发的过程中,我们常常会将耗时的一些操作放在子线程(work thread)中去执行,然后将执行的结果告诉 UI线程(main thread),熟悉Android的朋友都知道,UI的更新只能通过Main thread来...
  • 一文搞懂Handler机制

    万次阅读 多人点赞 2018-08-31 18:38:07
    什么是handler? Handler是进程内部、线程间的一种通信机制。 Handler、Looper、MessageQueen、Message的关系 Message: 消息对象 MessageQueen: 存储消息对象的队列 Looper:负责循环读取MessageQueen中...
  • Android_Handler详解(一)

    2016-03-23 19:13:36
    1.handler是什么? 2.handler怎么用? 3.为什么要用handler?
  • Handler机制——同步屏障

    千次阅读 多人点赞 2019-08-10 10:10:19
    关于Handler机制的基本原理不了解可以看这里: Handler机制源码解析。 Message分为3中:普通消息、屏障消息和异步消息。我们通常使用的都是普通消息,而屏障消息就是在消息队列中插入一个屏障,在屏障之后的所有普通...
  • Android异步通信:深入剖析Handler机制源码

    万次阅读 多人点赞 2018-05-21 10:08:17
    在Android开发的多线程应用场景中,Handler机制十分常用 今天,我将手把手带你深入分析 Handler机制的源码,希望你们会喜欢 目录 1. Handler 机制简介 定义 一套 Android 消息传递机制 作用 在多...
  • Android开发中Handler的经典总结

    千次阅读 多人点赞 2018-04-18 21:10:01
    概述对于刚入门的同学来说,往往都会对Handler比较迷茫,到底Handler是个什么样的东西。当然,可能对于一些有工作经验的工程师来说,他们也不一定能很准确地描述,我们来看下API的介绍。Handler是用来结合线程的消息...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 947,928
精华内容 379,171
关键字:

handler