Android Framework-Launcher-数据的加载

Launcher 在我个人看来是简单且又复杂的一个模块。 简单是只要认真看,基本能看通UI部分大体逻辑,稍微动手改一下,就能看到效果,成就感大大滴有。 复杂是和SystemUI之间强绑定,看起来让我这菜狗CPU嗡嗡的。

Launcher 继承StatefulActivity 实现Callbacks 涉及类如下

  • LauncherModel
  • LauncherAppState
  • LoaderTask
  • LoaderResults

进入LauncheronCreate 看一下

java 复制代码
//packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
protected void onCreate(Bundle savedInstanceState) {

LauncherAppState app = LauncherAppState.getInstance(this);
mModel = app.getModel();

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

}

LauncherAppState 采用单例模式 ,在双构造里面引入了老演员InvariantDeviceProfile。 到现在大家不用理解太多。反正知道LauncherAppState里面是嘛都有啊~。

java 复制代码
public LauncherAppState(Context context) {
    this(context, LauncherFiles.APP_ICONS_DB);
}

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);
            //出来吧我的model
    mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
            iconCacheFileName != null);
    mOnTerminateCallback.add(mIconCache::close);
}

LauncherModel

java 复制代码
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);
}

LoaderTask其中的mModelDelegate 是使用的 LauncherModel 传递过来的单例对象。

java 复制代码
public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
        ModelDelegate modelDelegate, LoaderResults results) {
    mApp = app;
    mBgAllAppsList = bgAllAppsList;
    mBgDataModel = dataModel;
    mModelDelegate = modelDelegate;
    mResults = results;

    mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
    mUserManager = mApp.getContext().getSystemService(UserManager.class);
    mUserCache = UserCache.INSTANCE.get(mApp.getContext());
    mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
    mIconCache = mApp.getIconCache();
}

我们直接进入mModel.addCallbacksAndLoad 这段代码。

java 复制代码
public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
    synchronized (mLock) {
        addCallbacks(callbacks);
        return startLoader(new Callbacks[] { callbacks });

    }
}

private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {

//省略部分代码
//调用新的执行之前会将老的停掉
                     stopLoader();
                mLoaderTask = new LoaderTask(
                        mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
                MODEL_EXECUTOR.post(mLoaderTask);
                
                //省略部分代码
}

为了避免重复执行代码,先 LoaderTask 继承了Runnable, 所以直接看run 方法。 允许我可耻的删除部分代码,我们只需要知道在该run 方法里面执行了5步

  • loadWorkspace 加载workSpace 数据,更新。
  • loadAllApps 加载所有app,更新。
  • loadDeepShortcuts 快捷方式,就是长按出来的那个小列表,更新。
  • allWidgetsList 加载小组件,更新。
  • loadFolderNames 加载文件夹,更新。
java 复制代码
   public void run() {
        synchronized (this) {
           //做了判断 如果stop没停止就不能开始
            if (mStopped) {
                return;
            }
        }

     
        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            List<ShortcutInfo> allShortcuts = new ArrayList<>();
          
            try {
                loadWorkspace(allShortcuts, memoryLogger);
            } finally {
                Trace.endSection();
            }
           

          //绑定
            mResults.bindWorkspace(true /* incrementBindId */);
            logASplit(logger, "bindWorkspace");

          //完成workSpace
            mModelDelegate.workspaceLoadComplete();


         
            // second step
            
            List<LauncherActivityInfo> allActivityList;
            try {
               allActivityList = loadAllApps();
            } finally {
                Trace.endSection();
            }
             //
            mResults.bindAllApps();
            logASplit(logger, "bindAllApps");
             
          //更新icon
            updateHandler.updateIcons(allActivityList,
                    LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                    mApp.getModel()::onPackageIconsUpdated);
        

       
    
            // third step
            List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
        
            verifyNotStopped();
            mResults.bindDeepShortcuts();
          
            updateHandler.updateIcons(allDeepShortcuts,
                    new ShortcutCachingLogic(), (pkgs, user) -> { });

            // Take a break
        

            // fourth step
            List<ComponentWithLabelAndIcon> allWidgetsList =
                    mBgDataModel.widgetsModel.update(mApp, null);
           
            mResults.bindWidgets();
          
           

            updateHandler.updateIcons(allWidgetsList,
                    new ComponentWithIconCachingLogic(mApp.getContext(), true),
                    mApp.getModel()::onWidgetLabelsUpdated);
           
            // fifth step
            loadFolderNames();

            verifyNotStopped();
            updateHandler.finish();
            logASplit(logger, "finish icon update");

            mModelDelegate.modelLoadComplete();
            transaction.commit();
          
        } 

注意在每个加载完成的时候都会调用一下对应的mResults.doxxxx(); 我很好奇bindAllApps 干了啥

java 复制代码
public void bindAllApps() {
    // shallow copy
    AppInfo[] apps = mBgAllAppsList.copyData();
    int flags = mBgAllAppsList.getFlags();
    executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor);
}



protected void executeCallbacksTask(CallbackTask task, Executor executor) {
    executor.execute(() -> {
        if (mMyBindingId != mBgDataModel.lastBindId) {
            Log.d(TAG, "Too many consecutive reloads, skipping obsolete data-bind");
            return;
        }
        for (Callbacks cb : mCallbacksList) {
            task.execute(cb);
        }
    });
}

okok,我们从上面代码知道Launcheroncreart() 方法里面把this 传给了LaucherMode,然后传给了LoaderResult,给到了作为参数构建了LoaderTask。 所以这里其实会回调回LauncherbindAllApplications

java 复制代码
@TargetApi(Build.VERSION_CODES.S)
public void bindAllApplications(AppInfo[] apps, int flags) {
    mAppsView.getAppsStore().setApps(apps, flags);
    PopupContainerWithArrow.dismissInvalidPopup(this);
    if (Utilities.ATLEAST_S) {
        Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
                DISPLAY_ALL_APPS_TRACE_COOKIE);
    }
}

同样bindWorkspace 回调 bindStringCache 方法,大家感兴趣可以看下其他方法。

相关推荐
诺诺Okami2 小时前
Android Framework-Launcher-Partner
android
2501_915918412 小时前
iOS 上架全流程指南 iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传 ipa 与审核实战经验分享
android·ios·小程序·uni-app·cocoa·iphone·webview
00后程序员张3 小时前
iOS App 混淆与加固对比 源码混淆与ipa文件混淆的区别、iOS代码保护与应用安全场景最佳实践
android·安全·ios·小程序·uni-app·iphone·webview
CrimsonHu4 小时前
Android高性能音频:写一个云顶S10强音争霸混音器
android·音视频开发
灿烂阳光g11 小时前
domain_auto_trans,source_domain,untrusted_app
android·linux
低调小一13 小时前
Android传统开发 vs Android Compose vs HarmonyOS ArkUI 对照表
android·华为·harmonyos
雨白13 小时前
Java 多线程指南:从基础用法到线程安全
android·java
00后程序员张14 小时前
详细解析苹果iOS应用上架到App Store的完整步骤与指南
android·ios·小程序·https·uni-app·iphone·webview