Android 7.0 中 Service bind 流程详解

我们知道,Android启动Service有两种方式,startService和bindService。

对于通过startService启动的Service,只能作为接收方,启动方可以通过onStartCommand来传递参数。这种启动方式其可以有多个启动者,不过在销毁的时候,一旦有任意一个启动调用了stopService或者自身stopSelf后,该Service就会停止,而启动者的生命周期无法影响到该Service。

对于通过bindService启动的Service,其和启动方有个“绑定”的过程,启动方可以通过Service的binder引用来调用Service的方法。不过这种方式Service的生命周期会关联着启动方的,启动方生命周期结束后,会默认unbindService来结束。

startService很多过程在bindService中都会得到体现,所以本文只介绍bindService的流程。

先看看bindService的调用过程

bindService

bindServiceCommon

这一步中首先会把ServiceConnection转成Binder对象

IServiceConnection sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);

这一步是在LoadApk中完成的

public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if (map != null) {
                sd = map.get(c);
            }
            if (sd == null) {
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (map == null) {
                    map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler);
            }
            return sd.getIServiceConnection();
        }
    }

可以看到,LoadApk会为每个context保存一个key为ServiceConnection、value为ServiceDispatcher的map,这样尽量做到了ServiceConnection对应的Binder对象的复用。如果没有可复用,则把ActivityThread的主Handler和ServiceConnection对应起来,这样在复用的时候,ServiceDispatcher会调用validate方法检查handler为当前主线程handler,保证不错乱。另外,ServiceConnection作为回调接口,其在IPC中实质是以ServiceDispatcher的内部类InnerConnection作为载体来代替的。

接下来,就交给ActivityManagerService处理了

int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());

ActiveService.bindServiceLocked

ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
        if (res == null) {
            return 0;
        }
        if (res.record == null) {
            return -1;
        }

这步顾名思义,是获取根据intent、binder等数据获取Service的信息,其中ServiceLookupResult包含了ServiceRecord和权限信息。进入函数我们可以看到如下:

ServiceRecord r = null;        
ServiceMap smap = getServiceMap(userId);
        final ComponentName comp = service.getComponent();
        if (comp != null) {
            r = smap.mServicesByName.get(comp);
        }
        if (r == null && !isBindExternal) {
            Intent.FilterComparison filter = new Intent.FilterComparison(service);
            r = smap.mServicesByIntent.get(filter);
        }
        if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
                && !callingPackage.equals(r.packageName)) {
            r = null;
        }

这一步可以看到,再取ServiceRecord的时候,会先去尝试用缓存,如果设置了FLAG_EXTERNAL_SERVICE且是外部Service调用,则禁止复用。如果无法复用,则创建新的ServiceRecord并赋值。

回到bindServiceLocked中,往下走

AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);

首先,ServiceRecord会根据自己的成员函数retrieveAppBindingLocked方法来获取一个AppBindRecord。再此后面会用到一些数据结构:AppBindRecord、ServiceRecord、IntentBindRecord、ProcessRecord、ConnectionRecord。他们之间的关系需要理清楚一下:

ServiceRecord:保存了Service的信息

ProcessRecord:进程的信息

ConnectionRecord:进程和Service建立起的本次通信,记一次ConnectionRecord

