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

来源:互联网 发布:老帅哥淘宝店 编辑:程序博客网 时间:2024/05/15 00:56

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

                                                                          ----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()方法;里面调用如下方法

[java] view plaincopyprint?
  1. if (!mRestoring) {  
  2.             mModel.startLoader(thistrue);  
  3.         }  


       

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

[java] view plaincopyprint?
  1. public void run() {  
  2.             // Optimize for end-user experience: if the Launcher is up and // running with the  
  3.             // All Apps interface in the foreground, load All Apps first. Otherwise, load the  
  4.             // workspace first (default).  
  5.             final Callbacks cbk = mCallbacks.get();  
  6.             final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;  
  7.   
  8.             keep_running: {  
  9.                 // Elevate priority when Home launches for the first time to avoid  
  10.                 // starving at boot time. Staring at a blank home is not cool.  
  11.                 synchronized (mLock) {  
  12.                     if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +  
  13.                             (mIsLaunching ? "DEFAULT" : "BACKGROUND"));  
  14.                     android.os.Process.setThreadPriority(mIsLaunching  
  15.                             ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);  
  16.                 }  
  17.                 if (loadWorkspaceFirst) {  
  18.                     if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");  
  19.                     loadAndBindWorkspace();  
  20.                 } else {  
  21.                     if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");  
  22.                     loadAndBindAllApps();  
  23.                 }  
  24.   
  25.                 if (mStopped) {  
  26.                     break keep_running;  
  27.                 }  
  28.   
  29.                 // Whew! Hard work done.  Slow us down, and wait until the UI thread has  
  30.                 // settled down.  
  31.                 synchronized (mLock) {  
  32.                     if (mIsLaunching) {  
  33.                         if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");  
  34.                         android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  35.                     }  
  36.                 }  
  37.                 waitForIdle();  
  38.   
  39.                 // second step  
  40.                 if (loadWorkspaceFirst) {  
  41.                     if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");  
  42.                     loadAndBindAllApps();  
  43.                 } else {  
  44.                     if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");  
  45.                     loadAndBindWorkspace();  
  46.                 }  
  47.   
  48.                 // Restore the default thread priority after we are done loading items  
  49.                 synchronized (mLock) {  
  50.                     android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);  
  51.                 }  
  52.             }  
  53.   
  54.   
  55.             // Update the saved icons if necessary  
  56.             if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");  
  57.             for (Object key : sDbIconCache.keySet()) {  
  58.                 updateSavedIcon(mContext, (ShortcutInfo) key, sDbIconCache.get(key));  
  59.             }  
  60.             sDbIconCache.clear();  
  61.   
  62.             // Clear out this reference, otherwise we end up holding it until all of the  
  63.             // callback runnables are done.  
  64.             mContext = null;  
  65.   
  66.             synchronized (mLock) {  
  67.                 // If we are still the last one to be scheduled, remove ourselves.  
  68.                 if (mLoaderTask == this) {  
  69.                     mLoaderTask = null;  
  70.                 }  
  71.             }  
  72.         }  

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

 

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

先根据:

[java] view plaincopyprint?
  1. final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  
  2.             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  

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

[java] view plaincopyprint?
  1. List<ResolveInfo> apps=packageManager.queryIntentActivities(mainIntent, 0);  

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

[java] view plaincopyprint?
  1. Collections.sort(apps,new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));  

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

[java] view plaincopyprint?
  1. for (int j=0; i<N && j<batchSize; j++) {  
  2.                     // This builds the icon bitmaps.  
  3.                     mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i),  
  4.                             mIconCache, mLabelCache));  
  5.                     i++;  
  6.                 }  

调到AllAppsList.java中的add方法:

[java] view plaincopyprint?
  1. public void add(ApplicationInfo info) {  
  2.         if (findActivity(data, info.componentName)) {  
  3.             return;  
  4.         }  
  5.         data.add(info);  
  6.         added.add(info);  
  7.     }  

这个added的定义就是:

[java] view plaincopyprint?
  1. public ArrayList<ApplicationInfo> added =  
  2.             new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);  

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

