Android Launcher源码研究(三) 加载app流程2

接上次的。

首先Launcher实现了LauncherModel.Callbacks接口,APP信息数据加载成功后 ,回调接口把app信息显示到Launcher的 workspace界面上,这个过程代码里面称为bind。

下面是个类调用过程的时序图,不是很标准,不过能表达基本调用顺序帮助我们理解。

首先就是Launcher OnCreate中调用LauncherMode startLoader方法,这里只看异步的方式 就是当前的页面下标为-1,加载所有app信息

mWorkspace.getCurrentPage()为-1的情况。

mModel.startLoader(true, -1);

在LauncherMode类的startLoader方法里面,我们需要实例化线程类 LoaderTask,大部分工作都在这里面完成.

run方法的代码:

            keep_running: {
                // Elevate priority when Home launches for the first time to avoid
                // starving at boot time. Staring at a blank home is not cool.
                synchronized (mLock) {
                    if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
                            (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
                    android.os.Process.setThreadPriority(mIsLaunching
                            ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
                }
                if (loadWorkspaceFirst) {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                    loadAndBindWorkspace();
                } else {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
                    loadAndBindAllApps();
                }

                if (mStopped) {
                    break keep_running;
                }

                // Whew! Hard work done.  Slow us down, and wait until the UI thread has
                // settled down.
                synchronized (mLock) {
                    if (mIsLaunching) {
                        if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
                        android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    }
                }
                waitForIdle();

                // second step
                if (loadWorkspaceFirst) {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                    loadAndBindAllApps();
                } else {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
                    loadAndBindWorkspace();
                }

                // Restore the default thread priority after we are done loading items
                synchronized (mLock) {
                    android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                }
            }

我们定位到 loadAndBindWorkspace 方法 加载workspace界面

        private void loadAndBindWorkspace() {
            mIsLoadingAndBindingWorkspace = true;

            // Load the workspace
            if (DEBUG_LOADERS) {
                Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
            }

            if (!mWorkspaceLoaded) { //第一次加载
                loadWorkspace(); //加载
                synchronized (LoaderTask.this) {
                    if (mStopped) {
                        return;
                    }
                    mWorkspaceLoaded = true;
                }
            }

            // Bind the workspace
            bindWorkspace(-1);//绑定数据到UI
        }
</pre><p></p><p>loadWorkspace方法就是加载手机里面的所有app信息,包括app widget, folder 并且存储到ArrayList中,后面显示这些东西到UI上。</p><p>数据我们存储好以后调用bindWorkspace(-1)开始显示数据到workspace,里面包含了很多调用方法,主要是数据排序,清除以前绑定的UI数据。</p><p>在bindWorkspace方法里面,调用了一些filter方法,这个地方主要是排序和过滤,分别判断需要加载到那个屏幕里面.</p><p></p><pre code_snippet_id="533506" snippet_file_name="blog_20141126_4_7063071" name="code" class="java">            r = new Runnable() {
                public void run() {
                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        callbacks.startBinding();//绑定之前,清理之前的数据,简单就这么理解
                    }
                }
            };
            runOnMainThread(r);//UI更新需要在主线程中

下面这个方法就是把数据添加到workspace的屏幕中去.

            bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
                    currentFolders, null);
      private void bindWorkspaceItems(final Callbacks oldCallbacks,
                final ArrayList<ItemInfo> workspaceItems,
                final ArrayList<LauncherAppWidgetInfo> appWidgets,
                final HashMap<Long, FolderInfo> folders,
                ArrayList<Runnable> deferredBindRunnables) {

            final boolean postOnMainThread = (deferredBindRunnables != null);

            // Bind the workspace items
            int N = workspaceItems.size();
            for (int i = 0; i < N; i += ITEMS_CHUNK) {
                final int start = i;
                final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
                final Runnable r = new Runnable() {
                    @Override
                    public void run() {
                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                        if (callbacks != null) {
                            callbacks.bindItems(workspaceItems, start, start+chunkSize);//显示app信息到UI上
                        }
                    }
                };
                if (postOnMainThread) {
                    deferredBindRunnables.add(r);
                } else {
                    runOnMainThread(r);
                }
            }

            // Bind the folders
            if (!folders.isEmpty()) {
                final Runnable r = new Runnable() {
                    public void run() {
                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                        if (callbacks != null) {
                            callbacks.bindFolders(folders);//显示folders
                        }
                    }
                };
                if (postOnMainThread) {
                    deferredBindRunnables.add(r);
                } else {
                    runOnMainThread(r);
                }
            }

            // Bind the widgets, one at a time
            N = appWidgets.size();
            for (int i = 0; i < N; i++) {
                final LauncherAppWidgetInfo widget = appWidgets.get(i);
                final Runnable r = new Runnable() {
                    public void run() {
                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                        if (callbacks != null) {
                            callbacks.bindAppWidget(widget);//绑定AppWidget信息
                        }
                    }
                };
                if (postOnMainThread) {
                    deferredBindRunnables.add(r);
                } else {
                    runOnMainThread(r);
                }
            }
        }

