滴滴插件化VirtualAPK框架原理解析之Service 管理

Posted 伯努力不努力

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了滴滴插件化VirtualAPK框架原理解析之Service 管理相关的知识,希望对你有一定的参考价值。

在前一篇博客滴滴插件化框架VirtualAPK原理解析(一)之插件Activity管理
中VirtualAPK是如何对Activity进行管理的,本篇博客,我们继续来学习这个框架,这次我们学习的是如何去管理Service。

Service工作原理分析

说道如何对Service进行插件化,肯定得先了解Service的工作过程,不然何谈插件化?所以我们先一起学习Service的工作原理。

Service分为两种形式:以startService启动的服务和用bindService绑定的服务;其实这两种方法的启动都是大同小异,所以这里,我们以bindService为例分析进行分析即可。

bindService的启动是通过Context类的bindService方法完成的,这个方法需要三个参数:第一个参数代表想要绑定的Service的Intent,第二个参数是一个ServiceConnetion,我们可以通过这个对象接收到Service绑定成功或者失败的回调;第三个参数则是绑定时候的一些FLAG,当然如果你对Service还不是很了解的话,可以看官方的training文档http://developer.android.com/intl/zh-cn/guide/components/services.html

Context的具体实现在ContextImpl类,ContextImpl中的bindService方法直接调用了bindServiceCommon方法

 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            UserHandle user) 
        IServiceConnection sd;
        if (conn == null) 
            throw new IllegalArgumentException("connection is null");
        
        if (mPackageInfo != null) 
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                    mMainThread.getHandler(), flags);
         else 
            throw new RuntimeException("Not supported in system context");
        
        validateServiceIntent(service);
        try 
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) 
                flags |= BIND_WAIVE_PRIORITY;
            
            service.prepareToLeaveProcess();
            int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(),
                service, service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, user.getIdentifier());
            if (res < 0) 
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            
            return res != 0;
         catch (RemoteException e) 
            return false;
        
    

通过代码,其实你会发现最后是通过ActivityManagerNative借助AMS进而完成Service的绑定过程,这里需要提及一下sd变量,这个变量的类型是IServiceConnection,这个IServiceConnection与IApplicationThread是相同的,都是ActivityThread给AMS提供的用来与之进行通信的Binder对象;这个接口的实现类为LoadedApk.ServiceDispatcher

接下来我们看ActivityManagerNative的bindService方法

  public int bindService(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType,
            IServiceConnection connection, int flags, int userId) 
        enforceNotIsolatedCaller("bindService");

        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) 
            throw new IllegalArgumentException("File descriptors passed in Intent");
        

        synchronized(this) 
            return mServices.bindServiceLocked(caller, token, service, resolvedType,
                    connection, flags, userId);
        
    

bindService中调用了ActivityServices类的bindServiceLocked方法:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags,
        String callingPackage, int userId) throws TransactionTooLargeException 
    final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
    // 省略部分代码

    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage,
                Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
    //  省略部分代码
    ServiceRecord s = res.record;

    final long origId = Binder.clearCallingIdentity();

    try 
        // ...  省略部分代码

        mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
                s.appInfo.uid, s.name, s.processName);

        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent);
        IBinder binder = connection.asBinder();
        ArrayList<ConnectionRecord> clist = s.connections.get(binder);

        // 对connection进行处理, 方便存取,略
        clist.add(c);

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

        // 与BIND_AUTO_CREATE不同的启动FLAG,原理与后续相同,略

     finally 
        Binder.restoreCallingIdentity(origId);
    

    return 1;

首先通过retrieveServiceLocked方法获取到了intent匹配到的需要bind到的Service组件res;然后把ActivityThread传递过来的IServiceConnection使用ConnectionRecord进行了包装,方便接下来使用;最后如果启动的FLAG为BIND_AUTO_CREATE,那么调用bringUpServiceLocked开始创建Service


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

    // 省略代码

    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 (app != null && app.thread != null) 
            try 
                app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                // 1. important !!!
                realStartServiceLocked(r, app, execInFg);
                return null;
             catch (TransactionTooLargeException e) 
                throw e;
             catch (RemoteException e) 
                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
            

        
     else 
        app = r.isolatedProc;
    

    // Not running -- get it started, and enqueue this service record
    // to be executed when the app comes up.
    if (app == null) 
        // 2. important !!!
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                "service", r.name, false, isolated, false)) == null) 
            bringDownServiceLocked(r);
            return msg;
        
        if (isolated) 
            r.isolatedProc = app;
        
    
    // 省略代码
    return null;

