从AILD与bindService谈Binder进程间通信原理(下)

Posted zero9988

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从AILD与bindService谈Binder进程间通信原理(下)相关的知识,希望对你有一定的参考价值。

从AILD与bindService谈Binder进程间通信原理(下)


上文回顾

从AILD与bindService谈Binder进程间通信原理(上)

上一篇文章主要讲述了,在Client进程向AMS所在进程发起bindService操作的服务请求的过程中,Binder是如何跨进程通信的。实际上,Java层的Binder进程间通信都是同样的流程。主要有以下2点:
1,A进程持有B进程的IBinder对象(BinderProxy对象,对应原生层的BpBinder对象),通过这个Binder代理A进程即可与B进程进程间通信。
2,Binder代理的获取有两种方式,一种是通过查询ServiceManager获取,一种是通过一个中间进程(例如AMS进程)互相交换各自binder代理(实质上ServiceManager也是一个中间进程,不过他把服务进程的binder代理给保存下来)。

正文

接下来,我们将分析剩余的bindService所涉及的操作步骤:

上篇文章讲述到在第22-27步中, 在AMS的Binder对象ActivityManagerNative中解包Parcel,获取Client进程传递过来的参数,主要包括Client进程的2个BinderProxy,Client创建的用于bindeService操作的ServiceConnection对象的BinderProxy,以及Intent对象。最后调用ActivityManagerService类中的bindeService方法。

28,AMS会执行自身的成员变量mServices的bindServiceLocked方法。mServices是一个ActiveServices对象。ActiveServices才是AMS中真正管理Service的实现类。

  int bindServiceLocked(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType,
            IServiceConnection connection, int flags, int userId) 

        …………………………………………………………………………………………………………

        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType,
                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);


        ServiceRecord s = res.record;

        …………………………………………………………………………………………………………

        try 

            …………………………………………………………………………………………………………

            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);//yyt

            IBinder binder = connection.asBinder();
            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
            if (clist == null) 
                clist = new ArrayList<ConnectionRecord>();
                s.connections.put(binder, clist);
            
            clist.add(c);//yyt
            b.connections.add(c);
            if (activity != null) 
                if (activity.connections == null) 
                    activity.connections = new HashSet<ConnectionRecord>();
                
                activity.connections.add(c);
            
            b.client.connections.add(c);
            if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) 
                b.client.hasAboveClient = true;
            
            if (s.app != null) 
                updateServiceClientActivitiesLocked(s.app, c, true);
            
            clist = mServiceConnections.get(binder);
            if (clist == null) 
                clist = new ArrayList<ConnectionRecord>();
                mServiceConnections.put(binder, clist);
            
            clist.add(c);

            if ((flags&Context.BIND_AUTO_CREATE) != 0) 
                s.lastActivity = SystemClock.uptimeMillis();
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) 
                    return 0;
                
            
            ………………………………………………………………………………………………………………………………………………………………

         finally 
            Binder.restoreCallingIdentity(origId);
        

        return 1;
    

在bindServiceLocked方法中,首先调用retrieveServiceLocked方法检索将要启动的Service所在,是否是在本应用中的Service,通过Service的name和filter来进行检索,找到目标Service后,查看权限(permission是否满足),如果该Service是第一次被检索,则会创建一个新的ServiceRecord对象保存这个Service的信息,这个ServiceRecord会在bringUpServiceLocked方法中被保存到一个mPendingServices数组集合中。

然后会把刚才的ServiceConnection对象封装成一个ConnectionRecord对象,并存入ServiceRecord的相关成员列表中。接着再调用bringUpServiceLocked方法

