牛年犇犇犇,剛剛完成
AMS
一半的學習進度,篇幅確實有點大,不過先更上兩篇吧java
咱們不止一次提到Android
一直想弱化進程的存在,可是,Android
畢竟是創建在Linux
系統之上,基礎的運行環境仍是由進程組成。android
咱們前面已經介紹,全部的Android
的應用程序都是由Zygote
進程fork
而來,所以,構成應用進程的底層基礎,像虛擬機、動態庫等都是相同的。web
有了從Zygote
中繼承來的通用基礎環境,Android
還在Java層
創建一套框架來管理運行的組件來進一步弱化進程的存在感。算法
不過因爲每一個應用的上層配置都不相同,所以,這個框架不能提早在Zygote
中徹底創建好後繼承,只能在應用啓動時建立。數據庫
而這套框架就構成了Android
應用開發的基礎,而這套框架的核心就是大名鼎鼎的ActivityManagerServie
。設計模式
學習
ActivityManagerServie
前咱們先簡單瞭解下Android
應用進程的組成數組
組成Android
應用進程的核心是ActivityThread
類,這個類包含了應用框架中其餘重要的類。相關類變量聲明以下:緩存
public final class ActivityThread extends ClientTransactionHandler {
...
// 一個Binder實體對象,AMS 經過它來調用應用的接口
final ApplicationThread mAppThread = new ApplicationThread();
Application mInitialApplication;
// 用來保存應用中的 Activity 對象
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
// 用來保存應用中的 Service 對象
final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
// 用來保存應用中的 ContentProvider 對象
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>();
// 用來保存包含代碼的的應用
final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
// 用來保存只包含資源文件的應用
final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();
// 應用資源管理對象
private final ResourcesManager mResourcesManager;
// 上下文對象
private ContextImpl mSystemContext;
private ContextImpl mSystemUiContext;
}
複製代碼
咱們先來看下ApplicationThread
類markdown
ApplicationThread
的做用ApplicationThread
是ActivityThread
的一個內部類,它和ActivityThread
同樣,雖然名字中都包含了Thread
,可是它們並非繼承自線程類Thread
。數據結構
ApplicationThread
是一個Binder
服務類,Android
的ActivityManagerService
操做應用就是經過ApplicationThread
來完成的。
咱們先看下AIDL
文件內容,路徑frameworks/base/core/java/android/app/IApplicationThread.aidl
,片斷以下:
oneway interface IApplicationThread {
......
void scheduleSleeping(IBinder token, boolean sleeping);
......
}
複製代碼
請留意
AIDL
中的oneway
關鍵字,修飾interface
意味着修飾接口中的全部方法
用
oneway
修飾表示一個異步過程,調用時不須要等待返回值
ApplicationThread
類的片斷以下:
private class ApplicationThread extends IApplicationThread.Stub {
......
public final void scheduleSleeping(IBinder token, boolean sleeping) {
sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
}
......// 省略大量接口
}
複製代碼
ApplicationThread
中接口的實現都是把Binder調用
轉換成消息來排隊處理。scheduleSleeping()
爲例,只是發送了一個SLEEPING
的消息Handler
對象mH
的handleMessage()
方法中集中處理。處理部分以下: public void handleMessage(Message msg) {
switch (msg.what) {
...
case SLEEPING:
handleSleeping((IBinder)msg.obj, msg.arg1 != 0);
break;
...
}
}
複製代碼
ActivityThread
的handleSleeping()
來執行在源碼中,ApplicationThread
中的接口一般都是以schedule*
開頭,而真正處理的方法以handle*
開頭,很容易分辨。
爲何要這樣設計呢?
主要是避免方法在調用和執行時出現耗時過長影響整個系統的運行:
IApplicationThread.aidl
中的oneway
麼
oneway
修飾就代表客戶端進行Binder
調用時不會等待服務端結果的返回(這部分能夠去看以前的Binder-3-原理部分)sendMessage
的方式將Binder調用
轉化爲消息的異步處理
H mH
這個Handler
ActivityThread
的靜態main()
方法中,會經過Looper.loop()
進入消息的循環處理Context
Context
的字面含義是上下文環境。應用的上層代碼經過Context
類提供的接口來操做Android
的組件和資源。在Android
的應用程序中,Context
無處不在,不少接口都要求使用Context
對象做爲參數。
Android
的應用本質上是一系列組件加上資源文件的容器,可是,這些組件的使用方式各不相同,並且比較複雜,Context
類的做用就是把這些細節封裝起來,這樣,即便開發人員不瞭解組件的運行原理也能使用它們。從某種意義上將,Context
包含了整個應用框架的操做接口。
對於應用開發者來講,大部分代碼都在Activity
或者Service
的繼承類中實現。爲了方便應用使用,這兩個類的父類也是Context
。
Context
是一個抽象類,Android
相關類的繼承關係設計以下:
Android
中Context
有兩個實現類
ContextImpl
:Context
真正的實現類
Android
中惟一的業務實現類ActivityThread
對象ContextWrapper
:方法的實現只是轉調成員變量mBase
的方法
mBase
自己也是Context
類型對象,它的具體類型是ContextImpl
上面這種設計模式稱爲proxy
模式或者wrapper
模式,它的好處就是將接口和實現分離,能夠動態的切換多種實現。
Activity
類有些特殊(畢竟承載了應用的顯示部分),Android
又爲它抽象出來了一個ContextThemeWrapper
類
Activity
中單獨的Theme
Theme
,同時每一個Activity
還能夠有本身的Theme
Application
類
Application
類也是繼承ContextWrapper
Application
類能夠看作是應用自己的抽象,一個應用進程只有一個Application
對象,在應用啓動時由框架建立。
Application
自己沒有實現太多功能,它的主要做用是提供一些回調接口來通知應用進程狀態的變化。相關的方法以下:
public class Application extends ContextWrapper implements ComponentCallbacks2 {
// 在應用建立時調用
public void onCreate();
// 在應用銷燬時調用
public void onTerminate();
// 在系統配置發生變化時調用
public void onConfigurationChanged(Configuration newConfig);
// 在系統內存不足時調用
public void onLowMemory();
// 在系統要求應用釋放多餘內存時調用
public void onTrimMemory(int level);
}
複製代碼
應用中能夠定義Application
的子類。子類必須在AndroidManifest.xml
的<application/>
標籤中定義,這樣系統才能在生成應用對象時使用子類來替換Application
類。
<application/>
標籤在Android SDK
文檔中有詳細的介紹,能夠先簡單閱讀標籤的屬性描述,否則到後面理解ActivityManagerService
的業務邏輯時可能會有障礙。
以allowTaskReparenting
爲例,它的描述是這樣的:
<activity>
標籤有其本身的 allowTaskReparenting
屬性,該屬性能夠替換此處設置的值。
allowTaskReparenting
的精彩講解能夠看這裏,扔物線大神視頻剩下的屬性你們能夠參考官方說明。傳送門:
<application/>
標籤
AMS
服務ActivityManagerService
是Android Framework
的核心,它管理着Android系統的4大組件:Activity
、Service
、ContentProvider
和Broadcast
。
前面已經介紹了,Android但願模糊進程的做用,取而代之以組件的概念,ActivityManagerService
正是這一理念的實現。ActivityManagerService
除了管理組件外,同時也管理和調度全部的用戶進程
爲了方便,
ActivityManagerService
後面就用AMS
來表示了哈
AMS
在SystemServer
中的啓動流程咱們已經知道
AMS
服務運行在SystemServer
進程中。
AMS
在SystemServer
的主要流程以下:
private void startBootstrapServices() {
// 建立 AMS 服務
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
......
// 調用 AMS 的 setSystemProcess
mActivityManagerService.setSystemProcess();
}
private void startOtherServices() {
// 調用 AMS 的 systemReady
mActivityManagerService.systemReady(...);
}
複製代碼
上面的代碼重點分爲三部分:
SystemServiceManager.startService
方法進行AMS
建立
Class
對象來建立對應類的實例對象ActivityManagerService.Lifecycle
類,類結構以下:public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService;
public Lifecycle(Context context) {
super(context);
// 建立 ActivityManagerService 實例對象
mService = new ActivityManagerService(context);
}
......
}
複製代碼
Lifecycle
的構造方法中初始化了AMS
對象AMS
的setSystemProcess
方法AMS
的systemReady
方法這三部分咱們詳細來看下
AMS
的初始化AMS
的構造方法以下:
public ActivityManagerService(Context systemContext) {
......
mContext = systemContext;
mFactoryTest = FactoryTest.getMode();
// 獲取運行在 SystemServer 中的 ActivityThread 對象
mSystemThread = ActivityThread.currentActivityThread();
mUiContext = mSystemThread.getSystemUiContext();
......
// 建立用於處理消息的線程和Handler對象
// 這裏建立了兩個消息處理線程:mHandlerThread 和 mProcStartHandlerThread
mHandlerThread = new ServiceThread(TAG,
THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
mHandler = new MainHandler(mHandlerThread.getLooper());
mUiHandler = mInjector.getUiHandler(this);
mProcStartHandlerThread = new ServiceThread(TAG + ":procStart",
THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
mProcStartHandlerThread.start();
mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
......
// 建立管理廣播的數據結構
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", BROADCAST_BG_TIMEOUT, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
// 建立管理組件 Service 的對象
mServices = new ActiveServices(this);
// 建立管理組件 Provider 的對象
mProviderMap = new ProviderMap(this);
mAppErrors = new AppErrors(mUiContext, this);
// 獲取系統的 system 和 data 目錄
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
systemDir.mkdirs();
// 建立 BatteryStatsService 服務
mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, mHandler);
......
// 建立 ProcessStatsService 服務
mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
// 建立 AppOpsService 服務
mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler);
// 打開 urigrants.xml 文件
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
// 建立 UserController ,UserController 對應的構造方法中會設置 USER_SYSTEM 用戶做爲第一個用戶
mUserController = new UserController(this);
// 建立 VrController ,用來進行部分 VR 狀態的監聽和切換工做
mVrController = new VrController(this);
// 獲取 opengles 的版本
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
......
// 建立 Activity 管理對象
mStackSupervisor = createStackSupervisor();
......
// 建立 Intent 防火牆
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
......
// 建立 CPU 使用狀況的統計線程
mProcessCpuThread = new Thread("CpuTracker") {
...
};
......
// 將服務添加到 WatchDog 進行監聽
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
......
}
複製代碼
AMS
構造方法的主要做用是建立出了4大組件Activity
、Service
、ContentProvider
和Broadcast
的管理對象和一些輔助服務,沒有很複雜的邏輯,註釋很詳細啦
AMS
的setSystemProcess
方法SystemServer
中建立完AMS
對象後,會調用它的setSystemProcess
方法,代碼以下:
public void setSystemProcess() {
try {
// =============== 將一些監測服務添加到 ServiceManager ===============
// AMS 服務
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
// ProcessStats 是能夠用來dump每一個進程內存使用狀況的服務
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
// MemBinder 是能夠用來dump每一個進程內存信息的服務
ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_HIGH);
// GraphicsBinder 是能夠用來dump每一個進程圖形加速狀態的服務
ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
// DbBinder 是能夠用來dump每一個進程數據庫狀態的服務
ServiceManager.addService("dbinfo", new DbBinder(this));
if (MONITOR_CPU_USAGE) {
// CPU 信息
ServiceManager.addService("cpuinfo", new CpuBinder(this),
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
}
// PermissionController 是用來檢查 Binder 調用權限的服務
ServiceManager.addService("permission", new PermissionController(this));
// ProcessInfoService 是能夠用來獲取進程相關信息的服務
ServiceManager.addService("processinfo", new ProcessInfoService(this));
// 獲取 framework-res.apk 的 ApplicationInfo 對象
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
// 將 SystemServer 進程自己添加到 AMS process 的管理中
ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
app.persistent = true;
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Unable to find android system package", e);
}
// 啓動應用操做監聽
mAppOpsService.startWatchingMode(......);
}
複製代碼
SystemServer
調用setSystemProcess
方法的主要工做是向ServiceManager
註冊一些服務。須要注意的是:
getApplicationInfo
方法獲取了framework-res.apk
的ApplicationInfo
對象newProcessRecordLocked()
方法將ApplicationInfo
對象轉化爲ProcessRecord
對象,並將其添加到AMS
的進程管理體系中
SystemServer
章節的createSystemContext()
介紹過,SystemServer
能夠當作是framework-res.apk
的應用進程。AMS
而言,它對系統進程的管理就不會存在遺漏了AMS
的systemReady
方法SystemServer
在啓動完全部服務以後,將調用AMS
的systemReady
方法。這個方法時Android進入用戶交互階段前最後進行的準備工做。
systemReady
方法比較長,咱們一點一點來分析
AMS
相關服務的準備階段相關代碼以下:
public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
synchronized(this) {
if (mSystemReady) {
if (goingCallback != null) {
goingCallback.run();
}
return;
}
......
// 通知 VrController 註冊 VrModeStateListener
mVrController.onSystemReady();
// 通知 UserController 裝載用戶的 Profile 信息
mUserController.onSystemReady();
// 通知 RecentTasks 重建帶有 persisitent 標記的 Task
mRecentTasks.onSystemReadyLocked();
// 通知 AppOpsService 啓動 APP 權限監聽
mAppOpsService.systemReady();
// 系統相關服務準備完成
mSystemReady = true;
}
......
// 查找待清理進程
ArrayList<ProcessRecord> procsToKill = null;
synchronized(mPidsSelfLocked) {
for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
ProcessRecord proc = mPidsSelfLocked.valueAt(i);
// 此處判斷進程是否有 FLAG_PERSISTENT 標誌
if (!isAllowedWhileBooting(proc.info)){
if (procsToKill == null) {
procsToKill = new ArrayList<ProcessRecord>();
}
// 將沒有 FLAG_PERSISTENT 標誌的進程添加到清理集合中
procsToKill.add(proc);
}
}
}
// 清理集合中的進程
synchronized(this) {
if (procsToKill != null) {
for (int i=procsToKill.size()-1; i>=0; i--) {
ProcessRecord proc = procsToKill.get(i);
removeProcessLocked(proc, true, false, "system update done");
}
}
// 進程清理完成
mProcessesReady = true;
}
Slog.i(TAG, "System now ready");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY, SystemClock.uptimeMillis());
......
}
複製代碼
上面註釋比較詳細,簡單總結主要流程以下:
VrController
、UserController
、RecentTasks
、AppOpsService
服務的*SystemReady()
方法進行對應服務的初始化工做
mPidsSelfLocked
集合中查找非FLAG_PERSISTENT
標記的進程,並將其清理
mPidsSelfLocked
集合存放的是已經運行了的進程ActivityThread.attach()
加入mPidsSelfLocked
集合的FLAG_PERSISTENT
標記的進程說明後面很快還會啓動,沒有必要清理在
SystemServer
啓動的過程會執行PMS
的updatePackagesIfNeeded()
方法,這個方法會發送一個ACTION_PRE_BOOT_COMPLETED
的廣播。
因此,若是咱們的應用響應這個ACTION_PRE_BOOT_COMPLETED
而且加入FLAG_PERSISTENT
標誌,那麼就能夠存活下來
FactoryMode
檢測階段相關代碼以下
synchronized(this) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
// 處於工廠模式,則查找對應的應用程序
ResolveInfo ri = mContext.getPackageManager()
.resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST),
STOCK_PM_FLAGS);
CharSequence errorMsg = null;
if (ri != null) {
ActivityInfo ai = ri.activityInfo;
ApplicationInfo app = ai.applicationInfo;
// 判斷找到的程序是否爲系統應用
if ((app.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
mTopAction = Intent.ACTION_FACTORY_TEST;
mTopData = null;
mTopComponent = new ComponentName(app.packageName,
ai.name);
} else {
errorMsg = mContext.getResources().getText(
com.android.internal.R.string.factorytest_not_system);
}
} else {
errorMsg = mContext.getResources().getText(
com.android.internal.R.string.factorytest_no_action);
}
if (errorMsg != null) {
......
// 錯誤消息不爲空,發送對應的信息
Message msg = Message.obtain();
msg.what = SHOW_FACTORY_ERROR_UI_MSG;
msg.getData().putCharSequence("msg", errorMsg);
mUiHandler.sendMessage(msg);
}
}
}
複製代碼
對於Android
產品,如手機、電視等,須要進入工廠模式來執行檢測程序,工廠模式下運行的程序須要響應Intent.ACTION_FACTORY_TEST
。
所以,當檢測處處於工廠模式時,會去查找響應該Intent
的程序,並放置到mTopComponent
中,這樣將會啓動對應的測試應用。若是找不到,則發送異常Message
相關代碼以下:
// 更新設置信息,包括是否支持畫中畫、多窗口等屬性
retrieveSettings();
final int currentUserId = mUserController.getCurrentUserId();
synchronized (this) {
readGrantedUriPermissionsLocked();
}
......
// 執行回調參數
if (goingCallback != null) goingCallback.run();
// 通知其餘服務,當前用戶開始運行
mSystemServiceManager.startUser(currentUserId);
synchronized (this) {
// 啓動帶有 FLAG_PERSISTENT 標記的應用
startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
mBooting = true;
......
// 啓動 Home 應用
startHomeActivityLocked(currentUserId, "systemReady");
......
long ident = Binder.clearCallingIdentity();
try {
// 發送 ACTION_USER_STARTED 廣播
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,...);
// 發送 ACTION_USER_STARTING 廣播
intent = new Intent(Intent.ACTION_USER_STARTING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,...);
} catch (Throwable t) {
Slog.wtf(TAG, "Failed sending first user broadcasts", t);
} finally {
Binder.restoreCallingIdentity(ident);
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
......
}
複製代碼
這一段的代碼主要做用是
systemReady
方法參數goingCallback
的回調方法run()
SystemServer
中定義的FLAG_PERSISTENT
標記的應用
addAppLocked()
方法,後面詳細介紹Home
應用
android.intent.action.MAIN
的Activity
Process
管理雖然Android
在應用開發中能夠弱化進程的概念,可是在AMS
中,還必須管理和調度進程。AMS
對進程的管理,主要體如今兩個方面:
mLruProcesses
列表的位置
updateLruProcessLocked
oom_adj
的值
updateOomAdjLocked
這兩項調整和Android
的內存回收機制有關。當內存不足時,系統會關閉一些進程來釋放內存。
前面介紹了,AMS
中啓動帶有FLAG_PERSISTENT
標記的應用是經過addAppLocked()
方法,代碼以下:
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, boolean disableHiddenApiChecks, String abiOverride) {
ProcessRecord app;
// isolated 爲 true 表示要啓動一個新的進程
if (!isolated) {
// 在已經啓動的進程列表中查找
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
info.uid, true);
} else {
app = null;
}
if (app == null) {
// 新建一個 ProcessRecord 對象
app = newProcessRecordLocked(info, customProcess, isolated, 0);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
try {
// 將包的stopped狀態設置爲false
// 若是爲ture那麼全部廣播都沒法接收,除非帶有標記FLAG_INCLUDE_STOPPED_PACKAGES的廣播
// 正經的廣播都不會帶有這個標記
AppGlobals.getPackageManager().setPackageStoppedState(info.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
......
}
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
// 設置 persistent 標記
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
// 啓動進程
startProcessLocked(app, "added application",
customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
abiOverride);
}
return app;
}
複製代碼
addAppLocked()
方法流程以下:
isolated
來決定是否啓動一個新進程
isolated
爲true
,即便系統中可能已經有一個同名的進程存在,也會經過newProcessRecordLocked()
再新建一個進程
updateLruProcessLocked()
方法更新運行中的進程的狀態updateOomAdjLocked()
方法來更新進程的優先級isolated
爲false
,會經過getProcessRecordLocked()
方法在當前運行的進程列表中查找persistent
標記
persistent
和maxAdj
屬性startProcessLocked()
來啓動進程updateLruProcessLocked()
、updateOomAdjLocked()
這兩個方法是進程管理的核心,等下細聊。
咱們先看下startProcessLocked()
的啓動邏輯:
private final boolean startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
......
long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != MY_PID) {
synchronized (mPidsSelfLocked) {
// 移除集合中的進程ID,避免重複
mPidsSelfLocked.remove(app.pid);
// 移除消息隊列中的 PROC_START_TIMEOUT_MSG 消息,避免干擾
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
}
......
mProcessesOnHold.remove(app);
...//省略大量的啓動參數準備過程
final String entryPoint = "android.app.ActivityThread";
return startProcessLocked(hostingType, hostingNameStr, entryPoint, ...);
}
private boolean startProcessLocked(hostingType, hostingNameStr, entryPoint,...) {
......
if (mConstants.FLAG_PROCESS_START_ASYNC) {
// 異步啓動的狀況,經過 mProcStartHandler 來處理
mProcStartHandler.post(() -> {
......
// 啓動應用
final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint, ...);
synchronized (ActivityManagerService.this) {
// 發送一個延時消息用來監聽應用啓動
handleProcessStartedLocked(app, startResult, startSeq);
}
......
});
return true;
} else {
// 啓動應用
final ProcessStartResult startResult = startProcess(hostingType, entryPoint, ...);
// 發送一個延時消息用來監聽應用啓動
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
return app.pid > 0;
}
}
private ProcessStartResult startProcess(String hostingType, String entryPoint, ...) {
......
final ProcessStartResult startResult;
if (hostingType.equals("webview_service")) {
// startWebView 是 Process 的靜態方法
// 啓動的是 WebViewZygote,應該是爲了和本地應用有所區分,不先管它。
startResult = startWebView(entryPoint,...);
} else {
// 經過 Process.start() 函數啓動應用
startResult = Process.start(entryPoint, ...);
}
...
return startResult;
}
複製代碼
註釋比較清楚。對於startProcessLocked()
方法,主要作了兩件事:
經過startProcess()
方法來啓動應用進程
startProcess()
方法最後是經過Process.start()
方法來完成。執行結果就是建立了一個新進程,並在其中執行ActivityThread
的main()
方法Process.start()
方法在深刻Android系統(七)Zygote進程中已經介紹過了,這裏就不展開啦而後經過handleProcessStartedLocked()
方法來檢測應用啓動是否超時
handleProcessStartedLocked()
方法以下:
static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
static final int PROC_START_TIMEOUT = 10*1000;
private boolean handleProcessStartedLocked(...) {
......
synchronized (mPidsSelfLocked) {
this.mPidsSelfLocked.put(pid, app);
if (!procAttached) {
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
// 發送一個延時消息
mHandler.sendMessageDelayed(msg, usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
}
return true;
}
複製代碼
handleProcessStartedLocked()
方法主要是發送了一個延時消息PROC_START_TIMEOUT_MSG
Android
的想法應該是:當應用啓動後去移除這個消息,若是消息在延時結束前沒有被移除,就認爲應用的啓動出了問題。
那麼,在哪裏移除的呢??
咱們已經知道,應用的啓動入口在ActivityThread
的main()
方法。而在main()
方法的執行邏輯中會執行一個attach()
方法(這個方法也很重要),方法以下:
private void attach(boolean system, long startSeq) {
......
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
......
}
複製代碼
attach()
方法中會調用AMS
的attachApplication()
方法,而在attachApplication()
中執行了 mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
666,把
Handler
使用的是淋漓盡致啊
LRU
LRU
是Least Recently Used
的縮寫,即最近最少使用,是一種經常使用的頁面置換算法,選擇最近最久未使用的頁面予以淘汰。
在
Android
中進程啓動後都會在AMS
的成員變量mLruProcesses
中保存其ProcessRecord
信息。mLruProcesses
以LRU
順序存儲了當前運行的應用程序進程信息,mLruProcesses
中的第一個元素就是最近最少使用的進程對應的ProcessRecord
。
AMS
中會經過updateLruProcessLocked()
來調整進程在mLruProcesses
列表中的位置,代碼以下:
final void updateLruProcessLocked(ProcessRecord app, boolean activityChange, ProcessRecord client) {
// app.activities.size() > 0 說明本進程中有活動的 Activity。app.activities 元素的類型是 ActivityRecord
// app.hasClientActivities 爲 true 表示綁定了本進程Service的客戶進程中有活動的 Activity
// app.treatLikeActivity 爲 true 表示 Service 啓動時帶有 BIND_TREAT_LIKE_ACTIVITY 標記,這個屬性和鍵盤邏輯關係比較大
// app.recentTasks.size() > 0 說明本進程中有活動的任務棧。app.recentTasks 元素的類型是 TaskRecord
final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
|| app.treatLikeActivity || app.recentTasks.size() > 0;
final boolean hasService = false; // not impl yet. app.services.size() > 0;
if (!activityChange && hasActivity) {
// 若是當前進程中存在 Activity 而且 Activity 的狀態沒有發生過變化
// 不作處理,直接返回
return;
}
mLruSeq++; // 執行次數 +1
final long now = SystemClock.uptimeMillis();
app.lastActivityTime = now; // 更新 lastActivityTime 時間
if (hasActivity) {
// 若是 hasActivity 爲 true 而且在 mLruProcesses 列表的最後一個位置
// 不須要再進行額外的操做,直接退出
final int N = mLruProcesses.size();
if (N > 0 && mLruProcesses.get(N-1) == app) {
return;
}
} else {
// hasActivity 爲 false,說明進程中沒有 Activity
// 若是位置位於 mLruProcessServiceStart-1 說明沒有問題
if (mLruProcessServiceStart > 0
&& mLruProcesses.get(mLruProcessServiceStart-1) == app) {
return;
}
}
// 獲取當前應用在集合中的位置
int lrui = mLruProcesses.lastIndexOf(app);
if (app.persistent && lrui >= 0) {
// 若是集合中已經存在而且應用屬於 persistent 類型,則不須要作優化處理,直接返回
return;
}
if (lrui >= 0) {
// 若是 mLruProcesses 集合中已經存在,而且不是 persistent 類型,先將其從集合中移除
// 並同時調整 mLruProcessActivityStart 和 mLruProcessServiceStart 的位置
if (lrui < mLruProcessActivityStart) {
mLruProcessActivityStart--;
}
if (lrui < mLruProcessServiceStart) {
mLruProcessServiceStart--;
}
mLruProcesses.remove(lrui);
}
int nextIndex;
if (hasActivity) {
// 當前集合中全部的進程數量,請注意此時 app 已經不在集合中了
final int N = mLruProcesses.size();
if ((app.activities.size() == 0 || app.recentTasks.size() > 0)
&& mLruProcessActivityStart < (N - 1)) {
// 進程中沒有 Activity,可是它的 Service 客戶進程有 Activity
// 將進程插入到集合的尾部
mLruProcesses.add(N - 1, app);
// 檢查集合中進程的uid,將與 app 相同uid的進程位置調整到一塊兒
final int uid = app.info.uid;
for (int i = N - 2; i > mLruProcessActivityStart; i--) {
ProcessRecord subProc = mLruProcesses.get(i);
if (subProc.info.uid == uid) {
// 若是app的前一個進程的uid與其相同,檢繼續查前一個進程
if (mLruProcesses.get(i - 1).info.uid != uid) {
// 兩個進程uid不一致進行位置互換,這裏不太清楚目的
ProcessRecord tmp = mLruProcesses.get(i);
mLruProcesses.set(i, mLruProcesses.get(i - 1));
mLruProcesses.set(i - 1, tmp);
i--;
}
} else {
// A gap, we can stop here.
break;
}
}
} else {
// 若是包含 Activity 直接添加到集合的尾部
mLruProcesses.add(app);
}
nextIndex = mLruProcessServiceStart;
} else if (hasService) {
mLruProcesses.add(mLruProcessActivityStart, app);
nextIndex = mLruProcessServiceStart;
mLruProcessActivityStart++;
} else {
// 沒有 Service 的狀況
int index = mLruProcessServiceStart;
......
mLruProcesses.add(index, app);
nextIndex = index-1;
mLruProcessActivityStart++;
mLruProcessServiceStart++;
}
// 將和本進程 Service 關聯的客戶進程的位置調整到本進程以後
for (int j=app.connections.size()-1; j>=0; j--) {
ConnectionRecord cr = app.connections.valueAt(j);
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
&& !cr.binding.service.app.persistent) {
nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
"service connection", cr, app);
}
}
// 將和本進程 ContentProvider 關聯的客戶進程的位置調整到本進程以後
for (int j=app.conProviders.size()-1; j>=0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
"provider reference", cpr, app);
}
}
}
複製代碼
updateLruProcessLocked()
方法中調整進程很重要的一個依據是進程中有沒有活動的Activity
Activity
用來顯示UI
,關係着用戶體驗,所以Android
儘可能不關閉運行Activity
組件的進程。除了進程自己存在Activity
對象外,若是和進程中運行的Service
相關聯的客戶進程中有Activity
,也算本進程擁有Activity
。
Activity
對象存在,那麼它的重要性也和真正擁有Activity
對象的進程至關。若是一個進程擁有Activity
,一般會把它放入隊列的最高端的位置(也就是集合索引值大的方向)
不然,只會把它放到全部沒有Activity
的進程的前面,也就是mLruProcessActivityStart
所表示的位置。
調整完某個進程的位置以後,還須要調整和該進程相關聯進程的位置
進程的關聯進程有兩種類型:
Service
的進程ContentProvider
的進程若是關聯進程自己有Activity
是不會調整的,須要調整的是那些沒有Activity
的進程,在updateLruProcessInternalLocked()
方法中會進行這種調整,咱們簡單看下:
private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index, String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
if (app.activities.size() > 0 || app.recentTasks.size() > 0) {
// 若是待調整進程包含 Activity ,不作調整,直接返回
return index;
}
int lrui = mLruProcesses.lastIndexOf(app);
if (lrui < 0) {
// 若是進程不在 mLruProcesses 集合中,直接返回
return index;
}
if (lrui >= index) {
// 若是進程當前位置高於要調整的位置,不做調整,直接返回
return index;
}
if (lrui >= mLruProcessActivityStart) {
// 若是進程當前位置比 Activity 進程的其實位置還要高,不作調整,直接返回
return index;
}
// 把進程調整到 index-1 的位置
mLruProcesses.remove(lrui);
if (index > 0) {
index--;
}
mLruProcesses.add(index, app);
return index;
}
複製代碼
註釋比較詳細,很少解釋了哈。
ProcessList
類前面介紹了進程列表的調整邏輯,在學習
oom_adj
調整前咱們先看下ProcessList
類的內容
ProcessList
類中定義了大量AMS
中用到的常量,部分定義以下:
public final class ProcessList {
// 定義進程發生 crash 的最小時間間隔,若是進程在小於這個時間內發生 crash,會被認爲是異常進程
static final int MIN_CRASH_INTERVAL = 60*1000;
......
// 處於某種不可知狀態的進程的 oom_adj 值
static final int UNKNOWN_ADJ = 1001;
// cached 進程的 oom_adj 的最大值和最小值定義
static final int CACHED_APP_MAX_ADJ = 906;
static final int CACHED_APP_MIN_ADJ = 900;
// 位於 B 列表的服務進程的 oom_adj 值。位於 B 列表的都是一些舊的、過期的服務進程
static final int SERVICE_B_ADJ = 800;
// 當前 Activity 的前一個 Activity 所處進程的 oom_adj 值
static final int PREVIOUS_APP_ADJ = 700;
// Home 進程的 oom_adj 值
static final int HOME_APP_ADJ = 600;
// 只包含組件 Service 的進程的 oom_adj 值
static final int SERVICE_ADJ = 500;
// heavy-weight 進程的 oom_adj 值
static final int HEAVY_WEIGHT_APP_ADJ = 400;
// 正在執行 backup 任務進程的 oom_adj 值
static final int BACKUP_APP_ADJ = 300;
// 不在前臺可是包含有用戶可感知組件的進程的 oom_adj 值(例如播放音樂的後臺進程)
static final int PERCEPTIBLE_APP_ADJ = 200;
// 僅包含 Activity 的可見進程的 oom_adj 值
static final int VISIBLE_APP_ADJ = 100;
......
// 定義用於內存回收的 oom_adj 閾值
private final int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
};
// 定義用於低配置(HVGA或更低,或內存小於512MB)設備內存回收的內存閾值,單位:KB
private final int[] mOomMinFreeLow = new int[] {
12288, 18432, 24576,
36864, 43008, 49152
};
// 用於定義高配置(高分辨率或內存1GB左右)設備內存回收的內存閾值,單位:KB
private final int[] mOomMinFreeHigh = new int[] {
73728, 92160, 110592,
129024, 147456, 184320
};
......
}
複製代碼
ProcessList
中定義的常量有不少和進程類型相關的,像cached進程
、service進程
等。關於Android
進程的分類官方將其分爲了4類,簡要以下:
foreground process
:前臺進程,是用戶目前執行操做所需的進程。在不一樣的狀況下,進程可能會由於其所包含的各類應用組件而被視爲前臺進程visible process
:可見進程,正在進行用戶當前知曉的任務,所以終止該進程會對用戶體驗形成明顯的負面影響service process
:服務進程,包含一個已使用startService()
方法啓動的Service
cached process
:緩存進程,是目前不須要的進程,所以,若是其餘地方須要內存,系統能夠根據須要自由地終止該進程進程具體的類型描述及終止規則就不詳細介紹了,具體你們能夠參考官網:進程和應用生命週期,很詳細喲
ProcessList
中一樣也定義了一個和oom_adj
相關的方法,用來將oom_adj
的數值發送給lmkd
,以下:
public static final void setOomAdj(int pid, int uid, int amt) {
if (pid <= 0) {
return;
}
if (amt == UNKNOWN_ADJ)
return;
long start = SystemClock.elapsedRealtime();
ByteBuffer buf = ByteBuffer.allocate(4 * 4);
buf.putInt(LMK_PROCPRIO);
buf.putInt(pid);
buf.putInt(uid);
buf.putInt(amt);
writeLmkd(buf);
}
複製代碼
oom_adj
值
Android
基於各個應用進程四大組件的狀態對應用進程進行重要性評估
,也就是計算更新oom_adj
值。並在系統內存緊張時根據重要性由低到高來選擇殺死應用進程,以達到釋放內存的目的
AMS
中調整進程oom_adj
值的方法是updateOomAdjLocked()
方法,簡要代碼以下:
final void updateOomAdjLocked() {
// 獲取位於前臺的 Activity 和它所在的進程
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
final long now = SystemClock.uptimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
final int N = mLruProcesses.size();
......
// 計算 slot 數值。這裏就用到了 ProcessList 類中的一些常量
// 根據常量數值,計算結果爲:(906-900+1)/2=3
int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
- ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
// 計算 empty進程 的數量
int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
if (numEmptyProcs > cachedProcessLimit) {
// 若是空進程的數量超過了 cache進程 的限制值,更新爲限制值
numEmptyProcs = cachedProcessLimit;
}
// 計算一個 slot 能夠存放的empty進程數
int emptyFactor = numEmptyProcs/numSlots;
if (emptyFactor < 1) emptyFactor = 1;
// 計算一個 slot 能夠存放的cache進程數
int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots;
if (cachedFactor < 1) cachedFactor = 1;
......
// 初始化 adj 數值
int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextCachedAdj = curCachedAdj+1;
int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextEmptyAdj = curEmptyAdj+2;
......
// 從 mLruProcesses 集合的後面向前處理
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
// 計算進程的 oom_adj 值
computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
......
if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
// 計算出的 oom_adj 值大於 ProcessList.UNKNOWN_ADJ(系統定義的最大值)的狀況
switch (app.curProcState) {
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
// 上面三種進程狀態按照cached進程的參數進行調整
app.curRawAdj = curCachedAdj;
// 修正 adj 數值
app.curAdj = app.modifyRawOomAdj(curCachedAdj);
if (curCachedAdj != nextCachedAdj) {
stepCached++;
if (stepCached >= cachedFactor) { // 一個 slot 中放不下了
stepCached = 0;
curCachedAdj = nextCachedAdj;
nextCachedAdj += 2; // 使用一個新的 slot,即將 adj 值增長 2
if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
break;
default:
// 其餘進程狀態按照empty進程的參數進行設置
app.curRawAdj = curEmptyAdj;
app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
if (curEmptyAdj != nextEmptyAdj) {
stepEmpty++;
if (stepEmpty >= emptyFactor) {
stepEmpty = 0;
curEmptyAdj = nextEmptyAdj;
nextEmptyAdj += 2;
if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
break;
}
}
}
}
......
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
// 更新進程的 oom_adj 值
applyOomAdjLocked(app, true, now, nowElapsed);
......
}
}
......
}
複製代碼
updateOomAdjLocked()
方法中
slot
和每一個slot
須要容納的進程數量,包括:cached進程
和empty進程
cached進程
和empty進程
分紅不一樣的級別oom_adj
值oom_adj
值爲2
computeOomAdjLocked()
方法來計算進程的oom_adj
的值
oom_adj
值的計算過程這裏也就先簡單略過啦,感興趣的同窗能夠前去學習一下curAdj
仍然大於系統定義的最大oom_adj
值(ProcessList.UNKNOWN_ADJ
),代表該進程屬於cached進程
或者empty進程
curProcState
來區分進程類型slot
級別中oom_adj
數值+2
)咱們須要注意的是,計算oom_adj
值的時候是從mLruProcesses
列表的尾部開始計算的
這就意味着排在後面(數組索引值大的)的進程若是變成了cahced進程
和empty進程
,將會比它前面的同類型進程有更小的oom_adj
值
而oom_adj
值越大越可能被關閉,這也就是爲何要常常調整進程在mLruProcesses
列表的位置
而對於applyOomAdjLocked()
方法,最核心的地方在於執行了ProcessList.setOomAdj()
方法,跟蹤方法調用會發現
lmkd
的Socket
發送數據,數據內容爲pid
、uid
及對應的adj
值lmkd
由lmkd進程
建立,因此後面的事情就由lmkd
來處理關於lmkd
咱們就不深刻了,你們能夠前去官網瞭解:低內存終止守護程序
官方簡介:Android 低內存終止守護程序 (
lmkd
) 進程可監控運行中的 Android 系統的內存狀態,並經過終止最沒必要要的進程來應對內存壓力大的問題,使系統以可接受的性能水平運行