Service源码浅析三

Service源码浅析一
Service源码浅析二
Service源码浅析三
Service源码浅析四

咱们还是带着问题继续分析Service源码,这篇解决以下三个问题:

  1. 启动Service时创建进程的流程
  2. 相同进程和不同进程,Service方法调用有什么区别
  3. AIDL方法中声明的in, out,oneway在哪个逻辑中起作用

咱们先从第一个问题开始:

启动Service时创建进程的流程

在上一篇文章Service源码浅析一中,咱们跟过startService的主流程,这里直接把部分内容拿过来,
ActiveServices#startServiceLocked -> startServiceInnerLocked -> bringUpServiceLocked -> bringUpServiceInnerLocked -> realStartServiceLocked
-> IApplicationThread#scheduleCreateService

--->bringUpServiceInnerLocked

if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);
            if (app != null) {
                final IApplicationThread thread = app.getThread();
                final int pid = app.getPid();
                final UidRecord uidRecord = app.getUidRecord();
                if (app.isThreadReady()) {
                    try {
                        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                    "realStartServiceLocked: " + r.shortInstanceName);
                        }
                        app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode,
                                mAm.mProcessStats);
                        realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
                                enqueueOomAdj, serviceBindingOomAdjPolicy);
                        return null;
                    } catch (TransactionTooLargeException e) {
                        throw e;
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
                    } finally {
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    }

                    // If a dead object exception was thrown -- fall through to
                    // restart the application.
                }
            }
        }

之前咱们按照目标进程已启动分析了Service的启动过程,现在看一下进程未启动的情况

// Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.
        if (app == null && !permissionsReviewRequired && !packageFrozen) {
            // TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
            //  was initiated from a notification tap or not.
            if (r.isSdkSandbox) {
                final int uid = Process.toSdkSandboxUid(r.sdkSandboxClientAppUid);
                app = mAm.startSdkSandboxProcessLocked(procName, r.appInfo, true, intentFlags,
                        hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, uid, r.sdkSandboxClientAppPackage);
                r.isolationHostProc = app;
            } else {
                app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                        hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated);
            }
            if (app == 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, enqueueOomAdj);
                return msg;
            }
            mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app,
                    r);
            if (isolated) {
                r.isolationHostProc = app;
            }
        }

这里调用了mAm.startProcessLocked

    @GuardedBy("this")
    final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
            boolean isolated) {
        return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
                hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
                false /* isSdkSandbox */, 0 /* sdkSandboxClientAppUid */,
                null /* sdkSandboxClientAppPackage */,
                null /* ABI override */, null /* entryPoint */,
                null /* entryPointArgs */, null /* crashHandler */);
    }

ProcessList是AMS中用来处理进程的类
ProcessList#startProcessLocked ->startProcessLocked-->startProcessLocked有一个重载方法,最终触发了startProcess方法

if (hostingRecord.usesWebviewZygote()) {
                startResult = startWebView(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null, app.info.packageName,
                        app.getDisabledCompatChanges(),
                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
            } else if (hostingRecord.usesAppZygote()) {
                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);

                // We can't isolate app data and storage data as parent zygote already did that.
                startResult = appZygote.getProcess().start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null, app.info.packageName,
                        /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
                        app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
                        false, false, false,
                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
            } else {
                regularZygote = true;
                startResult = Process.start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                        isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap,
                        allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                        bindOverrideSysprops,
                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
                // By now the process group should have been created by zygote.
                app.mProcessGroupCreated = true;
            }

最终会调用Process.start,从这里就进入zygote创建进程的流程了。
Process.start -> ZygoteProcess.start ->startViaZygote->zygoteSendArgsAndGetResult,
看到这里没有头绪了,好像是通过socket发出去了,这块留着看Zygote时候再补上吧,不管怎么样,最终肯定会调用目标进程的ActivityThread的main方法启动目标进程。

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // Install selective syscall interception
        AndroidOs.install();

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

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

        // Call per-process mainline module initialization.
        initializeMainlineModules();

        Looper.prepareMainLooper();

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

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

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

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

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

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

main方法会触发ActivityThread#attach方法,然后又进入AMS的attachApplication -> attachApplicationLocked,又会触发目标进程的ApplicationThread#bindApplication,然后是handleBindApplication,在这个方法最后会调用AMS的finishAttachApplication -> finishAttachApplicationInner -> ActiveServices#attachApplicationLocked