29,在bringUpServiceLocked方法中,判断目标Service所在的app进程是否已经启动,如果已经启动则直接调用realStartServiceLocked方法启动Service;如果app进程没启动,即为null,则调用startProcessLocked启动目标Service所在的目标app进程。


    private final String bringUpServiceLocked(ServiceRecord r,
            int intentFlags, boolean execInFg, boolean whileRestarting) 

    …………………………………………………………………………………………

        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
        final String procName = r.processName;
        ProcessRecord app;

        if (!isolated) 
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);
            if (app != null && app.thread != null) 
                try 
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                 catch (RemoteException e) 
                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                

                // If a dead object exception was thrown -- fall through to
                // restart the application.
            
         else 
            // If this service runs in an isolated process, then each time
            // we call startProcessLocked() we will get a new isolated
            // process, starting another process if we are currently waiting
            // for a previous process to come up.  To deal with this, we store
            // in the service any current isolated process it is running in or
            // waiting to have come up.
            app = r.isolatedProc;
        

        // Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.
        if (app == null) 
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) == null) 
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r);
                return msg;
            
            if (isolated) 
                r.isolatedProc = app;
            
        

        if (!mPendingServices.contains(r)) 
            mPendingServices.add(r);
        

       ………………………………………………………………………………………………………………

        return null;
    

在启动完目标进程后,会把刚才28中创建的ServiceRecord对象添加到mPendingServices数组集合中去,mPendingServices保存的是将要启动的Service记录。因此,由28可知,Client进程传入的ServiceConnection对象的binder代理实际就是保存在这里面。

   if (!mPendingServices.contains(r)) 
            mPendingServices.add(r);
        

30,startProcessLocked方法是在AMS中实现的,最终将调用Process.start方法启动目标进程。接着会调用startViaZygote,最后调用zygoteSendArgsAndGetResult,发送Socket请求给ZygoteInit进程。

31,在ZygoteInit的runSelectLoop方法中,获取Socket传来的消息,并执行,最终通过 Zygote.forkAndSpecialize方法fork一个新的进程。

32,新ActivityThread进程创建,调用其入口方法main。ActivityThread即应用的主线程,也叫做UI线程。这个main方法就是所有应用进程的入口点,

  • 创建了Looper对象,并开启Looper.loop()循环
  • 调用自身attach方法,会调用AMS的attachApplication方法,最终会执行自己的bindApplication方法
    • 待补充
  • 启动AsyncTask的init,这就是为什么主线程才能使用AsyncTask。
//ActivityThread.java
  public static void main(String[] args) 
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        Security.addProvider(new androidKeyStoreProvider());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) 
            sMainThreadHandler = thread.getHandler();
        

        AsyncTask.init();

        if (false) 
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    

33-34,在attach方法中,又获取了ActivityManagerNative的binder代理mgr,然后调用mgr.attachApplication(mAppThread)方法,即又通过AMS的Binder代理与AMS远程通信,实际执行AMS中的attachApplication方法。

            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try 
                mgr.attachApplication(mAppThread);
             catch (RemoteException ex) 
                // Ignore
            

主意这里的参数是mAppThread,是ActivityThread创建时一同创建的,是一个Binder对象,主要就是用来与AMS进行进程间通信的,这个Binder会传给AMS,然后AMS就是通过这个Binder对应的BinderProxy与对应的应用进程通信。

final ApplicationThread mAppThread = new ApplicationThread();

35,这里省略了进程间通信的详细过程(如3-27步骤所述),这里进程间通信的参数就只有一个刚才传入的mAppThread对象对应的BinderProxy对象。

