1、Launcher
Launcher作為Android系統(tǒng)的桌面涂身,它的作用有兩點(diǎn):
作為Android系統(tǒng)的啟動器,用于啟動應(yīng)用程序搓蚪;
作為Android系統(tǒng)的桌面蛤售,用于顯示和管理應(yīng)用程序的快捷圖標(biāo)或者其它桌面組件;
2妒潭、Launcher進(jìn)程啟動流程
2.1悴能、SystemServer調(diào)用
在SystemServer進(jìn)程啟動之后,執(zhí)行其run()函數(shù)雳灾,在里面執(zhí)行了大量的配置設(shè)置操作漠酿,并且啟動了各種引導(dǎo)服務(wù)、核心服務(wù)以及其他服務(wù)等谎亩,包括AMS炒嘲、PMS、WMS匈庭、電量管理服務(wù)等一系列服務(wù)夫凸,以及創(chuàng)建主線程Looper,并循環(huán)等待消息阱持;
其中在啟動引導(dǎo)服務(wù)方法中夭拌,啟動了ActivityManagerService,并且在啟動其他服務(wù)的方法中衷咽,調(diào)用AMS的systemReady()方法啼止,Launcher進(jìn)程就是從這兒開始啟動的;
public final class SystemServer {
private void run() {
...
startBootstrapServices();
startOtherServices();
...
}
private void startBootstrapServices() {
...
mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
...
}
private void startOtherServices() {
...
mActivityManagerService.systemReady(() -> {
}, BOOT_TIMINGS_TRACE_LOG);
}
}
在SystemServer啟動的時候兵罢,執(zhí)行startOtherServices()方法中献烦,里面調(diào)用了AMS的systemReady()方法,通過該方法來啟動Launcher卖词;
// Tag for timing measurement of main thread.
private static final String SYSTEM_SERVER_TIMING_TAG = "SystemServerTiming";
private static final TimingsTraceLog BOOT_TIMINGS_TRACE_LOG
= new TimingsTraceLog(SYSTEM_SERVER_TIMING_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
private void startOtherServices() {
...
mActivityManagerService.systemReady(() -> {
Slog.i(TAG, "Making services ready");
traceBeginAndSlog("StartActivityManagerReadyPhase");
mSystemServiceManager.startBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
...
}, BOOT_TIMINGS_TRACE_LOG);
}
2.2巩那、AMS執(zhí)行
在AMS中執(zhí)行systemReady()方法吏夯,在其中執(zhí)行startHomeActivityLocked()方法,傳入當(dāng)前用戶ID即横;
public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
...
synchronized (this) {
...
startHomeActivityLocked(currentUserId, "systemReady");
...
}
...
}
2.2.1噪生、獲取Launcher的Intent
在startHomeActivityLocked()方法中,首先通過getHomeIntent()方法东囚,獲取到要啟動的HomeActivity的intent對象跺嗽,其中mTopAction默認(rèn)為INTENT.ACTION_MAIN,并添加CATEGORY_HOME的category標(biāo)志页藻;
得到Intent對象桨嫁,通過PackageManager去獲取對應(yīng)符合的Activity,獲取對應(yīng)的ActivityInfo份帐,并獲取對應(yīng)的進(jìn)程記錄璃吧,此時對應(yīng)的進(jìn)程還沒啟動,后面繼續(xù)執(zhí)行废境,為intent添加FLAG_ACTIVITY_NEW_TASK啟動參數(shù)畜挨,開啟新棧,隨后調(diào)用ActivityStartController類的startHomeActivity()方法去執(zhí)行啟動噩凹;
boolean startHomeActivityLocked(int userId, String reason) {
...
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName, aInfo.applicationInfo.uid, true);
if (app == null || app.instr == null) {
intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
// For ANR debugging to verify if the user activity is the one that actually launched.
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
mActivityStartController.startHomeActivity(intent, aInfo, myReason);
}
}
...
return true;
}
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
return intent;
}
2.2.2巴元、啟動Launcher
在startHomeActivity()方法中,調(diào)用obtainStarter()方法獲取到一個ActivityStarter對象驮宴,setCallingUid()方法設(shè)置當(dāng)前調(diào)用的Uid=0务冕,然后執(zhí)行其execute()方法;
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(reason);
mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
.setOutActivity(tmpOutRecord)
.setCallingUid(0)
.setActivityInfo(aInfo)
.execute();
mLastHomeActivityStartRecord = tmpOutRecord[0];
if (mSupervisor.inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
// again. We need to schedule another resume.
mSupervisor.scheduleResumeTopActivities();
}
}
在ActivityStarter的execute()方法中幻赚,mayWait默認(rèn)為false禀忆,執(zhí)行startActivity()方法;
int execute() {
try {
// TODO(b/64750076): Look into passing request directly to these methods to allow
// for transactional diffs and preprocessing.
if (mRequest.mayWait) {
return startActivityMayWait(mRequest.caller, mRequest.callingUid, ...);
} else {
return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, ...);
}
} finally {
onExecutionComplete();
}
}
這里進(jìn)入了Activity的啟動流程落恼,Launcher本身就是一個系統(tǒng)APP箩退,用于顯示桌面等,LauncherApp啟動之后會執(zhí)行其生命周期方法初始化桌面布局佳谦;
2.3戴涝、初始化桌面圖標(biāo)
2.3.1、執(zhí)行onCreate()方法
@Override
protected void onCreate(Bundle savedInstanceState) {
...
LauncherAppState app = LauncherAppState.getInstance(this);
...
}
獲取LauncherAppState钻蔑,通過LauncherAppState的getInstance()方法獲取啥刻,該方法里面會判斷當(dāng)前線程是否為主線程,在主線程時還會直接new出對象咪笑,不在主線程時可帽,通過MainThreadExecutor的submit()方法向主線程提交一個任務(wù)去獲取該對象;
// We do not need any synchronization for this variable as its only written on UI thread.
private static LauncherAppState INSTANCE;
public static LauncherAppState getInstance(final Context context) {
if (INSTANCE == null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
INSTANCE = new LauncherAppState(context.getApplicationContext());
} else {
try {
return new MainThreadExecutor().submit(new Callable<LauncherAppState>() {
@Override
public LauncherAppState call() throws Exception {
return LauncherAppState.getInstance(context);
}
}).get();
} catch (InterruptedException|ExecutionException e) {
throw new RuntimeException(e);
}
}
}
return INSTANCE;
}
2.3.2窗怒、讀取安裝APP信息
在LauncherAppState的構(gòu)造方法中映跟,會新建InvariantDeviceProfile對象蓄拣,這個類主要是存儲App的基本配置信息,例如App圖標(biāo)的尺寸大小努隙,文字大小球恤,每個工作空間或文件夾能顯示多少App等;
在LauncherAppState的構(gòu)造方法中荸镊,會獲取WindowManager咽斧,并獲取屏幕的尺寸,解析桌面布局文件躬存,獲取默認(rèn)尺寸信息等张惹;
@TargetApi(23)
public InvariantDeviceProfile(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
...
ArrayList<InvariantDeviceProfile> closestProfiles = findClosestDeviceProfiles(minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context));
...
}
ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(Context context) {
ArrayList<InvariantDeviceProfile> profiles = new ArrayList<>();
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG) && "profile".equals(parser.getName())) {
TypedArray a = context.obtainStyledAttributes(Xml.asAttributeSet(parser), R.styleable.InvariantDeviceProfile);
int numRows = a.getInt(R.styleable.InvariantDeviceProfile_numRows, 0);
int numColumns = a.getInt(R.styleable.InvariantDeviceProfile_numColumns, 0);
float iconSize = a.getFloat(R.styleable.InvariantDeviceProfile_iconSize, 0);
profiles.add(new InvariantDeviceProfile(
a.getString(R.styleable.InvariantDeviceProfile_name),
a.getFloat(R.styleable.InvariantDeviceProfile_minWidthDps, 0),
a.getFloat(R.styleable.InvariantDeviceProfile_minHeightDps, 0),
numRows,
numColumns,
a.getInt(R.styleable.InvariantDeviceProfile_numFolderRows, numRows),
a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
iconSize,
a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0),
a.getResourceId(R.styleable.InvariantDeviceProfile_demoModeLayoutId, 0)));
a.recycle();
}
}
} catch (IOException|XmlPullParserException e) {
throw new RuntimeException(e);
}
return profiles;
}
2.3.3、注冊Intent廣播
新建LauncherModel對象优构,該對象是一個BroadcastReceiver,并添加App變化的回調(diào)雁竞,以及設(shè)置Filter并注冊廣播钦椭,用于監(jiān)聽桌面App的變化;
private LauncherAppState(Context context) {
...
mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
// Register intent receivers
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
// For handling managed profiles
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
...
mContext.registerReceiver(mModel, filter);
...
}
public class LauncherModel extends BroadcastReceiver ... {}
2.3.4碑诉、解析Launcher布局
繼續(xù)回到Launcher的onCreate()方法彪腔,將Launcher添加到LauncherModel中,是以弱引用的方式添加进栽,初始化一些其工作德挣,解析Launcher的布局,
2.3.5快毛、加載桌面
onCreate()方法中格嗅,通過LauncherModel的startLoader()來加載桌面App;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
if (!mModel.startLoader(currentScreen)) {
if (!internalStateHandled) {
// If we are not binding synchronously, show a fade in animation when
// the first page bind completes.
mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).setValue(0);
}
} else {
// Pages bound synchronously.
mWorkspace.setCurrentPage(currentScreen);
setWorkspaceLoading(true);
}
...
}
在LauncherModel的startLoader()方法中唠帝,新建了一個LoaderResults對象屯掖,并通過startLoaderForResults()方法創(chuàng)建出一個LoaderTask的Runnable任務(wù),將其在工作線程中執(zhí)行起來襟衰;
public boolean startLoader(int synchronousBindPage) {
...
synchronized (mLock) {
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
...
LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel, mBgAllAppsList, synchronousBindPage, mCallbacks);
if (mModelLoaded && !mIsLoaderTaskRunning) {
...
return true;
} else {
startLoaderForResults(loaderResults);
}
}
}
return false;
}
public void startLoaderForResults(LoaderResults results) {
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
runOnWorkerThread(mLoaderTask);
}
}
private static void runOnWorkerThread(Runnable r) {
if (sWorkerThread.getThreadId() == Process.myTid()) {
r.run();
} else {
// If we are not on the worker thread, then post to the worker handler
sWorker.post(r);
}
}
在LoaderTask的run()方法中贴铜,去加載手機(jī)已安裝的App的信息,查詢數(shù)據(jù)庫獲取已安裝的App的相關(guān)信息瀑晒,加載Launcher布局绍坝,并將數(shù)據(jù)轉(zhuǎn)化為View,綁定到界面上苔悦,由此我們就可以看到桌面顯示的宮格列表的桌面圖標(biāo)了轩褐;
public void run() {
...
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
// 查詢數(shù)據(jù)庫整理App信息,轉(zhuǎn)化為View綁定到界面
loadWorkspace();
mResults.bindWorkspace();
loadAllApps();
mResults.bindAllApps();
loadDeepShortcuts();
mResults.bindDeepShortcuts();
mBgDataModel.widgetsModel.update(mApp, null);
mResults.bindWidgets();
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
TraceHelper.partitionSection(TAG, "Cancelled");
}
TraceHelper.endSection(TAG);
}