boolean attachApplicationLocked(ProcessRecord proc, String processName)
            throws RemoteException {
        boolean didSomething = false;

        // Update the app background restriction of the caller
        proc.mState.setBackgroundRestricted(appRestrictedAnyInBackground(
                proc.uid, proc.info.packageName));

        // 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.isolationHostProc && (proc.uid != sr.appInfo.uid
                            || !processName.equals(sr.processName))) {
                        continue;
                    }

                    final IApplicationThread thread = proc.getThread();
                    final int pid = proc.getPid();
                    final UidRecord uidRecord = proc.getUidRecord();
                    mPendingServices.remove(i);
                    i--;
                    proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode,
                            mAm.mProcessStats);
                    try {
                        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                    "realStartServiceLocked: " + sr.shortInstanceName);
                        }
                        realStartServiceLocked(sr, proc, thread, pid, uidRecord, sr.createdFromFg,
                                true, SERVICE_BIND_OOMADJ_POLICY_LEGACY);
                    } finally {
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    }
                    didSomething = true;
                    if (!isServiceNeededLocked(sr, false, false)) {
                        // We were waiting for this service to start, but it is actually no
                        // longer needed.  This could happen because bringDownServiceIfNeeded
                        // won't bring down a service that is pending...  so now the pending
                        // is done, so let's drop it.
                        bringDownServiceLocked(sr, true);
                    }
                    /* Will be a no-op if nothing pending */
                    mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE);
                }
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception in new application when starting service "
                        + sr.shortInstanceName, e);
                throw e;
            }
        }

    ...
}

在这个方法中会启动等待的Service,并触发realStartServiceLocked,这就回到咱们之前的流程了,踉踉跄跄,这个问题总算整体流程下来了。

相同进程和不同进程,Service方法调用有什么区别

下面开始第二个问题,在第二篇文章中咱们分析过Service方法调用的流程,有兴趣的同学可以看一下。
咱们在第二篇文章中其实分析的是不同进程间的方法调用,这里咱们继续看一下,相同进程间的Service方法调用。

public static com.example.serverdemo.IRemoteService asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.serverdemo.IRemoteService))) {
        return ((com.example.serverdemo.IRemoteService)iin);
      }
      return new com.example.serverdemo.IRemoteService.Stub.Proxy(obj);
    }

咱们知道Stub的asInterface方法,在相同进程的情况下返回Stub对象本身,这样就简单了,客户端在调用时其实就是调用的咱们自己的实现类的方法

    private val binder = object : IRemoteService.Stub() {
        override fun getPid(): Int {
            return Process.myPid()
        }

        override fun basicTypes(
            anInt: Int,
            aLong: Long,
            aBoolean: Boolean,
            aFloat: Float,
            aDouble: Double,
            aString: String?
        ) {
        }

    }

根本就不存在跨进程调用了啊,第二个问题结束。

AIDL方法中声明的in, out,oneway在哪个逻辑中起作用

当在 AIDL 中使用非基本类型(对象)时,需要指定方向标签:in、out 或 inout。

  • in表示数据仅从客户端流向服务端
  • out表示数据仅从服务端流向客户端
  • inout表示数据可以双向流动

普通 AIDL 方法是同步调用(客户端会阻塞等待返回),使用 oneway 的方法则是异步调用(客户端不会等待),oneway 方法不能有返回值(必须声明为 void),也不能有 out 或 inout 参数(因为无法返回数据)。
咱们改造一下上边的AIDL文件。

interface IRemoteService {
    int getPid();
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
    void dataChange(in byte[] data);
    void sendData(out byte[] data);
    oneway void installTheme();
}

添加了三个方法,看一下编译之后生成的类文件

@Override public void dataChange(byte[] data) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeByteArray(data);
          boolean _status = mRemote.transact(Stub.TRANSACTION_dataChange, _data, _reply, 0);
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public void sendData(byte[] data) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeInt(data.length);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sendData, _data, _reply, 0);
          _reply.readException();
          _reply.readByteArray(data);
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public void installTheme() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_installTheme, _data, null, android.os.IBinder.FLAG_ONEWAY);
        }
        finally {
          _data.recycle();
        }
      }

咱们先看oneway,相比于普通方法,这里调用transact 时多了一个Flag,IBinder.FLAG_ONEWAY,咱们看看这个Flag会起什么作用
从生成代码中可以看到,相比于普通方法,oneway方法在触发mRemote.transact之后不会等待读取放回结果,即便是无返回值的普通方法,也会通过_reply.readException();读取返回结果。
猜测Binder驱动底层会等待服务端处理完成才返回结果。