如果Service所在的进程已经启动,那么直接调用realStartServiceLocked方法来真正启动Service组件;如果Service所在的进程还没有启动,那么先在AMS中记下这个要启动的Service组件,然后通过startProcessLocked启动新的进程。

先看Service进程已经启动的情况,realStartServiceLocked方法:

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException 

    // 省略代码

    boolean created = false;
    try 
        synchronized (r.stats.getBatteryStats()) 
            r.stats.startLaunchedLocked();
        
        mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
     catch (DeadObjectException e) 
        mAm.appDiedLocked(app);
        throw e;
     finally 
        // 省略代码
    

    requestServiceBindingsLocked(r, execInFg);

    //省略代码

调用了app.thread的scheduleCreateService方法,这是一个IApplicationThread对象,它是App所在进程提供给AMS的用来与App进程进行通信的Binder对象,这个Binder的Server端在ActivityThread的ApplicationThread类

public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) 
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
            s.compatInfo = compatInfo;

            sendMessage(H.CREATE_SERVICE, s);
        

这里发送一个消息给ActivityThread的H这个Handler,H类收到这个消息之后,直接调用了ActivityThread类的handleCreateService方法

 private void handleCreateService(CreateServiceData data) 
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try 
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
         catch (Exception e) 
            if (!mInstrumentation.onException(service, e)) 
                throw new RuntimeException(
                    "Unable to instantiate service " + data.info.name
                    + ": " + e.toString(), e);
            
        

        try 
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
            mServices.put(data.token, service);
            try 
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
             catch (RemoteException e) 
                // nothing to do.
            
         catch (Exception e) 
            if (!mInstrumentation.onException(service, e)) 
                throw new RuntimeException(
                    "Unable to create service " + data.info.name
                    + ": " + e.toString(), e);
            
        
    

1.使用ClassLoader加载并通过反射创建Service 对象
2.创建ContextImpl ,并绑定Service
3.makeApplication创建Application
4.service.attach方法将创建的Application和ContextImpl 和service进行绑定
5.service.onCreate()方法进行service的创建

虽然都是通过ClassLoader通过反射创建,但是Activity却把创建过程委托给了Instrumentation类,而Service则是直接进行。

ActivityThread里面的handleCreateService方法成功创建出了Service对象,并且调用了它的onCreate方法;到这里我们的Service已经启动成功。scheduleCreateService这个Binder调用过程结束,代码又回到了AMS进程的realStartServiceLocked方法。

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException 

    // 省略代码

    boolean created = false;
    try 
        synchronized (r.stats.getBatteryStats()) 
            r.stats.startLaunchedLocked();
        
        mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
     catch (DeadObjectException e) 
        mAm.appDiedLocked(app);
        throw e;
     finally 
        // 省略代码
    

    requestServiceBindingsLocked(r, execInFg);

    // 省略代码

scheduleCreateService这个binder调用之后,执行了一个requestServiceBindingsLocked方法

private final boolean requestServiceBindingLocked(ServiceRecord r,
            IntentBindRecord i, boolean execInFg, boolean rebind) 
        if (r.app == null || r.app.thread == null) 
            // If service is not currently running, can't yet bind.
            return false;
        
        if ((!i.requested || rebind) && i.apps.size() > 0) 
            try 
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if (!rebind) 
                    i.requested = true;
                
                i.hasBound = true;
                i.doRebind = false;
             catch (RemoteException e) 
                if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
                return false;
            
        
        return true;
    

又通过IApplicationThread这个Binder进行了一次IPC调用,我们跟踪ActivityThread类里面的ApplicationThread的scheduleBindService方法,发现这个方法不过通过Handler转发了一次消息,真正的处理代码在handleBindService里面:

  private void handleBindService(BindServiceData data) 
        Service s = mServices.get(data.token);
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if (s != null) 
            try 
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try 
                    if (!data.rebind) 
                        IBinder binder = s.onBind(data.intent);
                        ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                     else 
                        s.onRebind(data.intent);
                        ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    
                    ensureJitEnabled();
                 catch (RemoteException ex) 
                
             catch (Exception e) 
                if (!mInstrumentation.onException(s, e)) 
                    throw new RuntimeException(
                            "Unable to bind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                
            
        
    

要Bind的Service在这里完成了绑定,绑定之后又通过ActivityManagerNative这个Binder进行一次IPC调用,我们查看AMS的publishService方法,这个方法又调用了publishServiceLocked方法

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);
        
    

