本文将以代碼走讀的形式講解Launcher3的啟動過程,主要包括資料的加載,到界面的生成過程
1、入口Launcher.java
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// 1)加載布局
setContentView(R.layout.launcher);
setupViews();
registerContentObservers();
...
// 2)LauncherModel開始加載資料
<pre class="html" name="code"> if (!mRestoring) {
if (sPausedFromUserAction) {
mModel.startLoader(true, -1);
} else {
mModel.startLoader(true, mWorkspace.getCurrentPage());
}
}
...
}
2、資料加載LauncherModel,LauncherModel相當于Launcher的Controller
public void startLoader(boolean isLaunching, int synchronousBindPage) {
synchronized (mLock) {
...
if (mCallbacks != null && mCallbacks.get() != null) {
...
// 啟動mLoaderTask來加載資料,LoaderTask是一個實作了Runnable方法的内部類
mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching);
if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
}
}
}
}
LoaderTask的run()方法如下
<pre class="html" name="code"> public void run() {
keep_running: {
synchronized (mLock) {
// 1)加載workspace資料并綁定view
isUpgrade = loadAndBindWorkspace();
...
// 2)加載allApp資料并綁定view
loadAndBindAllApps();
...
}
...
}
2.1 先看workspace的加載
private boolean loadAndBindWorkspace() {
...
if (!mWorkspaceLoaded) {
// 1)加載workspace中的資料
isUpgradePath = loadWorkspace();
...
}
// 2)加載view
bindWorkspace(-1, isUpgradePath);
return isUpgradePath;
}
private boolean loadWorkspace() {
// 1)確定預設workspace的資料得到加載
LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);
synchronized (sBgLock) {
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
// 2)資料查詢
final Cursor c = contentResolver.query(contentUri, null, null, null, null);
// 3)解析Cursor
while (!mStopped && c.moveToNext()) {
...
}
...
// 4)更新螢幕次序
updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
...
}
}
// 在主線程中将資料加載到實際的View中
private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
...
// 1)資料預處理
filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems, otherWorkspaceItems);
filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets, otherAppWidgets);
filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders, otherFolders);
sortWorkspaceItemsSpatially(currentWorkspaceItems);
sortWorkspaceItemsSpatially(otherWorkspaceItems);
// 2)回調,對應 Launcher.java 中的 startBinding()
...
callbacks.startBinding();
// 3)添加界面控件,完成Workspace界面的繪制
bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, currentFolders, null);
bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
(isLoadingSynchronously ? mDeferredBindRunnables : null));
// 4)加載完成,回調Lancher.finishBindingItems(isUpgradePath)
callbacks.finishBindingItems(isUpgradePath);
...
}
private void bindWorkspaceItems(final Callbacks oldCallbacks, final ArrayList<ItemInfoBean> workspaceItems,
final ArrayList<LauncherAppWidgetInfoBean> appWidgets, final HashMap<Long, FolderInfoBean> folders,
ArrayList<Runnable> deferredBindRunnables) {
int N = workspaceItems.size();
// 1)ShortCut 回調Launcher中的bindItems(...)
for (int i = 0; i < N; i += ITEMS_CHUNK) {
callbacks.bindItems(workspaceItems, start, start + chunkSize, false);
}
// 2)Folders
callbacks.bindFolders(folders);
// 3)Widget
for (int i = 0; i < N; i++) {
callbacks.bindAppWidget(widget);
}
}
// 再看Launcher中的bindItems()
public void bindItems(final ArrayList<ItemInfoBean> shortcuts, final int start, final int end,
final boolean forceAnimateIcons) {
...
for (int i = start; i < end; i++) {
final ItemInfoBean item = shortcuts.get(i);
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
// 在Workspace.java的addInScreenFromBind(...)方法中真正添加各個View
// layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)
workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX, item.cellY, 1, 1);
...
}
}
2.2 AllApps的加載
private void loadAndBindAllApps() {
...
loadAllApps();
)
private void loadAllApps() {
// 1)查詢所有App的資訊
List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
// 2)根據名稱排序
Collections.sort(apps, new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));
// 3)添加圖示資訊
for (int i = 0; i < apps.size(); i++) {
ResolveInfo app = apps.get(i);
mBgAllAppsList.add(new AppInfoBean(packageManager, app, mIconCache, mLabelCache));
}
// 4)回調Launcher.bindAllApplications(ArrayList<AppInfoBean> beans)
callbacks.bindAllApplications(added);
...
}
因為進入Launcher先隻是顯示workspace界面,是以allapp界面暫時不顯示,view不會被添加進去,隻是準備好所有App的資料資訊,至此我們能夠看到加載了ShortCut,Folder,Widget的Launcher界面