Intent深入理解

Intent深入理解

Intent的通讯机制是基于Binder的,而Binder的机制本质上是共享内存

概念:

Intent的架构:

包括三方面

Client,也就是发送这个Intent的activity;
Server,也就是activityManagerService.java,它主要是负责分发这些Intent给适当的对象;
Target,也就是那些需要处理这个Intent的activity,我们称为Receiver;

 

发送过程在代码在ActivityManagerNative.java里面:
这是一个典型的Binder调用,从此以后代码的执行进入了另外一个进程

 

串行并行stickyBroadCast

串行:就表示这个Intent必须一个一个的发送给接收者;
并行:表示这个Intent可以同时发送给多个接收者,通常广播的消息都是并行的;
Sticky:这个类型的BroadCast比较难以理解,问了google也没有答案,我个人的理解是这样的,某些Intent需要被保留,当新的应用 起来后,需要关注这个消息,但是呢,又不需要启动这个应用来接收此消息,比如耳机插入等消息,这里说实话,真的很巧妙,我们以前在maemo上碰到过这个 问题,当时我们的策略是应用起来的时候自己查询耳机的状态,这里的处理明显就高明许多

 

Intent的启动过程:

intent发送执行准备过程

1、发送前的准备,在broadcastIntentLocked中完成:

完成Intetnt的检查工作;

通过queryIntent(Intentintent, String resolvedType, boolean defaultOnly)进行匹配;

2、如果是registeredReceivers不为空并且这个Intent不是串行的,也就是上一步已经取出了对应的接收者,那么就需要把这个Intent封装成一个BroadcastRecord,然 后加入到mParallelBroadcasts,这个称为并行广播,也就是说可以同时发送给多个接收者,再通过 scheduleBroadcastsLocked触发真正的发送;

3、然后对registeredReceivers和 receivers做一个合并,如果这两个都不为空的话,记住,合并前这个receivers标识了“具有固定对象的接收者或者是当前已经注册的接收者不包括广播接收者”,而registeredReceivers表示broadcastFilter,另外这步能合并的前提是这个Intent是串行的Intent,否则是不会合并的;

4、合并以后receivers表示所有的串行receivers通过mOrderedBroadcasts.add(r)加入到列表中去,再通过scheduleBroadcastsLocked触发真正的发送;

总结一下这个函数:它的主要作用是根据这个Intent的特点,构造BroadCastRecord加入到不同的列表,等待被处理;
OK,控制到了scheduleBroadcastsLocked这里,它的逻辑很简单:
private final void scheduleBroadcastsLocked() {
     if (mBroadcastsScheduled) {
         return;
     }
    mHandler.sendEmptyMessage(BROADCAST_INTENT_MSG);
     mBroadcastsScheduled = true;
}
先判断mBroadcastsScheduled是否为真,如果为真就直接返回,这个变量主要是实现scheduleBroadcastsLocked和 processNextBroadcast之间的顺序执行,后面会看到在processNextBroadcast函数里面会把它设置为false;
下面就是通过BROADCAST_INTENT_MSG消息放入到消息队列里面,从这个角度来说Intent最后也是通过线程本身的消息队列来实现Intent的分发的;

消息队列后的处理过程:

上面有提到会通过mHandler.sendEmptyMessage(BROADCAST_INTENT_MSG),把这个消息传递给mHandler;到这里消息就是按照时间顺序进入了mQueue了;

 

activity的thread是如何进入主循环的:
首先是通过prepareMainLooper建立基本的数据结构,包括mQueue以及mThread,mMainLooper;并把当前的这个Looper放入到线程独有的变量中;其次是通过Looper.loop进入到主循环,逻辑如下:
首先是取出当前进入主循环的Looper,然后取出这个looper所拥有的mQueue,接下来就开始处理这个队列里面的消息了;

根据处理方式分两种消息,
         一种是消息的处理由一个线程来完成;
        一种是消息的处理时由一个函数来完成;
后者的话也分两种,
        一种是handler创建的时候提供了callback,这种情况非常少见;
        一种是通过handleMessage的方式来处理,通常我们在创建handler的时候都会提供这样一个函数,于是消息就可以被处理了;

 

BROADCAST_INTENT_MSG的处理:
这是最重要的函数,如果说broadcastIntentLocked是负责把Intent转化为BroadCast的话放入不同的队列,那么这个函数主要就是负责分发了,当然也涉及一点接收的流程;

 

消息分发流程:

分析函数private final voidprocessNextBroadcast(boolean fromMsg);

 