我们这里只看第一个绑定shortcut信息代码,其他2个其实道理差不多.

    public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
        setLoadOnResume();

        // Get the list of added shortcuts and intersect them with the set of shortcuts here
        Set<String> newApps = new HashSet<String>();
        newApps = mSharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps);

        Workspace workspace = mWorkspace;
        for (int i = start; i < end; i++) {
            final ItemInfo item = shortcuts.get(i);

            // Short circuit if we are loading dock items for a configuration which has no dock
            if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
                    mHotseat == null) {
                continue;
            }

            switch (item.itemType) {
                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                    ShortcutInfo info = (ShortcutInfo) item;
                    String uri = info.intent.toUri(0).toString();
                    View shortcut = createShortcut(info);
                    workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
                            item.cellY, 1, 1, false);
                    boolean animateIconUp = false;
                    synchronized (newApps) {
                        if (newApps.contains(uri)) {
                            animateIconUp = newApps.remove(uri);
                        }
                    }
                    if (animateIconUp) {
                        // Prepare the view to be animated up
                        shortcut.setAlpha(0f);
                        shortcut.setScaleX(0f);
                        shortcut.setScaleY(0f);
                        mNewShortcutAnimatePage = item.screen;
                        if (!mNewShortcutAnimateViews.contains(shortcut)) {
                            mNewShortcutAnimateViews.add(shortcut);
                        }
                    }
                    break;
                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                    FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                            (FolderInfo) item, mIconCache);
                    workspace.addInScreen(newFolder, item.container, item.screen, item.cellX,
                            item.cellY, 1, 1, false);
                    break;
            }
        }

        workspace.requestLayout();
    }

重点就是下面这个,把数据画到UI上面,坐标信息,APP信息.

                    workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
                            item.cellY, 1, 1, false);

最后我们调用 finishBindingItems完成调用,这个方法里面还包括了滑动和显示APP动画方法的处理。

        if (mVisible || mWorkspaceLoading) {
            Runnable newAppsRunnable = new Runnable() {
                @Override
                public void run() {
                    runNewAppsAnimation(false);
                }
            };

            boolean willSnapPage = mNewShortcutAnimatePage > -1 &&
                    mNewShortcutAnimatePage != mWorkspace.getCurrentPage();
            if (canRunNewAppsAnimation()) {
                // If the user has not interacted recently, then either snap to the new page to show
                // the new-apps animation or just run them if they are to appear on the current page
                if (willSnapPage) {
                    mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable);
                } else {
                    runNewAppsAnimation(false);
                }
            } else {
                // If the user has interacted recently, then just add the items in place if they
                // are on another page (or just normally if they are added to the current page)
                runNewAppsAnimation(willSnapPage);
            }
        }

详细的我们到后面继续分析。还包括具体Workspace上面的界面格子cell是怎么设计和APP添加位置的计算问题。

若有问题,请指出,谢谢~ O(∩_∩)O~

时间: 2024-12-09 13:35:13

Android Launcher源码研究(三) 加载app流程2的相关文章

