1.简介
前边分析的都是ui功能相关的,核心的功能基本都了解了,现在就从头看下我们用到的数据都是如何获取的,比如allapps数据咋获取的,咋分发出去的?workspace里的数据都哪里来的? 那些默认的数据哪里来的?
1.1 shortcuts
android里的快捷方式是啥?如何添加,如何使用,参考下边这篇文章 juejin.cn/post/684490…
如下图,长按settings,弹出的红框里的那3个,就是快捷方式,点击长按还可以拖动出来放到workspace上,
1.2.widgets
小部件,长按桌面空白处,弹框里选择widgets,会弹出一个页面,里边会显示个列表,所有带有小部件的app都在这里了,有的app可能有多个小部件。
2. InvariantDeviceProfile.java
- 这个就是设备的不变的属性,也可以说是统一的属性吧,
- 后边的DeviceProfile就是单独对应某个屏幕的属性了,比如横竖屏就不一样的数据。
2.1.getIDP
- 调用的地方很多,单列模式,最早应该是TaskbarManager
public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
new MainThreadInitializedObject<>(LauncherAppState::new);//补充1
public static InvariantDeviceProfile getIDP(Context context) {
return InvariantDeviceProfile.INSTANCE.get(context);
}
>1.构造方法
private InvariantDeviceProfile(Context context) {
//本地存储的grid name,补充2
String gridName = getCurrentGridName(context);
//参考2.2
String newGridName = initGrid(context, gridName);
if (!newGridName.equals(gridName)) {
LauncherPrefs.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName)
.apply();
}
new DeviceGridState(this).writeToPrefs(context);
DisplayController.INSTANCE.get(context).setPriorityListener(
(displayContext, info, flags) -> {
if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS
| CHANGE_NAVIGATION_MODE)) != 0) {
onConfigChanged(displayContext);
}
});
}
>2.getCurrentGridName
public static String getCurrentGridName(Context context) {
return LauncherPrefs.getPrefs(context).getString(KEY_IDP_GRID_NAME, null);
}
2.2.initGrid
private String initGrid(Context context, String gridName) {
//见2.6
Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
//根据设备尺寸判断设备类型,平板,手机还是折叠屏
@DeviceType int deviceType = getDeviceType(displayInfo);
//获取预制的属性,读取xml文件
ArrayList<DisplayOption> allOptions =//补充1
getPredefinedDeviceProfiles(context, gridName, deviceType,
RestoreDbTask.isPending(context));
//根据info和type找出allOptions里最合适的一个返回
DisplayOption displayOption =
invDistWeightedInterpolate(displayInfo, allOptions, deviceType);//补充5
//
initGrid(context, displayInfo, displayOption, deviceType);
return displayOption.grid.name;
}
>1.getPredefinedDeviceProfiles
private static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context,
String gridName, @DeviceType int deviceType, boolean allowDisabledGrid) {
ArrayList<DisplayOption> profiles = new ArrayList<>();
//读取xml文件,进行解析,参考补充2
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)
&& GridOption.TAG_NAME.equals(parser.getName())) {
//解析tag (grid-option)解析成gridOption
GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser),
deviceType);
//isEnable的判断条件见补充3
if (gridOption.isEnabled || allowDisabledGrid) {
final int displayDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG
|| parser.getDepth() > displayDepth)
&& type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG) && "display-option".equals(
parser.getName())) {
//display optionn可能有多个
profiles.add(new DisplayOption(gridOption, context,
Xml.asAttributeSet(parser)));
}
}
}
}
}
} catch (IOException | XmlPullParserException e) {
}
ArrayList<DisplayOption> filteredProfiles = new ArrayList<>();
//gridname不为null的话,直接查找解析出来的数据
if (!TextUtils.isEmpty(gridName)) {
for (DisplayOption option : profiles) {
if (gridName.equals(option.grid.name)
&& (option.grid.isEnabled || allowDisabledGrid)) {
filteredProfiles.add(option);
}
}
}
//没找到对应的,把解析的都加进去
if (filteredProfiles.isEmpty()) {
for (DisplayOption option : profiles) {
if (option.canBeDefault) {
filteredProfiles.add(option);
}
}
}
if (filteredProfiles.isEmpty()) {
throw new RuntimeException("No display option with canBeDefault=true");
}
return filteredProfiles;
}
>2.device_profiles.xml
<grid-option
launcher:name="6_by_5"
launcher:numRows="5"
launcher:numColumns="6"
launcher:deviceCategory="tablet" >
<!--宽高完全匹配我们设备的时候会直接使用配置的属性,否则会动态计算,见补充5-->
<display-option
launcher:name="Tablet"
launcher:minWidthDps="900"
launcher:minHeightDps="820"
launcher:canBeDefault="true" />
</grid-option>
</profiles>
>3.isEnable的判断
-
上边的那个deviceCategory是tablet也就是2,下边的判断会返回true的
-
就是判断配置里读取的category是否和当前设备的type匹配
int deviceCategory = a.getInt(R.styleable.GridDisplayOption_deviceCategory, DEVICE_CATEGORY_ALL); isEnabled = (deviceType == TYPE_PHONE && ((deviceCategory & DEVICE_CATEGORY_PHONE) == DEVICE_CATEGORY_PHONE)) || (deviceType == TYPE_TABLET && ((deviceCategory & DEVICE_CATEGORY_TABLET) == DEVICE_CATEGORY_TABLET)) || (deviceType == TYPE_MULTI_DISPLAY && ((deviceCategory & DEVICE_CATEGORY_MULTI_DISPLAY) == DEVICE_CATEGORY_MULTI_DISPLAY));
//自定义属性
<attr name="deviceCategory" format="integer">
<!-- Enable on phone only -->
<flag name="phone" value="1" />
<!-- Enable on tablets only -->
<flag name="tablet" value="2" />
<!-- Enable on multi display devices only -->
<flag name="multi_display" value="4" />
</attr>
>4.initGrid
//窗口,这里横竖屏有3种
for (WindowBounds bounds : displayInfo.supportedBounds) {
//添加了3种DevicePorfile
localSupportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
.setIsMultiDisplay(deviceType == TYPE_MULTI_DISPLAY)
.setWindowBounds(bounds)
.setDotRendererCache(dotRendererCache)
.build());
//..
}
supportedProfiles = Collections.unmodifiableList(localSupportedProfiles);
>5.invDistWeightedInterpolate
- 根据屏幕尺寸,从上边读取的预定义的配置里找到最合适的,如果没找到,根据所有的points算出一个新的出来,
- 算法看不懂,暂不研究,具体看weight方法
- minWidthDps是配置里读取的dp值,
- 像素转dp算法,比如设备宽度是800px,density是200,那么dp值就是 800/(200/160),其中160是默认值
private static DisplayOption invDistWeightedInterpolate(
Info displayInfo, ArrayList<DisplayOption> points, @DeviceType int deviceType) {
int minWidthPx = Integer.MAX_VALUE;
int minHeightPx = Integer.MAX_VALUE;
//从支持的bounds里找出最小的宽和高
for (WindowBounds bounds : displayInfo.supportedBounds) {
boolean isTablet = displayInfo.isTablet(bounds);
if (isTablet && deviceType == TYPE_MULTI_DISPLAY) {
// For split displays, take half width per page
minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2);
minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
} else if (!isTablet && bounds.isLandscape()) {
// We will use transposed layout in this case
//手机横屏
minWidthPx = Math.min(minWidthPx, bounds.availableSize.y);
minHeightPx = Math.min(minHeightPx, bounds.availableSize.x);
} else {
//平板或者手机竖屏
minWidthPx = Math.min(minWidthPx, bounds.availableSize.x);
minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
}
}
//像素转换为DP值,见小节9
float width = dpiFromPx(minWidthPx, displayInfo.getDensityDpi());
float height = dpiFromPx(minHeightPx, displayInfo.getDensityDpi());
// Sort the profiles based on the closeness to the device size
//根据DisplayOption里的最小宽和高的DP值与上边算出的设备宽高值比较,按照差值排序
Collections.sort(points, (a, b) ->
Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
dist(width, height, b.minWidthDps, b.minHeightDps)));
//排序完取第一个,最接近的
DisplayOption closestPoint = points.get(0);
GridOption closestOption = closestPoint.grid;
float weights = 0;
//配置里的最小宽高和我们设备的宽高一模一样,直接返回,见补充5
//注意是dp值额
if (dist(width, height, closestPoint.minWidthDps, closestPoint.minHeightDps) == 0) {
//这个除非专门配置,默认的一般应该不会一样的。
return closestPoint;
}
//这里只复制了GridOption,具体逻辑见下边
DisplayOption out = new DisplayOption(closestOption);
for (int i = 0; i < points.size() && i < 3; ++i) {
DisplayOption p = points.get(i);
float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER/*5*/);
weights += w;
out.add(new DisplayOption().add(p).multiply(w));
}
out.multiply(1.0f / weights);
// Since the bitmaps are persisted, ensure that all bitmap sizes are not larger than
// predefined size to avoid cache invalidation
//iconSizes数组长度是4,存储了默认icon大小,横屏大小,多设备横屏以及竖屏大小,目前逻辑值都一样,都是默认值
for (int i = INDEX_DEFAULT; i < COUNT_SIZES; i++) {
//图片大小都设置成最小的那个值
out.iconSizes[i] = Math.min(out.iconSizes[i], closestPoint.iconSizes[i]);
}
return out;
}
>6.DisplayOption(grid)
就复制了gridOption的值,其他值都是空
DisplayOption(GridOption grid) {
this.grid = grid;
minWidthDps = 0;
minHeightDps = 0;
canBeDefault = false;
for (int i = 0; i < COUNT_SIZES; i++) {
iconSizes[i] = 0;
textSizes[i] = 0;
borderSpaces[i] = new PointF();
minCellSize[i] = new PointF();
allAppsCellSize[i] = new PointF();
allAppsIconSizes[i] = 0;
allAppsIconTextSizes[i] = 0;
allAppsBorderSpaces[i] = new PointF();
}
}
设置DisplayOption里的值
private DisplayOption add(DisplayOption p) {
for (int i = 0; i < COUNT_SIZES; i++) {
iconSizes[i] += p.iconSizes[i];
textSizes[i] += p.textSizes[i];
borderSpaces[i].x += p.borderSpaces[i].x;
borderSpaces[i].y += p.borderSpaces[i].y;
minCellSize[i].x += p.minCellSize[i].x;
minCellSize[i].y += p.minCellSize[i].y;
horizontalMargin[i] += p.horizontalMargin[i];
hotseatBarBottomSpace[i] += p.hotseatBarBottomSpace[i];
hotseatQsbSpace[i] += p.hotseatQsbSpace[i];
allAppsCellSize[i].x += p.allAppsCellSize[i].x;
allAppsCellSize[i].y += p.allAppsCellSize[i].y;
allAppsIconSizes[i] += p.allAppsIconSizes[i];
allAppsIconTextSizes[i] += p.allAppsIconTextSizes[i];
allAppsBorderSpaces[i].x += p.allAppsBorderSpaces[i].x;
allAppsBorderSpaces[i].y += p.allAppsBorderSpaces[i].y;
}
return this;
}
>7.dist
宽的差值和高的差值,算法是直角三角形的斜边
private static float dist(float x0, float y0, float x1, float y1) {
return (float) Math.hypot(x1 - x0, y1 - y0);
}
>8.weight
10万除以(d的5次冥)
private static float weight(float x0, float y0, float x1, float y1, float pow) {
float d = dist(x0, y0, x1, y1);
if (Float.compare(d, 0f) == 0) {
//这里应该不可能,为0说明两者的值一样,走不到这里,上边就直接返回closestPoint了
return Float.POSITIVE_INFINITY;
}
return (float) (WEIGHT_EFFICIENT / Math.pow(d, pow));
}
>9.dpiFromPx
public static float dpiFromPx(float size, int densityDpi) {
float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;//默认的是160dp
return (size / densityRatio);
}
2.3.writeToPrefs
默认配置数据处理好以后
public void writeToPrefs(Context context) {
LauncherPrefs.getPrefs(context).edit()
.putString(KEY_WORKSPACE_SIZE, mGridSizeString)
.putInt(KEY_HOTSEAT_COUNT, mNumHotseat)
.putInt(KEY_DEVICE_TYPE, mDeviceType)
.putString(KEY_DB_FILE, mDbFile)
.apply();
}
2.4.getDeviceProfile
public DeviceProfile getDeviceProfile(Context context) {
Resources res = context.getResources();
Configuration config = context.getResources().getConfiguration();
float screenWidth = config.screenWidthDp * res.getDisplayMetrics().density;
float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
int rotation = WindowManagerProxy.INSTANCE.get(context).getRotation(context);
return getBestMatch(screenWidth, screenHeight, rotation);
}
2.5.getBestMatch
public DeviceProfile getBestMatch(float screenWidth, float screenHeight, int rotation) {
DeviceProfile bestMatch = supportedProfiles.get(0);
float minDiff = Float.MAX_VALUE;
for (DeviceProfile profile : supportedProfiles) {
float diff = Math.abs(profile.widthPx - screenWidth)
+ Math.abs(profile.heightPx - screenHeight);
if (diff < minDiff) {
minDiff = diff;
bestMatch = profile;
} else if (diff == minDiff && profile.rotationHint == rotation) {
bestMatch = profile;
}
}
return bestMatch;
}
2.6.Info
>1.DisplayController.java
private DisplayController(Context context) {
//..
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context);
Context displayInfoContext = getDisplayInfoContext(display);
//构造方法里初始化一个Info
mInfo = new Info(displayInfoContext, wmProxy,
wmProxy.estimateInternalDisplayBounds(displayInfoContext));
}
>2.estimateInternalDisplayBounds
public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds(
Context displayInfoContext) {
CachedDisplayInfo info = getDisplayInfo(displayInfoContext).normalize();
//...
WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info);
ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>();
result.put(info, bounds);
return result;
}
>3.estimateWindowBounds
返回可能的4个方向的窗口尺寸数组
protected WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo displayInfo) {
//...
WindowBounds[] result = new WindowBounds[4];
Point tempSize = new Point();
for (int i = 0; i < 4; i++) {
int rotationChange = deltaRotation(rotation, i);
tempSize.set(displayInfo.size.x, displayInfo.size.y);
rotateSize(tempSize, rotationChange);
Rect bounds = new Rect(0, 0, tempSize.x, tempSize.y);
int navBarHeight, navbarWidth, statusBarHeight;
if (tempSize.y > tempSize.x) {
navBarHeight = navBarHeightPortrait;
navbarWidth = 0;
statusBarHeight = statusBarHeightPortrait;
} else {
navBarHeight = navBarHeightLandscape;
navbarWidth = navbarWidthLandscape;
statusBarHeight = statusBarHeightLandscape;
}
Rect insets = new Rect(safeCutout);
rotateRect(insets, rotationChange);
insets.top = Math.max(insets.top, statusBarHeight);
insets.bottom = Math.max(insets.bottom, navBarHeight);
if (i == Surface.ROTATION_270 || i == Surface.ROTATION_180) {
// On reverse landscape (and in rare-case when the natural orientation of the
// device is landscape), navigation bar is on the right.
insets.left = Math.max(insets.left, navbarWidth);
} else {
insets.right = Math.max(insets.right, navbarWidth);
}
result[i] = new WindowBounds(bounds, insets, i);
}
return result;
}
>4.Info构造方法
public Info(Context displayInfoContext,
WindowManagerProxy wmProxy,
Map<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) {
CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext);
normalizedDisplayInfo = displayInfo.normalize();
rotation = displayInfo.rotation;
currentSize = displayInfo.size;
cutout = displayInfo.cutout;
Configuration config = displayInfoContext.getResources().getConfiguration();
fontScale = config.fontScale;
densityDpi = config.densityDpi;
mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
navigationMode = wmProxy.getNavigationMode(displayInfoContext);
//perDisplayBoundsCache的value数组里有4个值,前边有贴初始化的代码
mPerDisplayBounds.putAll(perDisplayBoundsCache);
//...
//这里supportedBounds的结果是3个,原因是竖向的两个bounds里边的值一样,具体见下边WindowBounds
mPerDisplayBounds.values().forEach(
windowBounds -> Collections.addAll(supportedBounds, windowBounds));
}
>5.WindowBounds.java
可以看到,判断对象是否相等,就看bounds值和insets值,和参数rotationHint无关
public int hashCode() {
return Objects.hash(bounds, insets);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof WindowBounds)) {
return false;
}
WindowBounds other = (WindowBounds) obj;
return other.bounds.equals(bounds) && other.insets.equals(insets);
}
3.LauncherModel
LauncherModel(@NonNull final Context context, @NonNull final LauncherAppState app,
@NonNull final IconCache iconCache, @NonNull final AppFilter appFilter,
final boolean isPrimaryInstance) {
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
isPrimaryInstance);
}
3.1.getWriter
public ModelWriter getWriter(final boolean hasVerticalHotseat, final boolean verifyChanges,
@Nullable final Callbacks owner) {
return new ModelWriter(mApp.getContext(), this, mBgDataModel,
hasVerticalHotseat, verifyChanges, owner);
}
3.2.addCallbacksAndLoad
public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
synchronized (mLock) {
addCallbacks(callbacks);
return startLoader(new Callbacks[] { callbacks });
}
}
>2个地方用到
//launcher.java的onCreate方法
if (!mModel.addCallbacksAndLoad(this)) {
if (!internalStateHandled) {
// If we are not binding synchronously, pause drawing until initial bind complete,
// so that the system could continue to show the device loading prompt
mOnInitialBindListener = Boolean.FALSE::booleanValue;
}
}
//TaskbarViewController.java 的init方法
if (mActivity.isUserSetupComplete()) {
// Only load the callbacks if user setup is completed
LauncherAppState.getInstance(mActivity).getModel().addCallbacksAndLoad(mModelCallbacks);
}
3.3removeCallbacks
public void removeCallbacks(@NonNull final Callbacks callbacks) {
synchronized (mCallbacksList) {
Preconditions.assertUIThread();
if (mCallbacksList.remove(callbacks)) {
if (stopLoader()) {
// Rebind existing callbacks
startLoader();
}
}
}
}
>调用的地方
//多屏幕的时候也会调用,我们这里不考虑这种,
//一个是launcher的onDestroy
//一个是TaskbarViewController.java 的onDestroy方法,原因好像是TouchInteractionService.java这个服务会启动两次,所以第一次的会调用onDestroy方法
public void onDestroy() {
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
}
3.4.rebindCallbacks
public void rebindCallbacks() {
if (hasCallbacks()) {
startLoader();
}
}
3.5.forceReload
public void forceReload() {
synchronized (mLock) {
// Stop any existing loaders first, so they don't set mModelLoaded to true later
stopLoader();
mModelLoaded = false;
}
// Start the loader if launcher is already running, otherwise the loader will run,
// the next time launcher starts
if (hasCallbacks()) {
startLoader();
}
}
3.6.startLoader
private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
synchronized (mLock) {
// If there is already one running, tell it to stop.
boolean wasRunning = stopLoader();
//数据已经加载完成过并且当前没有加载数据
boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
if (callbacksList.length > 0) {
// Clear any pending bind-runnables from the synchronized load process.
for (Callbacks cb : callbacksList) {
//先调用callback的clear方法清除未执行的binds
MAIN_EXECUTOR.execute(cb::clearPendingBinds);
}
LoaderResults loaderResults = new LoaderResults(
mApp, mBgDataModel, mBgAllAppsList, callbacksList);
//刚开始走的是else
if (bindDirectly) {
//走这里说明数据已经加载完事了,这里直接同步返回数据
loaderResults.bindWorkspace(bindAllCallbacks);
// 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 {
//刚启动的时候走的是这里,异步加载数据
stopLoader();//先停掉已有的task
//开启新的task,就是个runnable
mLoaderTask = new LoaderTask(
mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
//异步执行,防止阻塞线程
MODEL_EXECUTOR.post(mLoaderTask);
}
}
}
return false;
}
>stopLoader
这个方法会修改task的状态为stop,方便跳出task,task里的任务每一步都会判断stop状态的
private boolean stopLoader() {
synchronized (mLock) {
LoaderTask oldTask = mLoaderTask;
mLoaderTask = null;
if (oldTask != null) {
oldTask.stopLocked();
return true;
}
return false;
}
}
>beginLoader
public LoaderTransaction beginLoader(@NonNull final LoaderTask task)
throws CancellationException {
return new LoaderTransaction(task);
}
>LoaderTransaction
private LoaderTransaction(@NonNull final LoaderTask task) throws CancellationException {
synchronized (mLock) {
if (mLoaderTask != task) {
throw new CancellationException("Loader already stopped");
}
mTask = task;
mIsLoaderTaskRunning = true;
mModelLoaded = false;
}
}
public void commit() {
synchronized (mLock) {
// Everything loaded bind the data.
mModelLoaded = true;
}
}
@Override
public void close() {
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
if (mLoaderTask == mTask) {
mLoaderTask = null;
}
mIsLoaderTaskRunning = false;
}
}
}
3.7.LoaderTask
public synchronized void stopLocked() {
mStopped = true;
this.notify();
}
//run方法里每一步都会判断stop状态,如果已经stop了,就直接抛出异常。
private synchronized void verifyNotStopped() throws CancellationException {
if (mStopped) {
throw new CancellationException("Loader stopped");
}
}
>run方法
这里会加载我们需要的各种数据,看名字大概就知道是啥数据了,具体的数据加载逻辑就不看了。
public void run() {
synchronized (this) {
// Skip fast if we are already stopped.
if (mStopped) {
return;
}
}
//实例化一个loader事务,控制task的状态
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
//这个是显示在桌面的shortcuts,就是长按app,选择一个shortcuts长按拖动到桌面
List<ShortcutInfo> allShortcuts = new ArrayList<>();
//#### 加载workspace页面需要的数据
try {
loadWorkspace(allShortcuts, memoryLogger);
} finally {
}
if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
verifyNotStopped();
sanitizeData();
}
verifyNotStopped();
//绑定数据,比如launcher页里给LauncherModel添加了callback,最终会调用对应的callback
mResults.bindWorkspace(true /* incrementBindId */);
mModelDelegate.workspaceLoadComplete();
sendFirstScreenActiveInstallsBroadcast();
// Take a break
waitForIdle();
verifyNotStopped();
// second step #### allapps页面的数据
List<LauncherActivityInfo> allActivityList;
try {
allActivityList = loadAllApps();
} finally {
}
verifyNotStopped();
mResults.bindAllApps();
verifyNotStopped();
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
setIgnorePackages(updateHandler);
//更新allapps图标
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.newInstance(mApp.getContext()),
mApp.getModel()::onPackageIconsUpdated);
verifyNotStopped();
//更新shortcuts图标
updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
mApp.getModel()::onPackageIconsUpdated);
// Take a break
waitForIdle();
verifyNotStopped();
// third step ****这里是所有的shortcuts,包括没显示在workspace上的
List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
verifyNotStopped();
mResults.bindDeepShortcuts();
verifyNotStopped();
updateHandler.updateIcons(allDeepShortcuts,
new ShortcutCachingLogic(), (pkgs, user) -> { });
// Take a break
waitForIdle();
verifyNotStopped();
// fourth step ****所有的小部件,一个app可能有多个
List<ComponentWithLabelAndIcon> allWidgetsList =
mBgDataModel.widgetsModel.update(mApp, null);
verifyNotStopped();
mResults.bindWidgets();
verifyNotStopped();
updateHandler.updateIcons(allWidgetsList,
new ComponentWithIconCachingLogic(mApp.getContext(), true),
mApp.getModel()::onWidgetLabelsUpdated);
// fifth step 文件夹
loadFolderNames();
verifyNotStopped();
updateHandler.finish();
mModelDelegate.modelLoadComplete();
transaction.commit();//这里表示一次task完成了,修改状态。
memoryLogger.clearLogs();
} catch (CancellationException e) {
//上边有很多verifyNotStopped,如果中途stop的话会抛出异常,走这里,任务结束
} catch (Exception e) {
memoryLogger.printLogs();
throw e;
} finally {
}
}
>loadWorkspace
private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger logger) {
loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI,
null /* selection */, logger);
}
>CONTENT_URI
可以看到,查找的是favorites表的数据
/**
* The content:// style URL for "favorites" table
*/
public static final Uri CONTENT_URI = Uri.parse("content://"
+ LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
3.8.shortcuts种类
//这种包括动态添加的,清单文件里静态的,以及固定在桌面的
public static final int ALL = ShortcutQuery.FLAG_MATCH_DYNAMIC
| ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_PINNED;
//动态的以及静态的,长按某个app,会查找这个app支持的shortcuts,会用到这个
public static final int PUBLISHED = ShortcutQuery.FLAG_MATCH_DYNAMIC
| ShortcutQuery.FLAG_MATCH_MANIFEST;
//这种是固定在workspace上的
public static final int PINNED = ShortcutQuery.FLAG_MATCH_PINNED;
3.9LoaderResults
还记得launcherModel里startTask的时候,里边初始化了一个laoderResults
LoaderResults loaderResults = new LoaderResults(
mApp, mBgDataModel, mBgAllAppsList, callbacksList);
然后在LoaderTask里会调用各种方法绑定数据
>bindWorkspace
public void bindWorkspace(boolean incrementBindId) {
// Save a copy of all the bg-thread collections
ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
final IntArray orderedScreenIds = new IntArray();
ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
synchronized (mBgDataModel) {
workspaceItems.addAll(mBgDataModel.workspaceItems);
appWidgets.addAll(mBgDataModel.appWidgets);
orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
mBgDataModel.extraItems.forEach(extraItems::add);
if (incrementBindId) {
mBgDataModel.lastBindId++;
}
mMyBindingId = mBgDataModel.lastBindId;
}
for (Callbacks cb : mCallbacksList) {
new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
}
}
>bindAllApps
public void bindAllApps() {
// shallow copy
AppInfo[] apps = mBgAllAppsList.copyData();
int flags = mBgAllAppsList.getFlags();
executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor);
}
3.10.WorkspaceBinder
WorkspaceBinder(Callbacks callbacks,
Executor uiExecutor,
LauncherAppState app,
BgDataModel bgDataModel,
int myBindingId,
ArrayList<ItemInfo> workspaceItems,
ArrayList<LauncherAppWidgetInfo> appWidgets,
ArrayList<FixedContainerItems> extraItems,
IntArray orderedScreenIds) {
mCallbacks = callbacks;
mUiExecutor = uiExecutor;
mApp = app;
mBgDataModel = bgDataModel;
mMyBindingId = myBindingId;
mWorkspaceItems = workspaceItems;
mAppWidgets = appWidgets;
mExtraItems = extraItems;
mOrderedScreenIds = orderedScreenIds;
}
>bind
private void bind() {
final IntSet currentScreenIds =
mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds);
//当前页和其他页面数据分离,各自包含2种数据类型
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
//根据容器id以及类型进行分类
filterCurrentWorkspaceItems(currentScreenIds, mWorkspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
//同上,这里分离的是小部件
filterCurrentWorkspaceItems(currentScreenIds, mAppWidgets, currentAppWidgets,
otherAppWidgets);
//排序
final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
sortWorkspaceItemsSpatially(idp, otherWorkspaceItems);
//即将开始进行绑定数据,回调告诉一下
executeCallbacksTask(c -> {
c.clearPendingBinds();
c.startBinding();
}, mUiExecutor);
// Bind workspace screens ,绑定页面,可以理解成创建容器
executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);
// Load items on the current page.加载数据到当前页面
bindWorkspaceItems(currentWorkspaceItems, mUiExecutor);
bindAppWidgets(currentAppWidgets, mUiExecutor);
mExtraItems.forEach(item ->
executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));
RunnableList pendingTasks = new RunnableList();
Executor pendingExecutor = pendingTasks::add;
//绑定其他页面数据
bindWorkspaceItems(otherWorkspaceItems, pendingExecutor);
bindAppWidgets(otherAppWidgets, pendingExecutor);
//绑定数据完成的回调
executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor);
//其他一些相关的回调
pendingExecutor.execute(
() -> {
MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.resumeModelPush(FLAG_LOADER_RUNNING);
});
executeCallbacksTask(
c -> {
MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
c.onInitialBindComplete(currentScreenIds, pendingTasks);
}, mUiExecutor);
mCallbacks.bindStringCache(mBgDataModel.stringCache.clone());
}
4.LauncherAppState.java
public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
mContext = context;
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
mIconProvider = new LauncherIconProvider(context);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
iconCacheFileName, mIconProvider);
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
iconCacheFileName != null);
mOnTerminateCallback.add(mIconCache::close);
}
//LauncherAppState
public LauncherAppState(Context context) {
this(context, LauncherFiles.APP_ICONS_DB);
//监听配置的改变,重新加载launcher
mInvariantDeviceProfile.addOnChangeListener(modelPropertiesChanged -> {
if (modelPropertiesChanged) {
refreshAndReloadLauncher();
}
});
mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
SimpleBroadcastReceiver modelChangeReceiver =
new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
if (FeatureFlags.IS_STUDIO_BUILD) {
modelChangeReceiver.register(mContext, Context.RECEIVER_EXPORTED, ACTION_FORCE_ROLOAD);
}
mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
CustomWidgetManager.INSTANCE.get(mContext)
.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext)
.addUserChangeListener(mModel::forceReload);
mOnTerminateCallback.add(userChangeListener::close);
IconObserver observer = new IconObserver();
SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
observer, MODEL_EXECUTOR.getHandler());
mOnTerminateCallback.add(iconChangeTracker::close);
MODEL_EXECUTOR.execute(observer::verifyIconChanged);
SharedPreferences prefs = LauncherPrefs.getPrefs(mContext);
prefs.registerOnSharedPreferenceChangeListener(observer);
mOnTerminateCallback.add(
() -> prefs.unregisterOnSharedPreferenceChangeListener(observer));
InstallSessionTracker installSessionTracker =
InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
mOnTerminateCallback.add(installSessionTracker::unregister);
// Register an observer to rebind the notification listener when dots are re-enabled.
SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
SettingsCache.OnChangeListener notificationLister = this::onNotificationSettingsChanged;
settingsCache.register(NOTIFICATION_BADGING_URI, notificationLister);
onNotificationSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI));
mOnTerminateCallback.add(() ->
settingsCache.unregister(NOTIFICATION_BADGING_URI, notificationLister));
}
4.1.refreshAndReloadLauncher
private void refreshAndReloadLauncher() {
LauncherIcons.clearPool();
mIconCache.updateIconParams(
mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize);
mModel.forceReload();
}
5.Launcher.java
5.1.onCreate
super.onCreate(savedInstanceState);
LauncherAppState app = LauncherAppState.getInstance(this);
mModel = app.getModel();
mRotationHelper = new RotationHelper(this);
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
initDeviceProfile(idp);
idp.addOnChangeListener(this);
mSharedPrefs = LauncherPrefs.getPrefs(this);
mIconCache = app.getIconCache();
mAccessibilityDelegate = createAccessibilityDelegate();
initDragController();
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new StateManager<>(this, NORMAL);
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
// TODO: move the SearchConfig to SearchState when new LauncherState is created.
mBaseSearchConfig = new BaseSearchConfig();
mAppWidgetManager = new WidgetManagerHelper(this);
mAppWidgetHolder = createAppWidgetHolder();
mAppWidgetHolder.startListening();
setupViews();
crossFadeWithPreviousAppearance();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
//..
mStateManager.reapplyState();
//..
//这个会启动loadtask加载数据
if (!mModel.addCallbacksAndLoad(this)) {
if (!internalStateHandled) {
// If we are not binding synchronously, pause drawing until initial bind complete,
// so that the system could continue to show the device loading prompt
mOnInitialBindListener = Boolean.FALSE::booleanValue;
}
}
// For handling default keys
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
setContentView(getRootView());
if (mOnInitialBindListener != null) {
getRootView().getViewTreeObserver().addOnPreDrawListener(mOnInitialBindListener);
}
getRootView().dispatchInsets();
// Listen for broadcasts
registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
mOverlayManager = getDefaultOverlay();
PluginManagerWrapper.INSTANCE.get(this).addPluginListener(this,
LauncherOverlayPlugin.class, false /* allowedMultiple */);
mRotationHelper.initialize();
TraceHelper.INSTANCE.endSection(traceToken);
mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener(
() -> getStateManager().goToState(NORMAL));
if (Utilities.ATLEAST_R) {
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
setTitle(R.string.home_screen);
}
6.idp数据相关
6.1.
LauncherAppState.getIDP(mContext)
7.总结
- InvariantDeviceProfile 通用配置文件数据的获取,之后是屏幕相关的DeviceProfile
- 数据的加载是通过LauncherModel的,在launcher和其他页面添加了callback以后,就会调用对应的方法,开启一个LoaderTask获取数据,之后通过callback把数据分发出去