1、先判断fromMsg,如果是通过消息发送过来的就为真,否则为假; 如果为真mBroadcastsScheduled= false,这样的话在函数scheduleBroadcastsLocked里面就可以再次发送BROADCAST_INTENT_MSG的消息从而触 发processNextBroadcast函数被再次调用;

 

2、先判断mParallelBroadcasts是 否为空,不为空就开始调用这个列表里面的receivers来接收消息,这个过程后面在串行intent的时候也会碰到,我们留到后面讨论,这里只需要知道它通过一个while循环把Intent发送给关注这个Intent的所有的receivers

 

3、再判断 mPendingBroadcast是否为空,如果不为空,就表示先前发送的串行的Intent还没有处理完毕,一般出现这种可能是因为我们要发送到的 receiver还没有启动,所以需要先启动这activity,然后等待起来的这个activity处理,这时候,这个mPendingBroadcast就为true;如果发送这种情况需要判断这个Activity是否死了,如果死了,那么就把mPendingBroadcast设为false,否则就直接返回,继续等待;

 

4、接下来就顺序的从 mOrderedBroadcasts里面取出BroadCastRecord消息,然后对这个消息的receiver一个一个的调用其接收流程,注意这 里要把这个BroadCast的所有的receivers串行发送,都发送完了,才会进入到下一个BroadCastRecord消息;对于这个消息的处 理,先判断其接收者是不是BroadFilter,如果是,就调用deliverToRegisteredReceiver来接收,它的处理流程和前面的处理并行BroadCast一样,所以留到后面讲;

 

5、如果不是BroadCastFilter,就需要找出这个reiver所在的进程,这时候通常就是一个IntentFilter所在的进程,如果这个进程活着,那么就调processCurBroadcastLocked(r, app)来处理

 

6、需要先启动这个进程,这就是startProcessLocked做的事情,然后设置mPendingBroadcast= r,这样等应用起来它会处理这个消息,后面会有进一步的说明;

 

到这里这个函数就结束了,比较复杂,里面还有一些安全的检查等等,上面遗留了三个问题
A)deliverToRegisteredReceiver的处理流程;
B)processCurBroadcastLocked的处理流程;
C)startProcessLocked以后的进程如何处理这个唤醒它的Intent;

 

deliverToRegisteredReceiver的逻辑
这里也分为这个receiver是否启动,如果已经启动就通过binder调用到了接收 activity的进程里面了,右边的分支performReceive也会调用到activityThread这边,留到接收过程再看;

 

processCurBroadcastLocked的逻辑
可以看到它和deliverToRegisteredReceive的最终差别,只在于一个调用的是ScheduleRegisterdReceiver,一个是scheduleReceiver,这两个函数最后都会进入到目标activity的线程;

 

startProcessLocked的逻辑
从这里可以看出最后通过Process.start启动了ActivityThread.java的进程,我们看看这个线程启动后的执行逻辑:
首先是在进入主循环之前调用attachApplication通过binder调用进入到activityManagerService.java的进程;

 

这个服务器进程在把我们先前设置的mPendingBroadcast设置为null,表示这个pending的broadcat已经得到处理了,然后调用 processCurBroadcastLocked来处理这个broadcast消息,最后通过app.thread.scheduleReceiver进入到目标线程的接收流程;
OK,到这里的话所有的发送分发流程已经结束了,剩下的就是两个接收函数还没有讨论一个就是ScheduleRegisterdReceiver,一个是scheduleReceiver;

 

Intet的接收过程:

1、对Receiver进行注册:

Receiver的注册一般分为动态注册和静态注册,动态注册就是通过API registerReceiver来注册,静态的一般就是写在AndroidManifest.xml;

静态注册:
<receiver android:name="MediaButtonIntentReceiver">
           <intent-filter>
               <action android:name="android.intent.action.MEDIA_BUTTON" />
               <action android:name="android.media.AUDIO_BECOMING_NOISY" />
           </intent-filter>
</receiver>
至于它的原理以后在分析packageManger的时候再分析;

动态注册:

不在ApplicationContent环境里面就需要通过context.registerReceiver来注册了,经过几层传递会通过registerReceiverInternal进入主题;

 

构造receiver放入到列表中去,只是中间又经历了Binder,这些receiver也就是我们先前在发送的过程中看到的那些receiver,当然它们能进入到broadcast的列表还要看发送的intent是否满足它们给自的filter;

 

现在可以看看我们在发送阶段遗留的两个函数:
scheduleReceiver
scheduleRegisteredReceiver;

 

