Android Launcher3应用卸载后桌面图标及快捷方式的删除流程

 

首先根据Launcher3的源码查找卸载后的图标删除流程,看看它在卸载后做了那些事。根据源码查找到LauncherAppState类的构造方法中有个叫LauncherAppsCompat的类,它监听着APP的变化,并且向它注册了一个callback:

LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);

这里的mModel就是LauncherModel对象,它实现了OnAppsChangedCallbackCompat接口。

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);
}

OnAppsChangedCallbackCompat接口有各种回调,其中onPackageRemoved方法就是卸载某一个APK时会回调的方法。紧接着我们看看它在LauncherModel里的实现。

@Override
public void onPackageRemoved(String packageName, UserHandleCompat user) {
    int op = PackageUpdatedTask.OP_REMOVE;
    enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }, user));
}

public void enqueueModelUpdateTask(ModelUpdateTask task) {
     task.init(mApp, this, sBgDataModel, mBgAllAppsList, mUiExecutor);
     runOnWorkerThread(task);
}

它启动了一个叫PackageUpdatedTask的Runnable,我们看看run()方法里面干了些什么。run()方法里面做了很多事情,这里我们只关心卸载相关的逻辑。

switch (mOp) {
    case OP_ADD: {
        ...........................
        break;
    }
    case OP_UPDATE:
       ............................
        break;
    case OP_REMOVE: {
        ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
        if (heuristic != null) {
            heuristic.processPackageRemoved(mPackages);
        }
        for (int i=0; i<N; i++) {
            if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
            mIconCache.removeIconsForPkg(packages[i], mUser);
        }
        // Fall through
    }
    //注意:这里并没有break,它是直接往下走的
    case OP_UNAVAILABLE:
        for (int i=0; i<N; i++) {
            if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
            mBgAllAppsList.removePackage(packages[i], mUser);
            mApp.getWidgetCache().removePackage(packages[i], mUser);
        }
        break;
}
............................
final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
............................
if (mBgAllAppsList.removed.size() > 0) {
    removedApps.addAll(mBgAllAppsList.removed);
    mBgAllAppsList.removed.clear();
}

这段逻辑特别要注意的地方是switch里面的OP_REMOVE处理,它是没有break的,它是直接走进了OP_UNAVAILABLE逻辑中,在这里它把这个卸载的应用从所有应用列表中删除mBgAllAppsList.removePackage(packages[i], mUser);,紧接着下面创建了一个removedApps的list存放着卸载数据。这个数据是在 mBgAllAppsList.removePackage(packages[i], mUser);中被添加到mBgAllAppsList.removed列表中的。

/**
 * Remove the apps for the given apk identified by packageName.
 */
public void removePackage(String packageName, UserHandleCompat user) {
    final List<AppInfo> data = this.data;
    for (int i = data.size() - 1; i >= 0; i--) {
        AppInfo info = data.get(i);
        final ComponentName component = info.intent.getComponent();
        if (info.user.equals(user) && packageName.equals(component.getPackageName())) {
            removed.add(info);
            data.remove(i);
        }
    }
}

把卸载的数据放入一个列表存起来干嘛呢?我们继续往下看,中间有一大段是新增和修改APP的处理逻辑,我们直接略过,我们依然只看卸载相关。

       final HashSet<String> removedPackages = new HashSet<>();
        final HashSet<ComponentName> removedComponents = new HashSet<>();
        if (mOp == OP_REMOVE) {
            // Mark all packages in the broadcast to be removed
            Collections.addAll(removedPackages, packages);

            // No need to update the removedComponents as
            // removedPackages is a super-set of removedComponents
        } else if (mOp == OP_UPDATE) {
            // Mark disabled packages in the broadcast to be removed
          .....................................
        }

        if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
            ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
                    .or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
                    .and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));//
            deleteAndBindComponentsRemoved(removeMatch); //关键代码1

            // Remove any queued items from the install queue
            InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
        }

        if (!removedApps.isEmpty()) {    
            // Remove corresponding apps from All-Apps
            scheduleCallbackTask(new CallbackTask() {
                @Override
                public void execute(Callbacks callbacks) {
                    callbacks.bindAppInfosRemoved(removedApps);//关键代码2
                }
            });
        }

1)首先分析ItemInfoMatcher 是ItemInfo 匹配器,这个匹配器非常有用。

  

/**
 * A utility class to check for {@link ItemInfo}
 */
public abstract class ItemInfoMatcher {

    public abstract boolean matches(ItemInfo info, ComponentName cn);

