Service源码浅析一
Service源码浅析二
Service源码浅析三
Service源码浅析四
咱们还是带着问题继续分析Service源码,这篇解决以下三个问题:
- 启动Service时创建进程的流程
- 相同进程和不同进程,Service方法调用有什么区别
- 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标识。
参考: