編輯:關於Android編程
繼續上篇的分析,接下來是第二個問題”Service的自動重啟問題“
(一)、Service的生命周期
(二)、Service的自動重啟問題
這裡要說服務的自動重啟問題,這個問題其實很簡單,只有兩個關鍵的方法。代碼如下:
這個方法在ActivityThread的一系列針對服務的handle方法中都有調用到ActivityManagerSerice的serviceDoneExecuting()方法,但是跟重啟有關的只有handleServiceArgs(),因為只有在這裡才有一個叫res的參數會起作用。
private void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
}
int res;
if (!data.taskRemoved) {
//就是回調了用戶服務的onStartCommand生命周期,這個做應用的都知道了,
//這裡可以通過設置其返回值來控制自己的服務是否允許被重新啟動,順理成章的這個值就是res
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
...............
try {
//看看系統用這個值都干了一些什麼導致有這個特性
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 1, data.startId, res);
} catch (RemoteException e) {
// nothing to do.
}
ensureJitEnabled();
}
..................
}
} 下面就是這個特性的關鍵代碼,裡面的注釋已經寫的很全了,關鍵其作用的就是stopIfKilled這個標志。 void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
if (type == 1) {
// This is a call from a service start... take care of
// book-keeping.
r.callStart = true;
switch (res) {
case Service.START_STICKY_COMPATIBILITY:
case Service.START_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, true);
// Don't stop if killed.
r.stopIfKilled = false;
break;
}
case Service.START_NOT_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, true);
if (r.getLastStartId() == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
r.stopIfKilled = true;
}
break;
}
case Service.START_REDELIVER_INTENT: {
// We'll keep this item until they explicitly
// call stop for it, but keep track of the fact
// that it was delivered.
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
if (si != null) {
si.deliveryCount = 0;
si.doneExecutingCount++;
// Don't stop if killed.
r.stopIfKilled = true;
}
break;
}
case Service.START_TASK_REMOVED_COMPLETE: {
// Special processing for onTaskRemoved(). Don't
// impact normal onStartCommand() processing.
r.findDeliveredStart(startId, true);
break;
}
default:
throw new IllegalArgumentException(
"Unknown service start result: " + res);
}
if (res == Service.START_STICKY_COMPATIBILITY) {
r.callStart = false;
}
}
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
Binder.restoreCallingIdentity(origId);
} else {
Slog.w(TAG, "Done executing unknown service from pid "
+ Binder.getCallingPid());
}
} 那麼這個標志位又是在哪些情況下使得服務可以重啟的呢?這種場景入口很多啊,比如系統清理進程等,總之就是APP Died的情況下,入口方法不列舉了,最後都會執行到這來: final void killServicesLocked(ProcessRecord app, boolean allowRestart) {
// Report disconnected services.
if (false) {
// XXX we are letting the client link to the service for
// death notifications.
if (app.services.size() > 0) {
Iterator it = app.services.iterator();
while (it.hasNext()) {
ServiceRecord r = it.next();
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList cl = r.connections.valueAt(conni);
for (int i=0; i=0; i--) {
ServiceRecord sr = app.services.valueAt(i);
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
}
if (sr.app != null) {
sr.app.services.remove(sr);
}
sr.app = null;
sr.isolatedProc = null;
sr.executeNesting = 0;
sr.forceClearTracker();
if (mDestroyingServices.remove(sr)) {
if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove destroying " + sr);
}
final int numClients = sr.bindings.size();
for (int bindingi=numClients-1; bindingi>=0; bindingi--) {
IntentBindRecord b = sr.bindings.valueAt(bindingi);
if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
+ ": shouldUnbind=" + b.hasBound);
b.binder = null;
b.requested = b.received = b.hasBound = false;
}
}
// Clean up any connections this application has to other services.
for (int i=app.connections.size()-1; i>=0; i--) {
ConnectionRecord r = app.connections.valueAt(i);
removeConnectionLocked(r, app, null);
}
app.connections.clear();
ServiceMap smap = getServiceMap(app.userId);
// Now do remaining service cleanup.
for (int i=app.services.size()-1; i>=0; i--) {
ServiceRecord sr = app.services.valueAt(i);
// Sanity check: if the service listed for the app is not one
// we actually are maintaining, drop it.
if (smap.mServicesByName.get(sr.name) != sr) {
ServiceRecord cur = smap.mServicesByName.get(sr.name);
Slog.wtf(TAG, "Service " + sr + " in process " + app
+ " not same as in map: " + cur);
app.services.removeAt(i);
continue;
}
// Any services running in the application may need to be placed
// back in the pending list.
// 這裡還是分很多種情況的
// 允許重啟時,如果當前服務所在進程crash超過兩次,並且不是persistent的進程就結束不會重啟了
if (allowRestart && sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) {
Slog.w(TAG, "Service crashed " + sr.crashCount
+ " times, stopping: " + sr);
EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
sr.userId, sr.crashCount, sr.shortName, app.pid);
bringDownServiceLocked(sr);
} else if (!allowRestart) {
// 不允許重啟直接掛掉
bringDownServiceLocked(sr);
} else {
//
boolean canceled = scheduleServiceRestartLocked(sr, true);
// Should the service remain running? Note that in the
// extreme case of so many attempts to deliver a command
// that it failed we also will stop it here.
if (sr.startRequested && (sr.stopIfKilled || canceled)) {
if (sr.pendingStarts.size() == 0) {
sr.startRequested = false;
if (sr.tracker != null) {
sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
if (!sr.hasAutoCreateConnections()) {
// Whoops, no reason to restart!
bringDownServiceLocked(sr);
}
}
}
}
}
if (!allowRestart) {
app.services.clear();
// Make sure there are no more restarting services for this process.
for (int i=mRestartingServices.size()-1; i>=0; i--) {
ServiceRecord r = mRestartingServices.get(i);
if (r.processName.equals(app.processName) &&
r.serviceInfo.applicationInfo.uid == app.info.uid) {
mRestartingServices.remove(i);
clearRestartingIfNeededLocked(r);
}
}
for (int i=mPendingServices.size()-1; i>=0; i--) {
ServiceRecord r = mPendingServices.get(i);
if (r.processName.equals(app.processName) &&
r.serviceInfo.applicationInfo.uid == app.info.uid) {
mPendingServices.remove(i);
}
}
}
// Make sure we have no more records on the stopping list.
int i = mDestroyingServices.size();
while (i > 0) {
i--;
ServiceRecord sr = mDestroyingServices.get(i);
if (sr.app == app) {
sr.forceClearTracker();
mDestroyingServices.remove(i);
if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove destroying " + sr);
}
}
app.executingServices.clear();
}
private final boolean scheduleServiceRestartLocked(ServiceRecord r,
boolean allowCancel) {
boolean canceled = false;
ServiceMap smap = getServiceMap(r.userId);
if (smap.mServicesByName.get(r.name) != r) {
ServiceRecord cur = smap.mServicesByName.get(r.name);
Slog.wtf(TAG, "Attempting to schedule restart of " + r
+ " when found in map: " + cur);
return false;
}
final long now = SystemClock.uptimeMillis();
if ((r.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) {
long minDuration = SERVICE_RESTART_DURATION;
long resetTime = SERVICE_RESET_RUN_DURATION;
// Any delivered but not yet finished starts should be put back
// on the pending list.
final int N = r.deliveredStarts.size();
if (N > 0) {
for (int i=N-1; i>=0; i--) {
ServiceRecord.StartItem si = r.deliveredStarts.get(i);
si.removeUriPermissionsLocked();
//注意了,這裡的canceled如果為true還是需要結束服務的
//還要關注一下delivery的上限和doneExecuting的上限
if (si.intent == null) {
// We'll generate this again if needed.
} else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
&& si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
//重新在pendingStart中添加si,所以會在下次執行時重新帶入intent進去
r.pendingStarts.add(0, si);
long dur = SystemClock.uptimeMillis() - si.deliveredTime;
dur *= 2;
if (minDuration < dur) minDuration = dur;
if (resetTime < dur) resetTime = dur;
} else {
Slog.w(TAG, "Canceling start item " + si.intent + " in service "
+ r.name);
canceled = true;
}
}
r.deliveredStarts.clear();
}
r.totalRestartCount++;
if (r.restartDelay == 0) {
r.restartCount++;
r.restartDelay = minDuration;
} else {
// If it has been a "reasonably long time" since the service
// was started, then reset our restart duration back to
// the beginning, so we don't infinitely increase the duration
// on a service that just occasionally gets killed (which is
// a normal case, due to process being killed to reclaim memory).
if (now > (r.restartTime+resetTime)) {
r.restartCount = 1;
r.restartDelay = minDuration;
} else {
r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
if (r.restartDelay < minDuration) {
r.restartDelay = minDuration;
}
}
}
r.nextRestartTime = now + r.restartDelay;
// Make sure that we don't end up restarting a bunch of services
// all at the same time.
boolean repeat;
do {
repeat = false;
for (int i=mRestartingServices.size()-1; i>=0; i--) {
ServiceRecord r2 = mRestartingServices.get(i);
if (r2 != r && r.nextRestartTime
>= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN)
&& r.nextRestartTime
< (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) {
r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN;
r.restartDelay = r.nextRestartTime - now;
repeat = true;
break;
}
}
} while (repeat);
} else {
// Persistent processes are immediately restarted, so there is no
// reason to hold of on restarting their services.
r.totalRestartCount++;
r.restartCount = 0;
r.restartDelay = 0;
r.nextRestartTime = now;
}
if (!mRestartingServices.contains(r)) {
r.createdFromFg = false;
mRestartingServices.add(r);
r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
}
r.cancelNotification();
mAm.mHandler.removeCallbacks(r.restarter);
// 最關鍵的操作在這裡,忘ActivityManagerService的handler裡面post一個重啟的Runnable
// 這個東西前面啟動過程創建ServiceRecord時有的,很簡單就是一個ServiceRestarter,它裡面保存了這個ServiceRecord本身
// 重啟的時候根據這個record就可以直接啟動服務了
mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
Slog.w(TAG, "Scheduling restart of crashed service "
+ r.shortName + " in " + r.restartDelay + "ms");
EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART,
r.userId, r.shortName, r.restartDelay);
return canceled;
} private class ServiceRestarter implements Runnable {
private ServiceRecord mService;
void setService(ServiceRecord service) {
mService = service;
}
public void run() {
synchronized(mAm) {
//後面的事情就順利成章了。
performServiceRestartLocked(mService);
}
}
} 整個這個過程中,有好幾個參數控制著是否需要重啟,也定了很多參數的上限等等,這裡單獨列出來解釋一下。最後將在下篇論第三個問題
(三)、Service與其客戶端的綁定如何實現,即跨進程調用問題。
Android更改桌面應用程序launcher的兩種方式
launcher,也就是Android的桌面應用程序。下圖是我正在使用的魅族手機的launcher應用程序: 接下來我們要開發一個自己的launcher,使其替
android產品研發(二十一)--)UI優化
上一篇文章中我們講解了android產品研發過程中的代碼Review。通過代碼Review能夠提高產品質量,增強團隊成員之間的溝通,提高開發效率,所以良好的產品開發迭代過
Android中圖案解鎖的設計原理和實現過程
Android中圖案解鎖首先要理解圖案的實現原理,上一張圖: 由上圖,可以看出,圖案中手勢的記錄是1-9或0-8的,保存的順序就是密碼,當然有些是可以重復的,為了安全,
Android工程目錄
創建一個Android應用程序 Android Application Project 設置工程的相關信息,默認會創建icon和activity