zoukankan      html  css  js  c++  java
  • Android4.0源码Launcher启动流程分析【android源码Launcher系列一】

        最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程。 Launcher其实是贯彻于手机的整个系统的,时时刻刻都在运行,要是Launcher不运行了,手机就得黑屏了。Launcher的 LauncherMode=singletask,所以说不管Launcher启动了哪个应用,总有个Launcher的实例在堆栈中,并且位于栈底。点 击Home键进入到Launcher,上篇Android的全局键(home键/长按耳机键)详解【android源码解析八】 中有详细的介绍。大致思路其实就是启动launcher的时候,新启动一个task。大致先说这么多,先看截图:

                                         

     

                                                                               图(1)

          上图是4.0的Launcher界面,下面我们分步来解析一下Launcher的启动过程。

         Step 0:首先要给大家介绍一下Launcher的数据库,这个数据库中存放着待机界面的图标,主屏底部的应用程序图标和桌面folder中各应用程序的图 标,ICS4.0的folder中只能放应用程序的快捷方式,shortcut不能放到这个folder中,先看截图: 



     

                                                                            图(2)

           说说各字段的含义:

                    title:表示桌面应用程序的名字,有的title为空,表示是widget的快捷方式;

                 intent:表示启动这个图标的intent放到数据库中,当click的时候就会调用这个字段,启动相应的应用程序;

           container:表示应用程序的容器,folder的容器为整数,-100:表示在桌面的程序,-101:表示是主屏底部的程序;

               screen:表示在第几个屏,folder的screen都是0, container=-101的为0,1,3,4;2为allapp的按钮;

                   cellX:表示在屏幕X轴的位置,(0,1,2,3),左上角为0点,往右依次增加;

                   cellY:表示在屏幕Y轴的位置,(0,1,2,3),左上角为0点,往下依次增加;

                  spallX:表示占X轴几个格;

                  spallY:表示占Y轴几个格;

             itemType:应用程序用0表示,shortcut用1表示,folder用2表示,widget用4表示;

        appWidgetId:-1表示不是widget,数字大于0表示才是widget;

           isShortCut:值为0表示不是应用程序的ShortCut,值为1表示是应用程序的ShortCut;

             iconType:值为0表示图标的名字被定义为包名的资源id,值为1表示图标用bitmap保存;

                     icon:表示应用程序的图标,二进制的;显示为一张图片;

           说明:folder中的应用快捷方式绑定folder---->是用container的值绑定folder的id的;

            详细的讲解请参考LauncherSettings.java这个类,有数据库字段的详细讲解;

             手 机是在第一次烧机完成后,数据库的值还没有,这时候launcher解析default_workspace.xml把这个值存到数据库中;所以说想定制 什么样的开机桌面就在default_workspace.xml中做相应的配置,具体参照我前面的博客:

    Android中源码Launcher主屏幕程序排列详解【安卓Launcher进化一】中有详细的介绍:

           i f (!convertDatabase(db)) {
                     // Populate favorites table with initial favorites
                    loadFavorites(db, R.xml.default_workspace);
            }

          Step 1:开机后先启动LauncherApplication.java这个类的onCreate()方法,下面看代码:   

       

    Java代码  收藏代码
    1. @Override  
    2.  public void onCreate() {  
    3.      super.onCreate();  
    4.   
    5.      // set sIsScreenXLarge and sScreenDensity *before* creating icon cache  
    6.      // 在创建图标缓存之前先设置sIsScreenXLarge和屏幕设备的分辨率  
    7.      final int screenSize = getResources().getConfiguration().screenLayout &  
    8.              Configuration.SCREENLAYOUT_SIZE_MASK;  
    9.      sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE ||  
    10.          screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE;  
    11.      sScreenDensity = getResources().getDisplayMetrics().density;  
    12.   
    13.      // 实例化图标缓存区的对象  
    14.      mIconCache = new IconCache(this);  
    15.      // 实例化一个LauncherModel对象,这个类是保存Launcher的内存启动状态,更新Launcher的数据库的作用  
    16.      mModel = new LauncherModel(this, mIconCache);  
    17.   
    18.      // Register intent receivers  
    19.      // 注册监听,应用package增加,删除,改变的监听。  
    20.      IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);  
    21.      filter.addAction(Intent.ACTION_PACKAGE_REMOVED);  
    22.      filter.addAction(Intent.ACTION_PACKAGE_CHANGED);  
    23.      filter.addDataScheme("package");  
    24.      registerReceiver(mModel, filter);  
    25.      filter = new IntentFilter();  
    26.      // 注册application是否可用,语言改变,方向改变的监听。4.0支持横竖屏  
    27.      filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);  
    28.      filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);  
    29.      filter.addAction(Intent.ACTION_LOCALE_CHANGED);  
    30.      filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);  
    31.      registerReceiver(mModel, filter);  
    32.      filter = new IntentFilter();  
    33.      filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);  
    34.      registerReceiver(mModel, filter);  
    35.      filter = new IntentFilter();  
    36.      filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);  
    37.      registerReceiver(mModel, filter);  
    38.   
    39.      // Register for changes to the favorites  
    40.      // 注册favorites应用程序数据库改变的监听  
    41.      ContentResolver resolver = getContentResolver();  
    42.      resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,  
    43.              mFavoritesObserver);  
    44.  }  
     

           Step 2:在LauncherApplication.java中onTerminate()的方法,解除监听的绑定;

     

    Java代码  收藏代码
    1. /** 
    2.     * There's no guarantee that this function is ever called. 
    3.     */  
    4.    @Override  
    5.    public void onTerminate() {  
    6.        super.onTerminate();  
    7.   
    8.        unregisterReceiver(mModel);  
    9.   
    10.        ContentResolver resolver = getContentResolver();  
    11.        resolver.unregisterContentObserver(mFavoritesObserver);  
    12.    }  

     

         Step 3:Step1中的数据库mFavoritesObserver监听内部类如下:

    Java代码  收藏代码
    1. /** 
    2.   * Receives notifications whenever the user favorites have changed. 
    3.   */  
    4.  private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) {  
    5.      @Override  
    6.      public void onChange(boolean selfChange) {  
    7.          mModel.startLoader(LauncherApplication.this, false);  
    8.      }  
    9.  };  
          说明:mModel.startLoader(。。,。。)是开启一个线程,设置线程的优先级NORM_PRIORITY,开始load桌面图标对应的数据库,这个过程是和Launcher.onCreate()同时进行的;
     

        Step 4: 接着我们来看看mModel.startLoader(LauncherApplication.this, false)的方法:

    Java代码  收藏代码
    1. public void startLoader(Context context, boolean isLaunching) {  
    2.      synchronized (mLock) {  
    3.          if (DEBUG_LOADERS) {  
    4.              Log.d(TAG, "startLoader isLaunching=" + isLaunching);  
    5.          }  
    6.   
    7.          // Don't bother to start the thread if we know it's not going to do anything  
    8.          if (mCallbacks != null && mCallbacks.get() != null) {  
    9.              // If there is already one running, tell it to stop.  
    10.              // also, don't downgrade isLaunching if we're already running  
    11.              isLaunching = isLaunching || stopLoaderLocked();  
    12.              mLoaderTask = new LoaderTask(context, isLaunching);  
    13.              sWorkerThread.setPriority(Thread.NORM_PRIORITY);  
    14.              sWorker.post(mLoaderTask);  
    15.          }  
    16.      }  
    17.  }  

      

       Step 5:接着我们来看看LoaderTask.java的run()方法:

     
    Java代码  收藏代码
    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. }  
    73.   
    74. public void stopLocked() {  
    75.     synchronized (LoaderTask.this) {  
    76.         mStopped = true;  
    77.         this.notify();  
    78.     }  
    79. }  
     

    加载桌面图标对应的数据库的值,这些值能把这些图标显示在屏幕上。

        Step 6:LauncherApplication.onCreate()方法启动完成后,接着开始调用Launcher.java的onCreate()方法。代码如下:

     
    Java代码  收藏代码
    1. @Override  
    2. protected void onCreate(Bundle savedInstanceState) {  
    3.     super.onCreate(savedInstanceState);  
    4.     // 得到LauncherApplication的对象app  
    5.     LauncherApplication app = ((LauncherApplication)getApplication());  
    6.     // 得到LauncherModel对象mModel,设置一个mCallbacks = new WeakReference<Callbacks>(callbacks)的  
    7.     // 回调callbacks  
    8.     mModel = app.setLauncher(this);  
    9.     // 得到图标缓存的对象mIconCache  
    10.     mIconCache = app.getIconCache();  
    11.     // 得到拖拽控制类DragController的对象  
    12.     mDragController = new DragController(this);  
    13.     // 得到一个LayoutInflater布局的对象  
    14.     mInflater = getLayoutInflater();  
    15.   
    16.     // 得到一个AppWidgetManager的对象  
    17.     mAppWidgetManager = AppWidgetManager.getInstance(this);  
    18.     // 得到LauncherAppWidgetHost的一个对象  
    19.     mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);  
    20.     // Start receiving onAppWidgetChanged calls for your AppWidgets.  
    21.     mAppWidgetHost.startListening();  
    22.   
    23.     if (PROFILE_STARTUP) {  
    24.         android.os.Debug.startMethodTracing(  
    25.                 Environment.getExternalStorageDirectory() + "/launcher");  
    26.     }  
    27.   
    28.     // 检查Locale的语言级别,mcc, mnc的改变  
    29.     checkForLocaleChange();  
    30.     // 加载Launcher.xml布局文件  
    31.     setContentView(R.layout.launcher);  
    32.     // Launcher的布局的初始化  
    33.     setupViews();  
    34.     // 第一次启动Android的展示设置向导,  
    35.     // 这个SharedPreferences中存在  
    36.     // <boolean name="cling.workspace.dismissed" value="true" />  
    37.     // 如果值为true,则不显示设置向导,为false,则显示设置向导。  
    38.     showFirstRunWorkspaceCling();  
    39.   
    40.     // 注册数据库观察者  
    41.     registerContentObservers();  
    42.   
    43.     lockAllApps();  
    44.   
    45.     mSavedState = savedInstanceState;  
    46.     restoreState(mSavedState);  
    47.   
    48.     // Update customization drawer _after_ restoring the states  
    49.     if (mAppsCustomizeContent != null) {  
    50.         mAppsCustomizeContent.onPackagesUpdated();  
    51.     }  
    52.   
    53.     if (PROFILE_STARTUP) {  
    54.         android.os.Debug.stopMethodTracing();  
    55.     }  
    56.   
    57.     if (!mRestoring) {  
    58.         mModel.startLoader(this, true);  
    59.     }  
    60.   
    61.     if (!mModel.isAllAppsLoaded()) {  
    62.         ViewGroup appsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent();  
    63.         mInflater.inflate(R.layout.apps_customize_progressbar, appsCustomizeContentParent);  
    64.     }  
    65.   
    66.     // For handling default keys  
    67.     mDefaultKeySsb = new SpannableStringBuilder();  
    68.     Selection.setSelection(mDefaultKeySsb, 0);  
    69.   
    70.     // 注册系统对话框消失的监听  
    71.     IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);  
    72.     registerReceiver(mCloseSystemDialogsReceiver, filter);  
    73.   
    74.     boolean searchVisible = false;  
    75.     boolean voiceVisible = false;  
    76.     // If we have a saved version of these external icons, we load them up immediately  
    77.     // 如果我们已经保存了外部图标的版本,我们立即加载它们  
    78.     int coi = getCurrentOrientationIndexForGlobalIcons();  
    79.     if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null ||  
    80.             sAppMarketIcon[coi] == null) {  
    81.         updateAppMarketIcon();  
    82.         searchVisible = updateGlobalSearchIcon();  
    83.         voiceVisible = updateVoiceSearchIcon(searchVisible);  
    84.     }  
    85.     if (sGlobalSearchIcon[coi] != null) {  
    86.          updateGlobalSearchIcon(sGlobalSearchIcon[coi]);  
    87.          searchVisible = true;  
    88.     }  
    89.     if (sVoiceSearchIcon[coi] != null) {  
    90.         updateVoiceSearchIcon(sVoiceSearchIcon[coi]);  
    91.         voiceVisible = true;  
    92.     }  
    93.     if (sAppMarketIcon[coi] != null) {  
    94.         updateAppMarketIcon(sAppMarketIcon[coi]);  
    95.     }  
    96.     mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);  
    97.   
    98.     // On large interfaces, we want the screen to auto-rotate based on the current orientation  
    99.     if (LauncherApplication.isScreenLarge() || Build.TYPE.contentEquals("eng")) {  
    100.         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);  
    101.     }  
    102. }  
     

        Step 7:其中LauncherModel这个类中有个回调接口,具体定义如下:

    Java代码  收藏代码
    1. public interface Callbacks {  
    2.        public boolean setLoadOnResume();  
    3.        public int getCurrentWorkspaceScreen();  
    4.        public void startBinding();  
    5.        public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);  
    6.        public void bindFolders(HashMap<Long,FolderInfo> folders);  
    7.        public void finishBindingItems();  
    8.        public void bindAppWidget(LauncherAppWidgetInfo info);  
    9.        public void bindAllApplications(ArrayList<ApplicationInfo> apps);  
    10.        public void bindAppsAdded(ArrayList<ApplicationInfo> apps);  
    11.        public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);  
    12.        public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);  
    13.        public void bindPackagesUpdated();  
    14.        public boolean isAllAppsVisible();  
    15.        public void bindSearchablesChanged();  
    16.    }  
     
    对LauncherModel进行初始化的时候mModel = app.setLauncher(this);---->mModel.initialize(launcher);----->

                  public void initialize(Callbacks callbacks) {
                          synchronized (mLock) {
                                  mCallbacks = new WeakReference<Callbacks>(callbacks);
                           }
                   }

    这 个callbacks就是定义的接口回调,具体实现是在Launcher.java中定义的,启动Launcher的过程中,这些实现是异步来实现的。还 有Launcher.java的onResume()方法没有讲解,到这儿基本上Android的Launcher已经启动起来了,这个 onResume()我研究后再更新。

          欢迎各界同僚留言指正错误和拍砖!欢迎留言!

  • 相关阅读:
    GridView中实现点击某行的任意位置就选中该行
    HtmlEncode、HtmlDecode、UrlEncode、UrlDecode
    Asp.net中从后台中如何获取html控件
    关于IE10出现LinkButton点击无效的解决方案
    【转】关于IE7 z-index问题完美解决方案
    【转】Asp.net中时间格式化的6种方法详细总结
    【转】asp.net Cookie值中文乱码问题解决方法
    asp.net中Cookie的用法【转】
    【转】ASP.NET Cookies简单应用 记住用户名和密码
    ASP.NET Cookie 概述【转】
  • 原文地址:https://www.cnblogs.com/xiaochao1234/p/4054109.html
Copyright © 2011-2022 走看看