由于日常工作不需要经常写android上层应用,对Android的Handler机制一直处于模模糊糊的状态。使用Handler之后,回去写c++代码时,时刻怀念Android里面的Handler,希望有一个c++版本的Handler。为了能自己实现c++版本的Handler,今天准备去梳理一下这Android中的Handler。

理解Android中的Handler

Handler的适用场景

只要写多线程程序,必不可少要碰到线程通信的问题。其中一个办法就是在工作线程中开辟一个消息队列,别的线程往消息队列发消息,即可完成与本线程的通信。工作线程顺序的从消息队列中读取消息并逐一处理。这种模型的好处是:

  • 消息可以缓存
  • 工作线程中消息是顺序处理的,所以不需要加锁。

Handler适用的就是这种场景:多个线程往一个工作线程发消息,工作线程顺序挨个处理。

Android中的Handler

google设计Handler最初的原因

在安卓应用中,多个线程去处理UI的刷新,而UI操作不是线程安全的,如果要实现多线程对UI的操作,就需要在每一个要进行UI操作的地方进行加锁。
加锁操作引入的问题

  • 锁的管理复杂,开发者容易忘记加锁,解锁,或者乱加锁造成死锁
  • 加锁是有性能消耗的
  • Android选用java作为开发语言就是看中其开发效率,引入锁加大开发难度了!

因此UI操作就该由一个单一线程进行处理,在Android中,UI线程就是应用进程的第一个线程–主线程。所以每一个android应用的开发者都会被告诫:不要在主线程里搞事情阻塞UI处理。

为了简化操作,google设计了Handler机制,开发者只需要创建一个Handler,然后在别的线程利用主线程的handler发送消息即可完成与主线程的通信。

Handler, Looper, Thread ,Message之间的关系

  • Looper: 用来管理消息队列的,它提供一个loop()接口,不停的从消息队列中取消息来处理。
  • Thread: 是Looper运行的载体,一个Thread有且只有一个Looper。
  • Handler: 是用来往运行与Thread里的Looper的消息队列发消息用的,同时Handler里有一个消息处理函数,Thread会用Looper取出消息,然后在Thread里执行这个函数来处理消息。
  • Message: 消息内容的封装,里边包含消息的处理函数,用户可以在发消息的时候动态指定其执行的处理函数

Android中Handler的使用

下面以实际代码简单看下如何用Handler与主线程的通信的:

class MainActivity extends Activity {Handler mHandler = new Handler(){//主线程Looper收到消息后,会当没有指定具体回调函数,会回调这个函数处理消息。//也就说下面的函数将会在主线程内执行@Overridepublic void handleMessage(Message msg) {switch(msg.what){case 1:break;default:break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {//onCreate这里是主线程中调用的,//使用Handler重要的一点就是要明白代码在哪个线程里执行//下面,我们在子线程里边给主线程发消息// 方式1: new Thread(){@Overridepublic void run(){//往主线程消息队列发消息//这种方式最终会在主线程中执行 mHandler.handleMessage(msg)mHandler.sendEmptyMessage(1);}}.start();// 方式2: new Thread(){@Overridepublic void run(){//我觉得这种方式才是Handler的精髓,用户在代码里随处都可以写处理UI操作//的代码mHandler.post(new Runnable() {@Overridepublic void run() {//这里的代码将会被在主线程中执行!真是太方便了}});}}.start();}
}

主线程里边的Looper是自动创建的吗?

初次使用Handler,都会有一个疑问:以上的代码,没有创建Looper,Looper是自动创建的吗?
实际上,不会自动创建,只是在执行到Activity的onCreate之前,系统已经帮忙创建Looper了。

framework/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {//这里创建main线程的LooperLooper.prepareMainLooper();//...//死循环处理消息,知道为什么不能阻塞主线程了吧!//主线程就是取消息然后以阻塞的方式一个个顺序执行//你在主线程中搞耗时的东西,你的UI就要卡在!!!Looper.loop();
}

Handler怎么关联上Looper的?

前面说的每一个线程里面有且仅有一个Looper,在创建Handler时,会去获取当前线程的Looper,并赋值给Handler对象。

请看Handler类的构造函数
frameworks/base/core/java/android/os/Handler.java

public Handler() {  //就是这个Looper.myLooper();mLooper = Looper.myLooper();//这里就是如果工作线程没有创建looper前,创建Handler会失败的原因if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}
} //myLooper利用的就是linux提供的线程局部存储实现的,每个线程都有独立的一个。
public static final Looper myLooper() {return (Looper)sThreadLocal.get();
}

还有另一个带参数的构造函数

public Handler(Looper looper) {...//最终执行mLooper = looper;
}

可见:Handler从创建开始就要指定其Looper, 默认构造函数就是使用当前线程的Looper。

自定义的工作线程怎么创建Looper和使用Handler?

如果我们要在自己的线程中使用Handler机制,则需要执行创建Looper对象。
创建Looper的方法很简单,就是执行Looper.prepair()即可。

class MyThread extends Thread {private Handler myHandler = null;private boolean mPrepaired = false;public Handler getMyHandler(){//没有创建Looper,并loop前,handler发消息没人去处理if (mPrepaired)return null;return myHandler;}@Overridepublic void run() {//第一步:创建当前线程对应的LooperLooper.prepare();//第二步:创建handler,会自己匹配到当前线程的Looper//如果没有执行第一步,这里就会报异常myHandler = new Handler();mPrepaired = true;//第三步:让Looper跑起来,开始消息的读取和消息处理//没有执行loop操作,handler发的消息是没人处理的Looper.loop();}
}

HandlerThread介绍

google封装了一个HandlerThread类,进一步简化自定义线程消息处理,就不用那么麻烦的处理Looper的创建和启动了。HandlerThread的使用类似下面这样:

class MyHandler extends Handler {//这种方式,使用的创建Handler所在线程的Looperpublic MyHandler(){}//为了与自定义的线程关联,需要指定对应线程的looper。public MyHandler(Looper looper){super(looper);}@Overridepublic void handleMessage(Message msg){}
}
HandlerThread t = new HandlerThread("mythread");
t.start();//注意那个t.getLooper()
Handler h = new MyHandler(t.getLooper());

//注意那个t.getLooper(),其实内部是阻塞等到线程执行完Looper.prepair()才返回的

frameworks/base/core/java/android/os/HandlerThread.java

public Looper getLooper() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() && mLooper == null) {try {//这里等looper创建完毕wait();} catch (InterruptedException e) {}}}return mLooper;
}

如何实现c++版本的Handler呢?

用了Handler后,回去写c++代码,没有这种东西,但是我们基本了解Handler的实现原理了,实现一个就行。

  1. 如何确保线程里有且仅有一个消息队列?
    linux的pthread库提供在线程内创建私有变量的接口:
#include <pthread.h>
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);
  1. 如何实现android中java的handler.post(Runnable)类型的接口?
    java里有匿名函数,使用很方便,其实在c++11里也支持这种写法了。