    /**
     * Filters {@param infos} to those satisfying the {@link #matches(ItemInfo, ComponentName)}.
     */
    public final HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) {
        HashSet<ItemInfo> filtered = new HashSet<>();
        for (ItemInfo i : infos) {
            if (i instanceof ShortcutInfo) {
                ShortcutInfo info = (ShortcutInfo) i;
                ComponentName cn = info.getTargetComponent();
                if (cn != null && matches(info, cn)) {
                    filtered.add(info);
                }
            } else if (i instanceof FolderInfo) {
                FolderInfo info = (FolderInfo) i;
                for (ShortcutInfo s : info.contents) {
                    ComponentName cn = s.getTargetComponent();
                    if (cn != null && matches(s, cn)) {
                        filtered.add(s);
                    }
                }
            } else if (i instanceof LauncherAppWidgetInfo) {
                LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
                ComponentName cn = info.providerName;
                if (cn != null && matches(info, cn)) {
                    filtered.add(info);
                }
            }
        }
        return filtered;
    }

    /**
     * Returns a new matcher with returns true if either this or {@param matcher} returns true.
     */
    public ItemInfoMatcher or(final ItemInfoMatcher matcher) {
       final ItemInfoMatcher that = this;
        return new ItemInfoMatcher() {
            @Override
            public boolean matches(ItemInfo info, ComponentName cn) {
                return that.matches(info, cn) || matcher.matches(info, cn);
            }
        };
    }

    /**
     * Returns a new matcher with returns true if both this and {@param matcher} returns true.
     */
    public ItemInfoMatcher and(final ItemInfoMatcher matcher) {
        final ItemInfoMatcher that = this;
        return new ItemInfoMatcher() {
            @Override
            public boolean matches(ItemInfo info, ComponentName cn) {
                return that.matches(info, cn) && matcher.matches(info, cn);
            }
        };
    }

    /**
     * Returns a new matcher which returns the opposite boolean value of the provided
     * {@param matcher}.
     */
    public static ItemInfoMatcher not(final ItemInfoMatcher matcher) {
        return new ItemInfoMatcher() {
            @Override
            public boolean matches(ItemInfo info, ComponentName cn) {
                return !matcher.matches(info, cn);
            }
        };
    }

    public static ItemInfoMatcher ofUser(final UserHandle user) {
        return new ItemInfoMatcher() {
            @Override
            public boolean matches(ItemInfo info, ComponentName cn) {
                return info.user.equals(user);
            }
        };
    }

    public static ItemInfoMatcher ofComponents(
            final HashSet<ComponentName> components, final UserHandle user) {
        return new ItemInfoMatcher() {
            @Override
            public boolean matches(ItemInfo info, ComponentName cn) {
                return components.contains(cn) && info.user.equals(user);
            }
        };
    }

    public static ItemInfoMatcher ofPackages(
            final HashSet<String> packageNames, final UserHandle user) {
        return new ItemInfoMatcher() {
            @Override
            public boolean matches(ItemInfo info, ComponentName cn) {
                return packageNames.contains(cn.getPackageName()) && info.user.equals(user);
            }
        };
    }

    public static ItemInfoMatcher ofShortcutKeys(final HashSet<ShortcutKey> keys) {
        return new ItemInfoMatcher() {
            @Override
            public boolean matches(ItemInfo info, ComponentName cn) {
                return info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
                        keys.contains(ShortcutKey.fromItemInfo(info));
            }
        };
    }

    public static ItemInfoMatcher ofItemIds(
            final LongArrayMap<Boolean> ids, final Boolean matchDefault) {
        return new ItemInfoMatcher() {
            @Override
            public boolean matches(ItemInfo info, ComponentName cn) {
                return ids.get(info.id, matchDefault);
            }
        };
    }
}

2、关键代码1 - deleteAndBindComponentsRemoved

    

    public void deleteAndBindComponentsRemoved(final ItemInfoMatcher matcher) {
        getModelWriter().deleteItemsFromDatabase(matcher);

        // Call the components-removed callback
        scheduleCallbackTask(new CallbackTask() {
            @Override
            public void execute(Callbacks callbacks) {
                callbacks.bindWorkspaceComponentsRemoved(matcher);
            }
        });
    }
ModelWriter 处理模式更新的类; deleteItemsFromDatabase 按照匹配过滤后的项删除.
    /**
     * Removes all the items from the database matching {@param matcher}.
     */
    public void deleteItemsFromDatabase(ItemInfoMatcher matcher) {
        deleteItemsFromDatabase(matcher.filterItemInfos(mBgDataModel.itemsIdMap));
    }

    /**
     * Removes the specified items from the database
     */
    public void deleteItemsFromDatabase(final Iterable<? extends ItemInfo> items) {
        ModelVerifier verifier = new ModelVerifier();

        mWorkerExecutor.execute(() -> {
            for (ItemInfo item : items) {
                final Uri uri = Favorites.getContentUri(item.id);
                mContext.getContentResolver().delete(uri, null, null);

                mBgDataModel.removeItem(mContext, item);
                verifier.verifyModel();
            }
        });
    }

