版权声明:本文为博主原创文章,欢迎转载。请注明原文链接 https://blog.csdn.net/ashqal/article/details/31772697

1 开头

上一讲讲到Looper,大家对Looper有了大概的了结(好几个月过去了…)

大家都知道一个Handler相应有一个MessageQueue,

在哪个线程上new Handler(假设不指定looper对象),那么这个handler就默认相应于这个线程上的prepare过的Looper

例如以下图Handler.java代码所看到的,mLooper由Looper.myLooper()指定,

public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
} mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

而Looper.myLooper()来自此线程里保存的looper对象(在looper.prepare时存入)

public static Looper myLooper() {
return sThreadLocal.get();
}

so。一个handler。相应了一套MessageQueue、Thread、Looper

这些都是 【从源代码看Android】01从Looper说起 讲过的东西,那么以下来些硬货

2 一个问题引入

从一个问题引入,假设在子线程12上创建了一个handler,

如今在主线程上调用handler.sendEmptyMessage,

handler怎样在主线程上处理这个msg,

然后从子线程12让handler的handleMessage函数处理呢?

那么这个时候就要引入一个跨线程的事件模型--epoll,

这一讲先把cpp epoll模型讲清楚,

下一讲再讲android里怎样利用这个模型的

3 epoll模型

epolldemo.cpp

#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <assert.h>
#include <fcntl.h> #define NUM_THREAD 4
#define NUM_LENGTH 200
#define MAX_EVENTS 20 #define USES_EPOLL #ifdef USES_EPOLL
/**** (1).创建一个epoll描写叙述符,调用epoll_create()来完毕,epoll_create()有一个整型的參数size。用来告诉内核,要创建一个有size个描写叙述符的事件列表(集合)
int epoll_create(int size) (2).给描写叙述符设置所关注的事件,并把它加入到内核的事件列表中去,这里须要调用epoll_ctl()来完毕。 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
这里op參数有三种,分别代表三种操作:
a. EPOLL_CTL_ADD, 把要关注的描写叙述符和对其关注的事件的结构,加入到内核的事件列表中去
b. EPOLL_CTL_DEL,把先前加入的描写叙述符和对其关注的事件的结构,从内核的事件列表中去除
c. EPOLL_CTL_MOD。改动先前加入到内核的事件列表中的描写叙述符的关注的事件 (3). 等待内核通知事件发生。得到发生事件的描写叙述符的结构列表,该过程由epoll_wait()完毕。 得到事件列表后,就能够进行事件处理了。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout) – EPOLLIN。读事件
– EPOLLOUT,写事件
– EPOLLPRI,带外数据。与select的异常事件集合相应
– EPOLLRDHUP,TCP连接对端至少写写半关闭
– EPOLLERR,错误事件
– EPOLLET,设置事件为边沿触发
– EPOLLONESHOT,仅仅触发一次,事件自己主动被删除 */
int g_epollfd;
int g_wakeFds[2];
#endif void awake()
{
ssize_t nWrite;
do
{
nWrite = write(g_wakeFds[1], "W", 1);
}
while (nWrite == -1);
} void awoken()
{ char buffer[16];
ssize_t nRead;
do {
nRead = read(g_wakeFds[0], buffer, sizeof(buffer));
} while ((nRead == -1 ) || nRead == sizeof(buffer));
} using namespace std;
void* threadRead(void* userdata)
{
queue<int>* q = (queue<int>*)userdata; struct epoll_event events[MAX_EVENTS];
while( true )
{
int fds = epoll_wait(g_epollfd, events, MAX_EVENTS, 1000);
if(fds < 0){
printf("epoll_wait error, exit\n");
break;
} for(int i = 0; i < fds; i++){
if( events[i].events & EPOLLIN ) // read event
{
printf("%s,%d/%d\n", "EPOLLIN",i,fds);
while( !q->empty() )
{
q->pop();
printf("removed! \n" );
}
}
}
awoken();
}
return userdata;
} void* threadRun(void* userdata)
{
queue<int>* q = (queue<int>*)userdata;
while( true )
{ #ifdef USES_EPOLL
q->push( 1 );
printf("%ld:%s\n",(long)pthread_self() ,"added!");
awake(); #else
#endif
usleep(1000*500);
}
printf("exit thread:%ld\n",(long)pthread_self() );
return userdata;
} int main(int argc, char const *argv[])
{
/**
pipe(建立管道):
1) 头文件 #include<unistd.h>
2) 定义函数: int pipe(int filedes[2]);
3) 函数说明: pipe()会建立管道,并将文件描写叙述词由參数filedes数组返回。 filedes[0]为管道里的读取端
filedes[1]则为管道的写入端。
*/
int result = pipe(g_wakeFds);
assert( result!=0 ); result = fcntl(g_wakeFds[0], F_SETFL, O_NONBLOCK);
assert(result!=0); result = fcntl(g_wakeFds[1], F_SETFL, O_NONBLOCK);
assert(result!=0); g_epollfd = epoll_create( MAX_EVENTS );
assert( g_epollfd > 0 ); struct epoll_event epv = {0, {0}};
//epv.data.ptr = userdata;
epv.data.fd = g_wakeFds[0];
epv.events = EPOLLIN; if(epoll_ctl(g_epollfd, EPOLL_CTL_ADD, g_wakeFds[0], &epv) < 0)
printf("Event Add failed[fd=%d], evnets[%d]\n", epv.data.fd, epv.events);
else
printf("Event Add OK[fd=%d], op=%d, evnets[%0X]\n", epv.data.fd, EPOLL_CTL_ADD, epv.events); queue<int> q;
vector<pthread_t> v;
for (int i = 0; i < NUM_THREAD; ++i)
{
pthread_t tid;
pthread_create(&tid,NULL,threadRun,&q);
v.push_back(tid);
} pthread_t tid;
pthread_create(&tid,NULL,threadRead,&q);
v.push_back(tid); for(vector<pthread_t>::const_iterator it = v.begin(); it < v.end(); ++it)
pthread_join(*it,NULL); return 0;
}