[java] view plaincopyprint?
  1. final ArrayList<ApplicationInfo> added = mAllAppsList.added;  
  2.                 mAllAppsList.added = new ArrayList<ApplicationInfo>();  
  3.   
  4.                 mHandler.post(new Runnable() {  
  5.                     public void run() {  
  6.                         final long t = SystemClock.uptimeMillis();  
  7.                         if (callbacks != null) {  
  8.                             if (first) {  
  9.                                 callbacks.bindAllApplications(added);  
  10.                             } else {  
  11.                                 callbacks.bindAppsAdded(added);  
  12.                             }  
  13.                             if (DEBUG_LOADERS) {  
  14.                                 Log.d(TAG, "bound " + added.size() + " apps in "  
  15.                                     + (SystemClock.uptimeMillis() - t) + "ms");  
  16.                             }  
  17.                         } else {  
  18.                             Log.i(TAG, "not binding apps: no Launcher activity");  
  19.                         }  
  20.                     }  
  21.                 });  

        

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

[java] view plaincopyprint?
  1. mAppsCustomizeContent.setApps(apps);  

       

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

[java] view plaincopyprint?
  1. public void setApps(ArrayList<ApplicationInfo> list) {  
  2.         mApps = list;  
  3.         Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);  
  4.         updatePageCounts();  
  5.   
  6.         // The next layout pass will trigger data-ready if both widgets and apps are set, so   
  7.         // request a layout to do this test and invalidate the page data when ready.  
  8.         if (testDataReady()) requestLayout();  
  9.     }  

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

 

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

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

[java] view plaincopyprint?
  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  2.         int width = MeasureSpec.getSize(widthMeasureSpec);  
  3.         int height = MeasureSpec.getSize(heightMeasureSpec);  
  4.         if (!isDataReady()) {  
  5.             if (testDataReady()) {  
  6.                 setDataIsReady();  
  7.                 setMeasuredDimension(width, height);  
  8.                 onDataReady(width, height);  
  9.             }  
  10.         }  
  11.   
  12.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  13.     }  

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

[java] view plaincopyprint?
  1. /** 
  2.      * This differs from isDataReady as this is the test done if isDataReady is not set. 
  3.      */  
  4.     private boolean testDataReady() {  
  5.         // We only do this test once, and we default to the Applications page, so we only really  
  6.         // have to wait for there to be apps.  
  7.         // TODO: What if one of them is validly empty  
  8.         return !mApps.isEmpty() && !mWidgets.isEmpty();  
  9.     }  

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

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

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

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

[java] view plaincopyprint?
  1. // Update all the pages  
  2.             syncPages();  
[java] view plaincopyprint?
  1. public void syncPages() {  
  2.         removeAllViews();  
  3.         cancelAllTasks();  
  4.   
  5.         Context context = getContext();  
  6.         for (int j = 0; j < mNumWidgetPages; ++j) {  
  7.             PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX,  
  8.                     mWidgetCountY);  
  9.             setupPage(layout);  
  10.             addView(layout, new PagedViewGridLayout.LayoutParams(LayoutParams.MATCH_PARENT,  
  11.                     LayoutParams.MATCH_PARENT));  
  12.         }  
  13.   
  14.         for (int i = 0; i < mNumAppsPages; ++i) {  
  15.             PagedViewCellLayout layout = new PagedViewCellLayout(context);  
  16.             setupPage(layout);  
  17.             addView(layout);  
  18.         }  
  19.     }  

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

[java] view plaincopyprint?
  1. // Load any pages that are necessary for the current window of views  
  2.            loadAssociatedPages(mCurrentPage, immediateAndOnly);  
  3.            requestLayout();  

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

[java] view plaincopyprint?
  1. @Override  
  2.     public void syncPageItems(int page, boolean immediate) {  
  3.         if (page < mNumAppsPages) {  
  4.             syncAppsPageItems(page, immediate);  
  5.         } else {  
  6.             syncWidgetPageItems(page - mNumAppsPages, immediate);  
  7.         }  
  8.     }  

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

