Android 13 Launcher 数据加载分析(二)

简介: 学习笔记

学习笔记:本篇文章只分析数据加载大概流程,不深入。


Android 10.0 launcher 启动流程

Android 13 Launcher 基础认识(一)

Android 13 Launcher 数据加载分析(二)

  有空准备详细分析下 Launcher 这个模块,启动流程、布局分析、手势上滑、Launcher 拖拽、其他(Launcher 状态、窗口周期、接口控制)。

后面文章则按以下几点深入分析:

  • Workspace 的数据加载及绑定
  • AllApp 的数据加载及绑定
  • Launcher 数据库分析
  • 布局更新分析
  • 负一屏
  • hotseat
  • 虚拟按键

  前面一篇 launcher 基础认识中,有分析到 launcher.java 这个类,在该类的 onCreate() 方法中,有 launcher 布局相关的初始化。这里还是从 onCreate() 方法出发,进行深入。

  说到数据加载,就不得不提到 launcher#onCreate() 方法,上篇说到该方法里面有布局相关的初始化,也是在 launcher#onCreate() 方法开始的。

先看 launcher#onCreate() 的部分代码:

// launcher.java
    protected void onCreate(Bundle savedInstanceState) {
        // 省略部分代码......
        // 初始化View,进行各种View的初始化事件绑定
        setupViews();
        // 省略部分代码......
        // 加载、绑定数据(这里与之前版本的 startLoader() 作用一样)
        // 重点关注 addCallbacksAndLoad()
        if (!mModel.addCallbacksAndLoad(this)) {
            if (!internalStateHandled) {
                Log.d(BAD_STATE, "Launcher onCreate not binding sync, prevent drawing");
                // 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;
            }
        }
          // 省略部分代码......
    }

上述代码调用了 LauncherModel 中的 addCallbacksAndLoad() 方法,一起看 LauncherModel#addCallbacksAndLoad()

// LauncherModel.java
    public boolean addCallbacksAndLoad(Callbacks callbacks) {
        synchronized (mLock) {
            addCallbacks(callbacks);
            // 重点关注
            return startLoader(new Callbacks[] { callbacks });
        }
    }
    // 该方法是加载程序的启动,在启动过程中并对于数据进行尝试同步绑定,
    // 若能够进行绑定则可以返回true,在实现的过程中调用的是工作线程LoaderTask
    private boolean startLoader(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) {
            // 对于旧的运行项进行一个清除
            boolean wasRunning = stopLoader();
            boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
            boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
            final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
            if (callbacksList.length > 0) {
                // 在同步加载过程中对于所有挂起的绑定可运行项进行清除
                for (Callbacks cb : callbacksList) {
                    MAIN_EXECUTOR.execute(cb::clearPendingBinds);
                }
                LoaderResults loaderResults = new LoaderResults(
                        mApp, mBgDataModel, mBgAllAppsList, callbacksList);
                if (bindDirectly) {
                    // 此处为数据恢复机制,例如:子用户切换到住用户时,会走到这一分支
                    // 将同步项绑定于workspace、allapps、快捷方式、桌面小组件
                    loaderResults.bindWorkspace(bindAllCallbacks);
                    loaderResults.bindAllApps();
                    loaderResults.bindDeepShortcuts();
                    loaderResults.bindWidgets();
                    return true;
                } else {
                    // 数据加载,首次启动时 mModelLoaded 为 false,会走到这一分支
                    stopLoader();  // 当有加载任务正在运行时则进行其停止运行
                    // 对于LocalderTask进行启动:
                    mLoaderTask = new LoaderTask(
                            mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
                    // 发布加载程序任务
                    MODEL_EXECUTOR.post(mLoaderTask);
                }
            }
        }
        return false;
    }

上述代码,我们重点关注 LoaderTask.java ,分析数据如何加载,先看 LoaderTask 的构造方法 LoaderTask.java

// LoaderTask.java
    public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
            ModelDelegate modelDelegate, LoaderResults results) {
        mApp = app;
        mBgAllAppsList = bgAllAppsList;
        mBgDataModel = dataModel;
        mModelDelegate = modelDelegate;
        mResults = results;
        // LauncherApps:用于检索当前用户和任何关联的托管配置文件的可启动活动列表的类。 这主要是供发射器使用的。 应用程序可以查询每个用户配置文件。
        // 由于PackageManager不会为其他配置文件传送包广播,因此您可以在此注册包更改。要监视正在添加或删除的托管配置文件,请注册以下广播: ACTION_MANAGED_PROFILE_ADDED、ACTION_MANAGED_PROFILE_REMOVED;
        // 从 Android O 开始,托管配置文件中的应用程序不再允许访问主配置文件中的应用程序。应用程序只能访问 {@link getProfiles()} 返回的配置文件。
        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();
    }

接着看 LoaderTask#run()

