关于Handler的拦截
来源:互联网 发布:中国和朝鲜的关系知乎 编辑:程序博客网 时间:2024/05/16 12:57
为什么要Hook Handler?Android系统中存在大量Handler,我们要改变系统的某些行为就需要Hook掉一些关键节点的Handler,为此,我们要清楚Handler的工作原理。
先做一个实验来看看Hook Handler的效果,如下:
public class MainActivity extends Activity { private Button mBtnShow; private Button mBtnHook; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, (CharSequence) msg.obj, Toast.LENGTH_SHORT).show(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtnShow = (Button) findViewById(R.id.btn); mBtnHook = (Button) findViewById(R.id.hook); mBtnShow.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub mHandler.obtainMessage(0, "hello world").sendToTarget(); } }); mBtnHook.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub try { hookHandler(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } private void hookHandler() throws IllegalAccessException, IllegalArgumentException { Field field = FieldUtils.getDeclaredField(Handler.class, "mCallback", true); field.set(mHandler, new Handler.Callback() { @Override public boolean handleMessage(Message msg) { // TODO Auto-generated method stub msg.obj = "hello china"; return false; } }); }}
放两个按钮,一个按钮是发送消息来显示Toast,另一个按钮是Hook Handler,改变发送的消息内容。
这里Hook所做的事情很简单,其实就是通过反射改变Hander的mCallback,值得注意的是这个Callback的handleMessage中改变了Message后返回了false。这样做是为了继续调用Handler的handleMessage,只是Handler不知道发给他的Message已经被暗中动了手脚。
我们看看Handler的内在实现,Handler其实只是一个工具,用于封装消息再丢给MessageQueue而已,真正的核心在Looper和MessageQueue。我们重点看一下Looper,里面有一个loop函数,就是循环处理消息的,如下:
public static void loop() { final Looper me = myLooper(); final MessageQueue queue = me.mQueue; for (;;) { Message msg = queue.next(); msg.target.dispatchMessage(msg); msg.recycle(); }}
逻辑很简单,通过queue.next()依次从MessageQueue中取出Message,然后调用msg.target的dispatchMessage,这个target其实就是Handler,我们来看看Handler是怎么dispatchMessage的:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}
首先优先调用Message中的Callback,如果没有再来调用Handler的全局Callback,如果没有mCallback则调用Handler的handleMessage,如果有mCallback但是mCallback.handleMessage返回false,同样会调用Handler的handleMessage,如果返回true则直接返回了。我们Hook的核心就在这里了。
好了,顺便再看看是怎么从MessageQueue中取出消息的,如下:
Message next() { for (;;) { .......... nativePollOnce(mPtr, nextPollTimeoutMillis); .......... if (mQuitting) { return null; } }}
这是个无限循环的过程,除非退出标志被置位。而从消息队列中取出消息可能被阻塞,原因是队列为空了。这里nativePollOnce就是等待队列中有消息,当有消息时立即返回。我们看看里面的实现,这个是native的函数:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz, jint ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(env, timeoutMillis);}int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { .......... pollInner(timeoutMillis);}int Looper::pollInner(int timeoutMillis) { .......... int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); for (int i = 0; i < eventCount; i++) { .......... }}
真相大白,原来是用epoll_wait来监测fd是否可读写,这个fd通常是管道,那么是否意味着当往队列里添加消息时,需要向这个fd里写一些数据呢,这样epoll_wait就能被唤醒。我们来看看消息是如何添加到队列里的:
boolean enqueueMessage(Message msg, long when) { .......... if (needWake) { nativeWake(mPtr); } return true;}
这里给消息添加到队列后,调用了nativeWake来唤醒队列:
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jint ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); return nativeMessageQueue->wake();}void NativeMessageQueue::wake() { mLooper->wake();}void Looper::wake() { ssize_t nWrite; do { nWrite = write(mWakeWritePipeFd, "W", 1); } while (nWrite == -1 && errno == EINTR); if (nWrite != 1) { if (errno != EAGAIN) { ALOGW("Could not write wake signal, errno=%d", errno); } }}
果然如我们所料,这里唤醒就是往fd中写入了一个W字符而已。
- 关于Handler的拦截
- 关于Handler的使用
- 关于Android的Handler
- 关于Handler 的 removemessage
- 关于 handler 的 水印 技术 、、
- 关于Android-Handler的详解
- 关于handler机制的总结
- 关于Handler 的Post方法
- 关于Handler的突然理解
- 关于handler机制的原理
- 关于handler的那些事
- 关于Handler的简单总结
- 关于Runable、Handler的循环
- Android--关于Handler的使用
- 关于struts2的拦截器
- 关于springmvc的拦截器
- 关于OKhttp的拦截器
- <mvc:default-servlet-handler/>不拦截静态资源的前提条件
- MYSQL服务无法启动:InnoDB: .\ibdata1 can't be opened in read-write mode
- UITextView 实现placeholder的方法
- 观察者模式
- 移动前端开发知识资源整理
- httpclient解析https网页
- 关于Handler的拦截
- Webshell-Part1&Part2
- 深入了解jBPM5与Activiti之间的差异对比
- shadowsocks
- oracle批量更新
- Linux中读取目录: opendir,fdopendir,readdir,rewinddir,closedir,telldir,seekdir
- 开始写博客啦!!
- Android学习笔记之AndroidManifest.xml文件解析
- docker创建disconf镜像并应用