这里终于到IServiceConnection的用处了,在bindServiceLocked方法里面,这个IServiceConnection放到了一个ConnectionRecord的List中存放在ServiceRecord里面,这里所做的就是取出已经被Bind的这个Service对应的IServiceConnection对象,然后调用它的connected方法;我们说过,这个IServiceConnection也是一个Binder对象,它的Server端在LoadedApk.ServiceDispatcher里面。接下来是LoadedApk.ServiceDispatcher的connected方法

 public void connected(ComponentName name, IBinder service) 
            if (mActivityThread != null) 
                mActivityThread.post(new RunConnection(name, service, 0));
             else 
                doConnected(name, service);
            
        

无论哪种判断,都会走到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所在进程已经存在的情况已经分析完毕,如果Service所在进程不存在,那么会调用startProcessLocked方法创建一个新的进程,并把需要启动的Service放在一个队列里面;创建进程的过程通过Zygote fork出来,进程创建成功之后会调用ActivityThread的main方法,在这个main方法里面间接调用到了AMS的attachApplication方法,在AMS的attachApplication里面会检查刚刚那个待启动Service队列里面的内容,并执行Service的启动操作;之后的启动过程与进程已经存在的情况下相同

如何对Service进行插件化?

通过对Service的工作原理的分析,我们知道Service通过Context类完成启动,接着通过ActivityMnagaerNative进入AMS,最后又通过IApplicationThread这个Binder IPC到App进程的Binder线程池,然后通过H转发消息到App进程的主线程,最终完成组件生命周期的回调

Activity与Service组件最大的不同点在于,Activity组件可以与用户进行交互;这一点意味着用户的行为会对Activity组件产生影响,对我们来说最重要的影响就是Activity组件的生命周期;用户点击按钮从界面A跳转到界面B,会引起A和B这两个Activity一系列生命周期的变化。而Service组件则代表后台任务,除了内存不足系统回收之外,它的生命周期完全由我们的代码控制,与用户的交互无关。

Activity组件的生命周期受用户交互影响,而这种变化只有Android系统才能感知,因此我们必须把插件的Activity交给系统管理,才能拥有完整的生命周期;但Service组件的生命周期不受外界因素影响,那么自然而然,我们可以手动控制它的生命周期,既然Service的生命周期可以由我们自己控制,那么我们可以有更简单的方案实现它的插件化。

可以通过手动控制Service组件的生命周期实现Service的插件化

Service的生命周期相当简单:整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。对于非绑定服务,就是从startService调用到stopService或者stopSelf调用。对于绑定服务,就是bindService调用到unbindService调用;

1.如果以startService方式启动插件Service,直接回调要启动的Service对象的onStartCommand方法即可;如果用stopService或者stopSelf的方式停止Service,只需要回调对应的Service组件的onDestroy方法。
2.如果用bindService方式绑定插件Service,可以调用对应Service对应的onBind方法,获取onBind方法返回的Binder对象,然后通过ServiceConnection对象进行回调统计;unBindService的实现同理。

VirtualAPK是如何实现Service插件化的

VirtualAPK中使用了一种代理分发的方法,注册一个真正的Service组件ProxyService,让这个Service承载一个真正的Service组件所具备的能力(进程优先级等);当启动插件的服务比如PluginService的时候,我们统一启动这个ProxyService,当这个ProxyService运行起来之后,再在它的onStartCommand等方法里面进行分发,执行PluginService的onStartCommond等对应的方法

1.注册代理Service
上面说道,我们需要一个货真价实的Service组件来承载进程优先级等功能,因此需要在AndroidManifest.xml中声明一个或多个这样的Sevice,打开VirtualAPK的代码,我们发现也确实这样的:

 <!-- Local Service running in main process -->
        <service android:name="com.didi.virtualapk.delegate.LocalService" />

        <!-- Daemon Service running in child process -->
        <service android:name="com.didi.virtualapk.delegate.RemoteService" android:process=":daemon">
            <intent-filter>
                <action android:name="$applicationId.intent.ACTION_DAEMON_SERVICE" />
            </intent-filter>
        </service>

可以发现,VirtualAPK注册了两个Service ,一个本进程的Service,一个是跨进程的Service

2.Hook IActivityManager,拦截startService等调用过程
要手动控制Service组件的生命周期,需要拦截startService,stopService等调用,并且把启动插件Service全部重定向为启动ProxyService(保留原始插件Service信息);这个拦截过程需要Hook ActvityManagerNative,还记得我们前面说VirtualAPK通过动态代理的方式Hook ActvityManagerNative方法吗?

