`
亚当爱上java
  • 浏览: 708921 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多

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

        前段时间研究了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的拖拽是类似的原理;

今天就总结到这里吧!

本文转载自:http://blog.csdn.net/wdaming1986/article/details/8478533

分享到:
评论

相关推荐

    AnderWeb-android-packages-apps-Launcher-4458ee4

    android studio期末作业AnderWeb-android_packages_apps_Launcher-4458ee4AnderWeb-android_packages_apps_Launcher-4458ee4AnderWeb-android_packages_apps_Launcher-4458ee4AnderWeb-android_packages_apps_...

    junit-platform-launcher-1.8.0-M1-API文档-中文版.zip

    赠送jar包:junit-platform-launcher-1.8.0-M1.jar; 赠送原API文档:junit-platform-launcher-1.8.0-M1-javadoc.jar; 赠送源代码:junit-platform-launcher-1.8.0-M1-sources.jar; 赠送Maven依赖信息文件:junit-...

    开发工具 ant-launcher-1.9.6

    开发工具 ant-launcher-1.9.6开发工具 ant-launcher-1.9.6开发工具 ant-launcher-1.9.6开发工具 ant-launcher-1.9.6开发工具 ant-launcher-1.9.6开发工具 ant-launcher-1.9.6开发工具 ant-launcher-1.9.6开发工具 ...

    AnderWeb-android-packages-apps-Launcher-4458ee4.zip

    AnderWeb-android_packages_apps_Launcher-4458ee4.zip AnderWeb-android_packages_apps_Launcher-4458ee4.zip AnderWeb-android_packages_apps_Launcher-4458ee4.zip AnderWeb-android_packages_apps_Launcher-4458...

    android-launcher-plus

    "Android-Launcher-Plus" 是一个专门为Android操作系统设计的自定义启动器,它扩展了原生Android启动器的功能,提供了更多个性化和高效的操作体验。这个项目可能包含了源代码、资源文件、配置文档等,旨在让开发者或...

    android4.0 Launcher2

    在Android 4.0(Ice Cream Sandwich,简称ICS)版本中,官方推出了改进版的Launcher——Launcher2,相较于早期版本,它在性能、用户体验以及可定制性方面都有所提升。本文将围绕"android4.0 Launcher2"这一主题,...

    junit-platform-launcher-1.7.0.jar

    junit-platform-launcher-1.7.0.jarjunit-platform-launcher-1.7.0.jar

    junit-platform-launcher-1.6.2.jar

    junit-platform-launcher-1.6.2.jarjunit-platform-launcher-1.6.2.jarjunit-platform-launcher-1.6.2.jar

    android 定制 launcher 4.0源码

    在Android 4.0(Ice Cream Sandwich,简称ICS)时代,对Launcher的定制提供了更多可能性,让我们深入探讨一下如何理解和分析“android 定制 launcher 4.0源码”。 首先,我们需要理解Android Launcher的基本架构。...

    android4.0-launcher可运行源码

    这篇内容将详述Android 4.0 Launcher的核心架构、主要组件及其工作流程。 首先,Android 4.0(代号Ice Cream Sandwich,简称ICS)的Launcher是基于Launcher2实现的,这也是压缩包中的文件名。Launcher2是在Android ...

    Apache Commons 所有包最新版本 含SRC (4/7)

    lang-2.4-src.zip&lt;br&gt;commons-launcher-1.1-src.zip&lt;br&gt;commons-launcher-1.1.zip&lt;br&gt;commons-logging-1.1.1-bin.zip&lt;br&gt;commons-logging-1.1.1-src.zip&lt;br&gt;commons-math-1.2-src.zip&lt;br&gt;commons-math-1.2.zip&lt;br&gt;...

    junit-platform-launcher-1.8.0-M1-API文档-中英对照版.zip

    赠送jar包:junit-platform-launcher-1.8.0-M1.jar; 赠送原API文档:junit-platform-launcher-1.8.0-M1-javadoc.jar; 赠送源代码:junit-platform-launcher-1.8.0-M1-sources.jar; 赠送Maven依赖信息文件:junit-...

    Android-Blur-Launcher,模糊发射器建立在谷歌的Nougat发射器3.zip

    【Android-Blur-Launcher】是一款基于谷歌Nougat(安卓7.0)启动器的开源项目,旨在提供一个强大的、具有模糊效果的用户界面,为用户带来独特的个性化体验。这款启动器允许用户自定义手机主屏幕,打造极具特色的桌面...

    Android-这个项目是对android-tv-launcher开源项目用gradle编译的升级改造

    【Android TV Launcher 开源项目与 Gradle 编译升级详解】 Android TV Launcher 是一个专为 Android TV 设备设计的应用启动器,它提供了用户界面和交互方式,使得电视用户能够便捷地浏览和启动各种应用程序。该项目...

    安卓Android源码——Android Launcher 源码修改可编译.zip

    "安卓Android源码——Android Launcher 源码修改可编译.zip" 这个标题表明我们关注的是Android操作系统的源代码,特别是与启动器(Launcher)相关的部分。Launcher是用户与Android系统交互的主要界面,它管理应用...

    android Launcher 4.0 (原生版本)

    Android Launcher 4.0,是Android操作系统的一个重要组成部分,它作为用户与系统交互的初始界面,扮演着桌面的角色。在Android 4.0(Ice Cream Sandwich,简称ICS)这个版本中,Launcher进行了诸多改进和优化,提升...

    junit-platform-launcher-1.6.1-API文档-中文版.zip

    赠送jar包:junit-platform-launcher-1.6.1.jar; 赠送原API文档:junit-platform-launcher-1.6.1-javadoc.jar; 赠送源代码:junit-platform-launcher-1.6.1-sources.jar; 赠送Maven依赖信息文件:junit-platform-...

    Android代码-android-rocket-launcher

    Android Rocket Launcher Gradle plugin that adds tasks to your android modules for installing and launching all variants. How to use Paste this code into your module's build.gradle apply plugin: '...

Global site tag (gtag.js) - Google Analytics