scheduleReceiver

它的入口通常是Binder的分发函数,如下:
右下方的这个函数scheduleReceiver才会真正调用到ActivityThread.java,这个就是目标activity的母体;
前面两个就是封装参数,最后放入到消息队列中,等待主循环的处理,这段逻辑我们前面已经看到了,就不再细说,总之会调用到handlemessage函数;

在收到这个消息的时候通过handleReceiver来处理;
这又是一个非常重要的函数,需要详细分析:
1,取得这个Intent指向的component,包括包名,类名;
2,取得包信息,这个结构提供了getClassLoader接口;
3,通过java.lang.ClassLoader cl =packageInfo.getClassLoader取得classLoader;
4,动态创建一个receiver,receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
5,调用receiver.onReceive(context.getReceiverRestrictedContext(),data.intent),进入到真正的处理流程中去了;(这里通过反射获取目标.class对象,然后获取他的接受对象)
6,调用finishReceiver来触发ActivityManagerService这个消息到其它receivers的发送或者下一个broadcast的发送;

这其中最重要的就是这个onReceive函数,我们通常都会实现这么一个函数,然后在里面处理我们收到的消息;

scheduleRegisteredReceiver的逻辑
入口还是Binder得分发函数,如下:
这种处理在Android的代码里面随处可见,都是在native文件里面通过onTransact分发调用service文件里面的同名函数来完成真正的功能;
逻辑如下:
也就是说,这里把参数打包放入到args里面去,然后通过post放入到消息队列里面等待处理,后面的逻辑和一个消息的发送很相似,如下:
这里需要关注两个点,
(1)m.callback=r,这个赋值会导致后面在分发消息的时候走不同的路径;
(2)Msg.target=this,表示将来分发的时候谁来处理这个消息,如果设置为null将会导致主循环退出;

public void dispatchMessage(Message msg)
{
    if (msg.callback != null)
    {
        handleCallback(msg);
    }
    else
    {
        if (mCallback != null)
        {
            if(mCallback.handleMessage(msg))
            {
               return;
            }
        }
        handleMessage(msg);
    }
}

 

这里就是需要先判断msg.callback是否为null,前面我们已经看到赋值了,所以这里不为null;
于是调用handleCallback,如下:
private final void handleCallback(Message message)
{
    message.callback.run();
}

这个callback我们也看到了其实就是我们封装的Args的args,原型为:
class Args implements Runnable,
也就是说它是一个类似线程的对象,它的run函数代码有点多;
基本上这个逻辑就和我们之前看到的逻辑一致了,会调用receiver提供的onReceive函数来处理,这个onReceive函数是需要我们自己提供的,里面一般的逻辑都是根据不同的消息做不同的处理;
最后就是通过finishReceiver来触发ActivityManagerService对Intent的其它receivers的发送;

 

总结:

Intent从使用的角度来说,就是构造 Intent,提供适当的参数,比如Action,比如数据类型,数据的uri等,然后发送出去;接收方需要注册一个receiver,然后提供onReceive函数就可以了;这个注册可以简单的写在AndroidManifest.xml里面也可以通过registerReceiver来完成;
发送的时候有三个API可以用:
sendBroadcast
sendStickyBroadcast
sendOrderedBroadcast
第一个用于发送并行广播;
第二个用于发送粘性广播;
第三个用于发送串行广播;

从原理的角度来说,本质上都是通过共享内存把信息传递给ActivityManagerService,它查询已经注册的哪些receiver的过滤器,看是否和这个Intent匹配,如果匹配成功就加 入到这个Intent的receiver列表中去,当然要根据这个Intent的参数决定加入到并行,串行,还是sticky的列表中,再通过 Message传递,到主循环的下一轮来分发;这时候控制已经到了另外一个进程,然后分发好以后再调用目标线程的处理函数,所以基本上就是涉及三个进程,源——>server——>receiver;当然,源和目的可以是同一个进程;
另外这里需要处理一种情况,就是这个消息发送的时候,目标线程还没有创建,比如我们系统里面的校准程序,需要在第一次开机的时候执行,那么就需要捕捉一个广播消息,比如:
<receiver android:name="StartupIntentReceiver" >
           <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.HOME" />
           </intent-filter>
   </receiver>
这个消息的意思就是说启动已经完毕了;
处理这个消息的类是StartupIntentReceiver,首先包含这个receiver的主activity将会被执行,然后再执行这个接收类的onReceive来接收消息并处理。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值