AppBindRecord:当某个进程需要用某个Intent启动Service的时候,这时候应用程序和Service的关系由AppBindRecord来维持。所以里面包含:谁启动(ProcessRecord)、启动的是哪个(ServiceRecord)、用什么来启动(IntentBindRecord)、所有启动记录的信息(ArraySet<ConnectionRecord>

IntentBindRecord:如在AppBindRecord所说,用什么来启动Service(Intent),里面携带了ServiceRecord、及所有用此Intent启动Service的ArrayMap<ProcessRecord, AppBindRecord>

举个很不恰当的比喻:我想在一张纸上盖一个“汪毅雄”的章。

ProcessRecord:我(行为发起者)

ServiceRecord:“汪毅雄”的章印(行为目标)

IntentBindRecord:制作刻了“汪毅雄”三个字、并且能记录盖章过程的一个智能章。(工具,有权限的人都能用。也可以有很多个不同的,只要能盖出“汪毅雄”这几个字就可以)

AppBindRecord:“我要盖章”这个过程一系列下来的所有记录。

ConnectionRecord:手摁章印的过程。

public AppBindRecord retrieveAppBindingLocked(Intent intent,
            ProcessRecord app) {
        Intent.FilterComparison filter = new Intent.FilterComparison(intent);
        IntentBindRecord i = bindings.get(filter);
        if (i == null) {
            i = new IntentBindRecord(this, filter);
            bindings.put(filter, i);
        }
        AppBindRecord a = i.apps.get(app);
        if (a != null) {
            return a;
        }
        a = new AppBindRecord(this, i, app);
        i.apps.put(app, a);
        return a;
    }

有上面对数据结构的认识,我们可以看到,要取一个AppbindRecord。

a、我们先用Intent去获取filter。

b、ServiceRecord里有一个成员Map bindings保存所有可以启动该Service的IntentBindRecord,获得filter后,利用它去bindings中尝试复用IntentBindRecord。

c、拿到IntentBindRecord后,再用ProcessRecord去检查IntentBindRecord,看看其里面有没有和ProcessRecord相同进程的且也是启动该Service的AppbindRecord,有的话当然不用再建一个AppBindRecord,直接复用返回。

继续往下

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);
            if (clist == null) {
                clist = new ArrayList<ConnectionRecord>();
                s.connections.put(binder, clist);
            }
            clist.add(c);
            b.connections.add(c);
            if (activity != null) {
                if (activity.connections == null) {
                    activity.connections = new HashSet<ConnectionRecord>();
                }
                activity.connections.add(c);
            }

获得AppBindRecord后,再利用传递过来的信息(包括IServiceConnection这个回调接口)创建一个ConnectionRecord,并且把ConnectionRecord分别保存到AppBindRecord、ServiceRecord、ActivityRecord中。

继续往下

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

如果flag不为BIND_AUTO_CREATE,或者bringUpServiceLocked成功(return null)后,则可以继续后续的操作,如:Service已存在的时候BIND_TREAT_LIKE_ACTIVITY可以降低Service被杀的概率、重新回调connected、rebind等,本文不做讨论。继续bringUpServiceLocked

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);
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                  
                }
            } 

在realStartServiceLocked之前,和Activity启动一样,我们先需要判断Service的进程是否存在,如果不存在,需要孵化新的进程,然后再继续Service的启动。这块在Activity启动过程中已经详述,这里就不重复说了,详见《Android 7.0中Launcher启动Activity过程》

当进程存在的时候,我们直接realStartServiceLocked

            mAm.notifyPackageUse(r.serviceInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;

上面可以看到,这块利用binder通信去告诉进程去创建一个Service---scheduleCreateService,实际创建过程是在用户进程(ActivityThread)中。

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会创建一个CreateServiceData,并把ServiceInfo等信息填入,交给main Handler处理,最终会到handleCreateService方法中。我们直接进入

private void handleCreateService(CreateServiceData data) {
        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) {
            ...
        }
        try {
            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) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
           ...
        }
    }

在这个方法中,首先获得Service的ClassLoader,用之load一个Service实例。然后再设置ContextImpl,然后初始化Service在进程中的信息和其binder对象,最后调用Service.onCreate方法,完成创建。然后通过binder告诉AMS service创建成功,也就是serviceDoneExecuting,可以看到告诉AMS的有个type的字段设为SERVICE_DONE_EXECUTING_ANON。

/** Type for IActivityManager.serviceDoneExecuting: anonymous operation */
    public static final int SERVICE_DONE_EXECUTING_ANON = 0;
    /** Type for IActivityManager.serviceDoneExecuting: done with an onStart call */
    public static final int SERVICE_DONE_EXECUTING_START = 1;
    /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */
    public static final int SERVICE_DONE_EXECUTING_STOP = 2;

这块是Service生命周期给AMS的回调的类型,有3类。但是在Service onCreate完成后,AMS收到后没太做处理,所以这部分跳过。

