本文我们来看下Launcher3应用的安装与卸载的处理流程。应用的安装与卸载是通过PackageManagerService来完成的,在成功安装或者卸载之后它是怎么通知Launcher3更新的呢?关于PackageManagerService那一块我们不作深入分析,我们知道LauncherModel是Launcher3的数据中心继承于BroadcastReceiver并且实现了LauncherAppsCompat.OnAppsChangedCallbackCompat接口,我们来看下接口的具体内容:
packages\apps\Launcher3\src\com\android\launcher3\compat\LauncherAppsCompat.java
public interface OnAppsChangedCallbackCompat {
void onPackageRemoved(String packageName, UserHandleCompat user);
void onPackageAdded(String packageName, UserHandleCompat user);
void onPackageChanged(String packageName, UserHandleCompat user);
void onPackagesAvailable(String[] packageNames, UserHandleCompat user, boolean replacing);
void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing);
void onPackagesSuspended(String[] packageNames, UserHandleCompat user);
void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user);
void onShortcutsChanged(String packageName, List shortcuts,
UserHandleCompat user);
}
可以看到这个接口包含了所有与apk有关的回调。接下来我们来看下LauncherAppsCompat是怎么注册这个接口的。
packages\apps\Launcher3\src\com\android\launcher3\Launcher.java
@Override
protected void onCreate(Bundle savedInstanceState) {
...
LauncherAppState app = LauncherAppState.getInstance();
}
packages\apps\Launcher3\src\com\android\launcher3\LauncherAppState.java
private LauncherAppState() {
...
LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);
}
packages\apps\Launcher3\src\com\android\launcher3\compat\LauncherAppsCompat.java
public static LauncherAppsCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
if (Utilities.ATLEAST_LOLLIPOP) {
//在V21之后是通过回调来通知应用更新的
sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
} else {
//在LOLLIPOP之前是通过注册广播来通知Launcher3应用更新的
sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
}
}
return sInstance;
}
}
通过代码我们看到不同的版本是通过不同的方式通知应用更新的。LOLLIPOP之前是用广播,之后采取回调的方式,这个过程有机会再详细分析。而在LauncherAppsCompat中通过addOnAppsChangedCallback()将mModel注册回调。所以当有应用安装成功后就会调用LauncherModel的onPackageAdded()方法。
packages\apps\Launcher3\src\com\android\launcher3\LauncherModel.java
@Override
public void onPackageChanged(String packageName, UserHandleCompat user) {
int op = PackageUpdatedTask.OP_UPDATE;
enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
user));
}
代码中直接新建了一个PackageUpdatedTask任务,我们来详细分析下这个任务的run()方法。
packages\apps\Launcher3\src\com\android\launcher3\LauncherModel.java
private class PackageUpdatedTask implements Runnable {
...
public void run() {
if (!mHasLoaderCompletedOnce) {
// Loader has not yet run.
return;
}
final Context context = mApp.getContext();
final String[] packages = mPackages;
final int N = packages.length;
FlagOp flagOp = FlagOp.NO_OP;
StringFilter pkgFilter = StringFilter.of(new HashSet<>(Arrays.asList(packages)));
switch (mOp) {
case OP_ADD: {
for (int i=0; i added = null;
ArrayList modified = null;
final ArrayList removedApps = new ArrayList();
if (mBgAllAppsList.added.size() > 0) {
added = new ArrayList<>(mBgAllAppsList.added);
mBgAllAppsList.added.clear();
}
...
final HashMap addedOrUpdatedApps = new HashMap<>();
if (added != null) {
addAppsToAllApps(context, added);
for (AppInfo ai : added) {
addedOrUpdatedApps.put(ai.componentName, ai);
}
}
...
// Update shortcut infos
if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
final ArrayList updatedShortcuts = new ArrayList();
final ArrayList removedShortcuts = new ArrayList();
final ArrayList widgets = new ArrayList();
synchronized (sBgLock) {
for (ItemInfo info : sBgItemsIdMap) {
if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
...
if (infoUpdated || shortcutUpdated) {
updatedShortcuts.add(si);
}
if (infoUpdated) {
updateItemInDatabase(context, si);
}
}
...
}
bindUpdatedShortcuts(updatedShortcuts, removedShortcuts, mUser);
if (!removedShortcuts.isEmpty()) {
deleteItemsFromDatabase(context, removedShortcuts);
}
...
}
....
// Notify launcher of widget update. From marshmallow onwards we use AppWidgetHost to
// get widget update signals.
if (!Utilities.ATLEAST_MARSHMALLOW &&
(mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) {
final Callbacks callbacks = getCallback();
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
callbacks.notifyWidgetProvidersChanged();
}
}
});
}
}
}
代码很长我们只抽取了关键代码。对于OP_ADD操作mIconCache列表,加入到mBgAllAppsList中,然后调用addAppsToAllApps函数更新应用列表。关于应用列表更新的还请查看另一篇博客。再通过一个循环更新所有的桌面图标,如果有更新的话就保存到数据库,最后发出通知更新widget组件。卸载的代码也是在这个函数里面,由于流程类似还请大家自行分析。
由于本人知识水平有限,本文难免有写的不对的地方,欢迎大家指正。