大致思路是这种:

a.127行開始建立管道g_wakeFds,g_wakeFds[0]是读取port。g_wakeFds[1]是写入port

b.136行创建全局的g_epollfd,即epoll文件描写叙述符,參数为这个文件描写叙述符所支持的最大事件数

c.144行epoll_ctl创建一个事件关联,即将g_epollfd与g_wakeFds[0]进行关联,假设g_wakeFds[0]发生变化,就会触发事件,而且事件为139创建的epoll_event实例

d.151-156行创建多个线程作为生产者,生产int放入queue中,放入完后调用awake()函数,向g_wakeFds[1]写入一字节,触发事件

f.158-160行创建一个消费者来消费生产的int

g.当中76行int fds = epoll_wait(g_epollfd, events, MAX_EVENTS, 1000);来等待生产者生产的int。当g_wakeFds[1]有数据写入时,g_wakeFds[0]就会触发刚刚注冊的事件。获取到注冊的事件后对事件进行处理(消费int)。随后调用awoken()清空g_wakeFds[0]。进入下一轮epoll_wait

注意:生产enqueue和消费dequeue是须要同步锁的,这里省略了这个过程。android在java中对Message实现的同步锁

4 执行结果

5 源代码下载

http://pan.baidu.com/s/1i3BTWpv

6 总结

当一个线程的消息队列没有消息须要处理时。它就会在这个管道的读端文件描写叙述符上进行睡眠等待,直到其它线程通过这个管道的写端文件描写叙述符来唤醒它。这样就节省了线程上对于cpu资源的消耗。

7 reference

《Android系统源代码情景分析》- 罗升阳

Android NDK 源代码

Android SDK 源代码