    //这里搞一个匿名函数,输入参数为空
    handler->post([](){});
    
  2. 伪代码实现
typedef void (*MessageCallback)();class Message {public:int what;int arg1;//void* obj;Message* obtain();MessageCallback callback;
private:vector<Message> mMsgPool;
};class Handler {public:virtual ~Handler();void sendMessage(Message* msg) {mLooper->enQueue(msg);}void setLooper(Looper* Looper);//可以像java,post(new Runagle())那样用void post(MessageCallback callback);virtual void handleMessage(Message* msg){}private:Looper* mLooper;
};class Looper{public:static void prepair();static void loop();//提供一个接口用来获取当前线程的Looperstatic Looper* getThreadLocalLooper();void enQueue(Message* msg){mMsgQ.enQueue(msg);}Message* deQueue(){return mMsgQ.deQueue();}
private:MessageQueue mMsgQ;
};class MessageQueue {public:void enQueue(Message* msg);Message* deQueue();
};void threadfunc(Handler* handler) {Message* msg = Message::obtain();handler->sendMessage(msg );
}int  main(int argc, char** argv) {Looper::prepair();Handler *handler = new Handler();//开个线程来往主线程发消息std::thread t(threadfun,handler);t.detach();Looper::loop();return 0;
}class MyThread {public:MyThread() {mThread = NULL;mPrepaired = false;}Looper* getLooper(){while(!mPrepaired ){sleep(1);}return Looper::getThreadLocalLooper();}void start() {if (NULL == mThread) {mThread = new thread(threadfunc, this);}}
private:static void threadfunc(MyThread* t) {Looper::prepair();Looper::loop();}bool mPrepaired;thread* mThread;
};//非主线程测试
int  main2(int argc, char** argv) {//创建自定义线程MyThread t;//在线程内构造自己的Loopert.start();//Handler *handler = new XXXHandler();Handler *handler = new Handler();handler->setLooper(t.getLooper());//主线程向自定义线程的消息队列发消息while ( true ) {Message* msg = Message::obtain();t.getHandler()->sendMessage(msg);/*也可以像java那样,直接指定消息的回调处理t.getHandler()->post([](){cout << "haha" << endl;});*/sleep(1);}return 0;
}

结束语

经过梳理过后,自己再实现一遍,对Handler才有更深的认识。实现个c++版本的Handler,以后用到的时候拿来用会很方便。上面是伪代码实现,具体的代码请关注我的公众号。回复"myhandler"获取完整代码。

自己写个C++版本Handler来理解Android的Handler机制相关推荐

