`
jandroid
  • 浏览: 1894508 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Android---->Allapps加载流程详解【AndroidICS4.0——>Launcher系列五】

阅读更多

工作需要总结,这样就能保证地基牢固,就能爬得更高;

----2013-01-07题记

转载请标明出处http://blog.csdn.net/wdaming1986/article/details/8478533

前段时间研究了Launcher的AllApps的加载流程,对这个进行了一点修改,呵呵,其实也不算太难,只要把Launcher的代码都能看个80%,基本就是想怎么改就怎么改!AllApps是什么,就是在Android的IDEL界面(主界面)点击MainMenu键进入后的界面,也就是所有应用程序界面;

先来看看它是怎么被手机加载上来的?

Step1:手机第一次开机,首先加载LauncherApplication,注册一些监听,共享数据,比如:LauncherModel对象,通过((LauncherApplication)getApplication());可以获取到LauncherApplication的对象;然后再加载Launcher.java这个类,先走onCreate()方法;里面调用如下方法

if (!mRestoring) {
            mModel.startLoader(this, true);
        }


Step2:在Step1中这个方法调到了LauncheModel.java的类里面了,在这个方法里面主要的工作就是启动一个线程,下面我们来看看在线程的run()方法做了哪些操作;

public void run() {
            // Optimize for end-user experience: if the Launcher is up and // running with the
            // All Apps interface in the foreground, load All Apps first. Otherwise, load the
            // workspace first (default).
            final Callbacks cbk = mCallbacks.get();
            final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;

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


            // Update the saved icons if necessary
            if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
            for (Object key : sDbIconCache.keySet()) {
                updateSavedIcon(mContext, (ShortcutInfo) key, sDbIconCache.get(key));
            }
            sDbIconCache.clear();

            // Clear out this reference, otherwise we end up holding it until all of the
            // callback runnables are done.
            mContext = null;

            synchronized (mLock) {
                // If we are still the last one to be scheduled, remove ourselves.
                if (mLoaderTask == this) {
                    mLoaderTask = null;
                }
            }
        }

其实主要的操作就是加载workspace和AllApps;loadAndBindAllApps()这个方法就是加载AllApps的;

Step3:在这个loadAndBindAllApps()里面,会调用loadAllAppsByBatch(),批量加载AllApps;

先根据:

final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);

创建一个带有CATEGORY_LAUNCHER这种类型的mainIntent,然后再通过

List<ResolveInfo> apps=packageManager.queryIntentActivities(mainIntent, 0);

过滤出所有的apps,通过sort对apps进行排序:

Collections.sort(apps,new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));

排序完成后,然后把这些apps逐个添加到ArrayList中去:代码如下:

for (int j=0; i<N && j<batchSize; j++) {
                    // This builds the icon bitmaps.
                    mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i),
                            mIconCache, mLabelCache));
                    i++;
                }

调到AllAppsList.java中的add方法:

public void add(ApplicationInfo info) {
        if (findActivity(data, info.componentName)) {
            return;
        }
        data.add(info);
        added.add(info);
    }

这个added的定义就是:

public ArrayList<ApplicationInfo> added =
            new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);

然后通过开启线程callback回调到Launcher.java的bindAllApplications()方法中:

final ArrayList<ApplicationInfo> added = mAllAppsList.added;
                mAllAppsList.added = new ArrayList<ApplicationInfo>();

                mHandler.post(new Runnable() {
                    public void run() {
                        final long t = SystemClock.uptimeMillis();
                        if (callbacks != null) {
                            if (first) {
                                callbacks.bindAllApplications(added);
                            } else {
                                callbacks.bindAppsAdded(added);
                            }
                            if (DEBUG_LOADERS) {
                                Log.d(TAG, "bound " + added.size() + " apps in "
                                    + (SystemClock.uptimeMillis() - t) + "ms");
                            }
                        } else {
                            Log.i(TAG, "not binding apps: no Launcher activity");
                        }
                    }
                });

Step4:在Launcher.java中bindAllApplications()方法中做的事:如果有对话框存在,就remove对话框,主要是

mAppsCustomizeContent.setApps(apps);

Step5:在AppsCustomizePagedView.java中的setApps()中主要做的事就是,赋值给mApps,再次对apps进行排序,计算apps的页数和widget占用的页数;代码如下

public void setApps(ArrayList<ApplicationInfo> list) {
        mApps = list;
        Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
        updatePageCounts();

        // The next layout pass will trigger data-ready if both widgets and apps are set, so 
        // request a layout to do this test and invalidate the page data when ready.
        if (testDataReady()) requestLayout();
    }

updatePageCounts()就是计算apps的页数和widget的页数;

Step6:而进入这个allapps的时候,就是进入到AppsCustomizePagedView.java这个类的时候会调用

onMeasure()这个方法;在这个里面首先会对allapps和widgets进行校验,

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (!isDataReady()) {
            if (testDataReady()) {
                setDataIsReady();
                setMeasuredDimension(width, height);
                onDataReady(width, height);
            }
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

通过testDataReady()这个方法来校验是否他们为空!如果为空就不加载他们;代码如下:

/**
     * This differs from isDataReady as this is the test done if isDataReady is not set.
     */
    private boolean testDataReady() {
        // We only do this test once, and we default to the Applications page, so we only really
        // have to wait for there to be apps.
        // TODO: What if one of them is validly empty
        return !mApps.isEmpty() && !mWidgets.isEmpty();
    }

当allapps和widgets的数据都准备好了的时候,给这个view设置宽和高setMeasuredDimension(width, height);

然后调用onDataReady(width, height);在这个方法中会计算占用的页数,内容宽度细胞的数量强制措施,以更新重新计算差距,存储页面,刷新数据显示上来通过invalidatePageData(Math.max(0, page), hostIsTransitioning);

这个调用到了PageView.java这个类(Launcher的主要精华类,写得相当有水准,看了好几遍,每次看都有收获)在这个方法里面主要做的是

(1)先加载apps和widgets的view,通过方法

// Update all the pages
            syncPages();
public void syncPages() {
        removeAllViews();
        cancelAllTasks();

        Context context = getContext();
        for (int j = 0; j < mNumWidgetPages; ++j) {
            PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX,
                    mWidgetCountY);
            setupPage(layout);
            addView(layout, new PagedViewGridLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                    LayoutParams.MATCH_PARENT));
        }

        for (int i = 0; i < mNumAppsPages; ++i) {
            PagedViewCellLayout layout = new PagedViewCellLayout(context);
            setupPage(layout);
            addView(layout);
        }
    }

(2)再刷新数据到每页的view界面中,通过方法:

 // Load any pages that are necessary for the current window of views
            loadAssociatedPages(mCurrentPage, immediateAndOnly);
            requestLayout();

在PageView.java中的loadAssociatedPages()方法中里面调用的主要的方法syncPageItems(i, (i == page) && immediateAndOnly);这个通过接口调到了AppsCustomizePagedView.java中的syncPageItems()方法中去了:

@Override
    public void syncPageItems(int page, boolean immediate) {
        if (page < mNumAppsPages) {
            syncAppsPageItems(page, immediate);
        } else {
            syncWidgetPageItems(page - mNumAppsPages, immediate);
        }
    }

里面就是刷新apps或者是widget的每一页;
再来看看syncAppsPageItems()这个方法:

public void syncAppsPageItems(int page, boolean immediate) {
        // ensure that we have the right number of items on the pages
        int numCells = mCellCountX * mCellCountY;
        int startIndex = page * numCells;
        int endIndex = Math.min(startIndex + numCells, mApps.size());
        PagedViewCellLayout layout = (PagedViewCellLayout) getPageAt(page);

        layout.removeAllViewsOnPage();
        ArrayList<Object> items = new ArrayList<Object>();
        ArrayList<Bitmap> images = new ArrayList<Bitmap>();
        for (int i = startIndex; i < endIndex; ++i) {
            ApplicationInfo info = mApps.get(i);
            PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(
                    R.layout.apps_customize_application, layout, false);
            icon.applyFromApplicationInfo(info, true, mHolographicOutlineHelper);
            icon.setOnClickListener(this);
            icon.setOnLongClickListener(this);
            icon.setOnTouchListener(this);
            icon.setOnKeyListener(this);

            int index = i - startIndex;
            int x = index % mCellCountX;
            int y = index / mCellCountX;
            layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));

            items.add(info);
            images.add(info.iconBitmap);
        }

        layout.createHardwareLayers();

        /* TEMPORARILY DISABLE HOLOGRAPHIC ICONS
        if (mFadeInAdjacentScreens) {
            prepareGenerateHoloOutlinesTask(page, items, images);
        }
        */
    }

当你看到这个addViewToCellLayout()方法的时候,我相信你就会有“山穷水复疑无路,柳暗花明又一村”的感觉了!这就是加载每个icon到view的那个位置;
syncWidgetPageItems()这个也是同理,代码我相信大家自己都能看明白了吧!

Step7:而这个widgets的数据是怎么加载上来的呢???这个是在Launcher.java中的onCreate()方法中一步一步加载的:

(1)在Launcher.java中的onCreate()方法中:

// Update customization drawer _after_ restoring the states
        if (mAppsCustomizeContent != null) {
            mAppsCustomizeContent.onPackagesUpdated();
        }

(2)调用到AppsCustomizePagedView.java中的onPackagesUpdated()的方法,这个里面主要做的是启动一个延迟的线程来加载widgets

public void onPackagesUpdated() {
        // TODO: this isn't ideal, but we actually need to delay here. This call is triggered
        // by a broadcast receiver, and in order for it to work correctly, we need to know that
        // the AppWidgetService has already received and processed the same broadcast. Since there
        // is no guarantee about ordering of broadcast receipt, we just delay here. Ideally,
        // we should have a more precise way of ensuring the AppWidgetService is up to date.
        postDelayed(new Runnable() {
           public void run() {
               updatePackages();
           }
        }, 500);
    }

(3)通过updatePackages()这个方法来实现的加载widgets的下面来看看代码:

public void updatePackages() {
        // Get the list of widgets and shortcuts
        boolean wasEmpty = mWidgets.isEmpty();
        mWidgets.clear();
        List<AppWidgetProviderInfo> widgets =
            AppWidgetManager.getInstance(mLauncher).getInstalledProviders();
        Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
        List<ResolveInfo> shortcuts = mPackageManager.queryIntentActivities(shortcutsIntent, 0);
        for (AppWidgetProviderInfo widget : widgets) {
            if (widget.minWidth > 0 && widget.minHeight > 0) {
                mWidgets.add(widget);
            } else {
                Log.e(LOG_TAG, "Widget " + widget.provider + " has invalid dimensions (" +
                        widget.minWidth + ", " + widget.minHeight + ")");
            }
        }
        mWidgets.addAll(shortcuts);
        Collections.sort(mWidgets,
                new LauncherModel.WidgetAndShortcutNameComparator(mPackageManager));
        updatePageCounts();

        if (wasEmpty) {
            // The next layout pass will trigger data-ready if both widgets and apps are set, so request
            // a layout to do this test and invalidate the page data when ready.
            if (testDataReady()) requestLayout();
        } else {
            cancelAllTasks();
            invalidatePageData();
        }
    }

相信大家看到这里,根据上面的分析,就应该明白了mWidgets数据的加载过程了吧!

Step8:置于里面的click事件就查看onClick()方法;

长按是调用到父类的PagedViewWithDraggableItems.java的onLongClick()事件:

@Override
    public boolean onLongClick(View v) {
        // Return early if this is not initiated from a touch
        if (!v.isInTouchMode()) return false;
        // Return early if we are still animating the pages
        if (mNextPage != INVALID_PAGE) return false;
        // When we have exited all apps or are in transition, disregard long clicks
        if (!mLauncher.isAllAppsCustomizeOpen() ||
                mLauncher.getWorkspace().isSwitchingState()) return false;

        return beginDragging(v);
    }

然后回调子类的AppsCustomizePagedView.java的beginDragging()方法的:

private void beginDraggingApplication(View v) {
        mLauncher.getWorkspace().onDragStartedWithItem(v);
        mLauncher.getWorkspace().beginDragShared(v, this);
    }

以后的流程大家可以自己跟跟,就明白拖拽事件的传递了,其实和Folder的拖拽是类似的原理;

今天就总结到这里吧!

2013年1月7日22:35于北京








分享到:
评论

相关推荐

    AndroidICS关机跟重新启动功能研究

    一&gt;、Android重启功能  在androidjava层执行shell命令来完成。但是笔者在开发测试中同样的代码发现用Eng编译出的版本可以重新启动,user版本不能完成重启,挂在关机那个界面。  /********************************...

    基于Springboot+Vue的墙绘产品展示交易平台毕业源码案例设计.zip

    网络技术和计算机技术发展至今,已经拥有了深厚的理论基础,并在现实中进行了充分运用,尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代,所以对于信息的宣传和管理就很关键。系统化是必要的,设计网上系统不仅会节约人力和管理成本,还会安全保存庞大的数据量,对于信息的维护和检索也不需要花费很多时间,非常的便利。 网上系统是在MySQL中建立数据表保存信息,运用SpringBoot框架和Java语言编写。并按照软件设计开发流程进行设计实现。系统具备友好性且功能完善。 网上系统在让售信息规范化的同时,也能及时通过数据输入的有效性规则检测出错误数据,让数据的录入达到准确性的目的,进而提升数据的可靠性,让系统数据的错误率降至最低。 关键词:vue;MySQL;SpringBoot框架 【引流】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    99-青海大学大数据中心建设分享.pptx

    99-青海大学大数据中心建设分享.pptx

    TD-LTE载波聚合方案.docx

    5G通信行业、网络优化、通信工程建设资料。

    10份网络优化创新案例.zip

    SA语音回落与切换流程冲突解决.pdf 计费模式错误导致SA语音承载建立失败,pdf BSF网元bug导致SA用户VOLTE业务故障,pdf SA基站SCTP偶联IP配置不规范导致切换失败的问题处理,pdf 第一医院SA+NSA双模基站方案保障5G查房车应用,pdf SA未配置互操作场景下终端语音业务研究案例,pdf SA站点天馈隔离度问题导致上行速率不及预期,pdf SA组网下微信小视频卡顿影响感知案例,pdf 基于八步法定位SA掉线问题.pdf SA站点测试宏微切换异常事件,pdf

    施工监理费计算依据.doc

    5G通信行业、网络优化、通信工程建设资料。

    wordpress插件WhatsApp右下角浮动悬浮客服按钮

    1、WhatsApp插件,可轻松实现wordpress后台设置,前台悬浮显示; 2、无缝集成:该插件将WordPress站点与WhatsApp无缝集成; 3、多人员支持:支持显示多个WhatsApp账户,让用户根据需求或偏好选择联系不同的团队成员 4、群组邀请:允许邀请用户加入特定的WhatsApp支持群组,便于群体咨询、公告发布或集体答疑。 5、响应式设计:插件具备响应式布局,确保在各种屏幕尺寸和设备类型上均能良好呈现并顺畅使用。 6、WooCommerce产品查询集成:针对电商网站,支持与WooCommerce产品查询功能结合,方便用户就具体商品提问。 7、带声音的自动弹窗:可设置带有声音提示的自动弹出窗口,提醒用户支持服务的存在 8、定制欢迎消息:设置个性化欢迎信息,向用户传递品牌关怀或引导其使用支持服务。 9、移动端与桌面端开关控制:可根据需要独立开启或关闭移动端或桌面端上的插件功能 10、GDPR合规:遵循欧盟GDPR数据保护法规,保障用户隐私及数据安全。

    基于YOLOv7的芯片表面缺陷检测系统

    目前随着电子领域的快速发展,芯片也已经成为日常生活中不可或缺的一部分。随着市场对芯片的需求不断增大,裸芯片表面缺陷检测任务的压力也越来越大。裸芯片表面的缺陷检测不仅能保证芯片成品的质量,而且有着统计缺陷数量,反馈给生产前道工序的重要意义,但是目前许多生产线对于裸芯片表面依旧采用人工目检的方法进行缺陷检测,不仅实时性差,耗时长,而且结果会受到检测人员主观因素的影响。  目前国内外的芯片表面缺陷检测设备不仅价格昂贵,而且功能比较单一,因此本文提出了一种基于深度学习的裸芯片表面缺陷检测算法,具有高效率,实时性好的特点,与传统人工目检的方式相比具有一定的优势

    基于SpringBoot的“大学生社团活动平台”的设计与实现.zip

    基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现基于SpringBoot的“大学生社团活动平台”的设计与实现

    英飞凌官方ADS库1.9.20版

    英飞凌官方ADS库1.9.20版

    汇编语言-assembly-贪吃蛇游戏-汇编语言期末大作业

    汇编语言——贪吃蛇游戏 GREEDY_SNAKE 是基于8086 汇编语言开发的,汇编语言风格是采用《汇编语言》第二版 王爽著; G_Snake.asm 本贪吃蛇游戏 实现了随机出现食物、统计分数、显示小蛇运动方向、响应键盘中断、指定方向自动移动、游戏结束恢复9h键盘中断和正常退出。 文件说明: 1. 安装DOSBOX:运行DOSBox0.74-win32-installer.exe即可安装; 2. 将Greedy_Snake clone到本地任意盘,eg:d:\Greedy_Snake - mount d:\Greedy_Snake 到一个指定虚拟盘符: - `mount k d:\Greedy_Snake` (why is k? because i like this charactor) 3. 运行G_Snake - 在DOSBOX的DOS提示符下键入: - `Z:\>K:`(回车) - `K:\>cd G_Snake`(回车) - 使用masm 5.0工具编译、链接、运行.asm源程序 - MASM.EXE、LINK.EXE、d

    物联网考试题库答案.doc

    5G通信行业、网络优化、通信工程建设资料

    基于Python的在线学习与推荐系统设计带vue前后端分离毕业源码案例设计.zip

    网络技术和计算机技术发展至今,已经拥有了深厚的理论基础,并在现实中进行了充分运用,尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代,所以对于信息的宣传和管理就很关键。系统化是必要的,设计网上系统不仅会节约人力和管理成本,还会安全保存庞大的数据量,对于信息的维护和检索也不需要花费很多时间,非常的便利。 网上系统是在MySQL中建立数据表保存信息,运用SpringBoot框架和Java语言编写。并按照软件设计开发流程进行设计实现。系统具备友好性且功能完善。 网上系统在让售信息规范化的同时,也能及时通过数据输入的有效性规则检测出错误数据,让数据的录入达到准确性的目的,进而提升数据的可靠性,让系统数据的错误率降至最低。 关键词:vue;MySQL;SpringBoot框架 【引流】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    考试资料+7、互联网与物联网.docx

    5G通信行业、网络优化、通信工程建设资料

    参考资料-人工智能对劳动力市场的影响机制研究.pdf

    参考资料-人工智能对劳动力市场的影响机制研究.pdf

    99-数据开放平台技术实现方案.pptx

    99-数据开放平台技术实现方案.pptx

    199-IBM数据治理新主张-数据治理及元数据管理.pptx

    199-IBM数据治理新主张-数据治理及元数据管理.pptx

    关注于一个操作系统框架的设计与实现。所使用的工具有gcc、nasm、bochs、gdb、vim等.zip

    【资源说明】【毕业设计】 1、该资源内项目代码都是经过测试运行成功,功能正常的情况下才上传的,请放心下载使用。 2、适用人群:主要针对计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、数学、电子信息等)的同学或企业员工下载使用,具有较高的学习借鉴价值。 3、不仅适合小白学习实战练习,也可作为大作业、课程设计、毕设项目、初期项目立项演示等,欢迎下载,互相学习,共同进步!

    自动驾驶中的数据闭环建立(一).pdf

    自动驾驶中的数据闭环链路的建立,数据驱动算法

    通信工程监理工作流程表.doc

    5G通信、网络优化与通信建设

Global site tag (gtag.js) - Google Analytics