hookSystemServices是在PluginManager中实现的

 private void prepare() 
        Systems.sHostContext = getHostContext();
        this.hookInstrumentationAndHandler();
        this.hookSystemServices();
    
 private void hookSystemServices() 
        try 
            Singleton<IActivityManager> defaultSingleton = (Singleton<IActivityManager>) ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault");
            IActivityManager activityManagerProxy = ActivityManagerProxy.newInstance(this, defaultSingleton.get());

            // Hook IActivityManager from ActivityManagerNative
            ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy);

            if (defaultSingleton.get() == activityManagerProxy) 
                this.mActivityManager = activityManagerProxy;
            
         catch (Exception e) 
            e.printStackTrace();
        
    

首先拿到ActivityManagerNative中的gDefault对象,该对象返回的是一个Singleton,然后拿到其mInstance对象,即IActivityManager对象(可以理解为和AMS交互的binder的client对象)对象。

然后通过动态代理的方式,替换为了一个代理对象。

ActivityManagerProxy.java
@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        if ("startService".equals(method.getName())) 
            try 
                return startService(proxy, method, args);
             catch (Throwable e) 
                Log.e(TAG, "Start service error", e);
            
         else if ("stopService".equals(method.getName())) 
            try 
                return stopService(proxy, method, args);
             catch (Throwable e) 
                Log.e(TAG, "Stop Service error", e);
            
         else if ("stopServiceToken".equals(method.getName())) 
            try 
                return stopServiceToken(proxy, method, args);
             catch (Throwable e) 
                Log.e(TAG, "Stop service token error", e);
            
         else if ("bindService".equals(method.getName())) 
            try 
                return bindService(proxy, method, args);
             catch (Throwable e) 
                e.printStackTrace();
            
         else if ("unbindService".equals(method.getName())) 
            try 
                return unbindService(proxy, method, args);
             catch (Throwable e) 
                e.printStackTrace();
            
         else if ("getIntentSender".equals(method.getName())) 
            try 
                getIntentSender(method, args);
             catch (Exception e) 
                e.printStackTrace();
            
         else if ("overridePendingTransition".equals(method.getName()))
            try 
                overridePendingTransition(method, args);
             catch (Exception e)
                e.printStackTrace();
            
        

        try 
            // sometimes system binder has problems.
            return method.invoke(this.mActivityManager, args);
         catch (Throwable th) 
            Throwable c = th.getCause();
            if (c != null && c instanceof DeadObjectException) 
                // retry connect to system binder
                IBinder ams = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                if (ams != null) 
                    IActivityManager am = ActivityManagerNative.asInterface(ams);
                    mActivityManager = am;
                
            

            Throwable cause = th;
            do 
                if (cause instanceof RemoteException) 
                    throw cause;
                
             while ((cause = cause.getCause()) != null);

            throw c != null ? c : th;
        

    

可以看到在ActivityManagerProxy中,对startService等将所有的Service操作进行拦截,然后调用了自己内部的相应方法,我们先看startService方法

3.拦截startService方法

 private Object startService(Object proxy, Method method, Object[] args) throws Throwable 
        IApplicationThread appThread = (IApplicationThread) args[0];
        Intent target = (Intent) args[1];
        ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
        if (null == resolveInfo || null == resolveInfo.serviceInfo) 
            // is host service
            return method.invoke(this.mActivityManager, args);
        

        return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE);
    

1.mPluginManager.resolveService方法通过Intent 过滤出要启动的插件Service的ResolveInfo

 public ResolveInfo resolveService(Intent intent, int flags) 
        for (LoadedPlugin plugin : this.mPlugins.values()) 
            ResolveInfo resolveInfo = plugin.resolveService(intent, flags);
            if (null != resolveInfo) 
                return resolveInfo;
            
        

        return null;
    

这个方法最终会到LoadedPlugin的resolveService方法中

 public ResolveInfo resolveService(Intent intent, int flags) 
        List<ResolveInfo> query = this.queryIntentServices(intent, flags);
        if (null == query || query.isEmpty()) 
            return null;
        

        ContentResolver resolver = this.mPluginContext.getContentResolver();
        return chooseBestActivity(intent, intent.resolveTypeIfNeeded(resolver), flags, query);
    