36,最终attachApplication会调用AMS中的attachApplicationLocked方法。在四大组件的启动过程中,如果对应的进程没有创建,那么都会最终调用到这个方法。在这个方法中会启动待启动的四大组件。

    private final boolean attachApplicationLocked(IApplicationThread thread,int pid) 

        ProcessRecord app;
        …………………………………………………………………………

        app.makeActive(thread, mProcessStats);//把thread对象保存到app中,thread即刚才所述的服务器进程的BinderProxy对象。

        ………………………………………………………………

        thread.bindApplication(processName, appInfo, providers,app.instrumentationClass,profilerInfo, app.instrumentationArguments,app.instrumentationWatcher,app.instrumentationUiAutomationConnection,testMode,enableOpenGlTrace,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(mConfiguration), app.compat,getCommonServicesLocked(),mCoreSettingsObserver.getCoreSettingsLocked());//通过thread与服务器进程通信,调用ActivityThread中的bindApplication方法,进程的ContentProvider组件在其中启动

        …………………………………………………………………

        // See if the top visible activity is waiting to run in this process...
        if (normalMode) 
            try 
                //启动待启动的Activity
                if (mStackSupervisor.attachApplicationLocked(app)) 
                    didSomething = true;
                
             catch (Exception e) 
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
            
        

        // Find any services that should be running in this process...
        if (!badApp) 
            try 
                //启动待启动的Service
                didSomething |= mServices.attachApplicationLocked(app, processName);
             catch (Exception e) 
                Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
                badApp = true;
            
        

        // Check if a next-broadcast receiver is in this process...
        if (!badApp && isPendingBroadcastProcessLocked(pid)) 
            try 
                //启动待启动的广播
                didSomething |= sendPendingBroadcastsLocked(app);
             catch (Exception e) 
                // If the app died trying to launch the receiver we declare it 'bad'
                Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
                badApp = true;
            
        
        ………………………………………………………………………………
    

在该方法中主要把服务端的BinderProxy保存到app变量中,然后通过该BinderProxy远程调用服务器进程的bindApplication方法;最后准备就绪,正式启动组件,因为我们是通过bindSerivice方法调用的,因此会调用mServices.attachApplicationLocked(app, processName)启动一个Service。如果是startActivity方法那么通过mStackSupervisor.attachApplicationLocked(app)启动Activity。

37-38,先说AMS通过服务进程的binder代理远程调用服务器进程的bindApplication方法,该方法会把参数打包成Message,然后提交给ActivityThread中的Handler对象进行处理,最终会调用handleBindApplication方法。


    private void handleBindApplication(AppBindData data) 

        …………………………………………………………………………

        …………………………………………………………………………

        try 
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if (!data.restrictedBackupMode) 
                List<ProviderInfo> providers = data.providers;
                if (providers != null) 
                    installContentProviders(app, providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                
            

            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            try 
                mInstrumentation.onCreate(data.instrumentationArgs);
            
            catch (Exception e) 
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            

            try 
                mInstrumentation.callApplicationOnCreate(app);
             catch (Exception e) 
                if (!mInstrumentation.onException(app, e)) 
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                
            
         finally 
            StrictMode.setThreadPolicy(savedPolicy);
        
    

该方法做了一系列的初始化,创建了进程包含的Application对象,然后如果该服务携带ContentProvider对象,则初始化该ContentProvider,并调用其onCreate方法,最后调用Application的onCreate方法。

39,在36步骤中,AMS的attachApplicationLocked方法会调用mServices.attachApplicationLocked,即调用ActiveServices中的的attachApplicationLocked,之前提到过ActiveServices是AMS真正处理Service的类。

//ActiveServices.java

    boolean attachApplicationLocked(ProcessRecord proc, String processName)
            throws RemoteException 
        boolean didSomething = false;
        // Collect any services that are waiting for this process to come up.
        if (mPendingServices.size() > 0) 
            ServiceRecord sr = null;
            try 
                for (int i=0; i<mPendingServices.size(); i++) 
                    sr = mPendingServices.get(i);
                    if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
                            || !processName.equals(sr.processName))) 
                        continue;
                    

                    mPendingServices.remove(i);
                    i--;
                    proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,
                            mAm.mProcessStats);
                    realStartServiceLocked(sr, proc, sr.createdFromFg);//启动该服务
                    didSomething = true;
                
             catch (RemoteException e) 
                Slog.w(TAG, "Exception in new application when starting service "
                        + sr.shortName, e);
                throw e;
            
        
     ……………………………………
        return didSomething;
    

之前28,29步骤中提到ServiceRecord被存入mPendingServices中,代表其等待被启动。因此,现在服务所在的进程已经启动,是时候从mPendingServices中取出待启动的ServiceRecord,并通过realStartServiceLocked方法启动该服务。

40,在realStartServiceLocked方法中,也是通过刚才36中保存的服务端的BinderProxy对象来远程调用服务进程中的scheduleCreateService方法和scheduleBindService方法,先是Create服务,然后是Bind服务。

//ActiveServices.java
private final void realStartServiceLocked(ServiceRecord r,ProcessRecord         app, boolean execInFg) throws RemoteException 

        …………………………………………………………………………    app.thread.scheduleCreateService(r,r.serviceInfo,mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);      
 requestServiceBindingsLocked(r, execInFg);//该方法中调用scheduleBindService

        …………………………………………………………………………
    

41-42,我们只关注bind方法,scheduleBindService方法最终会如38中一样通过Handler处理请求,调用ActivityThread中的handleBindService方法。

//ActivityThread.java
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().
                        publishService(data.token, data.intent, binder);

该方法首先调用Service的onBind方法,返回需要被绑定的IBinder对象,即AIDL通信过程中创建的Stub对象。
然后把onBind方法返回的Binder对象作为参数,调用AMS的Binder代理,发送给AMS进程,并远程调用AMS的publishService方法。

43,AMS中的publishService方法也是直接调用ActiveServices中的publishServiceLocked方法。

//ActivityManagerService.java
    public void publishService(IBinder token, Intent intent, IBinder service) 
        // Refuse possible leaked file descriptors
        if (intent != null && intent.hasFileDescriptors() == true) 
            throw new IllegalArgumentException("File descriptors passed in Intent");
        

        synchronized(this) 
            if (!(token instanceof ServiceRecord)) 
                throw new IllegalArgumentException("Invalid service token");
            
            mServices.publishServiceLocked((ServiceRecord)token, intent, service);
        
    

观察这里参数有三个,第一个是一个ServiceRecord对象,我们追溯这个ServiceRecord对象发现其实就是在39中所述的mPendingServices取出的待启动的Service记录ServiceRecord。因此,我们可以猜测,所谓的token其实都是应用进程保存在AMS中的Binder记录;第二个参数是一个intent对象,携带数据;第三个参数service就是刚才Service服务onBind方法返回的AIDL用于通信的BinderProxy对象。

44,在ActiveServices的publishServiceLocked方法中,主要就是获取之前保存的Client进程创建的ServiceConnection对象的BinderProxy对象。通过该对象与Client中的ServiceConnection对象进行交互,然后把Service服务端onBind方法返回的AIDL用于通信的BinderProxy对象远程传递给Client进程。当Client进程拥有Service服务进程用于进程间通信的Binder代理时,即可进行进程间通信。

//ActiveServices.java
    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) 
        final long origId = Binder.clearCallingIdentity();
        try 
            if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
                    + " " + intent + ": " + service);
            if (r != null) 
                Intent.FilterComparison filter
                        = new Intent.FilterComparison(intent);
                IntentBindRecord b = r.bindings.get(filter);
                if (b != null && !b.received) 
                    b.binder = service;
                    b.requested = true;
                    b.received = true;
                    for (int conni=r.connections.size()-1; conni>=0; conni--) 
                        ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                        for (int i=0; i<clist.size(); i++) 
                            ConnectionRecord c = clist.get(i);
                            if (!filter.equals(c.binding.intent.intent)) 
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG, "Not publishing to: " + c);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG, "Bound intent: " + c.binding.intent.intent);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG, "Published intent: " + intent);
                                continue;
                            
                            if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
                            try 
                                c.conn.connected(r.name, service);//关键语句
                             catch (Exception e) 
                                Slog.w(TAG, "Failure sending service " + r.name +
                                      " to connection " + c.conn.asBinder() +
                                      " (in " + c.binding.client.processName + ")", e);
                            
                        
                    
                

                serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
            
         finally 
            Binder.restoreCallingIdentity(origId);
        
    