刚才在realStartServiceLocked,AMS告诉进程创建Service,但是步骤并没有真正完成,我们回到刚才的方法

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        try {
            ...
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
//******************** 接这里 ************************//
        } catch (DeadObjectException e) {
        } finally {
          ...
        }
        requestServiceBindingsLocked(r, execInFg);
        updateServiceClientActivitiesLocked(app, null, true);
    }

看到这里,在创建完成后,会调用requestServiceBindingsLocked方法执行bind操作

private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
            throws TransactionTooLargeException {
        for (int i=r.bindings.size()-1; i>=0; i--) {
            IntentBindRecord ibr = r.bindings.valueAt(i);
            if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
                break;
            }
        }
    }

AMS进程中,会对bindings中的每一个IntentBindRecord尝试bind其进程。

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 (TransactionTooLargeException e) {
                ...
            } catch (RemoteException e) {
                ...
            }
        }

通过binder告诉ActivityThread,Service需要执行bind操作。然后在ActivityThread会调用到handleBindService方法。

private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                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) {
            }
        }
    }

可以看到如果不是rebind,Service会执行Service.onBind()方法,把IntentBindRecord中的Intent告诉给Service谁bind它了。然后再告诉AMS publishService。

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        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);
                    ...
                    try {
                        c.conn.connected(r.name, service);
                    } catch (Exception e) {
                    }
                }
            }
        }
    }

在publishServiceLocked中,对所有ConnectionRecord,把bind结果回调给其IServiceConnection。而IService的Stub在LoadApk中

private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
            public void connected(ComponentName name, IBinder service) throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service);
                }
            }
        }

之后会通过主handler post一个Connection Runnable

private final class RunConnection implements Runnable {
            public void run() {
                if (mCommand == 0) {
                    doConnected(mName, mService);
                } 
            }
        }

最终真正执行到ServiceConnection中的onServiceConnected方法,完成最终绑定的过程

public void doConnected(ComponentName name, IBinder service) {
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            }
        }

整个Service的bind过程就完成了!

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

Android开发模式之MVC,MVP和MVVM的简单介绍与区别

相信大家对MVC,MVP和MVVM都不陌生,作为三个最耳熟能详的Android框架,它们的应用可以是非常广泛的,但是对于一些新手来说,可能对于区分它们三个都有困...

4408
来自专栏Android Note

Android-实用的MVP

1063
来自专栏技术小黑屋

Read Output From Shell

Python provides a lot of method to read output from a just executed shell. Howev...

982
来自专栏求索之路

四大组件以及Application和Context的全面理解

1.概述 ? Context抽象结构 2.用处 1.Context的实现类有很多,但是ContextImpl(后称CI)是唯一做具体工作的,其他实现都是对CI做...

2865
来自专栏为数不多的Android技巧

Android 插件化原理解析——Service的插件化

在 Activity生命周期管理 以及 广播的管理 中我们详细探讨了Android系统中的Activity、BroadcastReceiver组件的工作原理以及...

722
来自专栏潇涧技术专栏

Android Dependency Injection Libraries

本文总结并对比了三种Android依赖注入库:Butter Knife、RoboGuice、Android Annotations的使用

661
来自专栏Android源码框架分析

Android Service重启恢复(Service进程重启)原理解析

Android系统中,APP进程被杀后,等一会经常发现进程又起来了,这个现象同APP中Service的使用有很大关系,本文指的Service是通过startSe...

1263
来自专栏CodingBlock

Android学习笔记(十一)BroadcastReceiver动态注册、注销示例

  在上一篇博文中简单介绍了一下BroadcastReceiver的相关知识点,本篇举一个在代码中动态的注册、注销BroadcastReceiver的栗子。 1...

1868
来自专栏向治洪

android断点下载

断点下载往往用在大文件的下载过程中,如传统的迅雷下载用的就是断点下载技术,说起来原理比较简单:对文件进行分片,并对分片的文件进行标记,然后分片下载,下载完成后对...

20610
来自专栏双十二技术哥

Android性能优化(七)之你真的理解ANR吗?

在上一篇文章《Android性能优化(六)之卡顿那些事》中,我们提到了卡顿的成因、检测卡顿的途径以及避免卡顿的方法。卡顿再扩大就会产生大名鼎鼎的ANR(Appl...

563

扫码关注云+社区