android8.0開始對于service的使用又收緊了權限,8.0之前startservice可以在后臺運行很長一段時間不會被殺死,但在8.0之后如果不顯式啟動前臺service,可能會出現啟動崩潰的異常。關于8.0后的service啟動兼容性處理網上應該能找到不少文章,但關于8.0service啟動源碼分析結合8.0之后的啟動前臺service似乎沒找到特別完整的分析,索性根據以往對于service源碼的理解結合8.0源碼整理出了這篇文章,溫故而知新,刷新下對于service的掌握。
service啟動方式
service啟動方式有兩種,一種就是比較常見的startService,另一種就是bindService。startService個人理解主要是用來處理同一個app內的操作,而bindservice更加偏向于進程間通信的形式,雖然同一個app同進程中也可以使用bindservice,但是有種殺雞用牛刀的感覺。8.0之后對于service的影響是針對startservice的啟動過程,bindservice的啟動沒有受到影響。所以文章主要分析startservice的啟動。通過一個完整的啟動流程從源碼的角度理解為什么8.0之后需要使用前臺service。
測試demo
比較簡單準備了兩個apk,一個apk作為service端,另一個apk作為客戶端發(fā)起service請求,區(qū)分兩個apk的目的僅僅是為了讓service進程和客戶端進程處在不同的進程中(實際上使用一個apk,設置service的processName屬性應該也可以,未測)。service聲明如下
<service
android:name="mandy.com.services.MyService"
android:exported="true" />
MyService代碼如下
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel mChannel;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
mChannel = new NotificationChannel("100100", "渠道名", NotificationManager.IMPORTANCE_HIGH);
if (notificationManager != null) {
notificationManager.createNotificationChannel(mChannel);
Notification notification = new Notification.Builder(getApplicationContext(), "100100").build();
startForeground(1, notification);
}
}
Log.e("mandy", "onCreate");
}
}
上述代碼是針對8.0進行兼容性處理的代碼,startForeground就是為了標明啟動的service是一個前臺service。8.0之后對于通知欄也進行了整改,增加了通知渠道,通知組的概念,所以notification的生成就是兼容8.0通知欄進行的適配,這塊不太了解的可以去查看下相關文章。service.apk中的代碼就這點東西,client.apk也很簡單一個按鈕,觸發(fā)啟動service,如下
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
以上代碼就處理完了8.0之后的service,當然這不是文章重點,我們接下來是要從源碼的角度來分析為什么需要這么處理。
startForegroundService
8.0之后通過startForegroundService來啟動前臺service,跟蹤對比下和startservice的區(qū)別,源碼在contextimpl當中
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
@Override
public ComponentName startForegroundService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, true, mUser);
}
很明顯兩者的區(qū)別就在于requireForeground參數一個為false,一個為true。實際上這個參數最終會傳遞到ams,ams正是通過該參數確定要啟動的service是前臺的還是后臺的。來看下startServiceCommon源碼
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
} else if (cn.getPackageName().equals("?")) {
throw new IllegalStateException(
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
很明顯通過ActivityManager.getService().startService來調用ams當中的服務,這里能看到會把requireForeground給傳遞給ams。還有一個地方需要注意的就是當返回的cn!=null時的各種檢查,8.0手機上如果沒有進行兼容性處理的啟動崩潰就是在這里發(fā)出的,舉個簡單的例子,當安裝好我前面提到的兩個apk之后,殺死service.apk所在進程的情況下去調用clientt.apk中的啟動service,你就會發(fā)現程序崩潰了!,崩潰信息大致如下:
java.lang.IllegalStateException: Not allowed to start service Intent { cmp=mandy.com.uiviews/mandy.com.services.MyService }: app is in background uid null
對照下上述源碼是不是找到了崩潰的出處。這里有幾個問題需要明確下:
(1)按照我上述操作,不一定會在真機上出現崩潰,國內手機廠商對于framework層源碼都有改動存在一定出入,實測在vivo x20手機上不會出現啟動service崩潰,但是點擊啟動沒有任何反應。
(2)必須是在service所在進程未啟動的情況下才有導致上述問題,這點可以在源碼中找到答案。
(3)未做兼容處理,也就是使用startService,使用startForegroundService不會出現崩潰
跟蹤到ams的startservice當中來看下具體操作
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, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
return res;
}
主要代碼見上,調用mServices.startServiceLocked來進一步處理,mServices是ActiveServices實例,requireForeground就是client進程中傳遞過來的參數。來看下startServiceLocked中代碼,代碼比較多,直接刪除了很多無關代碼
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
throws TransactionTooLargeException {
......
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false);
...
ServiceRecord r = res.record;
...
if (!r.startRequested && !fgRequired) {
// Before going further -- if this app is not allowed to start services in the
// background, then at this point we aren't going to let it period.
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.name.flattenToShortString()
+ " from pid=" + callingPid + " uid=" + callingUid
+ " pkg=" + callingPackage);
if (allowed == ActivityManager.APP_START_MODE_DELAYED) {
// In this case we are silently disabling the app, to disrupt as
// little as possible existing apps.
return null;
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
return new ComponentName("?", "app is in background uid " + uidRec);
}
}
......
r.startRequested = true;
r.delayedStop = false;
r.fgRequired = fgRequired;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants, callingUid));
......
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
}
從上到下進行簡單分析,retrieveServiceLocked是一個查詢service信息的方法,手機上各個apk中的service通過pms都可以查詢到,retrieveServiceLocked內部實際上就是通過pms進行查詢,得到相關service最終保存到ServiceLookupResult返回,并將得到的ServiceRecord賦值給一個r變量,ServiceRecord是一個很重要的類,里面保存了和service相關的很多重要變量,包括和該service相關的進程名,包名,啟動的intent等。
接下來代碼就來到了一個關鍵if分支,通過startRequested和fgRequired來確定是否進行到分支內部。startRequested用來確定一個service是否已經被啟動,該變量在service啟動的時候會置為true,在service被停止或者被殺死的時候重置為false。fgRequired即為上述文章提到client傳遞過來的參數。
前臺service流程
先來看一下正常情況下即fgRequired為true的邏輯,不進入if分支往下走,可以看到將startRequested設置為了true,并在pendingStarts中添加了一個StartItem,可以理解為每次啟動service都會添加一個StartItem,來標記此次的service,后面會用到。最終調用進入了startServiceInnerLocked。
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
...
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
...
}
調用bringUpServiceLocked,比較重要的一個方法,同樣保留關鍵代碼
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
...
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
...
ProcessRecord app;
if (!isolated) {
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) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
}
}
// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
if (app == null && !permissionsReviewRequired) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingType, r.name, false, isolated, false)) == 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);
return msg;
}
if (isolated) {
r.isolatedProc = app;
}
}
...
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
...
return null;
}
這個方法里面有幾點需要注意的地方,首先會判斷下r.app != null && r.app.thread != null,app是一個ProcessRecord用來保存和該service相關的進程信息,在service進程沒啟動或者service進程已經啟動但是還沒啟動service的情況下app都為null,所以不會進入到if分支內部。繼續(xù)往下走會調用getProcessRecordLocked獲取和service進程相關ProcessRecord,同樣會得到null。所以接下來要處理的就是先去啟動service所在的進程,調用startProcessLocked。startProcessLocked內部就不展開了,概括起來就是會調用newProcessRecordLocked去創(chuàng)建一個ProcessRecord,然后又會調用另一個startProcessLocked方法去啟動service所在進程。明白startProcessLocked作用后回到bringUpServiceLocked當中,由于此時service所在進程還沒啟動,會將serviceRecord先保存到mPendingServices。到此關于startForegroundService相關源碼就分析完了。
看完會不會有點懵,service的后繼操作oncreate,onStartCommand都哪里去了,實際上service的這些生命周期回調都是ams和service所在進程進行交互了,和client關系已經不大了,這里也可以解答之前自己的一個疑惑為什么在service的oncreate方法中進行耗時操作(僅測試,實際開發(fā)不推薦),client不會被阻塞在啟動service處直到oncreate執(zhí)行完畢。
那么service的生命周期是在哪里被調用到的,實際上是在service所在進程啟動后,會主動去通知ams,ams在得知進程啟動后會從mPendingServices中取出要啟動的serviceRecord,然后去調用相關的service生命周期方法。可以看出除了startservice是client發(fā)起的,后繼的生命周期交互工作都是ams和service進程之間進行的,client發(fā)出一個啟動命令后就可以去干自己的事情了,這就是一種典型的異步調用方式。bindservice的方式和startservice有些類似,也是異步調用,只不過bindservice會將一個回調參數serviceconn傳遞給ams,等ams和service進程交互完畢后會把binder通過serviceconn回調給client。
上述整個流程就是調用startForegroundService的情況,8.0以下的startservice啟動也大致是這個流程,也是在service進程不存在的情況下先拉起,如果進程已經存在但還沒啟動service則調用realStartServiceLocked。
fgRequired為false
以上分析的都是在fgRequired為true的情況下,現在分析下在fgRequire為false,也就是8.0之后直接調用startservice會發(fā)生什么,把之前的代碼再添一下
if (!r.startRequested && !fgRequired) {
// Before going further -- if this app is not allowed to start services in the
// background, then at this point we aren't going to let it period.
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.name.flattenToShortString()
+ " from pid=" + callingPid + " uid=" + callingUid
+ " pkg=" + callingPackage);
if (allowed == ActivityManager.APP_START_MODE_DELAYED) {
// In this case we are silently disabling the app, to disrupt as
// little as possible existing apps.
return null;
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
return new ComponentName("?", "app is in background uid " + uidRec);
}
}
進入if分支內部調用getAppStartModeLocked
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly) {
UidRecord uidRec = mActiveUids.get(uid);
if (uidRec == null || alwaysRestrict || uidRec.idle) {
boolean ephemeral;
...
if (ephemeral) {
// We are hard-core about ephemeral apps not running in the background.
return ActivityManager.APP_START_MODE_DISABLED;
} else {
if (disabledOnly) {
// The caller is only interested in whether app starts are completely
// disabled for the given package (that is, it is an instant app). So
// we don't need to go further, which is all just seeing if we should
// apply a "delayed" mode for a regular app.
return ActivityManager.APP_START_MODE_NORMAL;
}
final int startMode = (alwaysRestrict)
? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
: appServicesRestrictedInBackgroundLocked(uid, packageName,
packageTargetSdk);
}
...
return startMode;
}
}
return ActivityManager.APP_START_MODE_NORMAL;
}
UidRecord uidRec = mActiveUids.get(uid);見名知意通過service所在的uid來查詢UidRecord,在service所在進程沒啟動的情況下返回null,否則返回具體值。返回具體值的情況下會直接跳過if分支然后返回ActivityManager.APP_START_MODE_NORMAL,接下來的流程就和startForegroundService保持一致了。重點看下返回null的情況,ephemeral最終值為false,會進入到else分支內部。
disabledOnly和alwaysRestrict的值均為false,這兩值是直接從參數傳遞進來的,所以startMode的值最終由appServicesRestrictedInBackgroundLocked來決定。
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Persistent app?
if (mPackageManagerInt.isPackagePersistent(packageName)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " is persistent; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Non-persistent but background whitelisted?
if (uidOnBackgroundWhitelist(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on background whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Is this app on the battery whitelist?
if (isOnDeviceIdleWhitelistLocked(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on idle whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// None of the service-policy criteria apply, so we apply the common criteria
return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}
源碼內部有3個if判斷,只要滿足其中一個就是返回APP_START_MODE_NORMAL,否則會進入到appRestrictedInBackgroundLocked內部。這3個if判斷源碼已經給出了解釋,正常情況下app是不會進入到if內部,除非你的app是一個特殊app,關于省電白名單我特地在vivo真機的設置中查找了下,并未找到相關可以將app添加到省電白名單中的操作,所以最終會調用到appRestrictedInBackgroundLocked。
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
}
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
...
}
}
答案已經很明顯了,在8.0及以上直接就返回APP_START_MODE_DELAYED_RIGID,最終又回到方法中
if (!r.startRequested && !fgRequired) {
// Before going further -- if this app is not allowed to start services in the
// background, then at this point we aren't going to let it period.
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.name.flattenToShortString()
+ " from pid=" + callingPid + " uid=" + callingUid
+ " pkg=" + callingPackage);
if (allowed == ActivityManager.APP_START_MODE_DELAYED) {
// In this case we are silently disabling the app, to disrupt as
// little as possible existing apps.
return null;
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
return new ComponentName("?", "app is in background uid " + uidRec);
}
}
返回一個ComponentName("?", "app is in background uid " + uidRec)給client,回過頭再去看contextimpl中的這段判斷邏輯就比較清楚了
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
} else if (cn.getPackageName().equals("?")) {
throw new IllegalStateException(
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
cn就是ams返回的ComponentName,對照著看下就能知道會拋出一個IllegalStateException異常,到此應該就能明白為什么8.0直接使用startservice去啟動一個不存在進程的service時為什么會拋出這個異常了。
service ANR問題分析
回頭看下測試demo中的MyService就能知道,啟動的Service必須要在5秒內調用startForeground,否則會拋出一個anr的異常,又或者當在service的oncreate方法中去處理耗時操作,如果不能在一定時間內完成也會拋出anr異常,拋異常的原理比較簡單就是通過handler發(fā)送一個延遲處理的message,如果能在規(guī)定時間內執(zhí)行完成就remove掉該message,否則就執(zhí)行該message拋出一個異常。知道原理后剩下的就是在源碼中找到何時發(fā)起這個message,又是何時去remove掉該message。關鍵代碼在bumpServiceExecutingLocked這個方法內
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
+ why + " of " + r + " in app " + r.app);
else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
+ why + " of " + r.shortName);
long now = SystemClock.uptimeMillis();
if (r.executeNesting == 0) {
...
if (r.app != null) {
r.app.executingServices.add(r);
r.app.execServicesFg |= fg;
if (r.app.executingServices.size() == 1) {
scheduleServiceTimeoutLocked(r.app);
}
}
} else if (r.app != null && fg && !r.app.execServicesFg) {
r.app.execServicesFg = true;
scheduleServiceTimeoutLocked(r.app);
}
...
}
調用scheduleServiceTimeoutLocked發(fā)起一個延遲message,內部代碼很容易理解
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
可以看出會通過service是前臺還是后臺來決定延遲執(zhí)行的時間,如果在規(guī)定時間內service還沒處理完畢則最終會執(zhí)行到
if (anrMessage != null) {
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
即拋出一個anr,那么剩下的問題還有兩個(1)bumpServiceExecutingLocked在哪里被調用(2)又是在哪里remove掉message。
第一個問題可以直接在源碼中就能搜到調用處,比較常見的調用方法是realStartServiceLocked和sendServiceArgsLocked
如果對于service啟動流程比較熟悉應該能明白這兩個方法的調用。realStartServiceLocked最終會觸發(fā)client中的oncreate,而
sendServiceArgsLocked會最終觸發(fā)client的onStartCommand被調用。
第一個問題搞明白后剩下的就是在哪里remove掉message,實際上removemessage的發(fā)起時機在client端,當執(zhí)行完畢oncreate或者onStartCommand后都會調用ams中的serviceDoneExecuting方法,該方法最終就會調用到removemesage方法
移除掉拋anr異常的message。
8.0中的serviceANR
上述分析的anr都是早已存在的service中的anr,8.0之后啟動service后必須在5秒內調用startForeground,是在原有代碼的基礎上額外添加的anr邏輯,原理其實都是類似的,在sendServiceArgsLocked中可以找到對應的代碼塊
if (r.fgRequired && !r.fgWaiting) {
if (!r.isForeground) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
}
scheduleServiceForegroundTransitionTimeoutLocked(r);
} else {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Service already foreground; no new timeout: " + r);
}
r.fgRequired = false;
}
}
可以看出如果是一個前臺service最終會調用到一個巨長名字的方法,內部很簡單就是發(fā)起一個延遲message
void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
if (r.app.executingServices.size() == 0 || r.app.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);
msg.obj = r;
r.fgWaiting = true;
mAm.mHandler.sendMessageDelayed(msg, SERVICE_START_FOREGROUND_TIMEOUT);
}
而SERVICE_START_FOREGROUND_TIMEOUT的值正是5秒,正也就是為什么必須要在5秒內調用startForeground的原因了,startForeground內部做的事相信都能猜到就是remove掉這個message。
總結
到此關于8.0service啟動前臺service的相關代碼就分析完畢了,對于整個service啟動流程梳理了一遍,對于service anr的原理也進行了說明。當然上述的所有理解都是基于自己對于service的理解,所以文中難免有些理解不到位的地方,如果有什么誤導的地方還需多擔待。分析過程中源碼中還有很多的細節(jié)問題在和分析原理沒有太大聯系的基礎上都一一忽略了,畢竟太細節(jié)的東西容易遺忘,除非你是一個framework層開發(fā),需要對源碼就是二次開發(fā),作為一個應用層開發(fā)閱讀framework層源碼點到為止,明白整體原理即可。