  1. 理解 Android 的 Binder 机制

    Binder机制的工作流程 1.客户端获取服务端的代理对象(proxy).我们需要明确的是客户端进程并不能直接操作服务端中的方法,如果要操作服务端中的方法,那么有一个可行的解决方法就是在客户端建立一个 ...

  2. android handler 传递对象,Android之Handler消息传递机制详解

    前言 在Android开发中,多线程应用是非常频繁的,其中Handler机制随处可见. 下面就本人对Handle的一些理解与大家一起分享,共同回顾下Handle异步消息传递机制. 1.Handler是 ...

  3. 深入理解Android系统多用户机制

    一.引言 这篇文章以Android v28的源码为参考,介绍Android多用户的特性.使用方式和系统原理. 二.初识Android多用户 2.1 Android多用户简介 从Android 4.0开 ...

  4. android的handler使用方法,android中handler用法总结

    一.Handler的定义: Handler主要接收子线程发送的数据, 并用此数据配合主线程更新UI,用来跟UI主线程交互用.比如可以用handler发送一个message,然后在handler的线程中 ...

  5. 深入理解 Android 的 IPC 机制--------Binder

    在android中,,应用程序虽然是以独立的进程来运行的,但相互之间还是需要通信..比如,,我们的应用程序 和后台服务往往会运行不同的进程当中,,各自有这独立内存地址空间,,,但是又要彼此互相合作,, ...

  6. android handler的理解

    android handler的理解 在看handler源码前,我一直以为google构造handler的目的是方便开发者在其他线程中 调用执行主线程的方法或者在主线程中调用执行其他线程的方法.看完源 ...

  7. 我理解的Hanlder--android消息传递机制

    每一个学习Android的同学都会觉得Handler是一个神奇的东西,我也一样,开始我以为我懂了Handler的机制,后来发现自己是一知半解,昨天想想,我能否自己实现一个Handler,让子线程与Ac ...

  8. Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    <div id="container">         <div id="header">     <div class=&qu ...

  9. 通过源码简要分析之Android消息机制Handler、Looper、MessageQueue运行机制

    用了许久的Handler,对于Handler的使用确实是比较熟悉,但是具体内部是如何运作的,却只是模糊的.Handler发出的消息怎么到达MessageQueue?MessageQueue的数据怎么被 ...

最新文章

  1. 卷积核一定可以提升网络性能吗?-分类0,2
  2. Py之skflow:skflow的简介、安装、使用方法、代码实现之详细攻略
  3. ip动态分配痕迹会保留多久_16,虚拟机的服务IP老变,怎么办?
  4. 华为荣耀电脑第三方linux,【第三方Linux版】荣耀MagicBook Pro 16.1英寸全面屏如何?某东入手评测...
  5. html烟火源码,HTML5:烟火
  6. Linux 内存管理:DAX(Direct Access)机制的作用及实现原理
  7. 谷歌邮箱lmap服务器填什么_常用邮箱SMTP服务器设置
  8. pkg打包node项目文件
  9. 计算机本科科研什么项目,本科生做科研:大势所趋?
  10. 计算机组装有哪些,DIY电脑组装需要哪些东西
  11. 钉钉打新债自动提醒-python
  12. Numerical analysis second editon Timothy sauer 书附代码
  13. 采购订单中带账户分配的总账科目确定(M和E的区别)
  14. ssm健康饮食推荐系统分析与设计 毕业设计-附源码261631
  15. SAP 收货与发票校验税码
  16. 一些关于语音识别和语音情感识别的资源
  17. 2.古体诗的换韵和重韵
  18. [机缘参悟-37]:人感官系统的结构决定了人类是以自我为中心
  19. Python制作炫酷的词云图(包含停用词、词频统计)!!!
  20. 众测图数据库 Nebula Graph | 捉虫计划已开启,这项有礼

热门文章

  1. RT-Thread学习笔记——PIN 设备
  2. 数据结构与算法详解(含算法分析、动图图解、Java代码实现、注释解析)
  3. 数据结构与算法分析(六)队列总结
  4. mysql存储过程默认参数_mysql 存储过程 默认值解决办法
  5. html中的form表单
  6. 栈与队列2:用队列实现栈
  7. 按键精灵 缺少对象mysql_按键精灵无论2014最新版还是09版本的都出现缺少对象: 'QMLibrary' _ BUG反馈与功能建议 - 按键精灵论坛...
  8. python七夕快乐_七夕送小姐姐!女生勿扰,只适合男孩子的python爬虫!
  9. 3年自动化测试,你真的理解自动化测试持续集成(CI)吗?
  10. 第一章CSS层叠样式表