public void run() {
        synchronized (this) {
            // 如果已经停止,则快速跳过
            if (mStopped) {
                return;
            }
        }
        Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
        TimingLogger logger = new TimingLogger(TAG, "run");
        LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
        // 这里与 LauncherModel.LoaderTransaction 关联上
        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            List<ShortcutInfo> allShortcuts = new ArrayList<>();
            Trace.beginSection("LoadWorkspace");
            try {
                // 第一步:加载工作区。****重点关注****
                loadWorkspace(allShortcuts, memoryLogger);
            } finally {
                Trace.endSection();
            }
            logASplit(logger, "loadWorkspace");
            // 根据从数据库加载的工作空间,清理数据重新同步小部件快捷方式。
            // 如果桌面(workspace)是从 不变设备配置文件中定义的主数据库不同的数据库加载的,则不应调用 sanitizeData。 
            // 例如,网格预览和最小设备模式都使用不同的数据库
            // mDbName 在 loadWorkspace() 方法里赋值
            if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
                verifyNotStopped();
                sanitizeData();
                logASplit(logger, "sanitizeData");
            }
            verifyNotStopped();
            // 绑定 workspace
            mResults.bindWorkspace(true /* incrementBindId */);
            logASplit(logger, "bindWorkspace");
            mModelDelegate.workspaceLoadComplete();
            // 发送首屏广播。
            sendFirstScreenActiveInstallsBroadcast();
            logASplit(logger, "sendFirstScreenActiveInstallsBroadcast");
            // 等待桌面(workspace)加载完成再加载抽屉(AllApps)
            waitForIdle();
            logASplit(logger, "step 1 complete");
            verifyNotStopped();
            // 第二步:
            Trace.beginSection("LoadAllApps");
            List<LauncherActivityInfo> allActivityList;
            try {
               // 加载所有应用App
               allActivityList = loadAllApps();
            } finally {
                Trace.endSection();
            }
            logASplit(logger, "loadAllApps");
            verifyNotStopped();
            // 绑定 AllApps
            mResults.bindAllApps();
            logASplit(logger, "bindAllApps");
            verifyNotStopped();
            // 关于 IconCacheUpdateHandler  文章后面有补充。
            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);
            }
            // 等待上一操作加载完成,在进行加载应用程序快捷方式
            waitForIdle();
            logASplit(logger, "step 2 complete");
            verifyNotStopped();
            // 第三步:加载应用程序快捷方式
            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) -> { });
            }
            // 等待上一操作加载完成,在进行加载小部件等
            waitForIdle();
            logASplit(logger, "step 3 complete");
            verifyNotStopped();
            // 第四步:加载桌面小部件
            List<ComponentWithLabelAndIcon> allWidgetsList =
                    mBgDataModel.widgetsModel.update(mApp, null);
            logASplit(logger, "load widgets");
            verifyNotStopped();
            // 绑定 Widgets
            mResults.bindWidgets();
            logASplit(logger, "bindWidgets");
            verifyNotStopped();
             // 将小部件进行缓存
            updateHandler.updateIcons(allWidgetsList,
                    new ComponentWithIconCachingLogic(mApp.getContext(), true),
                    mApp.getModel()::onWidgetLabelsUpdated);
            logASplit(logger, "save widgets in icon cache");
            // 第五步
            if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
                // 加载文件夹名称
                loadFolderNames();
            }
            verifyNotStopped();
            updateHandler.finish();
            logASplit(logger, "finish icon update");
            mModelDelegate.modelLoadComplete();
            // 提交完成。
            transaction.commit();
            memoryLogger.clearLogs();
        } catch (CancellationException e) {
            // Loader stopped, ignore
            logASplit(logger, "Cancelled");
        } catch (Exception e) {
            memoryLogger.printLogs();
            throw e;
        } finally {
            logger.dumpToLog();
        }
        TraceHelper.INSTANCE.endSection(traceToken);
    }

上述 LoaderTask#run()代码可以清晰的看到每一步操作所要做的工作。loadWorkspace() 和 bindWorkspace(),也就是加载 workspace 的应用并且进行绑定,waitForIdle() 方法主要是等待加载数据结束。sendFirstScreenActiveInstallsBroadcast() 发送首屏广播。loadAllApps() 和 bindAllApps() 加载并绑定所有的 APP 信息,loadDeepShortcuts() 和 bindDeepShortcuts ()加载并绑定所有的快捷方式,然后加载并绑定所有的小部件。至此launcher数据加载基本就完成了。

补充:上述代码出现比较多的 IconCacheUpdateHandler。

IconCacheUpdateHandler:用于处理图标缓存更新的实用程序类。

IconCacheUpdateHandler#updateIcons(): 更新持久数据库,以便只有与应用程序相对应的条目保留在数据库中并进行更新。

相关文章
|
17天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
2月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
51 2
|
25天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
49 15
Android 系统缓存扫描与清理方法分析
|
1月前
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
2月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
139 3
|
1月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。
|
2月前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
3月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据着重要的位置。本文将深入探讨这两种操作系统的开发环境,从编程语言到开发工具,从用户界面设计到性能优化,以及市场趋势对开发者选择的影响。我们旨在为读者提供一个全面的比较视角,帮助理解不同平台的优势与挑战,并为那些站在选择十字路口的开发者提供有价值的参考信息。
|
2月前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
49 1
|
3月前
|
开发框架 Android开发 Swift
安卓与iOS应用开发对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。本文将深入探讨这两大操作系统在开发环境、编程语言、用户界面设计、性能优化及市场分布等方面的差异和特点。通过比较分析,旨在为开发者提供一个宏观的视角,帮助他们根据项目需求和目标受众选择最合适的开发平台。同时,文章还将讨论跨平台开发框架的利与弊,以及它们如何影响着移动应用的开发趋势。
下一篇
无影云桌面