美文网首页
Service启动之bindService

Service启动之bindService

作者: eliv_he | 来源:发表于2020-08-06 18:52 被阅读0次

话不多说,上代码(本文基于Android 29 源码分析):

bindService(Intent(this, MyService::class.java), MyConnection(), Context.BIND_AUTO_CREATE)

其中MyConection代码为:

inner class MyConnection : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
           
        }

    }

日常操作,就不用解释了,下面从 bindService 开始,跟踪源码,理解整个过程。activity继承至contextWrapper,contextWrapper其实是contextImpl的代理类,所以bindService直接跟进到contextImpl中,最终到bindServiceCommon:

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            String instanceName, Handler handler, Executor executor, UserHandle user) {
        // 后面会分析,这地方记住这个句柄 sd 是 IServiceConnection 对象
        IServiceConnection sd;
        ...
        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();
            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(this);
          // 关键点 1:跳转到AMS处理,开始了跨进程之旅
            int res = ActivityManager.getService().bindIsolatedService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

关键点1处注意几个参数:

getApplicationThread: 都知道进程启动、activity启动都会有一个ApplicationThread,同理这地方也是,在启动service的时候依然是走到 ActivityThread,走生命周期。
getActivityToken:每一个Activity都有一个token,该token会在ActivityRecord中保存,由WMS有关,这地方就是找到指定的Activity
sd:IServiceConnection对象,后面会用到

接着走到AMS,看一下AMS怎么做的处理:

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) {
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, instanceName, callingPackage, userId);
        }
    }

参数对应上,又调用 了bindServiceLocked方法,go on:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String instanceName, String callingPackage, final int userId)
            throws TransactionTooLargeException {
        ...
          //获取调用者线程
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        ...
          //本质上就是创建一个ServiceRecord对象,该对象实现了binder接口
        ServiceLookupResult res =
            retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true,
                    callerFg, isBindExternal, allowInstant);
        if (res == null) {
            return 0;
        }
        if (res.record == null) {
            return -1;
        }
        ServiceRecord s = res.record;
       ...
        try {
            ...
            //关键点1 
            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent,
                    callerApp.uid, callerApp.processName, callingPackage);

            IBinder binder = connection.asBinder();
            s.addConnection(binder, c);
            b.connections.add(c);
            if (activity != null) {
                activity.addConnection(c);
            }
            b.client.connections.add(c);
            c.startAssociationIfNeeded();
            if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                b.client.hasAboveClient = true;
            }
            if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
                s.whitelistManager = true;
            }
            if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
                s.setHasBindingWhitelistingBgActivityStarts(true);
            }
            if (s.app != null) {
                updateServiceClientActivitiesLocked(s.app, c, true);
            }
            ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
            if (clist == null) {
                clist = new ArrayList<>();
                mServiceConnections.put(binder, clist);
            }
            clist.add(c);

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

           ...
        return 1;
    }

该方法较长,分析关键 的流程,在关键点1处,创建了ConnectionRecord对象,从名称可看出就是我们bind service方法调用传的 Connection对象,不过这地方是包装的IServiceConnection ,这个就是上面说的sd对象,先知道是包装了Connection的对象,后面会分析,接着一些flag相关。关键点2 这个是真正重要的地方,先看flag判断,(flags&Context.BIND_AUTO_CREATE) != 0,如果参数是Context.BIND_AUTO_CREATE,就会走到bringUpServiceLocked,Context.BIND_AUTO_CREATE就是bindeservice方法最后一个参数,

bindService(Intent(this, MyService::class.java), MyConnection(), Context.BIND_AUTO_CREATE)

这个参数的意义就是,自动startService,在执行绑定操作,继续跟进bringUpServiceLocked,

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
            ...
            realStartServiceLocked(r, app, execInFg);
  ....
            }

该方法关键的就是执行realStartServiceLocked(r, app, execInFg);其他没什么好分析的 ,继续跟进:

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        if (app.thread == null) {
            throw new RemoteException();
        }
       
        r.setProcess(app);
      ...

        boolean created = false;
        try {
            //关键点1 : 开始回调调用者进程,熟悉的调用方式
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.getReportedProcState());
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
            Slog.w(TAG, "Application dead when creating service " + r);
            mAm.appDiedLocked(app);
            throw e;
        } finally {
           ...
        }
                ...

          //关键点 2
        requestServiceBindingsLocked(r, execInFg);

        updateServiceClientActivitiesLocked(app, null, true);
        ...
    }

看到关键点1 有没有熟悉的感觉,没错就是跟activity启动流程类似的调用,该方法的执行会到ActivityThread执行service的启动,同理,注意相关参数