Android Launcher源码研究(二) 加载app流程1

今天主要分析Android Launcher源码中的一些重要类之间的关系,基本的加载流程.先来看一个类图 Launcher.java 是主Activity 在onCreate方法里面初始化了LauncherMode实例. LauncherApplication app = ((LauncherApplication)getApplication()); mModel = app.setLauncher(this); 直接进入LauncherApplication.java的方法 Launcher

Android Launcher源码结构

Launcher 是 Android手机开启后第一个运行的 应用程序,也叫Home,或者叫做手机桌面. 本文介绍的是4.1源码的launcher2 app. Android41\packages\apps\Launcher2 首先找到主Activity,打开AndroidManifest.xml  入口是  com.android.launcher2.Launcher 这个类 Launcher 主界面包含 wallpaper墙纸,work_screen屏幕, 最底部的hotseat, 以及all

MyBatis 源码篇-资源加载

本章主要描述 MyBatis 资源加载模块中的 ClassLoaderWrapper 类和 Java 加载配置文件的三种方式. ClassLoaderWrapper 上一章的案例,使用 org.apache.ibatis.io.Resources#getResourceAsStream(java.lang.String) 方法加载 MyBatis 的配置文件.Resources 是一个提供了多个静态方法的工具类,内部封装了 ClassLoaderWrapper 类的静态字段,Resources 

[Android]Fragment源码分析(三) 事务

Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的非常的出彩.我们先引入一个简单常用的Fragment事务管理代码片段: FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction(); ft.add(R.id.fragmentContainer, fragment, "tag"); ft.addToBackStack("<span style="fo

Prism 源码解读3-Modules加载

原文:Prism 源码解读3-Modules加载 目录 介绍 0.Modules加载 1.通过AppSetting加载 2.通过代码加载 3.通过目录加载 4.通过手动方式加载 总结 回到顶部 介绍 在软件开发过程中,总想组件式的开发方式,各个组件之间最好互不影响,独立测试.Prism的Modules很好的满足了这一点. 这个架构图很好了讲解了Prism的Modules的概念 Prism支持通过配置文件,文件夹,手动载入Module的方式,并且对Module的载入进行验证,包括重复和循环依赖验证

spring源码-bean之加载-2

一.前面说了bean的容器初始化,后面当然是说bean的加载.这里还是不讲解ApplicationContext的bean的加载过程,还是通过最基础的XmlBeanFactory来进行讲解,主要是熟悉通过BeanFactory是怎么实现class的实例化的. 二.声明一下:XmlBeanFactory和ApplicationContext的不同在与,ApplicationContext在容器初始化的时候,就做了很多准备操作.而XmlBeanFactory是在获取bean的时候才会调用初始化的东西

Spring源码分析之-加载IOC容器

本文接上一篇文章 SpringIOC 源码,控制反转前的处理(https://mp.weixin.qq.com/s/9RbVP2ZQVx9-vKngqndW1w) 继续进行下面的分析 首先贴出 Spring bean容器的刷新的核心 11个步骤进行祭拜(一定要让我学会了...阿门) // 完成IoC容器的创建及初始化工作 @Override public void refresh() throws BeansException, IllegalStateException { synchroni

JQuery源码解析-Dom加载过程

下面的几个工具方法都是和dom加载有关,所以先从dom加载开始. 用到的方法: isReady:DOM是否已经加载完(内部使用) readyWait():等待多少文件的计时器(内部使用) holdReady()::推迟DOM触发 ready():准备DOM触发 jQuery.ready.promise = function( obj ) {}检测dom的异步操作 先看一下jQuery和原生js加载方式有什么不同: $(function () { }); window.onload = funct

SSH 之 Spring的源码(一)——Bean加载过程

看看Spring的源码,看看巨人的底层实现,拓展思路,为了更好的理解原理,看看源码,深入浅出吧.本文基于Spring 4.0.8版本. 首先Web项目使用Spring是通过在web.xml里面配置 org.springframework.web.context.ContextLoaderListener初始化IOC容器的 <span style="font-family:SimSun;font-size:18px;"><listener> <listene