版权声明:本文为博主原创文章,欢迎转载。请注明原文链接 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. sgsdg

    wrjow we wetwer werwer werwer werqw qweqwrq qwrqwr @ApiOperation("根据条件分页查询试卷") @ApiRespons ...

  2. Java中接口的特点

    Java接口在1.8之后发生了重大变化.所以谈Java接口特点可以分为1.8版本之前和1.8版本之后. 1.8版本之前的特点: 接口里只能有静态全局常量和public修饰的抽象方法. 为了代码简洁,在 ...

  3. 有趣:256个class选择器可以干掉1个id选择器——张鑫旭

    我们应该都知道,从选择器得分权重上将,id选择器(#aaa{})和class选择器(.aaa{})完全不是一个数量级的,前者:1-0-0; 而后者为0-1-0.因此: #id { color:dark ...

  4. php5.5过渡--变量

    单纯的定义变量,如: $usernumber = $_POST['usernumber']; 会出现警告: Notice: Undefined index: usernumber in ... 规范问 ...

  5. JS判断客户端是否是iOS或者Android端

    通过判断浏览器的userAgent,用正则来判断手机是否是 IOS 和 Android 客户端. 代码如下: (function(){ var u = navigator.userAgent; var ...

  6. elixir 模式匹配

    elixir 模式匹配刚接触还是有点不习惯,在Elixir里,=操作符被称为匹配操作符 iex(29)> x = 11iex(30)> x1iex(31)> 1 = x1iex(32 ...

  7. 强网杯2018 pwn复现

    前言 本文对强网杯 中除了 2 个内核题以外的 6 个 pwn 题的利用方式进行记录.题目真心不错 程序和 exp: https://gitee.com/hac425/blog_data/blob/m ...

  8. oracle sql 命令类别

    1.数据定义语言 DDL 有 create alter drop2.数据操纵语言 DML insert select delete update3.事务控制语言 TCL commit savepoin ...

  9. Automate the Sizing of your SGA in Oracle 10g

    How much memory does each of the individual components of the SGA need? Oracle now has methods to de ...

  10. Swagger RESTful API文档规范

    *注意编写的关键词:“必须”.“不能”.“需要”.“应当”,“不得”.“应该”.“不应该”,“推荐”.“可能”和“可选的” 原文链接:http://swagger.io/specification/ ...