【从源代码看Android】02MessageQueue的epoll原型的更多相关文章

  1. 从cocos2dx源代码看android和iOS跨平台那些事

    cocos2dx一个跨移动(平板)平台的游戏引擎,支持2d和3d,基于c/c++,网上介绍多在此不详叙.我们本篇关心的是跨平台那些事,自然而然就找到platform目录.好家伙,支持的操作平台还真不少 ...

  2. 在线看Android系统源码,那些相见恨晚的几种方案

    请尊重分享成果,转载请注明出处,本文来自逆流的鱼yuiop,原文链接:http://blog.csdn.net/hejjunlin/article/details/53454514 前言:最近在研究M ...

  3. 使用具体解释及源代码解析Android中的Adapter、BaseAdapter、ArrayAdapter、SimpleAdapter和SimpleCursorAdapter

    Adapter相当于一个数据源,能够给AdapterView提供数据.并依据数据创建相应的UI.能够通过调用AdapterView的setAdapter方法使得AdapterView将Adapter作 ...

  4. Android菜鸟的成长笔记(17)—— 再看Android中的Unbounded Service

    原文:Android菜鸟的成长笔记(17)-- 再看Android中的Unbounded Service 前面已经写过关于startService(Unbounded Service)的一篇文章:&l ...

  5. 源代码解析Android中View的layout布局过程

    Android中的Veiw从内存中到呈如今UI界面上须要依次经历三个阶段:量算 -> 布局 -> 画图,关于View的量算.布局.画图的整体机制可參见博文 < Android中Vie ...

  6. MonkeyRunner源代码分析Android通信设备

    正如前面<谁动了我的截图?--Monkeyrunner takeSnapshot方法源代码跟踪分析>所述,本文主要会尝试描写叙述android的自己主动化測试框架MonkeyRunner到 ...

  7. 结合源代码分析android的消息机制

    描写叙述 结合几个问题去看源代码. 1.Handler, MessageQueue, Message, Looper, LocalThread这5者在android的消息传递过程中扮演了什么样的角色? ...

  8. android sdk 编译--如何将源代码加入android.jar,以及make原理

    首先是这个问题如何修改. 在/frameworks/base/Android.mk中,找到如下行:packages_to_document :=在该变量的赋值语句最后添加xxxxx (这里是你的包的名 ...

  9. 深入源代码解析Android中的Handler,Message,MessageQueue,Looper

    本文主要是对Handler和消息循环的实现原理进行源代码分析.假设不熟悉Handler能够參见博文< Android中Handler的使用>,里面对Android为何以引入Handler机 ...

随机推荐

  1. [javaSE] 基本类型(String相关)

    字符串是一个特殊的对象 字符串一旦初始化就不可以被改变 获取字符串的长度 调用String对象的length()方法,返回int长度 获取某个索引位置的字符 调用String对象的charAt()方法 ...

  2. java 全自动生成Excel之ExcelUtil篇(上一篇的升级版 [针对实体类对象的遍历赋值])

    看了上一篇随笔之后可以对本篇有更好的了解! 使用的poi的jar包依然是上一篇的poi-3.17.jar.... import pojo.UserPojo(上一篇里有,这里就不粘贴了!) 不废话了,直 ...

  3. zookeeper watcher

    ZooKeeper 的  watcher 机制主要包括客户端线程.客户端 WatchManager 和 ZooKeeper 服务器三部分. 工作方式:客户端在向 zk 服务器注册 Watcher 的同 ...

  4. Linux常用指令大全

    2017-03-25   16:35:42 刚开始学习Linux,由于记忆力有限,把平时常用的Linux命令整理出来,以便随时查阅:  linux 基本命令    ls     (list 显示当前目 ...

  5. task16 表格增减笔记

    trim()方法会创建一个字符串副本,删除前置及后缀所有空格,然后返回结果(中间的空格符无法消除) match()方法可在字符串内检索指定的值,找到一个或多个正则表达式的匹配 正则表达式 匹配中文:[ ...

  6. drupal7,注册成功之后想跳转到指定页面,该怎么破?

     1.hook sigup form alter,修改跳转地址 .还没试过 2.安装一下logintoboggan模块,里面有个注册后跳转到哪个页面的设置 这个对于不写代码的是比较方便的方法    3 ...

  7. css之图像替换

    time: 2016-03-30 20:00 这个月有点忙,学业的事工作的事私人的事有点烦,但是不能停止学习更不能忘记写博客! 最近看了<精通css>这本书,挑了一个点纪录一下. 一.含义 ...

  8. Spring Boot—03REST请求

    package com.smartmap.sample.ch1.controller.rest; import java.util.List; import org.apache.commons.lo ...

  9. Java volatile关键字解惑

    volatile特性 内存可见性:通俗来说就是,线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的. volatile的使用场景 通过关 ...

  10. 612.1.002 ALGS4 | Analysis of Algorithms

    我们生活在大数的时代 培养数量级的敏感! Tip:见招拆招 作为工程师,你先要能实现出来. 充实基础,没有什么不好意思 哪怕不完美.但是有时候完成比完美更重要. 之后再去想优化 P.S.作者Rober ...