[java] view plaincopyprint?
  1. public void syncAppsPageItems(int page, boolean immediate) {  
  2.         // ensure that we have the right number of items on the pages  
  3.         int numCells = mCellCountX * mCellCountY;  
  4.         int startIndex = page * numCells;  
  5.         int endIndex = Math.min(startIndex + numCells, mApps.size());  
  6.         PagedViewCellLayout layout = (PagedViewCellLayout) getPageAt(page);  
  7.   
  8.         layout.removeAllViewsOnPage();  
  9.         ArrayList<Object> items = new ArrayList<Object>();  
  10.         ArrayList<Bitmap> images = new ArrayList<Bitmap>();  
  11.         for (int i = startIndex; i < endIndex; ++i) {  
  12.             ApplicationInfo info = mApps.get(i);  
  13.             PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(  
  14.                     R.layout.apps_customize_application, layout, false);  
  15.             icon.applyFromApplicationInfo(info, true, mHolographicOutlineHelper);  
  16.             icon.setOnClickListener(this);  
  17.             icon.setOnLongClickListener(this);  
  18.             icon.setOnTouchListener(this);  
  19.             icon.setOnKeyListener(this);  
  20.   
  21.             int index = i - startIndex;  
  22.             int x = index % mCellCountX;  
  23.             int y = index / mCellCountX;  
  24.             layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));  
  25.   
  26.             items.add(info);  
  27.             images.add(info.iconBitmap);  
  28.         }  
  29.   
  30.         layout.createHardwareLayers();  
  31.   
  32.         /* TEMPORARILY DISABLE HOLOGRAPHIC ICONS 
  33.         if (mFadeInAdjacentScreens) { 
  34.             prepareGenerateHoloOutlinesTask(page, items, images); 
  35.         } 
  36.         */  
  37.     }  

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

 

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

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

[java] view plaincopyprint?
  1. // Update customization drawer _after_ restoring the states  
  2.         if (mAppsCustomizeContent != null) {  
  3.             mAppsCustomizeContent.onPackagesUpdated();  
  4.         }  

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

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

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

[java] view plaincopyprint?
  1. public void updatePackages() {  
  2.         // Get the list of widgets and shortcuts  
  3.         boolean wasEmpty = mWidgets.isEmpty();  
  4.         mWidgets.clear();  
  5.         List<AppWidgetProviderInfo> widgets =  
  6.             AppWidgetManager.getInstance(mLauncher).getInstalledProviders();  
  7.         Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);  
  8.         List<ResolveInfo> shortcuts = mPackageManager.queryIntentActivities(shortcutsIntent, 0);  
  9.         for (AppWidgetProviderInfo widget : widgets) {  
  10.             if (widget.minWidth > 0 && widget.minHeight > 0) {  
  11.                 mWidgets.add(widget);  
  12.             } else {  
  13.                 Log.e(LOG_TAG, "Widget " + widget.provider + " has invalid dimensions (" +  
  14.                         widget.minWidth + ", " + widget.minHeight + ")");  
  15.             }  
  16.         }  
  17.         mWidgets.addAll(shortcuts);  
  18.         Collections.sort(mWidgets,  
  19.                 new LauncherModel.WidgetAndShortcutNameComparator(mPackageManager));  
  20.         updatePageCounts();  
  21.   
  22.         if (wasEmpty) {  
  23.             // The next layout pass will trigger data-ready if both widgets and apps are set, so request  
  24.             // a layout to do this test and invalidate the page data when ready.  
  25.             if (testDataReady()) requestLayout();  
  26.         } else {  
  27.             cancelAllTasks();  
  28.             invalidatePageData();  
  29.         }  
  30.     }  

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

 

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

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

[java] view plaincopyprint?
  1. @Override  
  2.     public boolean onLongClick(View v) {  
  3.         // Return early if this is not initiated from a touch  
  4.         if (!v.isInTouchMode()) return false;  
  5.         // Return early if we are still animating the pages  
  6.         if (mNextPage != INVALID_PAGE) return false;  
  7.         // When we have exited all apps or are in transition, disregard long clicks  
  8.         if (!mLauncher.isAllAppsCustomizeOpen() ||  
  9.                 mLauncher.getWorkspace().isSwitchingState()) return false;  
  10.   
  11.         return beginDragging(v);  
  12.     }  

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

[java] view plaincopyprint?
  1. private void beginDraggingApplication(View v) {  
  2.         mLauncher.getWorkspace().onDragStartedWithItem(v);  
  3.         mLauncher.getWorkspace().beginDragShared(v, this);  
  4.     }  

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

今天就总结到这里吧!

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

原创粉丝点击