美文网首页Android进阶笔记
Android进阶笔记-3. Service 启动过程 & 绑定

Android进阶笔记-3. Service 启动过程 & 绑定

作者: 今阳说 | 来源:发表于2022-01-07 18:43 被阅读0次

    startService和bindService

    • Service的启动方式主要有两种,分别是startService和bindService
    • 使用startService启动时是单独开一个服务,与Activity没有任何关系,而bindService方式启动时,Service会和Activity进行绑定,当对应的activity销毁时,对应的Service也会销毁
    • startService多次,onStart()会执行多次,onCreate()只执行一次,onStartCommand()也会执行多次。bindService多次,onCreate()与onBind()都只会调用一次。
    • Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。
    • bindService启动的服务和调用者之间是典型的Client-Server模式。调用者是client,Service则是Server
      端。Service只有一个,但绑定到Service上面的Client可以有一个或很多个。bindService启动服务的生
      命周期与其绑定的client息息相关。
    • 调用Context.startService方式启动Service时,如果Android面临内存匮乏,可能会销毁当前运行的
      Service,待内存充足时可以重建Service。而Service被Android系统强制销毁并再次重建的行为依赖于
      Service的onStartCommand()方法的返回值
      1. START_STICKY: 如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由 于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。 如果在此期间没有任何启动命令被传 递到service,那么参数Intent将为null。
      2. START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务
      3. START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
      4. START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

    Service的启动过程

    ContextWrapper.startService() & ContextImpl.startService()
    • 启动Service是通过调用startService实现的,其方法实现在Context的实现类ContextWrapper中
    Context mBase;
    
    @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
    
    • Context mBase 指的是什么呢?ActivityThread启动Activity时会调用performLaunchActivity创建Activity的上下文环境
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        //创建上下文对象appContext
        ContextImpl appContext = createBaseContextForActivity(r);
        ...
        try {
            ...
            if (activity != null) {
                ...
                //将appContext传入Activity的attach方法中,将Activity与appContext关联
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);
                ...
            }
            ...
        } catch ...
        return activity;
    }
    
    //其中createBaseContextForActivity实现如下
    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        ...
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
        ...
        return appContext;
    }
    
    • 从上面代码可以看出,Activity的attach方法中将ContextImpl赋值给ContextWrapper的成员变量mBase,因此mBase具体指向就是ContextImpl;
    • 那么来看一下ContextImpl的startService方法
    @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }
    
    //其中调用了startServiceCommon代码如下
    private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) {
        try {
            ...
            //
            ComponentName cn = ActivityManager.getService().startService(
                    mMainThread.getApplicationThread(), service,
                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                    getOpPackageName(), getAttributionTag(), user.getIdentifier());
            ...
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    
    • 上面代码调用ActivityManager.getService()获取到ActivityManagerService,并调用其startService方法
    AMS.startService()
    • ActivityManagerService.startService代码如下
    @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage,
            String callingFeatureId, int userId) throws TransactionTooLargeException {
        ...
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, callingFeatureId, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }
    
    ActiveServices.startServiceLocked
    • 上面调用了mServices.startServiceLocked方法,其中mServices的类型是ActiveServices
    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
                int callingPid, int callingUid, boolean fgRequired, String callingPackage,
                @Nullable String callingFeatureId, final int userId,
                boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
        ...
        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
        ...
        return cmp;
    }
    //startServiceLocked其中又调用了startServiceInnerLocked
    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        ...
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        ...
        return r.name;
    }
    //startServiceInnerLocked中又调用了bringUpServiceLocked
    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired) throws TransactionTooLargeException {
        ...
        //ServiceRecord的processName的值赋值给procName, 用来描述Service想要在哪个进程运行,默认是当前进程,
        //我们也可以在AndroidManifes配置文件中设置android:process属性来新开启一个进程运行Service
        final String procName = r.processName;
        ProcessRecord app;
        if (!isolated) {
            //将procName和Service的uid传入到AMS的getProcessRecordLocked方法,
            //来查询是否存在一个与Service对应的ProcessRecord类型的对象app,
            //ProcessRecord主要用来记录运行的应用程序进程的信息.
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
    
            //如果用来运行Service的应用程序进程存在,则调用realStartServiceLocked方法
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch ...
            }
        } else {
            app = r.isolatedProc;
            ...
        }
    
        //如果用来运行Service的应用程序进程不存在,或应用程序之间的组件调用不需要检查权限,
        //满足则调用AMS的startProcessLocked方法来创建对应的应用程序进程。
        if (app == null && !permissionsReviewRequired) {
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                bringDownServiceLocked(r);
                return msg;
            }
            ...
        }
        ...
        return null;
    }
    
    //realStartServiceLocked方法代码如下
    private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
        ...
        try {
            ...
            //app.thread的实现是ActivityThread的内部类ApplicationThread
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.getReportedProcState());
            r.postNotification();
            created = true;
        }
        ...
    }
    
    ActivityThread.ApplicationThread.scheduleCreateService()
    • scheduleCreateService代码如下
    public final void scheduleCreateService(IBinder token,
            ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
        updateProcessState(processState, false);
        //将要启动的信息封装成CreateServiceData 对象并传给sendMessage方法
        CreateServiceData s = new CreateServiceData();
        s.token = token;
        s.info = info;
        s.compatInfo = compatInfo;
        //sendMessage向H发送CREATE_SERVICE消息
        sendMessage(H.CREATE_SERVICE, s);
    }
    
    ActivityThread.sendMessage() & ActivityThread.H.handleMessage()
    • scheduleCreateServiceActivity调用sendMessage向H发送CREATE_SERVICE消息,Thread.H.handleMessage根据消息类型,调用handleCreateService方法
    class H extends Handler {
        ...
        public void handleMessage(Message msg) {
            switch (msg.what) {
                ...
                 case CREATE_SERVICE:
                      ...
                      //根据消息类型,调用handleCreateService方法
                      handleCreateService((CreateServiceData)msg.obj);
                      ...
                      break;
                ...
            }
            ...
        }
    }
    
    ActivityThread.handleCreateService
    • ActivityThread.H.handleMessage方法根据消息类型,调用了handleCreateService方法
    private void handleCreateService(CreateServiceData data) {
        ...
        //获取要启动Service的应用程序的LoadedApk,LoadedApk是一个APK文件的描述类
        LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            ...
            //创建Service的上下文环境ContextImpl对象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            //通过调用LoadedApk的getClassLoader方法来获取类加载器
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            //根据CreateServiceData对象中存储的Service信息,将Service加载到内存中
            service = packageInfo.getAppFactory().instantiateService(cl, data.info.name, data.intent);
            ...
            //调用attach方法来初始化Service
            service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService());
            //调用Service的onCreate方法,这样Service就启动
            service.onCreate();
            //将启动的Service加入到ActivityThread的成员变量mServices中,其中mServices是ArrayMap类型
            mServices.put(data.token, service);
            ...
        } catch...
    }
    
    • 最终在ActivityThread.handleCreateService中创建service,并调用 attach 和 onCreate 方法

    Service的绑定过程

    ContextWrapper.bindService() & ContextImpl.bindService
    • 绑定Service是通过调用bindService()实现的,其方法实现在Context的实现类ContextWrapper中
    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }
    
    • 之前已经说过mBase具体指向就是ContextImpl,那么来看一下ContextImpl.bindService
    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
                getUser());
    }
    
    • bindService中调用了bindServiceCommon,代码如下
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            String instanceName, Handler handler, Executor executor, UserHandle user) {
        IServiceConnection sd;
        ...
        //调用了LoadedApk类型的对象mPackageInfo的getServiceDispatcher方法,
        //主要作用是将ServiceConnection封装为IServiceConnection类型的对象sd,
        //从IServiceConnection的名字就能得知它实现了Binder机制,这样Service的绑定就支持了跨进程
        if (mPackageInfo != null) {
            if (executor != null) {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
            } else {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
            }
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            ...
            //调用AMS的bindService方法
            int res = ActivityManager.getService().bindIsolatedService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
            ...
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    
    AMS.bindIsolatedService()
    public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String instanceName,
            String callingPackage, int userId) throws TransactionTooLargeException {
        ...
        synchronized(this) {
            //调用ActiveServices类型的对象mServices的bindServiceLocked方法
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, instanceName, callingPackage, userId);
        }
    }
    
    ActiveServices.bindServiceLocked()
    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String instanceName, String callingPackage, final int userId) throws TransactionTooLargeException {
        ...
        try {
            ...
            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                //调用bringUpServiceLocked方法,在bringUpServiceLocked方法中又会调用realStartServiceLocked方法,
                //最终由ActivityThread来调用Service的onCreate方法启动Service,
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                        permissionsReviewRequired) != null) {
                    return 0;
                }
            }
            ...
            //s.app != null 表示Service已经运行,其中s是ServiceRecord类型对象,app是ProcessRecord类型对象
            //b.intent.received表示当前应用程序进程的Client端已经接收到绑定Service时返回的Binder,
            //这样应用程序进程的Client端就可以通过Binder来获取要绑定的Service的访问接口
            if (s.app != null && b.intent.received) {
                try {
                    //调用c.conn的connected方法,其中c.conn指的是IServiceConnection,
                    //它的具体实现为ServiceDispatcher.InnerConnection,其中ServiceDispatcher是LoadedApk的内部类,
                    //InnerConnection的connected方法内部会调用H的post方法向主线程发送消息,从而解决当前应用程序进程和Service跨进程通信的问题,
                    c.conn.connected(s.name, b.intent.binder, false);
                } catch ...
                if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                    //如果当前应用程序进程的Client端第一次与Service进行绑定的,并且Service已经调用过onUnBind方法,
                    requestServiceBindingLocked(s, b.intent, callerFg, true);
                }
            } else if (!b.intent.requested) {
                //如果应用程序进程的Client端没有发送过绑定Service的请求,rebind为false,表示不是重新绑定
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }
            ...
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
        return 1;
    }
    
    • 上面最终调用了ActiveServices.requestServiceBindingLocked代码如下
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        ...
        //i.requested表示是否发送过绑定Service的请求
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                ...
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.getReportedProcState());
                ...
            } catch ...
        }
        return true;
    }
    
    • 上面代码requestServiceBindingLocked中调用了ActivityThread.ApplicationThread.scheduleBindService
    ActivityThread.ApplicationThread.scheduleBindService()
    public final void scheduleBindService(IBinder token, Intent intent,
            boolean rebind, int processState) {
        updateProcessState(processState, false);
        BindServiceData s = new BindServiceData();
        s.token = token;
        s.intent = intent;
        s.rebind = rebind;
        //向H发送BIND_SERVICE消息
        sendMessage(H.BIND_SERVICE, s);
    }
    
    ActivityThread.sendMessage() & ActivityThread.H.handleMessage()
    class H extends Handler {
        ...
        public void handleMessage(Message msg) {
            switch (msg.what) {
                ...
                 case BIND_SERVICE:
                      ...
                      //根据消息类型,调用handleBindService方法
                      handleBindService((BindServiceData)msg.obj);
                      ...
                      break;
                ...
            }
            ...
        }
    }
    
    ActivityThread.handleBindService()
    private void handleBindService(BindServiceData data) {
        //从mServices中获取要绑定的Service
        Service s = mServices.get(data.token);
        if (s != null) {
            //如果取到了
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    //判断是否rebind
                    if (!data.rebind) {
                        //调用Service的onBind方法,这样Service处于绑定状态了
                        IBinder binder = s.onBind(data.intent);
                        //调用AMS的publishService方法
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                } catch ...
            } catch ...
        }
    }
    
    • handleBindService中调用了Service的onBind方法,又调用了AMS的publishService方法
    AMS.publishService()
    public void publishService(IBinder token, Intent intent, IBinder service) {
        ...
        synchronized(this) {
            ...
            //调用了ActiveServices类型的mServices对象的publishServiceLocked方法
            mServices.publishServiceLocked((ServiceRecord)token, intent, service);
        }
    }
    
    ActiveServices.publishServiceLocked()
    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try {
            ...
            if (r != null) {
                ...
                if (b != null && !b.received) {
                    ...
                    for (int conni = connections.size() - 1; conni >= 0; conni--) {
                        ...
                        for (int i=0; i<clist.size(); i++) {
                            ...
                            try {
                                //c.conn指的是IServiceConnection,它的具体实现为ServiceDispatcher.InnerConnection,
                                //其中ServiceDispatcher是LoadedApk的内部类
                                c.conn.connected(r.name, service, false);
                            } catch...
                        }
                    }
                }
                ...
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
    
    • publishServiceLocked中调用了LoadedApk.ServiceDispatcher.InnerConnection.connected()
    LoadedApk.ServiceDispatcher.InnerConnection.connected()
    public void connected(ComponentName name, IBinder service, boolean dead)
            throws RemoteException {
        LoadedApk.ServiceDispatcher sd = mDispatcher.get();
        if (sd != null) {
            sd.connected(name, service, dead);
        }
    }
    
    • 上面代码调用了ServiceDispatcher 类型的sd对象的connected方法
    LoadedApk.ServiceDispatcher.connected()
    public void connected(ComponentName name, IBinder service, boolean dead) {
        if (mActivityExecutor != null) {
            mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
        } else if (mActivityThread != null) {
            //调用Handler类型的对象mActivityThread的post方法,mActivityThread实际上指向的是H。
            //因此,通过调用H的post方法将RunConnection对象的内容运行在主线程中
            mActivityThread.post(new RunConnection(name, service, 0, dead));
        } else {
            doConnected(name, service, dead);
        }
    }
    
    • connected最终调用了doConnected方法
    LoadedApk.ServiceDispatcher.doConnected()
    public void doConnected(ComponentName name, IBinder service, boolean dead) {
        ...
        if (old != null) {
            mConnection.onServiceDisconnected(name);
        }
        if (dead) {
            mConnection.onBindingDied(name);
        }
        if (service != null) {
            //调用了ServiceConnection类型的对象mConnection的onServiceConnected方法,
            //这样在客户端中实现了ServiceConnection接口的类的onServiceConnected方法就会被执行
            mConnection.onServiceConnected(name, service);
        } else {
            mConnection.onNullBinding(name);
        }
    }
    
    为什么bindService可以跟Activity生命周期联动?
    1. bindService 方法执行时,LoadedApk 会记录 ServiceConnection 信息。
    2. Activity 执行 finish 方法时,会通过 LoadedApk 检查 Activity 是否存在未注销/解绑的 BroadcastReceiver和 ServiceConnection,如果有,那么会通知 AMS 注销/解绑对应的 BroadcastReceiver 和 Service,并打印异常信息,告诉用户应该主动执行注销/解绑的操作。
    如何保证Service不被杀死?
    • Android 进程不死从3个层面入手:
    1. 提供进程优先级,降低进程被杀死的概率
    • 方法一:监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。
    • 方法二:启动前台service。
    • 方法三:提升service优先级:在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。
    2. 在进程被杀死后,进行拉活
    • 方法一:注册高频率广播接收器,唤起进程。如网络变化,解锁屏幕,开机等
    • 方法二:双进程相互唤起。
    • 方法三:依靠系统唤起。
    • 方法四:onDestroy方法里重启service:service + broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service;
    3. 依靠第三方
    • 根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送;其他手机可以考虑接入腾讯信鸽或极光推送与小米推送做 A/B Test。

    参考

    我是今阳,如果想要进阶和了解更多的干货,欢迎关注微信公众号 “今阳说” 接收我的最新文章

    相关文章

      网友评论

        本文标题:Android进阶笔记-3. Service 启动过程 & 绑定

        本文链接:https://www.haomeiwen.com/subject/gltscrtx.html