然后会调用queryIntentServices

  public List<ResolveInfo> queryIntentServices(Intent intent, int flags) 
        ComponentName component = intent.getComponent();
        List<ResolveInfo> resolveInfos = new ArrayList<ResolveInfo>();
        ContentResolver resolver = this.mPluginContext.getContentResolver();

        for (PackageParser.Service service : this.mPackage.services) 
            if (service.getComponentName().equals(component)) 
                ResolveInfo resolveInfo = new ResolveInfo();
                resolveInfo.serviceInfo = service.info;
                resolveInfos.add(resolveInfo);
             else if (component == null) 
                // only match implicit intent
                for (PackageParser.ServiceIntentInfo intentInfo : service.intents) 
                    if (intentInfo.match(resolver, intent, true, TAG) >= 0) 
                        ResolveInfo resolveInfo = new ResolveInfo();
                        resolveInfo.serviceInfo = service.info;
                        resolveInfos.add(resolveInfo);
                        break;
                    
                
            
        

        return resolveInfos;
    

如果要启动的Intent中含有component信息,则根据component来匹配,否则component为null的话,则根据intentInfo进行匹配

回到ActivityManagerProxy的startService方法中,如果null == resolveInfo,则表明不是插件里的Service,否则调用startDelegateServiceForTarget

 private ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) 
        Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command);
        return mPluginManager.getHostContext().startService(wrapperIntent);
    

还记得我们说要将启动的PluginService先替换成ProxyService么,这个动作就是在wrapperTargetIntent方法中完成的

private Intent wrapperTargetIntent(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) 
        // fill in service with ComponentName
        target.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
        String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation();

        // start delegate service to run plugin service inside
        boolean local = PluginUtil.isLocalService(serviceInfo);
        Class<? extends Service> delegate = local ? LocalService.class : RemoteService.class;
        Intent intent = new Intent();
        intent.setClass(mPluginManager.getHostContext(), delegate);
        intent.putExtra(RemoteService.EXTRA_TARGET, target);
        intent.putExtra(RemoteService.EXTRA_COMMAND, command);
        intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation);
        if (extras != null) 
            intent.putExtras(extras);
        

        return intent;
    

这个方法主要做了以下步骤:
1.给target的Intent设置Component

target.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));

2.获取要启动的插件service的插件路径

String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation();

3.判断目标service是本地的还是跨进程的

boolean local = PluginUtil.isLocalService(serviceInfo);
        Class<? extends Service> delegate = local ? LocalService.class : RemoteService.class;

4.替换intent为启动代理的Service的intent,并将启动真正插件的Service的intent保存到RemoteService.EXTRA_TARGET参数中,携将command保存到EXTRA_COMMAND_START_SERVICE,以及插件apk路径保存到EXTRA_PLUGIN_LOCATION中。

Intent intent = new Intent();
        intent.setClass(mPluginManager.getHostContext(), delegate);
        intent.putExtra(RemoteService.EXTRA_TARGET, target);
        intent.putExtra(RemoteService.EXTRA_COMMAND, command);
        intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation);
        if (extras != null) 
            intent.putExtras(extras);
        

接下来就是启动代理service啦

private ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) 
        Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command);
        return mPluginManager.getHostContext().startService(wrapperIntent);
    

接下来就会到LocalService中了,直接来到onStartCommand方法

 if (null == intent || !intent.hasExtra(EXTRA_TARGET) || !intent.hasExtra(EXTRA_COMMAND)) 
            return START_STICKY;
        

        Intent target = intent.getParcelableExtra(EXTRA_TARGET);
        int command = intent.getIntExtra(EXTRA_COMMAND, 0);
        if (null == target || command <= 0) 
            return START_STICKY;
        

如果启动不是插件的service,那就原来咋样就咋样,否则就根据传入的command来走相应的步骤,首先是EXTRA_COMMAND_START_SERVICE

case EXTRA_COMMAND_START_SERVICE: 
                ActivityThread mainThread = (ActivityThread)ReflectUtil.getActivityThread(getBaseContext());
                IApplicationThread appThread = mainThread.getApplicationThread();
                Service service;

                if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) 
                    service = this.mPluginManager.getComponentsHandler().getService(component);
                 else 
                    try 
                        service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();

                        Application app = plugin.getApplication();
                        IBinder token = appThread.asBinder();
                        Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class);
             

以上是关于滴滴插件化VirtualAPK框架原理解析之Service 管理的主要内容,如果未能解决你的问题,请参考以下文章

滴滴开源Android插件化框架VirtualAPK原理分析

滴滴开源Android插件化框架VirtualAPK原理分析

滴滴开源Android插件化框架VirtualAPK原理分析

Android开发的艺术:插件化框架Virtual APK实现原理解析

十大android开发框架

android 插件化框架VitualAPK