Android中你需要知道的(一)

一、Activity的启动过程

ActivityManagerService和ActivityStack位于同一个进程中,而ApplicationThread和ActivityThread位于另一个进程中。其中,ActivityManagerService是负责管理Activity的生命周期的,ActivityManagerService还借助ActivityStack是来把所有的Activity按照后进先出的顺序放在一个堆栈中;对于每一个应用程序来说,都有一个ActivityThread来表示应用程序的主进程,而每一个ActivityThread都包含有一个ApplicationThread实例,它是一个Binder对象,负责和其它进程进行通信。

        下面简要介绍一下启动的过程:
        Step 1. 无论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity接口; 
        Step 2. ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息;
        Step 3. ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManagerService.startActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了;
        Step 4. ApplicationThread不执行真正的启动操作,它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity;
        Step 5. 对于通过点击应用程序图标来启动Activity的情景来说,ActivityManagerService在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动;
        Step 6. ActivityManagerServic调用ApplicationThread.scheduleLaunchActivity接口,通知相应的进程执行启动Activity的操作;

        Step 7. ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。


二、Android Binder深入理解

Android系统基本上可以看作是一个基于Binder通信的C/S架构。Binder就像网络一样,把系统各个部分连接在了一起。Android基于Client-Server的通信方式,诸如媒体播放,视音频频捕获,到各种让手机更智能的传感器(加速度,方位,温度,光亮度等)都由不同的Server负责管理,应用程序只需做为Client与这些Server建立连接便可以使用这些服务.。

目前linux支持的IPC包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。

Android底层是基于Linux的,Android没有选用传统Linux的IPC方式,而利用Binder进行IPC通信,我觉得主要原因有以下两点:

      一方面是传输性能。socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,安全性较低,难以使用。 

      还有一点是出于安全性考虑。传统IPC没有任何安全措施,完全依赖上层协议来确保。Android为每个安装好的应用程序分配了自己的UID,传统IPC的接收方无法获得对方进程可靠的UID和PID(用户ID进程ID),从而无法鉴别对方身份。Binder基于Client-Server通信模式,传输过程只需一次拷贝,为发送发添加UID/PID身份,既支持实名Binder也支持匿名Binder,安全性高。

Binder 通信模型 

Binder框架定义了四个角色:Server,Client,ServiceManager(以后简称SMgr)以及Binder驱动。其中Server,Client,SMgr运行于用户空间,驱动运行于内核空间。 

(1)Binder 驱动工作于内核态,驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。 
(2)ServiceManager 与实名Binder:SMgr的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。

SMgr和其它进程同样采用Binder通信,SMgr是Server端,有自己的Binder对象(实体),其它进程都是Client,需要通过这个Binder的引用来实现Binder的注册,查询和获取。

在SystemServer中生成该Service并添加到ServiceManager中 

frameworks/base/services/java/com/android/server/SystemServer.java文件: 
 // add DevInfoService   
try {   
    Slog.i(TAG, "DevInfoManager Service");   
    devInfo =  new DevInfoService(context);   
    ServiceManager.addService(Context.DEVINFO_SERVICE, devInfo);   
} catch (Throwable e) {   
    reportWtf("starting devInfo Service", e);   
}   
// end 
在frameworks/base/core/java/android/app/ContextImpl.java中增加service注册,如下: 

registerService(DEVINFO_SERVICE, new ServiceFetcher() {   
        public Object createService(ContextImpl ctx) {   
                IBinder b = ServiceManager.getService(DEVINFO_SERVICE);   
                IDevInfoManager service = IDevInfoManager.Stub.asInterface(b);   
                return new DevInfoManager(service, ctx.mMainThread.getHandler());   
        }   
}); 
(3)Client 获得实名Binder的引用:从面向对象的角度,这个Binder对象现在有了两个引用:一个位于SMgr中,一个位于发起请求的Client中。如果接下来有更多的Client请求该Binder,系统中就会有更多的引用指向该Binder,就象java里一个对象存在多个引用一样。而且类似的这些指向Binder的引用是强类型,从而确保只要有引用Binder实体就不会被释放掉。通过以上过程可以看出,SMgr象个火车票代售点,收集了所有火车的车票,可以通过它购买到乘坐各趟火车的票-得到某个Binder的引用。 