in/out/inout 主要用来控制参数传递方向,它们是参数修饰符,用于明确数据在跨进程调用时的传输方向。

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      java.lang.String descriptor = DESCRIPTOR;
      if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
        data.enforceInterface(descriptor);
      }
      if (code == INTERFACE_TRANSACTION) {
        reply.writeString(descriptor);
        return true;
      }
      switch (code)
      {
        case TRANSACTION_getPid:
        {
          int _result = this.getPid();
          reply.writeNoException();
          reply.writeInt(_result);
          break;
        }
        case TRANSACTION_basicTypes:
        {
          int _arg0;
          _arg0 = data.readInt();
          long _arg1;
          _arg1 = data.readLong();
          boolean _arg2;
          _arg2 = (0!=data.readInt());
          float _arg3;
          _arg3 = data.readFloat();
          double _arg4;
          _arg4 = data.readDouble();
          java.lang.String _arg5;
          _arg5 = data.readString();
          this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
          reply.writeNoException();
          break;
        }
        case TRANSACTION_dataChange:
        {
          byte[] _arg0;
          _arg0 = data.createByteArray();
          this.dataChange(_arg0);
          reply.writeNoException();
          break;
        }
        case TRANSACTION_sendData:
        {
          byte[] _arg0;
          int _arg0_length = data.readInt();
          if (_arg0_length < 0) {
            _arg0 = null;
          } else {
            _arg0 = new byte[_arg0_length];
          }
          this.sendData(_arg0);
          reply.writeNoException();
          reply.writeByteArray(_arg0);
          break;
        }
        case TRANSACTION_installTheme:
        {
          this.installTheme();
          break;
        }
        case TRANSACTION_normalMethod:
        {
          this.normalMethod();
          reply.writeNoException();
          break;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
      return true;
    }

这是上边几个方法对应的服务端源码,咱们知道,如果是跨进程通信,其实触发的是服务端的onTransact方法,进程内通信触发的是Stub的方法,也就是咱们定义的Stub实现类的方法。
从客户端和服务端的源码可以看到,对于添加了in修饰的方法和普通方法没有区别
对于添加了out修饰的方法,服务端会将修改后的值写回,回传给客户端,客户端会读取回传的值。
out 适用于服务端生成数据并返回的场景。

interface IDataService {
    // out 参数:服务端填充 config 对象
    void fetchRemoteConfig(out Config config);
}

ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        IDataService stub = IDataService.Stub.asInterface(service);
        Config config = new Config(); // 可以是空对象
        stub.fetchRemoteConfig(config); // 服务端将数据写入 config
        Log.d("Client", "Received config: " + config.getValue());
    }
};

private final IDataService.Stub binder = new IDataService.Stub() {
    @Override
    public void fetchRemoteConfig(Config config) {
        // 忽略客户端传入的 config 内容,直接填充新数据
        config.setValue("Server_Config_123"); 
    }
};

这个例子中,客户端触发服务端方法之后,可以从参数中获取服务端回传的数据,感觉和方法返回值比较类似。
客户端在调用服务端方法传参时,如果使用out修饰,服务端会拿不到数据,这是为什么呢?
从服务端代码可以看出

case TRANSACTION_dataChange:
        {
          byte[] _arg0;
          _arg0 = data.createByteArray();
          this.dataChange(_arg0);
          reply.writeNoException();
          break;
        }
        case TRANSACTION_sendData:
        {
          byte[] _arg0;
          int _arg0_length = data.readInt();
          if (_arg0_length < 0) {
            _arg0 = null;
          } else {
            _arg0 = new byte[_arg0_length];
          }
          this.sendData(_arg0);
          reply.writeNoException();
          reply.writeByteArray(_arg0);
          break;
        }

使用in修饰的dataChange方法,调用服务端方法之前会根据入参生成一个字节数组并传入,但使用out修饰的sendData方法,重新new了一个字节数组,导致传入的数据丢失了。
最后看一下inout修饰符,给normalMethod方法添加inout修饰的参数之后客户端和服务端生成的代码是这样的

@Override public void normalMethod(byte[] data) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeByteArray(data);
          boolean _status = mRemote.transact(Stub.TRANSACTION_normalMethod, _data, _reply, 0);
          _reply.readException();
          _reply.readByteArray(data);
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }


        case TRANSACTION_normalMethod:
        {
          byte[] _arg0;
          _arg0 = data.createByteArray();
          this.normalMethod(_arg0);
          reply.writeNoException();
          reply.writeByteArray(_arg0);
          break;
        }

其实和上边分析的in,out类似,只是它集合了两者的特点。
从表面上能看到的源码就是这些了。

服务端和客户端生成的代码是一样的吗?

是一样的。

客户端1调用服务端的耗时方法A到阻塞,客户端2调用服务端的其他方法,建议在服务端使用多线程进行耗时操作或添加oneway标识。

参考:

  1. Android-你真的懂AIDL的oneway嘛?
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容