一、AndroidMainifest文件
在Application的标签中有activity和provider分别是Launcher,settingsActivity和LauncherProvider
其中,Launcher是手机可以看到的桌面,Settings是桌面的设置页面,LauncherProvider是用来存储页面的布局信息的 。
二、桌面布局:
1、Launcher界面配置的文件:
该位置一般处于:
\Launcher\res\xml\default_workspace.xml
注意有可能存在partner app此时的设置需要在partner中设置
<appwidget
launcher:className="" //应用的类
launcher:packageName=""//应用的包名
launcher:screen=""当前处于哪一屏
launcher:x="" //图标X位置,左上角是第0个
launcher:y="" //图标Y位置,左上角是第0个
launhcer:spanX="" //在x方向所占的格数
launcher:spanY="" //在y方向所占的格数
>
2、Launcher.java的onCreate方法:
在该部分中,完成了对于Launcher的一些初始化的工作,建立了LaunhcerAppState对象和LauncherModel对象的初始化,以及其他信息的初始化。开启数据加载设置
对于状态进行判断,我们不需要进行任何的配置
对于事件进行绑定,如果没有绑定则进行事件的绑定
3、LauncherModel的startLoader方法
在该方法中主要实现了对于LoaderTask进行启动
LocalModel对象主要保存在数据库中,主要保存了各种布局信息的
- 对于当前的数据进行判断是否有效,在此时以线程的方式进行实现
private boolean mModelLoaded;
public boolean isModelLoaded(){
synchronized(mLock){
return mModelLoaded&& mLoaderTask==null;
}
}
- startLoader方法
该方法是加载程序的启动,在启动过程中并对于数据进行尝试同步绑定,若能够进行绑定则可以返回true,在实现的过程中调用的是工作线程LoaderTask
public boolean startLoader() {
//启动队列在数据加载之前,
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
synchronized (mLock) {//以单线程的方式执行
final Callbacks[] callbacksList = getCallbacks();
if (callbacksList.length > 0) {
//在同步加载过程中对于所有挂起的绑定可运行项进行清除
for (Callbacks cb : callbacksList) {
MAIN_EXECUTOR.execute(cb::clearPendingBinds);
}
//对于旧的运行项进行一个清除
stopLoader();
LoaderResults loaderResults = new LoaderResults(
mApp, mBgDataModel, mBgAllAppsList, callbacksList);
if (mModelLoaded && !mIsLoaderTaskRunning) {
//将同步项绑定于workspace、allapps、快捷方式、桌面小组件
loaderResults.bindWorkspace();
loaderResults.bindAllApps();
loaderResults.bindDeepShortcuts();
loaderResults.bindWidgets();
return true;
} else {
//正在工作 的不是空则无需绑定
startLoaderForResults(loaderResults);
}
}
}
return false;
}
//当有加载任务正在运行时则进行其停止运行
public boolean stopLoader() {
synchronized (mLock) {
LoaderTask oldTask = mLoaderTask;
mLoaderTask = null;
if (oldTask != null) {
oldTask.stopLocked();
return true;
}
return false;
}
}
//对于LocalderTask进行启动:
public void startLoaderForResults(LoaderResults results) {
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(
mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, results);
//发布加载程序任务
MODEL_EXECUTOR.post(mLoaderTask);
}
}
4、LocalTask中的run方法:
public void run() {
synchronized (this) {
//加锁的方式进行判断当mStopped是true时快速跳过
// Skip fast if we are already stopped.
if (mStopped) {
return;
}
}
Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
TimingLogger logger = new TimingLogger(TAG, "run");
//进行分批加载
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();//定义一个所有的快捷方式队列
//执行该方法是对于Workspace的一些加载包括屏幕数、应用数据、widget组建信息等
loadWorkspace(allShortcuts);
logASplit(logger, "loadWorkspace");
// Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
// sanitizeData should not be invoked if the workspace is loaded from a db different
// from the main db as defined in the invariant device profile.
// (e.g. both grid preview and minimal device mode uses a different db)
if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
verifyNotStopped();
sanitizeData();
logASplit(logger, "sanitizeData");
}
//在该方法中对于mStopped又进行了一次判断,若是true就抛出异常
verifyNotStopped();
//进行绑定
mResults.bindWorkspace();
logASplit(logger, "bindWorkspace");
mModelDelegate.workspaceLoadComplete();
// Notify the installer packages of packages with active installs on the first screen.
sendFirstScreenActiveInstallsBroadcast();
logASplit(logger, "sendFirstScreenActiveInstallsBroadcast");
// Take a break
waitForIdle();
logASplit(logger, "step 1 complete");
verifyNotStopped();
//加载所有的app
// second step
List<LauncherActivityInfo> allActivityList = loadAllApps();
logASplit(logger, "loadAllApps");
verifyNotStopped();
mResults.bindAllApps();
logASplit(logger, "bindAllApps");
verifyNotStopped();
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.newInstance(mApp.getContext()),
mApp.getModel()::onPackageIconsUpdated);
logASplit(logger, "update icon cache");
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
verifyNotStopped();
logASplit(logger, "save shortcuts in icon cache");
updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
mApp.getModel()::onPackageIconsUpdated);
}
// Take a break
waitForIdle();
logASplit(logger, "step 2 complete");
verifyNotStopped();
//加载所有的快捷方式
// third step
List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
logASplit(logger, "loadDeepShortcuts");
verifyNotStopped();
mResults.bindDeepShortcuts();
logASplit(logger, "bindDeepShortcuts");
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
verifyNotStopped();
logASplit(logger, "save deep shortcuts in icon cache");
updateHandler.updateIcons(allDeepShortcuts,
new ShortcutCachingLogic(), (pkgs, user) -> { });
}
// Take a break
waitForIdle();
logASplit(logger, "step 3 complete");
verifyNotStopped();
//加载桌面小部件
// fourth step
List<ComponentWithLabelAndIcon> allWidgetsList =
mBgDataModel.widgetsModel.update(mApp, null);
logASplit(logger, "load widgets");
verifyNotStopped();
mResults.bindWidgets();
logASplit(logger, "bindWidgets");
verifyNotStopped();
updateHandler.updateIcons(allWidgetsList,
new ComponentWithIconCachingLogic(mApp.getContext(), true),
mApp.getModel()::onWidgetLabelsUpdated);
logASplit(logger, "save widgets in icon cache");
//对于图标进行更新
// fifth step
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
loadFolderNames();
}
verifyNotStopped();
updateHandler.finish();
logASplit(logger, "finish icon update");
mModelDelegate.modelLoadComplete();
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
logASplit(logger, "Cancelled");
} finally {
logger.dumpToLog();
}
TraceHelper.INSTANCE.endSection(traceToken);
}
在该部分中完成了所有应用的图标和应用数据、所有应用的Widget数据、桌面已经添加的用户数据,在该部分中完成了分批加载分批绑定的方法:
5、workspace的加载流程
5、1了解BgDataModel.java
在这个方法中调可以看到所有应用图标对应的ItemInfo,所有AppWidgets数据对应的内容
private static final String TAG = "BgDataModel";
/**
* Map of all the ItemInfos (shortcuts, folders, and widgets) created by
* LauncherModel to their ids
*/
public final IntSparseArrayMap<ItemInfo> itemsIdMap = new IntSparseArrayMap<>();
/**
* List of all the folders and shortcuts directly on the home screen (no widgets
* or shortcuts within folders).
*/
public final ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
/**
* All LauncherAppWidgetInfo created by LauncherModel.
*/
public final ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
/**
* Map of id to FolderInfos of all the folders created by LauncherModel
*/
public final IntSparseArrayMap<FolderInfo> folders = new IntSparseArrayMap<>();
/**
* Extra container based items
*/
public final IntSparseArrayMap<FixedContainerItems> extraItems = new IntSparseArrayMap<>();
/**
* Maps all launcher activities to counts of their shortcuts.
*/
public final HashMap<ComponentKey, Integer> deepShortcutMap = new HashMap<>();
/**
* Entire list of widgets.
*/
public final WidgetsModel widgetsModel = new WidgetsModel();
public int lastBindId = 0;
5、2loadWorkspace的分析
在该模块中主要完成了加载默认配置xml文件中的items,并把默认数据写入到数据库2、加载数据库的数据到缓存中
①:先进行数据库的清理重置:
if (clearDb) {
Log.d(TAG, "loadWorkspace: resetting launcher database");
LauncherSettings.Settings.call(contentResolver,
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
}
在调用后跳转到LauncherProvider的loadDefaultFavoritesNecessary(),在该方法中判断是在deafultxml还是从数据库中读取数据,之后通过DatabaseHelper中的loadFavorites方法进行处理
case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
return null;
}
@Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
// TODO: Use multiple loaders with fall-back and transaction.
int count = loader.loadLayout(db, new IntArray());
// Ensure that the max ids are initialized
mMaxItemId = initializeMaxItemId(db);
mMaxScreenId = initializeMaxScreenId(db);
return count;
}
loadDefaultFavoritesNecessary()方法:若数据库木创建则加载默认的配置
synchronized private void loadDefaultFavoritesIfNecessary() {
SharedPreferences sp = Utilities.getPrefs(getContext());
//判断数据库是否创建
if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) {
Log.d(TAG, "loading default workspace");
AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
if (loader == null) {
loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
}
if (loader == null) {
final Partner partner = Partner.get(getContext().getPackageManager());
if (partner != null && partner.hasDefaultLayout()) {
final Resources partnerRes = partner.getResources();
int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
"xml", partner.getPackageName());
if (workspaceResId != 0) {
loader = new DefaultLayoutParser(getContext(), widgetHost,
mOpenHelper, partnerRes, workspaceResId);
}
}
}
final boolean usingExternallyProvidedLayout = loader != null;
if (loader == null) {
loader = getDefaultLayoutParser(widgetHost);
}
// There might be some partially restored DB items, due to buggy restore logic in
// previous versions of launcher.
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
// Populate favorites table with initial favorites
if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0)
&& usingExternallyProvidedLayout) {
// Unable to load external layout. Cleanup and load the internal layout.
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
getDefaultLayoutParser(widgetHost));
}
//用于清理冗余数据
clearFlagEmptyDbCreated();
}
}
实现步骤如下:
1、加载默认项的值
2、初始化数据信息并且将重置数据库信息
3、通过LauncherSettings.Favorites.CONTENT_URI查询Favorites表中的数据,
4、便利cursor进行数据整理。区分是应用、widget或是文件夹
5、读取完数据库后将需要更新和移除的item进行移除和更新
5、3数据绑定bindWorkspace():
根据获取的值将screenID将items分成了当前screen和其他screen,并进行排序
通过startBing方法进行一些清理工作,该方法无语Launcher.java中
public void startBinding() {
Object traceToken = TraceHelper.INSTANCE.beginSection("startBinding");
// Floating panels (except the full widget sheet) are associated with individual icons. If
// we are starting a fresh bind, close all such panels as all the icons are about
// to go away.
AbstractFloatingView.closeOpenViews(this, true, TYPE_ALL & ~TYPE_REBIND_SAFE);
setWorkspaceLoading(true);
// Clear the workspace because it's going to be rebound
mDragController.cancelDrag();
mWorkspace.clearDropTargets();
mWorkspace.removeAllWorkspaceScreens();
mAppWidgetHost.clearViews();
if (mHotseat != null) {
mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout());
}
TraceHelper.INSTANCE.endSection(traceToken);
}
- 通过bindWorkspaceScreens(oldCallbacks, orderedScreenIds);绑定workspace screen,Callback对象处于BgDataModel中
- 在Launcher,java中实现bindScreens和bindAddScreens方法
@Override
public void bindScreens(IntArray orderedScreenIds) {
// Make sure the first screen is always at the start.
if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
orderedScreenIds.removeValue(Workspace.FIRST_SCREEN_ID);
orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
} else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
// If there are no screens, we need to have an empty screen
mWorkspace.addExtraEmptyScreen();
}
bindAddScreens(orderedScreenIds);
// After we have added all the screens, if the wallpaper was locked to the default state,
// then notify to indicate that it can be released and a proper wallpaper offset can be
// computed before the next layout
mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout();
}
private void bindAddScreens(IntArray orderedScreenIds) {
int count = orderedScreenIds.size();
for (int i = 0; i < count; i++) {
int screenId = orderedScreenIds.get(i);
if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) {
// No need to bind the first screen, as its always bound.
mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
}
}
}