(4)匿名 Binder:并不是所有Binder都需要注册给SMgr广而告之的。Server端可以通过已经建立的Binder连接将创建的Binder实体传给Client,当然这条已经建立的Binder连接必须是通过实名Binder实现。由于这个Binder没有向SMgr注册名字,所以是个匿名Binder。Client将会收到这个匿名Binder的引用,通过这个引用向位于Server中的实体发送请求。匿名Binder为通信双方建立一条私密通道,只要Server没有把匿名Binder发给别的进程,别的进程就无法通过穷举或猜测等任何方式获得该Binder的引用,向该Binder发送请求。(比如用户自己写的基于service的Binder就是匿名的) 

总结

Binder采用面向对象的设计思想,一个Binder实体可以发送给其它进程从而建立许多跨进程的引用;另外这些引用也可以在进程之间传递,就象java里将一个引用赋给另一个引用一样。为Binder在不同进程中建立引用必须有驱动的参与,由驱动在内核创建并注册相关的数据结构后接收方才能使用该引用。

下面我们深入理解一下Binder 内存映射和接收缓存区管理 

暂且撇开Binder,考虑一下传统的IPC方式中,数据是怎样从发送端到达接收端的呢?通常的做法是,发送方将准备好的数据存放在缓存区中,调用API通过系统调用进入内核中。内核服务程序在内核空间分配内存,将数据从发送方缓存区复制到内核缓存区中。接收方读数据时也要提供一块缓存区,内核将数据从内核缓存区拷贝到接收方提供的缓存区中并唤醒接收线程,完成一次数据发送。这种存储-转发机制有两个缺陷:首先是效率低下,需要做两次拷贝:用户空间->内核空间->用户空间。Linux使用copy_from_user()和copy_to_user()实现这两个跨空间拷贝。其次是接收数据的缓存要由接收方提供,可接收方不知道到底要多大的缓存才够用,只能开辟尽量大的空间或先调用API接收消息头获得消息体大小,再开辟适当的空间接收消息体。两种做法都有不足,不是浪费空间就是浪费时间。 

Binder采用一种全新策略:由Binder驱动负责管理数据接收缓存。我们注意到Binder驱动实现了mmap()系统调用。 

Binder内核数据跨进程共享就是利用的Linux里面的映射函数mmap()实现了内存映射,将一片内存区域映射到进程地址空间(觉得映射就像指针一样,映射后进程就有指向这块内存的指针,就能直接找到该内存存储的数据进行访问,进程就可以把这快内存当作自己内存区域堆中的一部分),此时该进程就可以向操作自己的内存一样去操作映射过来的内存,而Binder也将该快内存映射到了Linux的内核空间,内核空间也就可以操作该内存快,当发送数据的进程发送数据时,Linux首先需要将数据拷贝到内核空间,此时内核时可以操作这个映射过来的内存的,所以就直接再用copy_from_user()拷贝该内存快,此时接受数据的进程也可以直接访问这个数据,所以就免去来Linux内核再去执行copy_to_user()的操作,也就减少了一次数据拷贝。 


三、Android BroadcastReceiver深入理解

从实现原理看上,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。具体实现流程要点粗略概括如下: 

1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册; 
2.广播发送者通过binder机制向AMS发送广播; 
3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中; 
4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。 
对于不同的广播类型,以及不同的BroadcastReceiver注册方式,具体实现上会有不同。但总体流程大致如上。 

简单地说,Android广播机制的主要工作是为了实现一处发生事情,多处得到通知的效果。这种通知工作常常要牵涉跨进程通讯,所以需要由AMS(Activity Manager Service)集中管理。 

在Android系统中,接收广播的组件叫作receiver,而且receiver还分为动态和静态的。动态receiver是在运行期通过调用registerReceiver()注册的,而静态receiver则是在AndroidManifest.xml中声明的。动态receiver比较简单,静态的就麻烦一些了,因为在广播递送之时,静态receiver所从属的进程可能还没有启动呢,这就需要先启动新的进程,费时费力。另一方面,有些时候用户希望广播能够按照一定顺序递送,为此,Android又搞出了ordered broadcast的概念。 