3)关键代码2  --callbacks.bindAppInfosRemoved(removedApps);

     这段代码在调度任务中执行的内容; 而这个任务获取一个callback; 这个call有时谁注册的呢?根据跟踪代码;

 scheduleCallbackTask(new CallbackTask() {
                @Override
                public void execute(Callbacks callbacks) {
                    callbacks.bindAppInfosRemoved(removedApps);//关键代码2
                }
            });   

 /**
     * Schedules a {@param task} to be executed on the current callbacks.
     */
    public final void scheduleCallbackTask(final CallbackTask task) {
        final Callbacks callbacks = mModel.getCallback();
        mUiExecutor.execute(() -> {
            Callbacks cb = mModel.getCallback();
            if (callbacks == cb && cb != null) {
                task.execute(callbacks);   --->执行关键代码2
            }
        });
    }

根据追踪是在LauncherAppState类中setLauncher方法中通过mModel.initialize(launcher);设置的Callbacks,实现接口的是Launcher类,那我们来看看里面是怎么实现的 

    LauncherAppState.java中

    LauncherModel setLauncher(Launcher launcher) {
        getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
        mModel.initialize(launcher);
        return mModel;
    }

    
   LauncherModel.java
     /**
     * Set this as the current Launcher activity object for the loader.
     */
    public void initialize(Callbacks callbacks) {
        synchronized (mLock) {
            Preconditions.assertUIThread();
            mCallbacks = new WeakReference<>(callbacks);
        }
    }

我们看在Launcher.java中如何实现的呢

   @Override
    public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
        mAppsView.getAppsStore().removeApps(appInfos);
    }

 AllappStore.java

    /**
     * Removes some apps from the list.
     */
    public void removeApps(List<AppInfo> apps) {
        for (AppInfo app : apps) {
            mComponentToAppMap.remove(app.toComponentKey());
        }
        notifyUpdate();
    }


    private void notifyUpdate() {
        if (mDeferUpdates) {
            mUpdatePending = true;
            return;
        }
        int count = mUpdateListeners.size();
        for (int i = 0; i < count; i++) {
            mUpdateListeners.get(i).onAppsUpdated();
        }
    }

  notifyUpdate通知更新

  经过代码跟踪

 AllAppsContainerView 构造函数中添加

mAllAppsStore.addUpdateListener(this::onAppsUpdated);

注意:

{

  ::是java8 中新引入的运算符

  • Class::function的时候function是属于Class的,应该是静态方法。
  • this::function的funtion是属于这个对象的。

}

    private void onAppsUpdated() {
        if (FeatureFlags.ALL_APPS_TABS_ENABLED) {
            boolean hasWorkApps = false;
            for (AppInfo app : mAllAppsStore.getApps()) {
                if (mWorkMatcher.matches(app, null)) {
                    hasWorkApps = true;
                    break;
                }
            }
            rebindAdapters(hasWorkApps);
        }
    }

    private void rebindAdapters(boolean showTabs, boolean force) {
        if (showTabs == mUsingTabs && !force) {
            return;
        }
        replaceRVContainer(showTabs);
        mUsingTabs = showTabs;

        mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
        mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.WORK].recyclerView);

        if (mUsingTabs) {
            mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
            mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
            onTabChanged(mViewPager.getNextPage());
        } else {
            mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
            mAH[AdapterHolder.WORK].recyclerView = null;
        }
        setupHeader();

        mAllAppsStore.registerIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
        mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
    }
AlphabeticalAppsList 实现了AllAppsStore.OnUpdateListener 并在构造函数中添加
mAllAppsStore.addUpdateListener(this);
   /**
     * Updates internals when the set of apps are updated.
     */
    @Override
    public void onAppsUpdated() {
        // Sort the list of apps
        mApps.clear();

        for (AppInfo app : mAllAppsStore.getApps()) {
            if (mItemFilter == null || mItemFilter.matches(app, null) || hasFilter()) {
                mApps.add(app);
            }
        }

        Collections.sort(mApps, mAppNameComparator);

        // As a special case for some languages (currently only Simplified Chinese), we may need to
        // coalesce sections
        Locale curLocale = mLauncher.getResources().getConfiguration().locale;
        boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
        if (localeRequiresSectionSorting) {
            // Compute the section headers. We use a TreeMap with the section name comparator to
            // ensure that the sections are ordered when we iterate over it later
            TreeMap<String, ArrayList<AppInfo>> sectionMap = new TreeMap<>(new LabelComparator());
            for (AppInfo info : mApps) {
                // Add the section to the cache
                String sectionName = getAndUpdateCachedSectionName(info.title);

                // Add it to the mapping
                ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
                if (sectionApps == null) {
                    sectionApps = new ArrayList<>();
                    sectionMap.put(sectionName, sectionApps);
                }
                sectionApps.add(info);
            }

            // Add each of the section apps to the list in order
            mApps.clear();
            for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
                mApps.addAll(entry.getValue());
            }
        } else {
            // Just compute the section headers for use below
            for (AppInfo info : mApps) {
                // Add the section to the cache
                getAndUpdateCachedSectionName(info.title);
            }
        }

        // Recompose the set of adapter items from the current set of apps
        updateAdapterItems();
    }


参考:https://www.jianshu.com/p/67d82d56ca1d ,并根据最新代码做了修改.
 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值