如28步骤中所述,Client进程的ServiceConnection对象的Binder代理就保存在ServiceRecord的ConnectionRecord中,最终调用 c.conn.connected(r.name, service)方法。

45,其中conn是一个InnerConnection对象,该对象是LoadedApk.java文件中的LoadedApk类的ServiceDispatcher子类的一个子类。

//LoadedApk.java
        private static class InnerConnection extends IServiceConnection.Stub 
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) 
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            

            public void connected(ComponentName name, IBinder service) throws RemoteException 
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) 
                    sd.connected(name, service);
                
            
        

最终调用ServiceDispatcher的doConnected方法

        public void doConnected(ComponentName name, IBinder service) 
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            synchronized (this) 
                if (mForgotten) 
                    // We unbound before receiving the connection; ignore
                    // any connection received.
                    return;
                
                old = mActiveConnections.get(name);
                if (old != null && old.binder == service) 
                    // Huh, already have this one.  Oh well!
                    return;
                

                if (service != null) 
                    // A new service is being connected... set it all up.
                    mDied = false;
                    info = new ConnectionInfo();
                    info.binder = service;
                    info.deathMonitor = new DeathMonitor(name, service);
                    try 
                        service.linkToDeath(info.deathMonitor, 0);
                        mActiveConnections.put(name, info);
                     catch (RemoteException e) 
                        // This service was dead before we got it...  just
                        // don't do anything with it.
                        mActiveConnections.remove(name);
                        return;
                    

                 else 
                    // The named service is being disconnected... clean up.
                    mActiveConnections.remove(name);
                

                if (old != null) 
                    old.binder.unlinkToDeath(old.deathMonitor, 0);
                
            

            // If there was an old service, it is not disconnected.
            if (old != null) 
                mConnection.onServiceDisconnected(name);
            
            // If there is a new service, it is now connected.
            if (service != null) 
                mConnection.onServiceConnected(name, service);
            
        

最终回调了接口方法onServiceConnected,该方法中把service(Server进程的BinderProxy)交给了Client进程。Client进程就通过该BinderProxy与Server进程进程间通信。

至此AIDL的实现,Binder进程间机制与bindService方法的全过程都已经一一分析完毕了。

下一篇文章因该会在通过几个实例,在巩固一下安卓进程间通信。

参考资料:(可能会有遗漏,尽请谅解)

进程启动过程
http://blog.csdn.net/luoshengyang/article/details/6747696

Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
http://blog.csdn.net/luoshengyang/article/details/6629298

开机SystemServer到ActivityManagerService启动过程分析
http://www.cnblogs.com/bastard/p/5770573.html

Android应用程序绑定服务(bindService)的过程源代码分析
http://blog.csdn.net/luoshengyang/article/details/6745181

Android:学习AIDL,这一篇文章就够了(上)
http://blog.csdn.net/luoyanglizi/article/details/51980630

Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析
http://blog.csdn.net/luoshengyang/article/details/6642463

Binder详解
http://blog.csdn.net/yangzhiloveyou/article/details/14043801

IBinder对象在进程间传递的形式(二)
http://blog.csdn.net/windskier/article/details/6913698

IBinder对象传递形式
http://www.cnblogs.com/zhangxinyan/p/3487866.html

以上是关于从AILD与bindService谈Binder进程间通信原理(下)的主要内容,如果未能解决你的问题,请参考以下文章

从AILD与bindService谈Binder进程间通信原理(上)

从AILD与bindService谈Binder进程间通信原理(上)

客户端调用bindService怎么让服务端的binder回调过来的?

客户端调用bindService怎么让服务端的binder回调过来的?

客户端调用bindService怎么让服务端的binder回调过来的?

Android使用bindService作为中间人对象开启服务