静态receiver 
静态receiver是指那些在AndroidManifest.xml文件中声明的receiver,它们的信息会在系统启动时,由Package Manager Service(PKMS)解析并记录下来。 
在系统启动时,会启动一个PackageManagerService服务,PackageManagerService服务会通过packagemanager对各个app安装目录的apk文件进行扫描解析,主要解析manifest里面注册的广播接收器,并且最终将所有的接收器保存到了List<ResolveInfo>列表里面。以后,当AMS调用PKMS的接口来查询“和intent匹配的组件”时,PKMS内部就会去查询当初记录下来的数据,并把结果返回AMS。有的同学认为静态receiver是常驻内存的,这种说法并不准确。因为常驻内存的只是静态receiver的描述性信息,并不是receiver实体本身。 
当发出一个广播ACTION_PRE_BOOT_COMPLETED时,PKMS向外界提供了queryIntentReceivers()函数,该函数可以返回一个List<ResolveInfo>列表,意思是查找有多少receiver对ACTION_PRE_BOOT_COMPLETED感兴趣。

动态receiver 
 动态receiver必须在运行期动态注册,其实际的注册动作由ContextImpl对象完成。最终通过跨进程的方式将注册信息发送到PackageManagerService中对其进行保存。 

发送广播 
在注册完了之后,广播主要就是发送和处理了。这个过程都是在activitymanagerservice里面进行的。 
ContextImpl.java文件中,可以看到很多的发送广播函数,其中sendBroadcast()是最简单的发送广播的动作。 
我们熟悉的发送广播代码如下: 

mContext = getApplicationContext(); 
Intent intent = new Intent();   
intent.setAction("com.android.xxxxx");   
mContext.sendBroadcast(intent); 
上面的mContext的内部其实是在调用一个ContextImpl对象的同名函数,所以我们继续查看ContextImpl.java文件。 

【frameworks/base/core/java/android/app/ContextImpl.java】 

@Override 
public void sendBroadcast(Intent intent) 
{ 
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());     
    try 
    { 
        intent.setAllowFds(false); 
        ActivityManagerNative.getDefault().broadcastIntent( 
            mMainThread.getApplicationThread(), intent, resolvedType, null, 
            Activity.RESULT_OK, null, null, null, false, false, 
            Binder.getOrigCallingUser()); 
    } catch (RemoteException e) { 
    } 
} 
ActivityManagerNative是一个Binder对象我们调用broadcastIntent属于内部代理ActivityManagerProxy的方法,他将调用transact()方法将广播的信息发送到ActivityManagerService。 

 public int broadcastIntent(IApplicationThread caller, 
         Intent intent, String resolvedType,  IIntentReceiver resultTo, 
         int resultCode, String resultData, Bundle map, 
         String requiredPermission, int appOp, boolean serialized, 
         boolean sticky, int userId) throws RemoteException 
 { 
     Parcel data = Parcel.obtain(); 
     Parcel reply = Parcel.obtain(); 
     data.writeInterfaceToken(IActivityManager.descriptor); 
     data.writeStrongBinder(caller != null ? caller.asBinder() : null); 
     intent.writeToParcel(data, 0); 
     data.writeString(resolvedType); 
     data.writeStrongBinder(resultTo != null ? resultTo.asBinder() : null); 
     data.writeInt(resultCode); 
     data.writeString(resultData); 
     data.writeBundle(map); 
     data.writeString(requiredPermission); 
     data.writeInt(appOp); 
     data.writeInt(serialized ? 1 : 0); 
     data.writeInt(sticky ? 1 : 0); 
     data.writeInt(userId); 
     mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0); 
     reply.readException(); 
     int res = reply.readInt(); 
     reply.recycle(); 
     data.recycle(); 
     return res; 
 } 
用户进程把发送广播的语义传递到AMS之后,最终会由AMS的broadcastIntentLocked(......)处理广播的发送。broadcastIntentLocked()代码太长,而且里面有很多判断,这里就不贴出源码了,下面我们可以将其逻辑大致整理成以下几步: 
1) 为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记; 
2) 处理和package相关的广播; 
3) 处理其他一些系统广播; 
4) 判断当前是否有权力发出广播; 
5) 如果要发出sticky广播,那么要更新一下系统中的sticky广播列表; 
6) 查询和intent匹配的静态receivers; 
7) 查询和intent匹配的动态receivers; 
8) 尝试向并行receivers递送广播; 
9) 整合(剩下的)并行receivers,以及静态receivers,形成一个串行receivers表; 
10) 尝试逐个向串行receivers递送广播。 

由于篇幅,先写这么多,希望对大家有帮助,接下来我会出“Android中你需要知道的(二)”,敬请期待!!!




  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值