r:ServiceRecord,service的记录,后续会通过该对象作为key保存启动的Service

跨进程到了调用者进程,找到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);
}

包装一个CreateServiceData,发送一个message ,进入主线程调用,最终到:

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 = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw new RuntimeException(
                "Unable to instantiate service " + data.info.name
                + ": " + e.toString(), e);
        }
    }

    try {
                // 创建了一个context 对象,本质上一个service对应一个context
        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,
                ActivityManager.getService());
      // 关键点1 执行service的oncreate 生命周期
        service.onCreate();
        mServices.put(data.token, service);
        try {
          // 关键点2 继续跨进程到AMS 继续执行后续操作
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw new RuntimeException(
                "Unable to create service " + data.info.name
                + ": " + e.toString(), e);
        }
    }
}

从关键点1看出我们的service的生命周期开始执行了,关键点2,又开始了跨进程之旅,到这我门的bind操作还没真正执行,目前只是start service了,好继续serviceDoneExecuting的跟踪,其实serviceDoneExecuting的执行很简单,就是没有做任何有用的操作,这地方有兴趣的自己可以跟踪过去,注意对照SERVICE_DONE_EXECUTING_ANON参数看。
我们回到上面的realStartServiceLocked方法,继续看下面的关键点2的执行,发现执行完启动过程,会紧跟着执行requestServiceBindingsLocked方法,这个又是做什么的,源码如下:

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        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); 
              // 关键点1 ,熟悉的调用方式
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.getReportedProcState());
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {
                // Keep the executeNesting count accurate.
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                throw e;
            } catch (RemoteException e) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
                // Keep the executeNesting count accurate.
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                return false;
            }
        }
        return true;
    }

看到在关键点1处,又跨进程执行了ActivityThread的scheduleBindService方法,从名字也可猜出执行到了我们的bind操作,nice,继续看ActivityThread的执行过程,最终经过 handler 回调到 handleBindService:

private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
   
    if (s != null) {
           ...
                if (!data.rebind) {
                  //关键点1 终于到了我们的onbind
                    IBinder binder = s.onBind(data.intent);
                  //关键点2 再一次跨进程到AMS
                    ActivityManager.getService().publishService(
                            data.token, data.intent, binder);
                } else {
                    s.onRebind(data.intent);
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                }
        ...
    }
}

关键点1 终于到了我们的onbind方法,还记得我们service中onbind会返回bind对象,然后关键点2处又开始将我们返回的binder对象传递到AMS ,那么继续跟进去看看 :

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
  ...
    ConnectionRecord c = clist.get(i);
    // conn :IServiceConnectio  service: 对应上面的返回的binder
     c.conn.connected(r.name, service, false);
    ...
}

该方法做了精简主要的地方就是调用了connected方法,conn还记得开始分析的IServiceConnection吗,没错,就是我们执行bindServiceCommon方法中的sd对象(忘了的翻到上面的bindServiceCommon方法看看就知道了),我们看看这个对象怎么生成的,
很简单跟踪源码可以看到就是new ServiceDispatcher(c, context, executor, flags)的操作,对应的构造函数

ServiceDispatcher(ServiceConnection conn,
        Context context, Executor activityExecutor, int flags) {
    mIServiceConnection = new InnerConnection(this);
    mConnection = conn;
    mContext = context;
    mActivityThread = null;
    mActivityExecutor = activityExecutor;
    mLocation = new ServiceConnectionLeaked(null);
    mLocation.fillInStackTrace();
    mFlags = flags;
}

回到上面的c.conn.connected(r.name, service, false); 就是ServiceDispatcher的connected(r.name, service, false),这个方法如下:

public void connected(ComponentName name, IBinder service, boolean dead) {
    if (mActivityExecutor != null) {
        mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
    } else if (mActivityThread != null) {
      //执行到这,毕竟 ActivityThread 是存在的,service:对应上面的返回的binder
        mActivityThread.post(new RunConnection(name, service, 0, dead));
    } else {
        doConnected(name, service, dead);
    }
}

这地方post了一个runnable对象,就是执行到了相关的run方法,在run方法中会调用到我们的onServiceConnected(name, service);

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

    synchronized (this) {
       ...
    // 关键点1 终于执行了onServiceConnected 
    if (service != null) {
        mConnection.onServiceConnected(name, service);
    } else {
        // The binding machinery worked, but the remote returned null from onBind().
        mConnection.onNullBinding(name);
    }
   }
}

就是我们自己写的MyConnection中的onServiceConnected,这地方的service参数就是service的onbind方法返回的binder对象,到此跟踪了整个service的bind过程,bindService方式的源码分析也就结束了。

相关文章

网友评论

      本文标题:Service启动之bindService

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