Launcher3的主界面是
packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
首先分析onCreate
@Override
protected void onCreate(Bundle savedInstanceState) {
if (DEBUG_STRICT_MODE) {
//StrictMode被称为严苛模式,google提供用来进行测试的类
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
if (LauncherAppState.PROFILE_STARTUP) {
Trace.beginSection("Launcher-onCreate");
}
if (mLauncherCallbacks != null) {
mLauncherCallbacks.preOnCreate();
}
WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
wallpaperColorInfo.setOnThemeChangeListener(this);
overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
super.onCreate(savedInstanceState);
//单例模式,初始化LauncherAppState
LauncherAppState app = LauncherAppState.getInstance(this);
// Load configuration-specific DeviceProfile
//初始化手机固件信息对象DeviceProfile
mDeviceProfile = app.getInvariantDeviceProfile().getDeviceProfile(this);
if (isInMultiWindowModeCompat()) {
Display display = getWindowManager().getDefaultDisplay();
Point mwSize = new Point();
display.getSize(mwSize);
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
//获取屏幕方向
mOrientation = getResources().getConfiguration().orientation;
mSharedPrefs = Utilities.getPrefs(this); //获取sharedPreferences
mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this); //获取LauncherModel实例
mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
mIconCache = app.getIconCache();//获取IconCache实例,此类主要保存图标信息
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
//拖拽
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); //获取AppWidgetManager实例,用来管理widge
mAppWidgetHost = new LauncherAppWidgetHost(this);
if (Utilities.ATLEAST_MARSHMALLOW) {
mAppWidgetHost.addProviderChangeListener(this);
}
mAppWidgetHost.startListening();
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
// this also ensures that any synchronous binding below doesn't re-trigger another
// LauncherModel load.
mPaused = false;
//设置布局,此时布局是不包含参数布局
mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
//初始化View,进行各种View的初始化事件绑定
setupViews();
mDeviceProfile.layout(this, false /* notifyListeners */);//布局的参数设置
loadExtractedColorsAndColorItems();
mPopupDataProvider = new PopupDataProvider(this);
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.addAccessibilityStateChangeListener(this);
lockAllApps();
restoreState(savedInstanceState);
if (LauncherAppState.PROFILE_STARTUP) {
Trace.endSection();
}
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
int currentScreen = PagedView.INVALID_RESTORE_PAGE;
if (savedInstanceState != null) {
currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen);
}
if (!mModel.startLoader(currentScreen)) {
// If we are not binding synchronously, show a fade in animation when
// the first page bind completes.
mDragLayer.setAlpha(0);
} else {
// Pages bound synchronously.
mWorkspace.setCurrentPage(currentScreen);
setWorkspaceLoading(true);
}
// For handling default keys
mDefaultKeySsb = new SpannableStringBuilder();
Selection.setSelection(mDefaultKeySsb, 0);
mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
// In case we are on a device with locked rotation, we should look at preferences to check
// if the user has specifically allowed rotation.
if (!mRotationEnabled) {
mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
mRotationPrefChangeHandler = new RotationPrefChangeHandler();
mSharedPrefs.registerOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
}
if (PinItemDragListener.handleDragRequest(this, getIntent())) {
// Temporarily enable the rotation
mRotationEnabled = true;
}
// On large interfaces, or on devices that a user has specifically enabled screen rotation,
// we want the screen to auto-rotate based on the current orientation
setOrientation();
setContentView(mLauncherView);
// Listen for broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
registerReceiver(mReceiver, filter);
mShouldFadeInScrim = true;
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
}
private LauncherAppState(Context context) {
if (getLocalProvider(context) == null) {
throw new RuntimeException(
"Initializing LauncherAppState in the absence of LauncherProvider");
}
Log.v(Launcher.TAG, "LauncherAppState initiated");
Preconditions.assertUIThread();
mContext = context;
if (TestingUtils.MEMORY_DUMP_ENABLED) {
TestingUtils.startTrackingMemory(mContext);
}
//初始化固定的设备配置
mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
//初始化图标管理工具
mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
//初始化Widget加载缓存工具
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
//初始化广播
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);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
// For extracting colors from the wallpaper
if (Utilities.ATLEAST_NOUGAT) {
// TODO: add a broadcast entry to the manifest for pre-N.
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
}
//注册广播
mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
new ConfigMonitor(mContext).register();
ExtractionUtils.startColorExtractionServiceIfNecessary(mContext);
if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
mNotificationBadgingObserver = null;
} else {
// Register an observer to rebind the notification listener when badging is re-enabled.
mNotificationBadgingObserver = new SettingsObserver.Secure(
mContext.getContentResolver()) {
@Override
public void onSettingChanged(boolean isNotificationBadgingEnabled) {
if (isNotificationBadgingEnabled) {
NotificationListener.requestRebind(new ComponentName(
mContext, NotificationListener.class));
}
}
};
mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
}
}
从代码中可以看出,首先调用了LauncherAppState.getInstance(this)来初始化一个单例对象。LauncherAppState里面保存了一些比较常用的对象,方便其他地方通过单例来获取,比如IconCache、LauncherModel等。
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() {
@Override
public LauncherAppState call() throws Exception {
return LauncherAppState.getInstance(context);
}
}).get();
} catch (InterruptedException|ExecutionException e) {
throw new RuntimeException(e);
}
}
}
return INSTANCE;
}
注意这里初始化使用的application的Context,因为单例作为static对象,生命周期是与application生命周期一样长的,如果这里使用了Activity的Contxet,会导致activity退出后,该Context依然被单例持有而无法回收,于是出现内存泄漏。
private LauncherAppState(Context context) {
if (getLocalProvider(context) == null) {
throw new RuntimeException(
"Initializing LauncherAppState in the absence of LauncherProvider");
}
Log.v(Launcher.TAG, "LauncherAppState initiated");
Preconditions.assertUIThread();
mContext = context;
if (TestingUtils.MEMORY_DUMP_ENABLED) {
TestingUtils.startTrackingMemory(mContext);
}
//初始化固定的设备配置
mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
//初始化图标管理工具
mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
//初始化Widget加载混存工具
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
//初始化广播
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);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
// For extracting colors from the wallpaper
if (Utilities.ATLEAST_NOUGAT) {
// TODO: add a broadcast entry to the manifest for pre-N.
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
}
mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
new ConfigMonitor(mContext).register();
ExtractionUtils.startColorExtractionServiceIfNecessary(mContext);
if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
mNotificationBadgingObserver = null;
} else {
// Register an observer to rebind the notification listener when badging is re-enabled.
mNotificationBadgingObserver = new SettingsObserver.Secure(
mContext.getContentResolver()) {
@Override
public void onSettingChanged(boolean isNotificationBadgingEnabled) {
if (isNotificationBadgingEnabled) {
NotificationListener.requestRebind(new ComponentName(
mContext, NotificationListener.class));
}
}
};
mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
}
}
这里面其实是各个对象的实例创建过程,并且注册了一些系统事件的监听。创建完成LauncherAppState后,会执行mModel = app.setLauncher(this); //获取LauncherModel实例
LauncherModel setLauncher(Launcher launcher) {
getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
return mModel;
}
Launcher3/src/com/android/launcher3/LauncherModel.java
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
Preconditions.assertUIThread();
mCallbacks = new WeakReference<>(callbacks);
}
}
这里将传过来的Callbacks对象(也就是Launcher,Launcher实现了Callbacks接口)保存为了弱引用。同样是基于避免内存泄漏的考虑。这里的LauncherModel是LauncherAppState内部的一个成员变量,生命周期也是比Launcher这个Activity要长。
总结一下onCreate的工作:初始化对象、加载布局、注册一些事件监听、以及开启数据加载。
数据加载是通过mModel.startLoader。现在进入LauncherModel
Launcher3/src/com/android/launcher3/LauncherModel.java
/**
* Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible.
* @return true if the page could be bound synchronously.
*/
public boolean startLoader(int synchronousBindPage) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
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) {
final Callbacks oldCallbacks = mCallbacks.get();
// Clear any pending bind-runnables from the synchronized load process.
mUiExecutor.execute(new Runnable() {
public void run() {
oldCallbacks.clearPendingBinds();
}
});
// If there is already one running, tell it to stop.
stopLoader();
LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel,
mBgAllAppsList, synchronousBindPage, mCallbacks);
if (mModelLoaded && !mIsLoaderTaskRunning) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
loaderResults.bindWorkspace();
// For now, continue posting the binding of AllApps as there are other
// issues that arise from that.
loaderResults.bindAllApps();
loaderResults.bindDeepShortcuts();
loaderResults.bindWidgets();
return true;
} else {
startLoaderForResults(loaderResults);
}
}
}
return false;
}
public void startLoaderForResults(LoaderResults results) {
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
runOnWorkerThread(mLoaderTask);
}
}
/** Runs the specified runnable immediately if called from the worker thread, otherwise it is
* posted on the worker thread handler. */
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。
Launcher3/src/com/android/launcher3/model/LoaderTask.java
public void run() {
synchronized (this) {
// Skip fast if we are already stopped.
if (mStopped) {
return;
}
}
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
long now = 0;
if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
loadWorkspace();
verifyNotStopped();
if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
mResults.bindWorkspace();
// Take a break
if (DEBUG_LOADERS) {
Log.d(TAG, "step 1 completed, wait for idle");
now = SystemClock.uptimeMillis();
}
waitForIdle();
if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
verifyNotStopped();
// second step
if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
loadAllApps();
if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps");
verifyNotStopped();
mResults.bindAllApps();
verifyNotStopped();
if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache");
updateIconCache();
// Take a break
if (DEBUG_LOADERS) {
Log.d(TAG, "step 2 completed, wait for idle");
now = SystemClock.uptimeMillis();
}
waitForIdle();
if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
verifyNotStopped();
// third step
if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
loadDeepShortcuts();
verifyNotStopped();
if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
mResults.bindDeepShortcuts();
// Take a break
if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
waitForIdle();
verifyNotStopped();
// fourth step
if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
mBgDataModel.widgetsModel.update(mApp, null);
verifyNotStopped();
if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets");
mResults.bindWidgets();
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
if (DEBUG_LOADERS) {
Log.d(TAG, "Loader cancelled", e);
}
}
}
在上面代码中,主要有4步,由于Launcher里面数据比较多,在这里采用了分批加载、分批绑定的做法。
这里以Workspace为例,分析加载和绑定。加载桌面是loadWorkspace方法,需要先了解BgDataModel类(Launcher3/src/com/android/launcher3/model/BgDataModel.java)。
BgDataModel是android O版本新增的,对memory缓存进行了封装,可以看到有workspaceItems(所有应用图标数据对应的ItemInfo),appWidgets(所有AppWidgets数据对应的LauncherAppWidgetInfo)等等。
public final LongArrayMap itemsIdMap = new LongArrayMap<>();
public final ArrayList workspaceItems = new ArrayList<>();
public final ArrayList appWidgets = new ArrayList<>();
public final LongArrayMap folders = new LongArrayMap<>();
public final ArrayList workspaceScreens = new ArrayList<>();
public final Map pinnedShortcutCounts = new HashMap<>();
public boolean hasShortcutHostPermission;
public final MultiHashMap deepShortcutMap = new MultiHashMap<>();
public final WidgetsModel widgetsModel = new WidgetsModel();
loadWorkspace的代码比较长,主要做两件事情:1、加载默认配置xml文件中的items,并把默认数据写入数据库。2、加载数据库中的数据到缓存。先看加载默认布局
if (clearDb) {
Log.d(TAG, "loadWorkspace: resetting launcher database");
LauncherSettings.Settings.call(contentResolver,
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
}
最终会其会调用LauncherProvider的loadDefaultFavoritesIfNecessary()方法。
再分析加载数据库
final LoaderCursor c = new LoaderCursor(contentResolver.query(
LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
1、通过LauncherSettings.Favorites.CONTENT_URI查询Favorites表中所有内容,拿到cursor。
2、遍历cursor,进行数据的整理。每一行数据都有一个对应的itemType,标志着这一行数据对应得的是一个应用、还是一个Widget或文件夹等,不同类型会进行不同的处理。
3、对于图标类型(itemType是ITEM_TYPE_SHORTCUT,ITEM_TYPE_APPLICATION,ITEM_TYPE_DEEP_SHORTCUT
),首先经过一系列判断,判断其是否还可用(比如应用在Launcher未启动时被卸载导致不可用),不可用的话就标记为可删除,继续循环。如果可用的话,就根据当前cursor的内容,生成一个ShortInfo对象,保存到BgDataModel。
4、对于文件夹类型(itemType是ITEM_TYPE_FOLDER
),直接生成一个对应的FolderInfo对象,保存到BgDataModel。
5、对于文件类型(itemType是ITEM_TYPE_FOLDER),直接生成一个对应的Folder对象,保存到BgDataModel。
6、对于AppWidget(itemType是ITEM_TYPE_APPWIDGET
,ITEM_TYPE_CUSTOM_APPWIDGET
),也需要经过是否可用的判断,但是可用条件与图标类型是有差异的。如果可用,生成一个LauncherAppWidgetInfo对象,保存到BgDataModel。
7、经过上述流程,现在所有数据库里读出的内容已经分类完毕,并且保存到了内存(BgDataModel)中。然后开始处理之前标记为可删除的内容。显示从数据库中删除对应的行,然后还要判断此次删除操作是否带来了其他需要删除的内容。比如某个文件夹或者某一页只有一个图标,这个图标因为某些原因被删掉了,那么此文件夹或页面也需要被删掉。
数据加载完毕后开始绑定数据使用mResults.bindWorkspace()
Launcher3/src/com/android/launcher3/model/LoaderResults.java
// Separate the items that are on the current screen, and all the other remaining items
ArrayList currentWorkspaceItems = new ArrayList<>();
ArrayList otherWorkspaceItems = new ArrayList<>();
ArrayList currentAppWidgets = new ArrayList<>();
ArrayList otherAppWidgets = new ArrayList<>();
filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
filterCurrentWorkspaceItems(currentScreenId, appWidgets, currentAppWidgets,
otherAppWidgets);
sortWorkspaceItemsSpatially(currentWorkspaceItems);
sortWorkspaceItemsSpatially(otherWorkspaceItems);
上面这段代码是把Launcher启动后默认显示出来的那一页所拥有的数据筛选到
currentWorkspaceItems与currentAppWidgets,其他页的数据筛选到otherWorkspaceItems与otherAppWidgets。然后对每个list,按照从上到下,从左到右的顺序进行排序。然后可以开始绑定了。
// Tell the workspace that we're about to start binding items
r = new Runnable() {
public void run() {
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
callbacks.clearPendingBinds();
callbacks.startBinding();
}
}
};
mUiExecutor.execute(r);
// Bind workspace screens
mUiExecutor.execute(new Runnable() {
@Override
public void run() {
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
callbacks.bindScreens(orderedScreenIds);
}
}
});
Executor mainExecutor = mUiExecutor;
// Load items on the current page.
bindWorkspaceItems(currentWorkspaceItems, currentAppWidgets, mainExecutor);
上面的Callbacks就是Launcher activity实例,首先通知Launcher要开始绑定(callbacks.startBinding()),然后把空白页添加到View tree中(callbacks.bindScreens(orderedScreenIds)),之后先绑定默认页面的所有元素( bindWorkspaceItems(currentWorkspaceItems, currentAppWidgets, mainExecutor)),当然这些所有的操作都是通过mUiExecutor放到主线程执行的。
private void bindWorkspaceItems(final ArrayList workspaceItems,
final ArrayList appWidgets,
final Executor executor) {
// Bind the workspace items
int N = workspaceItems.size();
for (int i = 0; i < N; i += ITEMS_CHUNK) {
final int start = i;
final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
final Runnable r = new Runnable() {
@Override
public void run() {
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
callbacks.bindItems(workspaceItems.subList(start, start+chunkSize), false);
}
}
};
executor.execute(r);
}
// Bind the widgets, one at a time
N = appWidgets.size();
for (int i = 0; i < N; i++) {
final ItemInfo widget = appWidgets.get(i);
final Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
callbacks.bindItems(Collections.singletonList(widget), false);
}
}
};
executor.execute(r);
}
}
最后通过callbacks调用在Launcher中的bindItems方法
@Override
public void bindItems(final List items, final boolean forceAnimateIcons) {
...
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
ShortcutInfo info = (ShortcutInfo) item;
view = createShortcut(info);
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
view = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(FolderInfo) item);
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
view = inflateAppWidget((LauncherAppWidgetInfo) item);
if (view == null) {
continue;
}
break;
}
default:
throw new RuntimeException("Invalid Item Type");
}
...
workspace.addInScreenFromBind(view, item);
...
}
上面的代码主要是根据不同的itemType来生产不同的View,然后通过addInScreenFromBind函数将View add到相应的ViewGroup中去。
默认页的元素绑定完了,然后继续绑定其他页面的元素。
// In case of validFirstPage, only bind the first screen, and defer binding the
// remaining screens after first onDraw (and an optional the fade animation whichever
// happens later).
// This ensures that the first screen is immediately visible (eg. during rotation)
// In case of !validFirstPage, bind all pages one after other.
final Executor deferredExecutor =
validFirstPage ? new ViewOnDrawExecutor(mUiExecutor) : mainExecutor;
mainExecutor.execute(new Runnable() {
@Override
public void run() {
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
callbacks.finishFirstPageBind(
validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
}
}
});
bindWorkspaceItems(otherWorkspaceItems, otherAppWidgets, deferredExecutor);
从上面代码可以看出,Launcher为了让默认页尽快显示,自定义了一个ViewOnDrawExecutor,这里面会让绑定其他页的操作在绑定完第一页的元素并且第一次onDraw执行完之后才执行。读者有兴趣的话可以去看看这个Executor的实现。
桌面数据的加载与绑定完之后,我们看这里执行了一个waitForIdle的函数,然后才是继续执行第二步,这里面涉及到一个应用启动优化的技术。我们知道应用的启动优化可以有延迟加载、懒加载、异步加载等手段。而用一个名为IdleHandler
的类,就可以比较方便的实现延迟加载。
参考资料:
Android 9.0 Launcher源码分析(二)——Launcher应用启动
墨香带你学Launcher之(二)-数据加载流程