Android Launcher全面剖析

2024-03-31 08:32
文章标签 android 剖析 全面 launcher

本文主要是介绍Android Launcher全面剖析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.csdn.net/andy_android/article/details/6966073


  Android  Launcher全面剖析

            首先来说说我为什么写这篇文章,最近公司要我负责搞Launcher,网上一查这方面的资料比较少,并且不全,研究起来相当困难,所以就写了这篇文章,希望对大家有帮助。这篇文章是相当长的,希望读者能耐心读下去,实际上也花了我很长时间来写。好了闲话少说,我们切入正题。

             这篇文章我会讲以下Launcher内容:

                        Launcher  UI总体架构

                       Launcher   Res下的Layout

                       Launcher   Res下的Xml文件

                       Launcher    Manifest文件

                       Launcher   常用类介绍

                       Launcher    启动过程

                      Launcher    widget添加过程

                       Launcher      celllayout的介绍

                     

一      Launcher  UI总体架构

         

         Home screen可以说是一个手机的最重要应用,就像一个门户网站的首页,直接决定了用户的第一印象。下面对home screen做一简要分析。

home screen的代码位于packages/apps/Launcher目录。从文件launcher.xml,workspace_screen.xml可获知home screen的UI结构如下图所示: 

 

整个homescreen是一个包含三个child view的FrameLayout(com.android.launcher.DragLayer)。 

第一个child就是桌面com.android.launcher.Workspace。这个桌面又包含三个child。每个child就对应一个桌面。这就是你在Android上看到的三个桌面。每个桌面上可以放置下列对象:应用快捷方式,appwidget和folder。

第二个child是一个SlidingDrawer控件,这个控件由两个子控件组成。一个是com.android.launcher.HandleView,就是Android桌面下方的把手,当点击这个把手时,另一个子控件,com.android.launcher.AllAppsGridView就会弹出,这个子控件列出系统中当前安装的所有类型为category.launcher的Activity。

第三个child是com.android.launcher.DeleteZone。当用户在桌面上长按一个widget时,把手位置就会出现一个垃圾桶形状的控件,就是这个控件。

在虚拟桌面上可以摆放四种类型的对象: 
1. ITEM_SHORTCUT,应用快捷方式 
2. ITEM_APPWIDGET,app widget 
3. ITEM_LIVE_FOLDER,文件夹 
4. ITEM_WALLPAPER,墙纸。 

类AddAdapter(AddAdapter.java)列出了这四个类型对象。当用户在桌面空白处长按时,下列函数序列被执行: 
Launcher::onLongClick --> 
Launcher::showAddDialog --> 
Launcher::showDialog(DIALOG_CREATE_SHORTCUT); --> 
Launcher::onCreateDialog --> 
Launcher::CreateShortcut::createDialog:这个函数创建一个弹出式对话框,询问用户是要添加什么(快捷方式,appwidget, 文件夹和墙纸)其内容就来自AddAdapter。

类Favorites(LauncherSettings.java)和类LauncherProvider定义了一个content provider,用来存储桌面上可以放置的几个对象,包括shortcut, search和clock等。

类DesktopItemsLoader负责将桌面上所有的对象从content provider中提取。 

线程private ApplicationsLoader mApplicationsLoader负责从包管理器中获取系统中安装的应用列表。(之后显示在AllAppsGridView上)。ApplicationsLoader::run实现:
1)通过包管理器列出系统中所有类型为Launcher,action为MAIN的activity; 
2)对每一个Activity, 
      a) 将Activity相关元数据信息,如title, icon, intent等缓存到appInfoCache; 
      b) 填充到ApplicationsAdapter 中。填充过程中用到了一些小技巧,每填充4(UI_NOTIFICATION_RATE)个activity更新一下相应view。

在Launcher::onCreate中,函数startLoaders被调用。而该函数接着调用loadApplications和loadUserItems,分别获取系统的应用列表,以及显示在桌面上的对象列表(快捷方式,appwidget,folder等)。

Launcher上排列的所有应用图标由AllAppsGridView对象呈现。这个对象是一个GridView。其对应的Adapter是ApplicationsAdapter,对应的model则是ApplicationInfo数组。数组内容是由ApplicationsLoader装载的。
private class ApplicationsLoader implements Runnable。

 

Launcher中的AndroidManifest.xml可以看出整个Launcher的代码结构。

首先,是一些权限的声明。例如:

view plain copy to clipboard
  1. <uses-permission android:name="android.permission.CALL_PHONE" />  
  2. <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />  
[c-sharp]  view plain copy
  1. <uses-permission android:name="android.permission.CALL_PHONE" />  
  2. <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />  
 

这部分可以略过;

其次,Application的构成,如上图:

 

(1)LauncherHomeScreenActivity

 

view plain copy to clipboard
  1. <intent-filter>  
  2.  <action android:name="android.intent.action.MAIN" />  
  3.  <category android:name="android.intent.category.HOME"/>  
  4.  <category android:name="android.intent.category.DEFAULT" />  
  5. <category android:name="android.intent.category.MONKEY" /> </intent-filter>  

[c-sharp]  view plain copy
  1. <intent-filter>  
  2.  <action android:name="android.intent.action.MAIN" />  
  3.  <category android:name="android.intent.category.HOME"/>  
  4.  <category android:name="android.intent.category.DEFAULT" />  
  5. <category android:name="android.intent.category.MONKEY" /> </intent-filter>  

上面这段代码就标志着它是开机启动后HomeActivity。通过Launcher.javaonCreat()的分析我们可以大致把握屏幕的主要活动:

 

view plain copy to clipboard
  1. protected void onCreate(Bundle savedInstanceState) {  
  2.         super.onCreate(savedInstanceState);  
  3. //把xml文件的内容实例化到View中   
  4.         mInflater = getLayoutInflater();  
  5. //监听应用程序控件改变事件   
  6.         mAppWidgetManager = AppWidgetManager.getInstance(this);  
  7.         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);  
  8.         mAppWidgetHost.startListening();  
  9. // 用于调试?       
  10.         if (PROFILE_STARTUP) {  
  11.             android.os.Debug.startMethodTracing("/sdcard/launcher");  
  12.         }  
  13.   //监听locale,mcc,mnc是否改变,如果改变,则重写新配置   
  14.       //mcc:mobile country code(国家代码China 460); mnc:mobile network code(网络代码)   
  15.         checkForLocaleChange();  
  16.        /*This allows such applications to have a virtual wallpaper that is larger than the physical screen, matching the size of their workspace.*/  
  17.         setWallpaperDimension();  
  18. //显示主屏幕UI元素,workspace,slidingdrawer(handleview and appgridview),deletezone   
  19.         setContentView(R.layout.launcher);  
  20. //Finds all the views we need and configure them properly.   
  21. //完成workspace,slidingdrawer,deletezone的各种事件操作和监听   
  22.         setupViews();  
  23. //Registers various intent receivers.   
  24. //允许其他应用对本应用的操作   
  25.         registerIntentReceivers();  
  26. //Registers various content observers.   
  27. //例如,注册一个内容观察者跟踪喜爱的应用程序   
  28.         registerContentObservers();  
  29. //重新保存前一个状态(目的??)   
  30.         mSavedState = savedInstanceState;  
  31.         restoreState(mSavedState);  
  32. //调试?   
  33.         if (PROFILE_STARTUP) {  
  34.             android.os.Debug.stopMethodTracing();  
  35.         }  
  36. //Loads the list of installed applications in mApplications.   
  37.         if (!mRestoring) {  
  38.             startLoaders();  
  39.         }  
  40.         // For handling default keys??   
  41.         mDefaultKeySsb = new SpannableStringBuilder();  
  42.         Selection.setSelection(mDefaultKeySsb, 0);  
  43.     }  
[c-sharp]  view plain copy
  1. protected void onCreate(Bundle savedInstanceState) {  
  2.         super.onCreate(savedInstanceState);  
  3. //把xml文件的内容实例化到View中  
  4.         mInflater = getLayoutInflater();  
  5. //监听应用程序控件改变事件  
  6.         mAppWidgetManager = AppWidgetManager.getInstance(this);  
  7.         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);  
  8.         mAppWidgetHost.startListening();  
  9. // 用于调试?      
  10.         if (PROFILE_STARTUP) {  
  11.             android.os.Debug.startMethodTracing("/sdcard/launcher");  
  12.         }  
  13.   //监听locale,mcc,mnc是否改变,如果改变,则重写新配置  
  14.       //mcc:mobile country code(国家代码China 460); mnc:mobile network code(网络代码)  
  15.         checkForLocaleChange();  
  16.        /*This allows such applications to have a virtual wallpaper that is larger than the physical screen, matching the size of their workspace.*/  
  17.         setWallpaperDimension();  
  18. //显示主屏幕UI元素,workspace,slidingdrawer(handleview and appgridview),deletezone  
  19.         setContentView(R.layout.launcher);  
  20. //Finds all the views we need and configure them properly.  
  21. //完成workspace,slidingdrawer,deletezone的各种事件操作和监听  
  22.         setupViews();  
  23. //Registers various intent receivers.  
  24. //允许其他应用对本应用的操作  
  25.         registerIntentReceivers();  
  26. //Registers various content observers.  
  27. //例如,注册一个内容观察者跟踪喜爱的应用程序  
  28.         registerContentObservers();  
  29. //重新保存前一个状态(目的??)  
  30.         mSavedState = savedInstanceState;  
  31.         restoreState(mSavedState);  
  32. //调试?  
  33.         if (PROFILE_STARTUP) {  
  34.             android.os.Debug.stopMethodTracing();  
  35.         }  
  36. //Loads the list of installed applications in mApplications.  
  37.         if (!mRestoring) {  
  38.             startLoaders();  
  39.         }  
  40.         // For handling default keys??  
  41.         mDefaultKeySsb = new SpannableStringBuilder();  
  42.         Selection.setSelection(mDefaultKeySsb, 0);  
  43.     }  
 

方法onActivityResult():完成在workspace上增加shortcutappwidgeLivefolder

方法onSaveInstantceState()和onRestoreInstanceState():为了防止SensorLandPort布局自动切换时数据被置空,通过onSaveInstanceState方法可以保存当前窗口的状态,在即将布局切换前将当前的Activity压入历史堆栈。如果我们的Activity在后台没有因为运行内存吃紧被清理,则切换时回触发onRestoreIntanceState()


(2)WallpaperChooser:设置墙纸。

同理我们从onCreat()作为入口来分析这个活动的主要功能。

view plain copy to clipboard
  1. public void onCreate(Bundle icicle) {  
  2.         super.onCreate(icicle);  
  3. //设置允许改变的窗口状态,需在 setContentView 之前调用   
  4.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  5. / /添加墙纸资源,将资源标识符加入到动态数组中  
  6.         findWallpapers();  
  7. //显示墙纸设置屏幕的UI元素,Imageview,Gallery and Button(LinearLayout)   
  8.         setContentView(R.layout.wallpaper_chooser);  
  9. //图片查看功能的实现   
  10.         mGallery = (Gallery) findViewById(R.id.gallery);  
  11.         mGallery.setAdapter(new ImageAdapter(this));  
  12.         mGallery.setOnItemSelectedListener(this);  
  13.         mGallery.setCallbackDuringFling(false);  
  14. //Button事件监听,点击选择setWallpaper(Resid)   
  15.         findViewById(R.id.set).setOnClickListener(this);  
  16.         mImageView = (ImageView) findViewById(R.id.wallpaper);  
  17.     }  
[c-sharp]  view plain copy
  1. public void onCreate(Bundle icicle) {  
  2.         super.onCreate(icicle);  
  3. //设置允许改变的窗口状态,需在 setContentView 之前调用  
  4.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  5. / /添加墙纸资源,将资源标识符加入到动态数组中  
  6.         findWallpapers();  
  7. //显示墙纸设置屏幕的UI元素,Imageview,Gallery and Button(LinearLayout)  
  8.         setContentView(R.layout.wallpaper_chooser);  
  9. //图片查看功能的实现  
  10.         mGallery = (Gallery) findViewById(R.id.gallery);  
  11.         mGallery.setAdapter(new ImageAdapter(this));  
  12.         mGallery.setOnItemSelectedListener(this);  
  13.         mGallery.setCallbackDuringFling(false);  
  14. //Button事件监听,点击选择setWallpaper(Resid)  
  15.         findViewById(R.id.set).setOnClickListener(this);  
  16.         mImageView = (ImageView) findViewById(R.id.wallpaper);  
  17.     }  
 

 

(3)default_searchable

对于home中任意的Acitivty,使能系统缺省Search模式,这样就可以使用android系统默认的search UI


 

(4)InstallShortcutReceiver

继承自BroadcastReceiver,重写onReceier()方法,对于发送来的Broadcast(这里指Intent)进行过滤(IntentFilt)并且响应(这里是InstallShortcut())。这里分析下onReceive():

view plain copy to clipboard
  1. <!-- Enable system-default search mode for any activity in Home -->  
  2. <!-- Intent received used to install shortcuts from other applications -->  
  3. public void onReceive(Context context, Intent data) {  
  4.        //接受并过滤Intent   
  5. if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {  
  6.             return;  
  7.         }  
  8.       //获取屏幕   
  9.         int screen = Launcher.getScreen();  
  10. //安装快捷方式   
  11.         if (!installShortcut(context, data, screen)) {  
  12.             //如果屏幕已满,搜寻其他屏幕   
  13.             for (int i = 0; i < Launcher.SCREEN_COUNT; i++) {  
  14.                 if (i != screen && installShortcut(context, data, i)) break;  
  15.             }  
  16.         }  
  17.     }  
[c-sharp]  view plain copy
  1. <!-- Enable system-default search mode for any activity in Home -->  
  2. <!-- Intent received used to install shortcuts from other applications -->  
  3. public void onReceive(Context context, Intent data) {  
  4.        //接受并过滤Intent  
  5. if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {  
  6.             return;  
  7.         }  
  8.       //获取屏幕  
  9.         int screen = Launcher.getScreen();  
  10. //安装快捷方式  
  11.         if (!installShortcut(context, data, screen)) {  
  12.             //如果屏幕已满,搜寻其他屏幕  
  13.             for (int i = 0; i < Launcher.SCREEN_COUNT; i++) {  
  14.                 if (i != screen && installShortcut(context, data, i)) break;  
  15.             }  
  16.         }  
  17.     }  
 

其中IntallShortcut()方法:首先,对传入的坐标进行判断(findEmptyCell(),如果是空白位置,则可以放置快捷方式;其次,缺省情况下,我们允许创建重复的快捷方式,具体创建过程(addShortcut())就是把快捷方式的信息传入数据库(addItemToDatabase())。


(5)UninstallShortcutReceiver

同理,UninstallShortcutReceiver()继承自BroadcastReceiver(),实现onReceiver()方法。定义一个ContentResolver对象完成对数据库的访问和操作(通过URI定位),进而删除快捷方式 。


 

(6)LauncherProvider

继承自ContentProvider(),主要是建立一个数据库来存放HomeScreen中的数据信息,并通过内容提供者来实现其他应用对launcher中数据的访问和操作。

重写了ContentProvider()中的方法

getType():返回数据类型。如果有自定义的全新类型,通过此方法完成数据的访问。

query():查询数据。传入URI,返回一个Cursor对象,通过Cursor完成对数据库数据的遍历访问。

Insert():插入一条数据。

bulkInsert():大容量数据的插入。

delete():删除一条数据。

update():更改一条数据。

sendNotify():发送通知。

DatabaseHelper继承自一个封装类SQLiteOpenHelper(),方便了数据库的管理和维护。

重写的方法:

onCreate():创建一个表。其中db.execSQL()方法执行一条SQL语句,通过一条字符串执行相关的操作。当然,对SQL基本语句应该了解。

onUpgrade():升级数据库。

HomeScreen数据库操作的一些方法:

addClockWidget(),addSearchWidget,addShortcut,addAppShortcut,

loadFavorites(),launcherAppWidgetBinder(),convertWidget(),updateContactsShortcuts(),

copyFromCursor()

补充:

类AddAdapter(AddAdapter.java)列出了这四个类型对象。当用户在桌面空白处长按时,下列函数序列被执行:

Launcher::onLongClick -->

Launcher::showAddDialog -->

Launcher::showDialog(DIALOG_CREATE_SHORTCUT); -->

Launcher::onCreateDialog -->

Launcher::CreateShortcut::createDialog:这个函数创建一个弹出式对话框,询问用户是要添加什么(快捷方式,appwidget, 文件夹和墙纸)其内容就来自AddAdapter。

类DesktopItemsLoader负责将桌面上所有的对象从content provider中提取。

线程private ApplicationsLoader mApplicationsLoader负责从包管理器中获取系统中安装的应用列表。(之后显示在AllAppsGridView上)。ApplicationsLoader::run实现:
1)通过包管理器列出系统中所有类型为Launcher,action为MAIN的activity;
2)对每一个Activity,
       a) 将Activity相关元数据信息,如title, icon, intent等缓存到appInfoCache;
         b) 填充到ApplicationsAdapter 中。填充过程中用到了一些小技巧,每填充4(UI_NOTIFICATION_RATE)个activity更新一下相应view。

在Launcher::onCreate中,函数startLoaders被调用。而该函数接着调用loadApplications和loadUserItems,分别获取系统的应用列表,以及显示在桌面上的对象列表(快捷方式,appwidget,folder等)。
Launcher上排列的所有应用图标由AllAppsGridView对象呈现。这个对象是一个GridView。其对应的Adapter是ApplicationsAdapter,对应的model则是ApplicationInfo数组。数组内容是由ApplicationsLoader装载的。

 

 

二         Launcher   Res下的Layout

 

             现在我们来看res目录里的布局文件,布局文件都放在layout*目录里。
本以为launcher的layout都放在layout目录里,由于屏幕放置方式的不同会对桌面造成一定的影响,所以googleAndroid项目组就决定因地制宜。比如当你横着放置屏幕的时候就会使用layout-land目录里的文件来对系统launcher进行布局,竖着屏幕的时候会使用layout-port内的布局文件来对launcher来布局。
横竖屏幕切换之际,会重新进行布局。那我们就以layout-land目录为例来看吧。
layout-land/launcuer.xml
 

XML/HTML代码
  1. <?xml version="1.0" encoding="utf-8"?>     
  2. <!--     
  3. /*     
  4. **     
  5. ** Copyright 2008, The Android Open Source Project     
  6. **     
  7. ** Licensed under the Apache License, Version 2.0 (the "License");     
  8. ** you may not use this file except in compliance with the License.     
  9. ** You may obtain a copy of the License at     
  10. **     
  11. **     http://www.apache.org/licenses/LICENSE-2.0     
  12. **     
  13. ** Unless required by applicable law or agreed to in writing, software     
  14. ** distributed under the License is distributed on an "AS IS" BASIS,     
  15. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.     
  16. ** See the License for the specific language governing permissions and     
  17. ** limitations under the License.     
  18. */     
  19. -->     
  20. <manifest     
  21.     xmlns:android="http://schemas.android.com/apk/res/android"     
  22.     package="com.android.zkx_launcher"     
  23.     android:sharedUserId="@string/sharedUserId"     
  24.     >     
  25.      
  26.     <!-- 应用程序的包名 -->     
  27.     <!-- <original-package android:name="com.android.zkx_launcher2" /> -->     
  28.     <!-- 对系统资源的访问权限 -->     
  29.     <permission     
  30.         android:name="com.android.zkx_launcher.permission.INSTALL_SHORTCUT"     
  31.         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"     
  32.         android:protectionLevel="normal"     
  33.         android:label="@string/permlab_install_shortcut"     
  34.         android:description="@string/permdesc_install_shortcut" />     
  35.     <permission     
  36.         android:name="com.android.zkx_launcher.permission.UNINSTALL_SHORTCUT"     
  37.         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"     
  38.         android:protectionLevel="normal"     
  39.         android:label="@string/permlab_uninstall_shortcut"     
  40.         android:description="@string/permdesc_uninstall_shortcut"/>     
  41.     <permission     
  42.         android:name="com.android.zkx_launcher.permission.READ_SETTINGS"     
  43.         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"     
  44.         android:protectionLevel="normal"     
  45.         android:label="@string/permlab_read_settings"     
  46.         android:description="@string/permdesc_read_settings"/>     
  47.     <permission     
  48.         android:name="com.android.zkx_launcher.permission.WRITE_SETTINGS"     
  49.         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"     
  50.         android:protectionLevel="normal"     
  51.         android:label="@string/permlab_write_settings"     
  52.         android:description="@string/permdesc_write_settings"/>     
  53.      
  54.     <uses-permission android:name="android.permission.CALL_PHONE" />     
  55.     <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />     
  56.     <uses-permission android:name="android.permission.GET_TASKS" />     
  57.     <uses-permission android:name="android.permission.READ_CONTACTS"/>     
  58.     <uses-permission android:name="android.permission.SET_WALLPAPER" />     
  59.     <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />     
  60.     <uses-permission android:name="android.permission.VIBRATE" />     
  61.     <uses-permission android:name="android.permission.WRITE_SETTINGS" />     
  62.     <uses-permission android:name="android.permission.BIND_APPWIDGET" />     
  63.     <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />     
  64.     <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />     
  65.     <!-- 对应用程序的配置 -->     
  66.     <application     
  67.         android:name="LauncherApplication"     
  68.         android:process="@string/process"     
  69.         android:label="@string/application_name"     
  70.         android:icon="@drawable/ic_launcher_home">     
  71.         <!--配置应用程序额的名字,进程,标签,和图标             
  72.             label的值为values/strings.xml中application_name 键值对的值     
  73.             icon为drawable目录下名为的ic_launcher_home的图片     
  74.              实际上该图片的位置位于drawable-hdpi(高分辨率)目录下,是个小房子这个主要是为了支持多分辨率的.     
  75.             hdpi里面主要放高分辨率的图片,     
  76.              如WVGA (480x800),FWVGA (480x854)mdpi里面主要放中等分辨率的图片,     
  77.              如HVGA (320x480)ldpi里面主要放低分辨率的图片,     
  78.              如QVGA (240x320)系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片     
  79.              所以在开发程序时为了兼容不同平台不同屏幕,建议各自文件夹根据需求均存放不同版本图片.     
  80.                   
  81.             只需要在res目录下创建不同的layout文件夹,     
  82.             比如layout-640x360,layout-800x480,     
  83.             所有的layout文件在编译之后都会写入R.java里,     
  84.             而系统会根据屏幕的大小自己选择合适的layout进行使用 -->       
  85.                  
  86.         <!--设置intent-filter可以先启动该Activity -->     
  87.         <activity     
  88.             android:name="Launcher"     
  89.             android:launchMode="singleTask"     
  90.             android:clearTaskOnLaunch="true"     
  91.             android:stateNotNeeded="true"     
  92.             android:theme="@style/Theme"     
  93.             android:screenOrientation="nosensor"     
  94.             android:windowSoftInputMode="stateUnspecified|adjustPan">     
  95.             <intent-filter>     
  96.                 <action android:name="android.intent.action.MAIN" />     
  97.                 <category android:name="android.intent.category.HOME" />     
  98.                 <category android:name="android.intent.category.DEFAULT" />     
  99.                 <category android:name="android.intent.category.MONKEY"/>     
  100.             </intent-filter>     
  101.         </activity>     
  102.              
  103.         <!--设置Wallpapaer的Activity -->     
  104.         <activity     
  105.             android:name="WallpaperChooser"     
  106.             android:label="@string/pick_wallpaper"     
  107.             android:icon="@drawable/ic_launcher_wallpaper"     
  108.             android:screenOrientation="nosensor"     
  109.             android:finishOnCloseSystemDialogs="true">     
  110.             <intent-filter>     
  111.                 <action android:name="android.intent.action.SET_WALLPAPER" />     
  112.                 <category android:name="android.intent.category.DEFAULT" />     
  113.             </intent-filter>     
  114.         </activity>     
  115.      
  116.         <!--安装快捷方式的Intent -->     
  117.         <!-- Intent received used to install shortcuts from other applications -->     
  118.         <receiver     
  119.             android:name="InstallShortcutReceiver"     
  120.             android:permission="com.android.zkx_launcher.permission.INSTALL_SHORTCUT">     
  121.             <intent-filter>     
  122.                 <action android:name="com.android.zkx_launcher.action.INSTALL_SHORTCUT" />     
  123.             </intent-filter>     
  124.         </receiver>     
  125.      
  126.         <!--卸载快捷方式的Intent -->     
  127.         <!-- Intent received used to uninstall shortcuts from other applications -->     
  128.         <receiver     
  129.             android:name="UninstallShortcutReceiver"     
  130.             android:permission="com.android.zkx_launcher.permission.UNINSTALL_SHORTCUT">     
  131.             <intent-filter>     
  132.                 <action android:name="com.android.zkx_launcher.action.UNINSTALL_SHORTCUT" />     
  133.             </intent-filter>     
  134.         </receiver>     
  135.      
  136.         <!-- 供应商信息-->     
  137.         <!-- The settings provider contains Home's data, like the workspace favorites -->     
  138.         <provider     
  139.             android:name="LauncherProvider"     
  140.             android:authorities="com.android.zkx_launcher.settings"     
  141.             android:writePermission="com.android.zkx_launcher.permission.WRITE_SETTINGS"     
  142.             android:readPermission="com.android.zkx_launcher.permission.READ_SETTINGS" />     
  143.     </application>     
  144. </manifest>    

三         Launcher   Res下的Xml文件

           

            Res/xml下有两个xml文件,default_workspace.xml&&default_wallpaper.xml  

             Andorid这个默认壁纸不在launcher里,在源码中frameworks/base/core/res/res /drawable/default_wallpaper.jpg.
frameworks/base/core/res/res路径下包含很多default资源。如果需要修改默认设置可以尝试到这里来找一找
<favorites xmlns:launcher="http://schemas.android.com/apk/res/com.unique.launcher">
    <!--  Far-left screen [0] -->

    <!--  Left screen [1] -->
    <appwidget
        launcher:packageName="com.google.android.apps.genie.geniewidget"
        launcher:className="com.google.android.apps.genie.geniewidget.miniwidget.MiniWidgetProvider"
        launcher:screen="1"
        launcher:x="0"
        launcher:y="0"
        launcher:spanX="4"
        launcher:spanY="1" />
#天气新闻时钟插件
#packageName:widgetpackageName
#className :实现 widget receiver 类的名称
#launcher:container 放置的位 置(只能为desktop
#screen : 在哪一个screen添加

#x,y: 在screen中的位置

#launcher:spanX:在x方向上所占格数
#launcher:spanY:在y方向上所占格数

        
    <!--  Middle screen [2] -->
    <search
        launcher:screen="2"
        launcher:x="0"
        launcher:y="0" />

 
    <appwidget
        launcher:packageName="com.android.protips"
        launcher:className="com.android.protips.ProtipWidget"
        launcher:screen="2"
        launcher:x="0"
        launcher:y="1"
        launcher:spanX="4"
        launcher:spanY="1 " />


    <!--  Right screen [3] -->
    <appwidget
        launcher:packageName="com.android.music"
        launcher:className="com.android.music.MediaAppWidgetProvider"
        launcher:screen="3"
        launcher:x="0"
        launcher:y="0"
        launcher:spanX="4"
        launcher:spanY="1" />

 
    <appwidget
        launcher:packageName="com.android.vending"
        launcher:className="com.android.vending.MarketWidgetProvider"
        launcher:screen="3"
        launcher:x="1"
        launcher:y="1"
        launcher:spanX="2"
        launcher:spanY="2" />
#电子市场Android Market
    <!--  Far-right screen [4] -->

</favorites> 

四         Launcher    Manifest文件
Launcher的AndroidManifest.xml文件有很多特殊性,分析一下就会理解整个程序的大概结构。
代码如下:

<manifestxmlns:android="http://schemas.android.com/apk/res/android"

  package="net.sunniwell.launcher"

  android:versionCode="1"android:versionName="1.0.1">

关于自定义权限,这是很好的例子,其他apk程序要想使用Launcher的功能必须添加这些权限,而这些权限都是在这里声明的。


这个是安装快捷方式的权限定义:


<permission

      android:name="com.android.launcher.permission.INSTALL_SHORTCUT"

      android:permissionGroup="android.permission-group.SYSTEM_TOOLS"

      android:protectionLevel="normal"

      android:label="@string/permlab_install_shortcut"

      android:description="@string/permdesc_install_shortcut"/>



这个是卸载快捷方式的权限定义:


<permission

      android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"

      android:permissionGroup="android.permission-group.SYSTEM_TOOLS"

      android:protectionLevel="normal"

      android:label="@string/permlab_uninstall_shortcut"

      android:description="@string/permdesc_uninstall_shortcut"/>


这个是读取launcher.db内容的权限定义:


<permission

      android:name="net.sunniwell.launcher.permission.READ_SETTINGS"

      android:permissionGroup="android.permission-group.SYSTEM_TOOLS"

      android:protectionLevel="normal"

      android:label="@string/permlab_read_settings"

      android:description="@string/permdesc_read_settings"/>


这个是修改和删除launcher.db内容的权限定义:


<permission

      android:name="net.sunniwell.launcher.permission.WRITE_SETTINGS"

      android:permissionGroup="android.permission-group.SYSTEM_TOOLS"

      android:protectionLevel="normal"

      android:label="@string/permlab_write_settings"

      android:description="@string/permdesc_write_settings"/>

这些是Launcher的权限声明,通过这些就能看出launcher的大概功能了:

打电话权限:


<uses-permissionandroid:name="android.permission.CALL_PHONE"/>

使用状态栏权限:


<uses-permissionandroid:name="android.permission.EXPAND_STATUS_BAR"/>

获取当前或最近运行的任务的信息的权限:


<uses-permissionandroid:name="android.permission.GET_TASKS"/>

读取通信录权限:


<uses-permissionandroid:name="android.permission.READ_CONTACTS"/>

设置壁纸权限:

<uses-permissionandroid:name="android.permission.SET_WALLPAPER"/>

允许程序设置壁纸hits的权限:

<uses-permissionandroid:name="android.permission.SET_WALLPAPER_HINTS"/>

使用震动功能权限:

<uses-permissionandroid:name="android.permission.VIBRATE"/>

修改删除launcher.db内容权限:

<uses-permissionandroid:name="android.permission.WRITE_SETTINGS"/>

绑定widget权限:

<uses-permissionandroid:name="android.permission.BIND_APPWIDGET"/>

读取launcher.db内容权限:

<uses-permissionandroid:name="net.sunniwell.launcher.permission.READ_SETTINGS"/>

修改删除launcher.db内容权限:

<uses-permissionandroid:name="net.sunniwell.launcher.permission.WRITE_SETTINGS"/>

读写外部存储设备权限:

<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>




<application

      android:name="LauncherApplication"

  activity应该运行的进程的名字:

android:process="android.process.acore"

      android:label="@string/application_name"

      android:icon="@drawable/swicon">


<activity

        android:name="Launcher"

         是否

android:launchMode="singleTask"

        android:clearTaskOnLaunch="true"

        这个activity是否在被杀死或者重启后能恢复原来的状态:

android:stateNotNeeded="true"

        android:theme="@style/Theme"

        android:screenOrientation="landscape"

        android:windowSoftInputMode="stateUnspecified|adjustPan">


<intent-filter>


<actionandroid:name="android.intent.action.MAIN"/>


<categoryandroid:name="android.intent.category.LAUNCHER"/>



桌面应用的标记:

<categoryandroid:name="android.intent.category.HOME"/>


<categoryandroid:name="android.intent.category.DEFAULT"/>


自动化测试工具Monkey的标记,待研究

<categoryandroid:name="android.intent.category.MONKEY"/>



</intent-filter>


</activity>

选择壁纸的activity:

<activity

        android:name="WallpaperChooser"

        android:label="@string/pick_wallpaper"

        android:icon="@drawable/ic_launcher_gallery">


设置壁纸的intent-filter


<intent-filter>


<actionandroid:name="android.intent.action.SET_WALLPAPER"/>


<categoryandroid:name="android.intent.category.DEFAULT"/>


</intent-filter>

搜索的activity

</activity>


<!-- Enable system-default search mode for any activity in Home -->


<meta-data

        android:name="android.app.default_searchable"

        android:value="*"/>

安装快捷方式的广播接收器:


<!-- Intent received used to install shortcuts from other applications -->



<receiver

        android:name=".InstallShortcutReceiver"

        android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">


<intent-filter>


<actionandroid:name="com.android.launcher.action.INSTALL_SHORTCUT"/>


</intent-filter>


</receiver>


<!-- Intent received used to uninstall shortcuts from other applications -->

卸载快捷方式的广播接收器:


<receiver

        android:name=".UninstallShortcutReceiver"

        android:permission="com.android.launcher.permission.UNINSTALL_SHORTCUT">


<intent-filter>


<actionandroid:name="com.android.launcher.action.UNINSTALL_SHORTCUT"/>


</intent-filter>


</receiver>

声明ContentProvider,用于对launcher.db操作:


<!-- The settings provider contains Home's data, like the workspace favorites -->


<provider

        android:name="SWLauncherProvider"

        android:authorities="net.sunniwell.launcher.settings"

        android:writePermission="net.sunniwell.launcher.permission.WRITE_SETTINGS"

        android:readPermission="net.sunniwell.launcher.permission.READ_SETTINGS"/>


</application>


<uses-sdkandroid:minSdkVersion="4"/>

</manifest>
说明:
1.
<manifest标签头部还应声明:

android:sharedUserId="android.uid.shared",作用是获得系统权限,但是这样的程序属性只能在build整个系统时放进去(就是系统软件)才起作用,手动安装是没有权限的。

五          Launcher   常用类介绍

AddAdapter: 维护了 live fold  , widget , shortcut , wallpaper 4 个 ListItem , 长按桌面会显示该列表

AllAppsGridView :显示 APP 的网格

ApplicationInfo :一个可启动的应用

ApplicationsAdapter : gridview 的 adapter

BubbleTextView: 一个定制了的 textview

CellLayout: 屏幕网格化

DeleteZone : UI 的一部分

DragController , dragscroller, dragsource, droptarget: 支持拖拽操作

DragLayer :内部支持拖拽的 viewgroup

FastBitmapDrawable :工具

Folder : Icons 的集合

FolderIcon: 出现在 workspace 的 icon 代表了一个 folder

FolderInfo: ItemInfo 子类

HandleView :一个 imageview 。

InstallShortcutReceiver , UninstallShortcutReceiver :一个 broadcastrecier

ItemInfo: 代表 Launcher 中一个 Item (例如 folder )

Launcher: Launcher 程序的主窗口

LauncherApplication :在 VM 中设置参数

LauncherAppWidgetHost , LauncherAppWidgetHostView ,: Widget 相关

LauncherModel : MVC 中的 M

LauncherProvider :一个 contentprovider ,为 Launcher 存储信息

LauncherSettings: 设置相关的工具

LiveFolder , LiveFolderAdapter , LiveFolderIcon , LiveFolderInfo : livefolder 相关

Search : 搜索

UserFolder , UserFolderInfo :文件夹包含 applications ,shortcuts

Utilities: 小工具

WallpaperChooser :选择 wallpaper 的 activity

Workspace: 屏幕上的一块区域

widget : 代表启动的 widget 实例,例如搜索

总结

1) Launcher中实现了MVC模式(M:launchermode , V:draglayer ,C: launcher),以此为主线,可以得到 Launcher对各个组件管理的细节(如drag的实现)。

 

 

 

六     Launcher   起动过程

 

                 Android系统在启动时会安装应用程序,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应用程序就是Launcher了,我将详细分析Launcher应用程序的启动过程。

        Android系统的Home应用程序Launcher是由ActivityManagerService启动的,而ActivityManagerService和PackageManagerService一样,都是在开机时由SystemServer组件启动的,SystemServer组件首先是启动ePackageManagerServic,由它来负责安装系统的应用程序,系统中的应用程序安装好了以后,SystemServer组件接下来就要通过ActivityManagerService来启动Home应用程序Launcher了,Launcher在启动的时候便会通过PackageManagerServic把系统中已经安装好的应用程序以快捷图标的形式展示在桌面上,这样用户就可以使用这些应用程序了。

              下面详细分析每一个步骤。

        Step 1. SystemServer.main

             这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中:

view plain copy to clipboard
  1. public class SystemServer  
  2. {  
  3.     ......  
  4.   
  5.     native public static void init1(String[] args);  
  6.   
  7.     ......  
  8.   
  9.     public static void main(String[] args) {  
  10.         ......  
  11.   
  12.         init1(args);  
  13.   
  14.         ......  
  15.     }  
  16.   
  17.     ......  
  18. }  
[java]  view plain copy
  1. public class SystemServer  
  2. {  
  3.     ......  
  4.   
  5.     native public static void init1(String[] args);  
  6.   
  7.     ......  
  8.   
  9.     public static void main(String[] args) {  
  10.         ......  
  11.   
  12.         init1(args);  
  13.   
  14.         ......  
  15.     }  
  16.   
  17.     ......  
  18. }  
        SystemServer组件是由Zygote进程负责启动的,启动的时候就会调用它的main函数,这个函数主要调用了JNI方法init1来做一些系统初始化的工作。

        Step 2. SystemServer.init1

        这个函数是一个JNI方法,实现在 frameworks/base/services/jni/com_android_server_SystemServer.cpp文件中:

view plain copy to clipboard
  1. namespace android {  
  2.   
  3. extern "C" int system_init();  
  4.   
  5. static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz)  
  6. {  
  7.     system_init();  
  8. }  
  9.   
  10. /* 
  11.  * JNI registration. 
  12.  */  
  13. static JNINativeMethod gMethods[] = {  
  14.     /* name, signature, funcPtr */  
  15.     { "init1""([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 },  
  16. };  
  17.   
  18. int register_android_server_SystemServer(JNIEnv* env)  
  19. {  
  20.     return jniRegisterNativeMethods(env, "com/android/server/SystemServer",  
  21.             gMethods, NELEM(gMethods));  
  22. }  
  23.   
  24. }; // namespace android  
[cpp]  view plain copy
  1. namespace android {  
  2.   
  3. extern "C" int system_init();  
  4.   
  5. static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz)  
  6. {  
  7.     system_init();  
  8. }  
  9.   
  10. /* 
  11.  * JNI registration. 
  12.  */  
  13. static JNINativeMethod gMethods[] = {  
  14.     /* name, signature, funcPtr */  
  15.     { "init1""([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 },  
  16. };  
  17.   
  18. int register_android_server_SystemServer(JNIEnv* env)  
  19. {  
  20.     return jniRegisterNativeMethods(env, "com/android/server/SystemServer",  
  21.             gMethods, NELEM(gMethods));  
  22. }  
  23.   
  24. }; // namespace android  
        这个函数很简单,只是调用了system_init函数来进一步执行操作。

        Step 3. libsystem_server.system_init

        函数system_init实现在libsystem_server库中,源代码位于frameworks/base/cmds/system_server/library/system_init.cpp文件中:

view plain copy to clipboard
  1. extern "C" status_t system_init()  
  2. {  
  3.     LOGI("Entered system_init()");  
  4.   
  5.     sp<ProcessState> proc(ProcessState::self());  
  6.   
  7.     sp<IServiceManager> sm = defaultServiceManager();  
  8.     LOGI("ServiceManager: %p\n", sm.get());  
  9.   
  10.     sp<GrimReaper> grim = new GrimReaper();  
  11.     sm->asBinder()->linkToDeath(grim, grim.get(), 0);  
  12.   
  13.     char propBuf[PROPERTY_VALUE_MAX];  
  14.     property_get("system_init.startsurfaceflinger", propBuf, "1");  
  15.     if (strcmp(propBuf, "1") == 0) {  
  16.         // Start the SurfaceFlinger   
  17.         SurfaceFlinger::instantiate();  
  18.     }  
  19.   
  20.     // Start the sensor service   
  21.     SensorService::instantiate();  
  22.   
  23.     // On the simulator, audioflinger et al don't get started the   
  24.     // same way as on the device, and we need to start them here   
  25.     if (!proc->supportsProcesses()) {  
  26.   
  27.         // Start the AudioFlinger   
  28.         AudioFlinger::instantiate();  
  29.   
  30.         // Start the media playback service   
  31.         MediaPlayerService::instantiate();  
  32.   
  33.         // Start the camera service   
  34.         CameraService::instantiate();  
  35.   
  36.         // Start the audio policy service   
  37.         AudioPolicyService::instantiate();  
  38.     }  
  39.   
  40.     // And now start the Android runtime.  We have to do this bit   
  41.     // of nastiness because the Android runtime initialization requires   
  42.     // some of the core system services to already be started.   
  43.     // All other servers should just start the Android runtime at   
  44.     // the beginning of their processes's main(), before calling   
  45.     // the init function.   
  46.     LOGI("System server: starting Android runtime.\n");  
  47.   
  48.     AndroidRuntime* runtime = AndroidRuntime::getRuntime();  
  49.   
  50.     LOGI("System server: starting Android services.\n");  
  51.     runtime->callStatic("com/android/server/SystemServer""init2");  
  52.   
  53.     // If running in our own process, just go into the thread   
  54.     // pool.  Otherwise, call the initialization finished   
  55.     // func to let this process continue its initilization.   
  56.     if (proc->supportsProcesses()) {  
  57.         LOGI("System server: entering thread pool.\n");  
  58.         ProcessState::self()->startThreadPool();  
  59.         IPCThreadState::self()->joinThreadPool();  
  60.         LOGI("System server: exiting thread pool.\n");  
  61.     }  
  62.   
  63.     return NO_ERROR;  
  64. }  
[cpp]  view plain copy
  1. extern "C" status_t system_init()  
  2. {  
  3.     LOGI("Entered system_init()");  
  4.   
  5.     sp<ProcessState> proc(ProcessState::self());  
  6.   
  7.     sp<IServiceManager> sm = defaultServiceManager();  
  8.     LOGI("ServiceManager: %p\n", sm.get());  
  9.   
  10.     sp<GrimReaper> grim = new GrimReaper();  
  11.     sm->asBinder()->linkToDeath(grim, grim.get(), 0);  
  12.   
  13.     char propBuf[PROPERTY_VALUE_MAX];  
  14.     property_get("system_init.startsurfaceflinger", propBuf, "1");  
  15.     if (strcmp(propBuf, "1") == 0) {  
  16.         // Start the SurfaceFlinger  
  17.         SurfaceFlinger::instantiate();  
  18.     }  
  19.   
  20.     // Start the sensor service  
  21.     SensorService::instantiate();  
  22.   
  23.     // On the simulator, audioflinger et al don't get started the  
  24.     // same way as on the device, and we need to start them here  
  25.     if (!proc->supportsProcesses()) {  
  26.   
  27.         // Start the AudioFlinger  
  28.         AudioFlinger::instantiate();  
  29.   
  30.         // Start the media playback service  
  31.         MediaPlayerService::instantiate();  
  32.   
  33.         // Start the camera service  
  34.         CameraService::instantiate();  
  35.   
  36.         // Start the audio policy service  
  37.         AudioPolicyService::instantiate();  
  38.     }  
  39.   
  40.     // And now start the Android runtime.  We have to do this bit  
  41.     // of nastiness because the Android runtime initialization requires  
  42.     // some of the core system services to already be started.  
  43.     // All other servers should just start the Android runtime at  
  44.     // the beginning of their processes's main(), before calling  
  45.     // the init function.  
  46.     LOGI("System server: starting Android runtime.\n");  
  47.   
  48.     AndroidRuntime* runtime = AndroidRuntime::getRuntime();  
  49.   
  50.     LOGI("System server: starting Android services.\n");  
  51.     runtime->callStatic("com/android/server/SystemServer""init2");  
  52.   
  53.     // If running in our own process, just go into the thread  
  54.     // pool.  Otherwise, call the initialization finished  
  55.     // func to let this process continue its initilization.  
  56.     if (proc->supportsProcesses()) {  
  57.         LOGI("System server: entering thread pool.\n");  
  58.         ProcessState::self()->startThreadPool();  
  59.         IPCThreadState::self()->joinThreadPool();  
  60.         LOGI("System server: exiting thread pool.\n");  
  61.     }  
  62.   
  63.     return NO_ERROR;  
  64. }  
        这个函数首先会初始化SurfaceFlinger、SensorService、AudioFlinger、MediaPlayerService、CameraService和AudioPolicyService这几个服务,然后就通过系统全局唯一的AndroidRuntime实例变量runtime的callStatic来调用SystemServer的init2函数了。关于这个AndroidRuntime实例变量runtime的相关资料,可能参考前面一篇文章 Android应用程序进程启动过程的源代码分析一文。

        Step 4. AndroidRuntime.callStatic

        这个函数定义在frameworks/base/core/jni/AndroidRuntime.cpp文件中:

view plain copy to clipboard
  1. /* 
  2. * Call a static Java Programming Language function that takes no arguments and returns void. 
  3. */  
  4. status_t AndroidRuntime::callStatic(const char* className, const char* methodName)  
  5. {  
  6.     JNIEnv* env;  
  7.     jclass clazz;  
  8.     jmethodID methodId;  
  9.   
  10.     env = getJNIEnv();  
  11.     if (env == NULL)  
  12.         return UNKNOWN_ERROR;  
  13.   
  14.     clazz = findClass(env, className);  
  15.     if (clazz == NULL) {  
  16.         LOGE("ERROR: could not find class '%s'\n", className);  
  17.         return UNKNOWN_ERROR;  
  18.     }  
  19.     methodId = env->GetStaticMethodID(clazz, methodName, "()V");  
  20.     if (methodId == NULL) {  
  21.         LOGE("ERROR: could not find method %s.%s\n", className, methodName);  
  22.         return UNKNOWN_ERROR;  
  23.     }  
  24.   
  25.     env->CallStaticVoidMethod(clazz, methodId);  
  26.   
  27.     return NO_ERROR;  
  28. }  
[cpp]  view plain copy
  1. /* 
  2. * Call a static Java Programming Language function that takes no arguments and returns void. 
  3. */  
  4. status_t AndroidRuntime::callStatic(const char* className, const char* methodName)  
  5. {  
  6.     JNIEnv* env;  
  7.     jclass clazz;  
  8.     jmethodID methodId;  
  9.   
  10.     env = getJNIEnv();  
  11.     if (env == NULL)  
  12.         return UNKNOWN_ERROR;  
  13.   
  14.     clazz = findClass(env, className);  
  15.     if (clazz == NULL) {  
  16.         LOGE("ERROR: could not find class '%s'\n", className);  
  17.         return UNKNOWN_ERROR;  
  18.     }  
  19.     methodId = env->GetStaticMethodID(clazz, methodName, "()V");  
  20.     if (methodId == NULL) {  
  21.         LOGE("ERROR: could not find method %s.%s\n", className, methodName);  
  22.         return UNKNOWN_ERROR;  
  23.     }  
  24.   
  25.     env->CallStaticVoidMethod(clazz, methodId);  
  26.   
  27.     return NO_ERROR;  
  28. }  
        这个函数调用由参数className指定的java类的静态成员函数,这个静态成员函数是由参数methodName指定的。上面传进来的参数className的值为"com/android/server/SystemServer",而参数methodName的值为"init2",因此,接下来就会调用SystemServer类的init2函数了。

        Step 5. SystemServer.init2

        这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中:

view plain copy to clipboard
  1. public class SystemServer  
  2. {  
  3.     ......  
  4.   
  5.     public static final void init2() {  
  6.         Slog.i(TAG, "Entered the Android system server!");  
  7.         Thread thr = new ServerThread();  
  8.         thr.setName("android.server.ServerThread");  
  9.         thr.start();  
  10.     }  
  11. }  
[java]  view plain copy
  1. public class SystemServer  
  2. {  
  3.     ......  
  4.   
  5.     public static final void init2() {  
  6.         Slog.i(TAG, "Entered the Android system server!");  
  7.         Thread thr = new ServerThread();  
  8.         thr.setName("android.server.ServerThread");  
  9.         thr.start();  
  10.     }  
  11. }  
        这个函数创建了一个ServerThread线程,PackageManagerService服务就是这个线程中启动的了。这里调用了ServerThread实例thr的start函数之后,下面就会执行这个实例的run函数了。

        Step 6. ServerThread.run

        这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中:

view plain copy to clipboard
  1. class ServerThread extends Thread {  
  2.     ......  
  3.   
  4.     @Override  
  5.     public void run() {  
  6.         ......  
  7.   
  8.         IPackageManager pm = null;  
  9.   
  10.         ......  
  11.   
  12.         // Critical services...   
  13.         try {  
  14.             ......  
  15.   
  16.             Slog.i(TAG, "Package Manager");  
  17.             pm = PackageManagerService.main(context,  
  18.                         factoryTest != SystemServer.FACTORY_TEST_OFF);  
  19.   
  20.             ......  
  21.         } catch (RuntimeException e) {  
  22.             Slog.e("System""Failure starting core service", e);  
  23.         }  
  24.   
  25.         ......  
  26.     }  
  27.   
  28.     ......  
  29. }  
[java]  view plain copy
  1. class ServerThread extends Thread {  
  2.     ......  
  3.   
  4.     @Override  
  5.     public void run() {  
  6.         ......  
  7.   
  8.         IPackageManager pm = null;  
  9.   
  10.         ......  
  11.   
  12.         // Critical services...  
  13.         try {  
  14.             ......  
  15.   
  16.             Slog.i(TAG, "Package Manager");  
  17.             pm = PackageManagerService.main(context,  
  18.                         factoryTest != SystemServer.FACTORY_TEST_OFF);  
  19.   
  20.             ......  
  21.         } catch (RuntimeException e) {  
  22.             Slog.e("System""Failure starting core service", e);  
  23.         }  
  24.   
  25.         ......  
  26.     }  
  27.   
  28.     ......  
  29. }  
        这个函数除了启动PackageManagerService服务之外,还启动了其它很多的服务,例如在前面学习Activity和Service的几篇文章中经常看到的ActivityManagerService服务,有兴趣的读者可以自己研究一下。
Step 7. ActivityManagerService.main

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:

view plain copy to clipboard
  1. public final class ActivityManagerService extends ActivityManagerNative  
  2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
  3.     ......  
  4.   
  5.     public static final Context main(int factoryTest) {  
  6.         AThread thr = new AThread();  
  7.         thr.start();  
  8.   
  9.         synchronized (thr) {  
  10.             while (thr.mService == null) {  
  11.                 try {  
  12.                     thr.wait();  
  13.                 } catch (InterruptedException e) {  
  14.                 }  
  15.             }  
  16.         }  
  17.   
  18.         ActivityManagerService m = thr.mService;  
  19.         mSelf = m;  
  20.         ActivityThread at = ActivityThread.systemMain();  
  21.         mSystemThread = at;  
  22.         Context context = at.getSystemContext();  
  23.         m.mContext = context;  
  24.         m.mFactoryTest = factoryTest;  
  25.         m.mMainStack = new ActivityStack(m, context, true);  
  26.   
  27.         m.mBatteryStatsService.publish(context);  
  28.         m.mUsageStatsService.publish(context);  
  29.   
  30.         synchronized (thr) {  
  31.             thr.mReady = true;  
  32.             thr.notifyAll();  
  33.         }  
  34.   
  35.         m.startRunning(nullnullnullnull);  
  36.   
  37.         return context;  
  38.     }  
  39.   
  40.     ......  
  41. }  
[java]  view plain copy
  1. public final class ActivityManagerService extends ActivityManagerNative  
  2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
  3.     ......  
  4.   
  5.     public static final Context main(int factoryTest) {  
  6.         AThread thr = new AThread();  
  7.         thr.start();  
  8.   
  9.         synchronized (thr) {  
  10.             while (thr.mService == null) {  
  11.                 try {  
  12.                     thr.wait();  
  13.                 } catch (InterruptedException e) {  
  14.                 }  
  15.             }  
  16.         }  
  17.   
  18.         ActivityManagerService m = thr.mService;  
  19.         mSelf = m;  
  20.         ActivityThread at = ActivityThread.systemMain();  
  21.         mSystemThread = at;  
  22.         Context context = at.getSystemContext();  
  23.         m.mContext = context;  
  24.         m.mFactoryTest = factoryTest;  
  25.         m.mMainStack = new ActivityStack(m, context, true);  
  26.   
  27.         m.mBatteryStatsService.publish(context);  
  28.         m.mUsageStatsService.publish(context);  
  29.   
  30.         synchronized (thr) {  
  31.             thr.mReady = true;  
  32.             thr.notifyAll();  
  33.         }  
  34.   
  35.         m.startRunning(nullnullnullnull);  
  36.   
  37.         return context;  
  38.     }  
  39.   
  40.     ......  
  41. }  
        这个函数首先通过AThread线程对象来内部创建了一个ActivityManagerService实例,然后将这个实例保存其成员变量mService中,接着又把这个ActivityManagerService实例保存在ActivityManagerService类的静态成员变量mSelf中,最后初始化其它成员变量,就结束了。

        Step 8. PackageManagerService.main

这个函数定义在frameworks/base/services/java/com/android/server/PackageManagerService.java文件中:

view plain copy to clipboard
  1. class PackageManagerService extends IPackageManager.Stub {  
  2.     ......  
  3.   
  4.     public static final IPackageManager main(Context context, boolean factoryTest) {  
  5.         PackageManagerService m = new PackageManagerService(context, factoryTest);  
  6.         ServiceManager.addService("package", m);  
  7.         return m;  
  8.     }  
  9.   
  10.     ......  
  11. }  
[cpp]  view plain copy
  1. class PackageManagerService extends IPackageManager.Stub {  
  2.     ......  
  3.   
  4.     public static final IPackageManager main(Context context, boolean factoryTest) {  
  5.         PackageManagerService m = new PackageManagerService(context, factoryTest);  
  6.         ServiceManager.addService("package", m);  
  7.         return m;  
  8.     }  
  9.   
  10.     ......  
  11. }  
        这个函数创建了一个PackageManagerService服务实例,然后把这个服务添加到ServiceManager中去,ServiceManager是Android系统Binder进程间通信机制的守护进程,负责管理系统中的Binder对象,在创建这个PackageManagerService服务实例时,会在PackageManagerService类的构造函数中开始执行安装应用程序的过程:

view plain copy to clipboard
  1. class PackageManagerService extends IPackageManager.Stub {  
  2.     ......  
  3.   
  4.     public PackageManagerService(Context context, boolean factoryTest) {  
  5.         ......  
  6.   
  7.         synchronized (mInstallLock) {  
  8.             synchronized (mPackages) {  
  9.                 ......  
  10.   
  11.                 File dataDir = Environment.getDataDirectory();  
  12.                 mAppDataDir = new File(dataDir, "data");  
  13.                 mSecureAppDataDir = new File(dataDir, "secure/data");  
  14.                 mDrmAppPrivateInstallDir = new File(dataDir, "app-private");  
  15.   
  16.                 ......  
  17.   
  18.                 mFrameworkDir = new File(Environment.getRootDirectory(), "framework");  
  19.                 mDalvikCacheDir = new File(dataDir, "dalvik-cache");  
  20.   
  21.                 ......  
  22.   
  23.                 // Find base frameworks (resource packages without code).   
  24.                 mFrameworkInstallObserver = new AppDirObserver(  
  25.                 mFrameworkDir.getPath(), OBSERVER_EVENTS, true);  
  26.                 mFrameworkInstallObserver.startWatching();  
  27.                 scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM  
  28.                     | PackageParser.PARSE_IS_SYSTEM_DIR,  
  29.                     scanMode | SCAN_NO_DEX, 0);  
  30.   
  31.                 // Collect all system packages.   
  32.                 mSystemAppDir = new File(Environment.getRootDirectory(), "app");  
  33.                 mSystemInstallObserver = new AppDirObserver(  
  34.                     mSystemAppDir.getPath(), OBSERVER_EVENTS, true);  
  35.                 mSystemInstallObserver.startWatching();  
  36.                 scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM  
  37.                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  
  38.   
  39.                 // Collect all vendor packages.   
  40.                 mVendorAppDir = new File("/vendor/app");  
  41.                 mVendorInstallObserver = new AppDirObserver(  
  42.                     mVendorAppDir.getPath(), OBSERVER_EVENTS, true);  
  43.                 mVendorInstallObserver.startWatching();  
  44.                 scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM  
  45.                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  
  46.   
  47.   
  48.                 mAppInstallObserver = new AppDirObserver(  
  49.                     mAppInstallDir.getPath(), OBSERVER_EVENTS, false);  
  50.                 mAppInstallObserver.startWatching();  
  51.                 scanDirLI(mAppInstallDir, 0, scanMode, 0);  
  52.   
  53.                 mDrmAppInstallObserver = new AppDirObserver(  
  54.                     mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);  
  55.                 mDrmAppInstallObserver.startWatching();  
  56.                 scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,  
  57.                     scanMode, 0);  
  58.   
  59.                 ......  
  60.             }  
  61.         }  
  62.     }  
  63.   
  64.     ......  
  65. }  
[java]  view plain copy
  1. class PackageManagerService extends IPackageManager.Stub {  
  2.     ......  
  3.   
  4.     public PackageManagerService(Context context, boolean factoryTest) {  
  5.         ......  
  6.   
  7.         synchronized (mInstallLock) {  
  8.             synchronized (mPackages) {  
  9.                 ......  
  10.   
  11.                 File dataDir = Environment.getDataDirectory();  
  12.                 mAppDataDir = new File(dataDir, "data");  
  13.                 mSecureAppDataDir = new File(dataDir, "secure/data");  
  14.                 mDrmAppPrivateInstallDir = new File(dataDir, "app-private");  
  15.   
  16.                 ......  
  17.   
  18.                 mFrameworkDir = new File(Environment.getRootDirectory(), "framework");  
  19.                 mDalvikCacheDir = new File(dataDir, "dalvik-cache");  
  20.   
  21.                 ......  
  22.   
  23.                 // Find base frameworks (resource packages without code).  
  24.                 mFrameworkInstallObserver = new AppDirObserver(  
  25.                 mFrameworkDir.getPath(), OBSERVER_EVENTS, true);  
  26.                 mFrameworkInstallObserver.startWatching();  
  27.                 scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM  
  28.                     | PackageParser.PARSE_IS_SYSTEM_DIR,  
  29.                     scanMode | SCAN_NO_DEX, 0);  
  30.   
  31.                 // Collect all system packages.  
  32.                 mSystemAppDir = new File(Environment.getRootDirectory(), "app");  
  33.                 mSystemInstallObserver = new AppDirObserver(  
  34.                     mSystemAppDir.getPath(), OBSERVER_EVENTS, true);  
  35.                 mSystemInstallObserver.startWatching();  
  36.                 scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM  
  37.                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  
  38.   
  39.                 // Collect all vendor packages.  
  40.                 mVendorAppDir = new File("/vendor/app");  
  41.                 mVendorInstallObserver = new AppDirObserver(  
  42.                     mVendorAppDir.getPath(), OBSERVER_EVENTS, true);  
  43.                 mVendorInstallObserver.startWatching();  
  44.                 scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM  
  45.                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  
  46.   
  47.   
  48.                 mAppInstallObserver = new AppDirObserver(  
  49.                     mAppInstallDir.getPath(), OBSERVER_EVENTS, false);  
  50.                 mAppInstallObserver.startWatching();  
  51.                 scanDirLI(mAppInstallDir, 0, scanMode, 0);  
  52.   
  53.                 mDrmAppInstallObserver = new AppDirObserver(  
  54.                     mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);  
  55.                 mDrmAppInstallObserver.startWatching();  
  56.                 scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,  
  57.                     scanMode, 0);  
  58.   
  59.                 ......  
  60.             }  
  61.         }  
  62.     }  
  63.   
  64.     ......  
  65. }  
        这里会调用scanDirLI函数来扫描移动设备上的下面这五个目录中的Apk文件:

        /system/framework

        /system/app

        /vendor/app

        /data/app

        /data/app-private

 

Step 9. ActivityManagerService.setSystemProcess

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:

view plain copy to clipboard
  1. public final class ActivityManagerService extends ActivityManagerNative  
  2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
  3.     ......  
  4.   
  5.     public static void setSystemProcess() {  
  6.         try {  
  7.             ActivityManagerService m = mSelf;  
  8.   
  9.             ServiceManager.addService("activity", m);  
  10.             ServiceManager.addService("meminfo"new MemBinder(m));  
  11.             if (MONITOR_CPU_USAGE) {  
  12.                 ServiceManager.addService("cpuinfo"new CpuBinder(m));  
  13.             }  
  14.             ServiceManager.addService("permission"new PermissionController(m));  
  15.   
  16.             ApplicationInfo info =  
  17.                 mSelf.mContext.getPackageManager().getApplicationInfo(  
  18.                 "android", STOCK_PM_FLAGS);  
  19.             mSystemThread.installSystemApplicationInfo(info);  
  20.   
  21.             synchronized (mSelf) {  
  22.                 ProcessRecord app = mSelf.newProcessRecordLocked(  
  23.                     mSystemThread.getApplicationThread(), info,  
  24.                     info.processName);  
  25.                 app.persistent = true;  
  26.                 app.pid = MY_PID;  
  27.                 app.maxAdj = SYSTEM_ADJ;  
  28.                 mSelf.mProcessNames.put(app.processName, app.info.uid, app);  
  29.                 synchronized (mSelf.mPidsSelfLocked) {  
  30.                     mSelf.mPidsSelfLocked.put(app.pid, app);  
  31.                 }  
  32.                 mSelf.updateLruProcessLocked(app, truetrue);  
  33.             }  
  34.         } catch (PackageManager.NameNotFoundException e) {  
  35.             throw new RuntimeException(  
  36.                 "Unable to find android system package", e);  
  37.         }  
  38.     }  
  39.     ......  
  40. }  

[java]  view plain copy
  1. public final class ActivityManagerService extends ActivityManagerNative  
  2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
  3.     ......  
  4.   
  5.     public static void setSystemProcess() {  
  6.         try {  
  7.             ActivityManagerService m = mSelf;  
  8.   
  9.             ServiceManager.addService("activity", m);  
  10.             ServiceManager.addService("meminfo"new MemBinder(m));  
  11.             if (MONITOR_CPU_USAGE) {  
  12.                 ServiceManager.addService("cpuinfo"new CpuBinder(m));  
  13.             }  
  14.             ServiceManager.addService("permission"new PermissionController(m));  
  15.   
  16.             ApplicationInfo info =  
  17.                 mSelf.mContext.getPackageManager().getApplicationInfo(  
  18.                 "android", STOCK_PM_FLAGS);  
  19.             mSystemThread.installSystemApplicationInfo(info);  
  20.   
  21.             synchronized (mSelf) {  
  22.                 ProcessRecord app = mSelf.newProcessRecordLocked(  
  23.                     mSystemThread.getApplicationThread(), info,  
  24.                     info.processName);  
  25.                 app.persistent = true;  
  26.                 app.pid = MY_PID;  
  27.                 app.maxAdj = SYSTEM_ADJ;  
  28.                 mSelf.mProcessNames.put(app.processName, app.info.uid, app);  
  29.                 synchronized (mSelf.mPidsSelfLocked) {  
  30.                     mSelf.mPidsSelfLocked.put(app.pid, app);  
  31.                 }  
  32.                 mSelf.updateLruProcessLocked(app, truetrue);  
  33.             }  
  34.         } catch (PackageManager.NameNotFoundException e) {  
  35.             throw new RuntimeException(  
  36.                 "Unable to find android system package", e);  
  37.         }  
  38.     }  
  39.     ......  
  40. }  
        这个函数首先是将这个ActivityManagerService实例添加到ServiceManager中去托管,这样其它地方就可以通过ServiceManager.getService接口来访问这个全局唯一的ActivityManagerService实例了,接着又通过调用mSystemThread.installSystemApplicationInfo函数来把应用程序框架层下面的android包加载进来 ,这里的mSystemThread是一个ActivityThread类型的实例变量,它是在上面的Step 7中创建的,后面就是一些其它的初始化工作了。

        Step 10.  ActivityManagerService.systemReady

        这个函数是在上面的Step 6中的ServerThread.run函数在将系统中的一系列服务都初始化完毕之后才调用的,它定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:

view plain copy to clipboard
  1. public final class ActivityManagerService extends ActivityManagerNative  
  2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
  3.     ......  
  4.   
  5.     public void systemReady(final Runnable goingCallback) {  
  6.         ......  
  7.   
  8.         synchronized (this) {  
  9.             ......  
  10.   
  11.             mMainStack.resumeTopActivityLocked(null);  
  12.         }  
  13.     }  
  14.   
  15.     ......  
  16. }  

[java]  view plain copy
  1. public final class ActivityManagerService extends ActivityManagerNative  
  2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
  3.     ......  
  4.   
  5.     public void systemReady(final Runnable goingCallback) {  
  6.         ......  
  7.   
  8.         synchronized (this) {  
  9.             ......  
  10.   
  11.             mMainStack.resumeTopActivityLocked(null);  
  12.         }  
  13.     }  
  14.   
  15.     ......  
  16. }  
        这个函数的内容比较多,这里省去无关的部分,主要关心启动Home应用程序的逻辑,这里就是通过mMainStack.resumeTopActivityLocked函数来启动Home应用程序的了,这里的mMainStack是一个ActivityStack类型的实例变量。

        Step 11. ActivityStack.resumeTopActivityLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

view plain copy to clipboard
  1. public class ActivityStack {  
  2.     ......  
  3.   
  4.     final boolean resumeTopActivityLocked(ActivityRecord prev) {  
  5.         // Find the first activity that is not finishing.   
  6.         ActivityRecord next = topRunningActivityLocked(null);  
  7.   
  8.         ......  
  9.   
  10.         if (next == null) {  
  11.             // There are no more activities!  Let's just start up the   
  12.             // Launcher...   
  13.             if (mMainStack) {  
  14.                 return mService.startHomeActivityLocked();  
  15.             }  
  16.         }  
  17.   
  18.         ......  
  19.     }  
  20.   
  21.     ......  
  22. }  

[java]  view plain copy
  1. public class ActivityStack {  
  2.     ......  
  3.   
  4.     final boolean resumeTopActivityLocked(ActivityRecord prev) {  
  5.         // Find the first activity that is not finishing.  
  6.         ActivityRecord next = topRunningActivityLocked(null);  
  7.   
  8.         ......  
  9.   
  10.         if (next == null) {  
  11.             // There are no more activities!  Let's just start up the  
  12.             // Launcher...  
  13.             if (mMainStack) {  
  14.                 return mService.startHomeActivityLocked();  
  15.             }  
  16.         }  
  17.   
  18.         ......  
  19.     }  
  20.   
  21.     ......  
  22. }  
        这里调用函数topRunningActivityLocked返回的是当前系统Activity堆栈最顶端的Activity,由于此时还没有Activity被启动过,因此,返回值为null,即next变量的值为null,于是就调用mService.startHomeActivityLocked语句,这里的mService就是前面在Step 7中创建的ActivityManagerService实例了。

        Step 12. ActivityManagerService.startHomeActivityLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:

view plain copy to clipboard
  1. public final class ActivityManagerService extends ActivityManagerNative  
  2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
  3.     ......  
  4.   
  5.     boolean startHomeActivityLocked() {  
  6.         ......  
  7.   
  8.         Intent intent = new Intent(  
  9.             mTopAction,  
  10.             mTopData != null ? Uri.parse(mTopData) : null);  
  11.         intent.setComponent(mTopComponent);  
  12.         if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {  
  13.             intent.addCategory(Intent.CATEGORY_HOME);  
  14.         }  
  15.         ActivityInfo aInfo =  
  16.             intent.resolveActivityInfo(mContext.getPackageManager(),  
  17.             STOCK_PM_FLAGS);  
  18.         if (aInfo != null) {  
  19.             intent.setComponent(new ComponentName(  
  20.                 aInfo.applicationInfo.packageName, aInfo.name));  
  21.             // Don't do this if the home app is currently being   
  22.             // instrumented.   
  23.             ProcessRecord app = getProcessRecordLocked(aInfo.processName,  
  24.                 aInfo.applicationInfo.uid);  
  25.             if (app == null || app.instrumentationClass == null) {  
  26.                 intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);  
  27.                 mMainStack.startActivityLocked(null, intent, nullnull0, aInfo,  
  28.                     nullnull000falsefalse);  
  29.             }  
  30.         }  
  31.   
  32.         return true;  
  33.     }  
  34.   
  35.     ......  
  36. }  

[java]  view plain copy
  1. public final class ActivityManagerService extends ActivityManagerNative  
  2.         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
  3.     ......  
  4.   
  5.     boolean startHomeActivityLocked() {  
  6.         ......  
  7.   
  8.         Intent intent = new Intent(  
  9.             mTopAction,  
  10.             mTopData != null ? Uri.parse(mTopData) : null);  
  11.         intent.setComponent(mTopComponent);  
  12.         if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {  
  13.             intent.addCategory(Intent.CATEGORY_HOME);  
  14.         }  
  15.         ActivityInfo aInfo =  
  16.             intent.resolveActivityInfo(mContext.getPackageManager(),  
  17.             STOCK_PM_FLAGS);  
  18.         if (aInfo != null) {  
  19.             intent.setComponent(new ComponentName(  
  20.                 aInfo.applicationInfo.packageName, aInfo.name));  
  21.             // Don't do this if the home app is currently being  
  22.             // instrumented.  
  23.             ProcessRecord app = getProcessRecordLocked(aInfo.processName,  
  24.                 aInfo.applicationInfo.uid);  
  25.             if (app == null || app.instrumentationClass == null) {  
  26.                 intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);  
  27.                 mMainStack.startActivityLocked(null, intent, nullnull0, aInfo,  
  28.                     nullnull000falsefalse);  
  29.             }  
  30.         }  
  31.   
  32.         return true;  
  33.     }  
  34.   
  35.     ......  
  36. }  
        函数首先创建一个CATEGORY_HOME类型的Intent,然后通过Intent.resolveActivityInfo函数向PackageManagerService查询Category类型为HOME的Activity,这里我们假设只有系统自带的Launcher应用程序注册了HOME类型的Activity(见packages/apps/Launcher2/AndroidManifest.xml文件):

view plain copy to clipboard
  1. <manifest  
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.android.launcher"  
  4.     android:sharedUserId="@string/sharedUserId"  
  5.     >  
  6.   
  7.     ......  
  8.   
  9.     <application  
  10.         android:name="com.android.launcher2.LauncherApplication"  
  11.         android:process="@string/process"  
  12.         android:label="@string/application_name"  
  13.         android:icon="@drawable/ic_launcher_home">  
  14.   
  15.         <activity  
  16.             android:name="com.android.launcher2.Launcher"  
  17.             android:launchMode="singleTask"  
  18.             android:clearTaskOnLaunch="true"  
  19.             android:stateNotNeeded="true"  
  20.             android:theme="@style/Theme"  
  21.             android:screenOrientation="nosensor"  
  22.             android:windowSoftInputMode="stateUnspecified|adjustPan">  
  23.             <intent-filter>  
  24.                 <action android:name="android.intent.action.MAIN" />  
  25.                 <category android:name="android.intent.category.HOME" />  
  26.                 <category android:name="android.intent.category.DEFAULT" />  
  27.                 <category android:name="android.intent.category.MONKEY"/>  
  28.                 </intent-filter>  
  29.         </activity>  
  30.   
  31.         ......  
  32.     </application>  
  33. </manifest>  

[html]  view plain copy
  1. <manifest  
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.android.launcher"  
  4.     android:sharedUserId="@string/sharedUserId"  
  5.     >  
  6.   
  7.     ......  
  8.   
  9.     <application  
  10.         android:name="com.android.launcher2.LauncherApplication"  
  11.         android:process="@string/process"  
  12.         android:label="@string/application_name"  
  13.         android:icon="@drawable/ic_launcher_home">  
  14.   
  15.         <activity  
  16.             android:name="com.android.launcher2.Launcher"  
  17.             android:launchMode="singleTask"  
  18.             android:clearTaskOnLaunch="true"  
  19.             android:stateNotNeeded="true"  
  20.             android:theme="@style/Theme"  
  21.             android:screenOrientation="nosensor"  
  22.             android:windowSoftInputMode="stateUnspecified|adjustPan">  
  23.             <intent-filter>  
  24.                 <action android:name="android.intent.action.MAIN" />  
  25.                 <category android:name="android.intent.category.HOME" />  
  26.                 <category android:name="android.intent.category.DEFAULT" />  
  27.                 <category android:name="android.intent.category.MONKEY"/>  
  28.                 </intent-filter>  
  29.         </activity>  
  30.   
  31.         ......  
  32.     </application>  
  33. </manifest>  

        因此,这里就返回com.android.launcher2.Launcher这个Activity了。由于是第一次启动这个Activity,接下来调用函数getProcessRecordLocked返回来的ProcessRecord值为null,于是,就调用mMainStack.startActivityLocked函数启动com.android.launcher2.Launcher这个Activity了,这里的mMainStack是一个ActivityStack类型的成员变量。

        Step 13.  ActivityStack.startActivityLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中       

 

       Step 14. Launcher.onCreate

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/Launcher.java文件中:

view plain copy to clipboard
  1. public final class Launcher extends Activity  
  2.         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {  
  3.     ......  
  4.   
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         ......  
  8.   
  9.         if (!mRestoring) {  
  10.             mModel.startLoader(thistrue);  
  11.         }  
  12.   
  13.         ......  
  14.     }  
  15.   
  16.     ......  
  17. }  

[java]  view plain copy
  1. public final class Launcher extends Activity  
  2.         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {  
  3.     ......  
  4.   
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         ......  
  8.   
  9.         if (!mRestoring) {  
  10.             mModel.startLoader(thistrue);  
  11.         }  
  12.   
  13.         ......  
  14.     }  
  15.   
  16.     ......  
  17. }  
        这里的mModel是一个LauncherModel类型的成员变量,这里通过调用它的startLoader成员函数来执行加应用程序的操作。

        Step 15. LauncherModel.startLoader

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:

view plain copy to clipboard
  1. public class LauncherModel extends BroadcastReceiver {  
  2.     ......  
  3.   
  4.     public void startLoader(Context context, boolean isLaunching) {  
  5.         ......  
  6.   
  7.                 synchronized (mLock) {  
  8.                      ......  
  9.   
  10.                      // Don't bother to start the thread if we know it's not going to do anything  
  11.                      if (mCallbacks != null && mCallbacks.get() != null) {  
  12.                          // If there is already one running, tell it to stop.   
  13.                          LoaderTask oldTask = mLoaderTask;  
  14.                          if (oldTask != null) {  
  15.                              if (oldTask.isLaunching()) {  
  16.                                  // don't downgrade isLaunching if we're already running  
  17.                                  isLaunching = true;  
  18.                              }  
  19.                              oldTask.stopLocked();  
  20.                  }  
  21.                  mLoaderTask = new LoaderTask(context, isLaunching);  
  22.                  sWorker.post(mLoaderTask);  
  23.                 }  
  24.            }  
  25.     }  
  26.   
  27.     ......  
  28. }  

[java]  view plain copy
  1. public class LauncherModel extends BroadcastReceiver {  
  2.     ......  
  3.   
  4.     public void startLoader(Context context, boolean isLaunching) {  
  5.         ......  
  6.   
  7.                 synchronized (mLock) {  
  8.                      ......  
  9.   
  10.                      // Don't bother to start the thread if we know it's not going to do anything  
  11.                      if (mCallbacks != null && mCallbacks.get() != null) {  
  12.                          // If there is already one running, tell it to stop.  
  13.                          LoaderTask oldTask = mLoaderTask;  
  14.                          if (oldTask != null) {  
  15.                              if (oldTask.isLaunching()) {  
  16.                                  // don't downgrade isLaunching if we're already running  
  17.                                  isLaunching = true;  
  18.                              }  
  19.                              oldTask.stopLocked();  
  20.                  }  
  21.                  mLoaderTask = new LoaderTask(context, isLaunching);  
  22.                  sWorker.post(mLoaderTask);  
  23.                 }  
  24.            }  
  25.     }  
  26.   
  27.     ......  
  28. }  
        这里不是直接加载应用程序,而是把加载应用程序的操作作为一个消息来处理。这里的sWorker是一个Handler,通过它的post方式把一个消息放在消息队列中去,然后系统就会调用传进去的参数mLoaderTask的run函数来处理这个消息,这个mLoaderTask是LoaderTask类型的实例,于是,下面就会执行LoaderTask类的run函数了。

        Step 16. LoaderTask.run

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:

view plain copy to clipboard
  1. public class LauncherModel extends BroadcastReceiver {  
  2.     ......  
  3.   
  4.     private class LoaderTask implements Runnable {  
  5.         ......  
  6.   
  7.         public void run() {  
  8.             ......  
  9.   
  10.             keep_running: {  
  11.                 ......  
  12.   
  13.                 // second step   
  14.                 if (loadWorkspaceFirst) {  
  15.                     ......  
  16.                     loadAndBindAllApps();  
  17.                 } else {  
  18.                     ......  
  19.                 }  
  20.   
  21.                 ......  
  22.             }  
  23.   
  24.             ......  
  25.         }  
  26.   
  27.         ......  
  28.     }  
  29.   
  30.     ......  
  31. }  

[java]  view plain copy
  1. public class LauncherModel extends BroadcastReceiver {  
  2.     ......  
  3.   
  4.     private class LoaderTask implements Runnable {  
  5.         ......  
  6.   
  7.         public void run() {  
  8.             ......  
  9.   
  10.             keep_running: {  
  11.                 ......  
  12.   
  13.                 // second step  
  14.                 if (loadWorkspaceFirst) {  
  15.                     ......  
  16.                     loadAndBindAllApps();  
  17.                 } else {  
  18.                     ......  
  19.                 }  
  20.   
  21.                 ......  
  22.             }  
  23.   
  24.             ......  
  25.         }  
  26.   
  27.         ......  
  28.     }  
  29.   
  30.     ......  
  31. }  
        这里调用loadAndBindAllApps成员函数来进一步操作。

        Step 17. LoaderTask.loadAndBindAllApps
        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:

view plain copy to clipboard
  1. public class LauncherModel extends BroadcastReceiver {  
  2.     ......  
  3.   
  4.     private class LoaderTask implements Runnable {  
  5.         ......  
  6.   
  7.         private void loadAndBindAllApps() {  
  8.             ......  
  9.   
  10.             if (!mAllAppsLoaded) {  
  11.                 loadAllAppsByBatch();  
  12.                 if (mStopped) {  
  13.                     return;  
  14.                 }  
  15.                 mAllAppsLoaded = true;  
  16.             } else {  
  17.                 onlyBindAllApps();  
  18.             }  
  19.         }  
  20.   
  21.   
  22.         ......  
  23.     }  
  24.   
  25.     ......  
  26. }  

[java]  view plain copy
  1. public class LauncherModel extends BroadcastReceiver {  
  2.     ......  
  3.   
  4.     private class LoaderTask implements Runnable {  
  5.         ......  
  6.   
  7.         private void loadAndBindAllApps() {  
  8.             ......  
  9.   
  10.             if (!mAllAppsLoaded) {  
  11.                 loadAllAppsByBatch();  
  12.                 if (mStopped) {  
  13.                     return;  
  14.                 }  
  15.                 mAllAppsLoaded = true;  
  16.             } else {  
  17.                 onlyBindAllApps();  
  18.             }  
  19.         }  
  20.   
  21.   
  22.         ......  
  23.     }  
  24.   
  25.     ......  
  26. }  
        由于还没有加载过应用程序,这里的mAllAppsLoaded为false,于是就继续调用loadAllAppsByBatch函数来进一步操作了。

        Step 18. LoaderTask.loadAllAppsByBatch
        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:

view plain copy to clipboard
  1. public class LauncherModel extends BroadcastReceiver {  
  2.     ......  
  3.   
  4.     private class LoaderTask implements Runnable {  
  5.         ......  
  6.   
  7.         private void loadAllAppsByBatch() {   
  8.             ......  
  9.   
  10.             final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  
  11.             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  
  12.   
  13.             final PackageManager packageManager = mContext.getPackageManager();  
  14.             List<ResolveInfo> apps = null;  
  15.   
  16.             int N = Integer.MAX_VALUE;  
  17.   
  18.             int startIndex;  
  19.             int i=0;  
  20.             int batchSize = -1;  
  21.             while (i < N && !mStopped) {  
  22.                 if (i == 0) {  
  23.                     mAllAppsList.clear();  
  24.                     ......  
  25.                     apps = packageManager.queryIntentActivities(mainIntent, 0);  
  26.                       
  27.                     ......  
  28.   
  29.                     N = apps.size();  
  30.                       
  31.                     ......  
  32.   
  33.                     if (mBatchSize == 0) {  
  34.                         batchSize = N;  
  35.                     } else {  
  36.                         batchSize = mBatchSize;  
  37.                     }  
  38.   
  39.                     ......  
  40.   
  41.                     Collections.sort(apps,  
  42.                         new ResolveInfo.DisplayNameComparator(packageManager));  
  43.                 }  
  44.   
  45.                 startIndex = i;  
  46.                 for (int j=0; i<N && j<batchSize; j++) {  
  47.                     // This builds the icon bitmaps.   
  48.                     mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));  
  49.                     i++;  
  50.                 }  
  51.   
  52.                 final boolean first = i <= batchSize;  
  53.                 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);  
  54.                 final ArrayList<ApplicationInfo> added = mAllAppsList.added;  
  55.                 mAllAppsList.added = new ArrayList<ApplicationInfo>();  
  56.               
  57.                 mHandler.post(new Runnable() {  
  58.                     public void run() {  
  59.                         final long t = SystemClock.uptimeMillis();  
  60.                         if (callbacks != null) {  
  61.                             if (first) {  
  62.                                 callbacks.bindAllApplications(added);  
  63.                             } else {  
  64.                                 callbacks.bindAppsAdded(added);  
  65.                             }  
  66.                             ......  
  67.                         } else {  
  68.                             ......  
  69.                         }  
  70.                     }  
  71.                 });  
  72.   
  73.                 ......  
  74.             }  
  75.   
  76.             ......  
  77.         }  
  78.   
  79.         ......  
  80.     }  
  81.   
  82.     ......  
  83. }  

[java]  view plain copy
  1. public class LauncherModel extends BroadcastReceiver {  
  2.     ......  
  3.   
  4.     private class LoaderTask implements Runnable {  
  5.         ......  
  6.   
  7.         private void loadAllAppsByBatch() {   
  8.             ......  
  9.   
  10.             final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  
  11.             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  
  12.   
  13.             final PackageManager packageManager = mContext.getPackageManager();  
  14.             List<ResolveInfo> apps = null;  
  15.   
  16.             int N = Integer.MAX_VALUE;  
  17.   
  18.             int startIndex;  
  19.             int i=0;  
  20.             int batchSize = -1;  
  21.             while (i < N && !mStopped) {  
  22.                 if (i == 0) {  
  23.                     mAllAppsList.clear();  
  24.                     ......  
  25.                     apps = packageManager.queryIntentActivities(mainIntent, 0);  
  26.                       
  27.                     ......  
  28.   
  29.                     N = apps.size();  
  30.                       
  31.                     ......  
  32.   
  33.                     if (mBatchSize == 0) {  
  34.                         batchSize = N;  
  35.                     } else {  
  36.                         batchSize = mBatchSize;  
  37.                     }  
  38.   
  39.                     ......  
  40.   
  41.                     Collections.sort(apps,  
  42.                         new ResolveInfo.DisplayNameComparator(packageManager));  
  43.                 }  
  44.   
  45.                 startIndex = i;  
  46.                 for (int j=0; i<N && j<batchSize; j++) {  
  47.                     // This builds the icon bitmaps.  
  48.                     mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));  
  49.                     i++;  
  50.                 }  
  51.   
  52.                 final boolean first = i <= batchSize;  
  53.                 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);  
  54.                 final ArrayList<ApplicationInfo> added = mAllAppsList.added;  
  55.                 mAllAppsList.added = new ArrayList<ApplicationInfo>();  
  56.               
  57.                 mHandler.post(new Runnable() {  
  58.                     public void run() {  
  59.                         final long t = SystemClock.uptimeMillis();  
  60.                         if (callbacks != null) {  
  61.                             if (first) {  
  62.                                 callbacks.bindAllApplications(added);  
  63.                             } else {  
  64.                                 callbacks.bindAppsAdded(added);  
  65.                             }  
  66.                             ......  
  67.                         } else {  
  68.                             ......  
  69.                         }  
  70.                     }  
  71.                 });  
  72.   
  73.                 ......  
  74.             }  
  75.   
  76.             ......  
  77.         }  
  78.   
  79.         ......  
  80.     }  
  81.   
  82.     ......  
  83. }  
        函数首先构造一个CATEGORY_LAUNCHER类型的Intent:

view plain copy to clipboard
  1. final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  
  2. mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  

[java]  view plain copy
  1. final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  
  2. mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  
        接着从mContext变量中获得PackageManagerService的接口:

view plain copy to clipboard
  1. final PackageManager packageManager = mContext.getPackageManager();  

[java]  view plain copy
  1. final PackageManager packageManager = mContext.getPackageManager();  

       下一步就是通过这个PackageManagerService.queryIntentActivities接口来取回所有Action类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity了。

       我们先进入到PackageManagerService.queryIntentActivities函数中看看是如何获得这些Activity的,然后再回到这个函数中来看其余操作。

       Step 19. PackageManagerService.queryIntentActivities

       这个函数定义在frameworks/base/services/java/com/android/server/PackageManagerService.java文件中:

view plain copy to clipboard
  1. class PackageManagerService extends IPackageManager.Stub {  
  2.     ......  
  3.   
  4.     public List<ResolveInfo> queryIntentActivities(Intent intent,  
  5.             String resolvedType, int flags) {  
  6.         ......  
  7.   
  8.         synchronized (mPackages) {  
  9.             String pkgName = intent.getPackage();  
  10.             if (pkgName == null) {  
  11.                 return (List<ResolveInfo>)mActivities.queryIntent(intent,  
  12.                         resolvedType, flags);  
  13.             }  
  14.   
  15.             ......  
  16.         }  
  17.   
  18.         ......  
  19.     }  
  20.   
  21.     ......  
  22. }  

[java]  view plain copy
  1. class PackageManagerService extends IPackageManager.Stub {  
  2.     ......  
  3.   
  4.     public List<ResolveInfo> queryIntentActivities(Intent intent,  
  5.             String resolvedType, int flags) {  
  6.         ......  
  7.   
  8.         synchronized (mPackages) {  
  9.             String pkgName = intent.getPackage();  
  10.             if (pkgName == null) {  
  11.                 return (List<ResolveInfo>)mActivities.queryIntent(intent,  
  12.                         resolvedType, flags);  
  13.             }  
  14.   
  15.             ......  
  16.         }  
  17.   
  18.         ......  
  19.     }  
  20.   
  21.     ......  
  22. }  

       系统在启动PackageManagerService时,会把系统中的应用程序都解析一遍,然后把解析得到的Activity都保存在mActivities变量中,这里通过这个mActivities变量的queryIntent函数返回符合条件intent的Activity,这里要返回的便是Action类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity了。

        回到Step 18中的 LoaderTask.loadAllAppsByBatch函数中,从queryIntentActivities函数调用处返回所要求的Activity后,便调用函数tryGetCallbacks(oldCallbacks)得到一个返CallBack接口,这个接口是由Launcher类实现的,接着调用这个接口的.bindAllApplications函数来进一步操作。注意,这里又是通过消息来处理加载应用程序的操作的。

        Step 20. Launcher.bindAllApplications

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/Launcher.java文件中:

view plain copy to clipboard
  1. public final class Launcher extends Activity  
  2.         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {  
  3.     ......  
  4.   
  5.     private AllAppsView mAllAppsGrid;  
  6.   
  7.     ......  
  8.   
  9.     public void bindAllApplications(ArrayList<ApplicationInfo> apps) {  
  10.         mAllAppsGrid.setApps(apps);  
  11.     }  
  12.   
  13.     ......  
  14. }  

[java]  view plain copy
  1. public final class Launcher extends Activity  
  2.         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {  
  3.     ......  
  4.   
  5.     private AllAppsView mAllAppsGrid;  
  6.   
  7.     ......  
  8.   
  9.     public void bindAllApplications(ArrayList<ApplicationInfo> apps) {  
  10.         mAllAppsGrid.setApps(apps);  
  11.     }  
  12.   
  13.     ......  
  14. }  
        这里的mAllAppsGrid是一个AllAppsView类型的变量,它的实际类型一般就是AllApps2D了。

        Step 21. AllApps2D.setApps

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/AllApps2D.java文件中:

view plain copy to clipboard
  1. public class AllApps2D  
  2.     extends RelativeLayout  
  3.     implements AllAppsView,  
  4.         AdapterView.OnItemClickListener,  
  5.         AdapterView.OnItemLongClickListener,  
  6.         View.OnKeyListener,  
  7.         DragSource {  
  8.   
  9.     ......  
  10.   
  11.     public void setApps(ArrayList<ApplicationInfo> list) {  
  12.         mAllAppsList.clear();  
  13.         addApps(list);  
  14.     }  
  15.   
  16.     public void addApps(ArrayList<ApplicationInfo> list) {  
  17.         final int N = list.size();  
  18.   
  19.         for (int i=0; i<N; i++) {  
  20.             final ApplicationInfo item = list.get(i);  
  21.             int index = Collections.binarySearch(mAllAppsList, item,  
  22.                 LauncherModel.APP_NAME_COMPARATOR);  
  23.             if (index < 0) {  
  24.                 index = -(index+1);  
  25.             }  
  26.             mAllAppsList.add(index, item);  
  27.         }  
  28.         mAppsAdapter.notifyDataSetChanged();  
  29.     }  
  30.   
  31.     ......  
  32. }  

[java]  view plain copy
  1. public class AllApps2D  
  2.     extends RelativeLayout  
  3.     implements AllAppsView,  
  4.         AdapterView.OnItemClickListener,  
  5.         AdapterView.OnItemLongClickListener,  
  6.         View.OnKeyListener,  
  7.         DragSource {  
  8.   
  9.     ......  
  10.   
  11.     public void setApps(ArrayList<ApplicationInfo> list) {  
  12.         mAllAppsList.clear();  
  13.         addApps(list);  
  14.     }  
  15.   
  16.     public void addApps(ArrayList<ApplicationInfo> list) {  
  17.         final int N = list.size();  
  18.   
  19.         for (int i=0; i<N; i++) {  
  20.             final ApplicationInfo item = list.get(i);  
  21.             int index = Collections.binarySearch(mAllAppsList, item,  
  22.                 LauncherModel.APP_NAME_COMPARATOR);  
  23.             if (index < 0) {  
  24.                 index = -(index+1);  
  25.             }  
  26.             mAllAppsList.add(index, item);  
  27.         }  
  28.         mAppsAdapter.notifyDataSetChanged();  
  29.     }  
  30.   
  31.     ......  
  32. }  
        函数setApps首先清空mAllAppsList列表,然后调用addApps函数来为上一步得到的每一个应用程序创建一个ApplicationInfo实例了,有了这些ApplicationInfo实例之后,就可以在桌面上展示系统中所有的应用程序了。

        到了这里,系统默认的Home应用程序Launcher就把PackageManagerService中的应用程序加载进来了,当我们在屏幕上点击下面这个图标时,就会把刚才加载好的应用程序以图标的形式展示出来了:

        点击这个按钮时,便会响应Launcher.onClick函数:

view plain copy to clipboard
  1. public final class Launcher extends Activity  
  2.         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {  
  3.     ......  
  4.   
  5.     public void onClick(View v) {  
  6.         Object tag = v.getTag();  
  7.         if (tag instanceof ShortcutInfo) {  
  8.             ......  
  9.         } else if (tag instanceof FolderInfo) {  
  10.             ......  
  11.         } else if (v == mHandleView) {  
  12.             if (isAllAppsVisible()) {  
  13.                 ......  
  14.             } else {  
  15.                 showAllApps(true);  
  16.             }  
  17.         }  
  18.     }  
  19.   
  20.     ......  
  21. }  

[java]  view plain copy
  1. public final class Launcher extends Activity  
  2.         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {  
  3.     ......  
  4.   
  5.     public void onClick(View v) {  
  6.         Object tag = v.getTag();  
  7.         if (tag instanceof ShortcutInfo) {  
  8.             ......  
  9.         } else if (tag instanceof FolderInfo) {  
  10.             ......  
  11.         } else if (v == mHandleView) {  
  12.             if (isAllAppsVisible()) {  
  13.                 ......  
  14.             } else {  
  15.                 showAllApps(true);  
  16.             }  
  17.         }  
  18.     }  
  19.   
  20.     ......  
  21. }  
        接着就会调用showAllApps函数显示应用程序图标:

view plain copy to clipboard
  1. public final class Launcher extends Activity  
  2.         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {  
  3.     ......  
  4.   
  5.     void showAllApps(boolean animated) {  
  6.         mAllAppsGrid.zoom(1.0f, animated);  
  7.   
  8.         ((View) mAllAppsGrid).setFocusable(true);  
  9.         ((View) mAllAppsGrid).requestFocus();  
  10.   
  11.         // TODO: fade these two too   
  12.         mDeleteZone.setVisibility(View.GONE);  
  13.     }  
  14.   
  15.     ......  
  16. }  

[java]  view plain copy
  1. public final class Launcher extends Activity  
  2.         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {  
  3.     ......  
  4.   
  5.     void showAllApps(boolean animated) {  
  6.         mAllAppsGrid.zoom(1.0f, animated);  
  7.   
  8.         ((View) mAllAppsGrid).setFocusable(true);  
  9.         ((View) mAllAppsGrid).requestFocus();  
  10.   
  11.         // TODO: fade these two too  
  12.         mDeleteZone.setVisibility(View.GONE);  
  13.     }  
  14.   
  15.     ......  
  16. }  
        这样我们就可以看到系统中的应用程序了:



        当点击上面的这些应用程序图标时,便会响应AllApps2D.onItemClick函数:

view plain copy to clipboard
  1. public class AllApps2D  
  2.     extends RelativeLayout  
  3.     implements AllAppsView,  
  4.         AdapterView.OnItemClickListener,  
  5.         AdapterView.OnItemLongClickListener,  
  6.         View.OnKeyListener,  
  7.         DragSource {  
  8.   
  9.     ......  
  10.   
  11.     public void onItemClick(AdapterView parent, View v, int position, long id) {  
  12.         ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);  
  13.         mLauncher.startActivitySafely(app.intent, app);  
  14.     }  
  15.   
  16.   
  17.     ......  
  18. }  

[java]  view plain copy
  1. public class AllApps2D  
  2.     extends RelativeLayout  
  3.     implements AllAppsView,  
  4.         AdapterView.OnItemClickListener,  
  5.         AdapterView.OnItemLongClickListener,  
  6.         View.OnKeyListener,  
  7.         DragSource {  
  8.   
  9.     ......  
  10.   
  11.     public void onItemClick(AdapterView parent, View v, int position, long id) {  
  12.         ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);  
  13.         mLauncher.startActivitySafely(app.intent, app);  
  14.     }  
  15.   
  16.   
  17.     ......  
  18. }  
        这里的成员变量mLauncher的类型为Launcher,于是就调用Launcher.startActivitySafely函数来启动应用程序了。

 

 

 

 七         Launcher    widget添加过程

 

           

 

Android中的AppWidget与google widget和中移动的widget并不是一个概念,这里的AppWidget只是把一个进程的控件嵌入到别外一个进程的窗口里的一种方法。View在另外一个进程里显示,但事件的处理方法还是在原来的进程里。这有点像 X Window中的嵌入式窗口。 

 首先我们需要了解RemoteViews, AppWidgetHost, AppWidgetHostView等概念

RemoteViews:并不是一个真正的View,它没有实现View的接口,而只是一个用于描述View的实体。比如:创建View需要的资源ID和各个控件的事件响应方法。RemoteViews会通过进程间通信机制传递给AppWidgetHost。

AppWidgetHost

AppWidgetHost是真正容纳AppWidget的地方,它的主要功能有两个:

o 监听来自AppWidgetService的事件:

[c]  view plain copy
  1. class Callbacks extends IAppWidgetHost.<span style="color:#202020">Stub</span><span> </span><span>{</span><span> </span>public<span> </span><span style="color:#993333">void</span><span> </span>updateAppWidget<span>(</span><span style="color:#993333">int</span><span> </span>appWidgetId<span style="color:#339933">,</span>RemoteViews views<span>)</span><span> </span><span>{</span><span> </span>Message msg<span> </span><span style="color:#339933">=</span><span> </span>mHandler.<span style="color:#202020">obtainMessage</span><span>(</span>HANDLE_UPDATE<span>)</span><span style="color:#339933">;</span><span> </span>msg.<span style="color:#202020">arg1</span><span> </span><span style="color:#339933">=</span><span> </span>appWidgetId<span style="color:#339933">;</span>msg.<span style="color:#202020">obj</span><span> </span><span style="color:#339933">=</span><span> </span>views<span style="color:#339933">;</span><span> </span>msg.<span style="color:#202020">sendToTarget</span><span>(</span><span>)</span><span style="color:#339933">;</span><span> </span><span>}</span><span> </span>  public<span> </span><span style="color:#993333">void</span><span> </span>providerChanged<span>(</span><span style="color:#993333">int</span><span> </span>appWidgetId<span style="color:#339933">,</span>AppWidgetProviderInfo info<span>)</span><span> </span><span>{</span><span> </span>Message msg<span> </span><span style="color:#339933">=</span><span> </span>mHandler.<span style="color:#202020">obtainMessage</span><span>(</span>HANDLE_PROVIDER_CHANGED<span>)</span><span style="color:#339933">;</span>msg.<span style="color:#202020">arg1</span><span> </span><span style="color:#339933">=</span><span> </span>appWidgetId<span style="color:#339933">;</span><span> </span>msg.<span style="color:#202020">obj</span><span> </span><span style="color:#339933">=</span><span> </span>info<span style="color:#339933">;</span><span> </span>msg.<span style="color:#202020">sendToTarget</span><span>(</span><span>)</span><span style="color:#339933">;</span><span> </span><span>}</span><span> </span><span>}</span>  

这是主要处理update和provider_changed两个事件,根据这两个事件更新widget。

[c]  view plain copy
  1. class UpdateHandler extends Handler<span> </span><span>{</span><span> </span>public UpdateHandler<span>(</span>Looper looper<span>)</span><span> </span><span>{</span><span> </span>super<span>(</span>looper<span>)</span><span style="color:#339933">;</span><span> </span><span>}</span><span> </span>  public<span> </span><span style="color:#993333">void</span><span> </span>handleMessage<span>(</span>Message msg<span>)</span><span> </span><span>{</span><span> </span><span>switch</span><span> </span><span>(</span>msg.<span style="color:#202020">what</span><span>)</span><span> </span><span>{</span><span> </span><span>case</span><span> </span>HANDLE_UPDATE<span style="color:#339933">:</span><span> </span><span>{</span>updateAppWidgetView<span>(</span>msg.<span style="color:#202020">arg1</span><span style="color:#339933">,</span><span> </span><span>(</span>RemoteViews<span>)</span>msg.<span style="color:#202020">obj</span><span>)</span><span style="color:#339933">;</span><span> </span><span style="color:#000000; font-weight:bold">break</span><span style="color:#339933">;</span><span> </span><span>}</span><span> </span><span>case</span><span> </span>HANDLE_PROVIDER_CHANGED<span style="color:#339933">:</span><span> </span><span>{</span>onProviderChanged<span>(</span>msg.<span style="color:#202020">arg1</span><span style="color:#339933">,</span><span> </span><span>(</span>AppWidgetProviderInfo<span>)</span>msg.<span style="color:#202020">obj</span><span>)</span><span style="color:#339933">;</span><span> </span><span style="color:#000000; font-weight:bold">break</span><span style="color:#339933">;</span><span> </span><span>}</span><span> </span><span>}</span><span> </span><span>}</span><span> </span><span>}</span>  

o 另外一个功能就是创建AppWidgetHostView。前面我们说过RemoteViews不是真正的View,只是View的描述,而AppWidgetHostView才是真正的View。这里先创建AppWidgetHostView,然后通过AppWidgetService查询appWidgetId对应的RemoteViews,最后把RemoteViews传递给AppWidgetHostView去updateAppWidget。

[c]  view plain copy
  1. public final AppWidgetHostView createView<span>(</span>Context context<span style="color:#339933">,</span><span> </span><span style="color:#993333">int</span><span> </span>appWidgetId<span style="color:#339933">,</span><span> </span>AppWidgetProviderInfo appWidget<span>)</span><span> </span><span>{</span><span> </span>AppWidgetHostView view<span> </span><span style="color:#339933">=</span><span> </span>onCreateView<span>(</span>context<span style="color:#339933">,</span><span> </span>appWidgetId<span style="color:#339933">,</span><span> </span>appWidget<span>)</span><span style="color:#339933">;</span>view.<span style="color:#202020">setAppWidget</span><span>(</span>appWidgetId<span style="color:#339933">,</span><span> </span>appWidget<span>)</span><span style="color:#339933">;</span><span> </span>synchronized<span> </span><span>(</span>mViews<span>)</span><span> </span><span>{</span><span> </span>mViews.<span style="color:#202020">put</span><span>(</span>appWidgetId<span style="color:#339933">,</span><span> </span>view<span>)</span><span style="color:#339933">;</span><span> </span><span>}</span>RemoteViews views<span> </span><span style="color:#339933">=</span><span> </span><span style="color:#000000; font-weight:bold">null</span><span style="color:#339933">;</span><span> </span>try<span> </span><span>{</span><span> </span>views<span> </span><span style="color:#339933">=</span><span> </span>sService.<span style="color:#202020">getAppWidgetViews</span><span>(</span>appWidgetId<span>)</span><span style="color:#339933">;</span><span> </span><span>}</span><span> </span>catch<span>(</span>RemoteException e<span>)</span><span> </span><span>{</span><span> </span>throw new RuntimeException<span>(</span><span>"system server dead?"</span><span style="color:#339933">,</span><span> </span>e<span>)</span><span style="color:#339933">;</span><span> </span><span>}</span>view.<span style="color:#202020">updateAppWidget</span><span>(</span>views<span>)</span><span style="color:#339933">;</span><span> </span><span>return</span><span> </span>view<span style="color:#339933">;</span><span> </span><span>}</span>  

AppWidgetHostView

AppWidgetHostView是真正的View,但它只是一个容器,用来容纳实际的AppWidget的View。这个AppWidget的View是根据RemoteViews的描述来创建。这是在updateAppWidget里做的:

[c]  view plain copy
  1. public<span> </span><span style="color:#993333">void</span><span> </span>updateAppWidget<span>(</span>RemoteViews remoteViews<span>)</span><span> </span><span>{</span><span> </span>...<span> </span><span>if</span><span> </span><span>(</span>content<span> </span><span style="color:#339933">==</span><span> </span><span style="color:#000000; font-weight:bold">null</span><span> </span><span style="color:#339933">&&</span><span> </span>layoutId<span> </span><span style="color:#339933">==</span>mLayoutId<span>)</span><span> </span><span>{</span><span> </span>try<span> </span><span>{</span><span> </span>remoteViews.<span style="color:#202020">reapply</span><span>(</span>mContext<span style="color:#339933">,</span><span> </span>mView<span>)</span><span style="color:#339933">;</span><span> </span>content<span> </span><span style="color:#339933">=</span><span> </span>mView<span style="color:#339933">;</span><span> </span>recycled<span> </span><span style="color:#339933">=</span><span> </span><span style="color:#000000; font-weight:bold">true</span><span style="color:#339933">;</span><span> </span><span>if</span><span>(</span>LOGD<span>)</span><span> </span>Log.<span style="color:#202020">d</span><span>(</span>TAG<span style="color:#339933">,</span><span> </span><span>"was able to recycled existing layout"</span><span>)</span><span style="color:#339933">;</span><span> </span><span>}</span><span> </span>catch<span> </span><span>(</span>RuntimeException e<span>)</span><span> </span><span>{</span><span> </span>exception<span style="color:#339933">=</span><span> </span>e<span style="color:#339933">;</span><span> </span><span>}</span><span> </span><span>}</span><span> </span> <span> </span><span style="color:#666666; font-style:italic">// Try normal RemoteView inflation</span><span> </span><span>if</span><span> </span><span>(</span>content<span> </span><span style="color:#339933">==</span><span> </span><span style="color:#000000; font-weight:bold">null</span><span>)</span><span> </span><span>{</span><span> </span>try<span> </span><span>{</span><span> </span>content<span> </span><span style="color:#339933">=</span>remoteViews.<span style="color:#202020">apply</span><span>(</span>mContext<span style="color:#339933">,</span><span> </span>this<span>)</span><span style="color:#339933">;</span><span> </span><span>if</span><span> </span><span>(</span>LOGD<span>)</span><span> </span>Log.<span style="color:#202020">d</span><span>(</span>TAG<span style="color:#339933">,</span><span> </span><span>"had to inflate new layout"</span><span>)</span><span style="color:#339933">;</span><span> </span><span>}</span><span> </span>catch<span>(</span>RuntimeException e<span>)</span><span> </span><span>{</span><span> </span>exception<span> </span><span style="color:#339933">=</span><span> </span>e<span style="color:#339933">;</span><span> </span><span>}</span><span> </span><span>}</span><span> </span>...<span> </span><span>if</span><span> </span><span>(</span><span style="color:#339933">!</span>recycled<span>)</span><span> </span><span>{</span><span> </span>prepareView<span>(</span>content<span>)</span><span style="color:#339933">;</span>addView<span>(</span>content<span>)</span><span style="color:#339933">;</span><span> </span><span>}</span><span> </span> <span> </span><span>if</span><span> </span><span>(</span>mView<span> </span><span style="color:#339933">!=</span><span> </span>content<span>)</span><span> </span><span>{</span><span> </span>removeView<span>(</span>mView<span>)</span><span style="color:#339933">;</span><span> </span>mView<span> </span><span style="color:#339933">=</span><span> </span>content<span style="color:#339933">;</span><span> </span><span>}</span><span> </span>...<span> </span><span>}</span>  

remoteViews.apply创建了实际的View,下面代码可以看出:

[c]  view plain copy
  1. public View apply<span>(</span>Context context<span style="color:#339933">,</span><span> </span>ViewGroup parent<span>)</span><span> </span><span>{</span><span> </span>View result<span> </span><span style="color:#339933">=</span><span> </span><span style="color:#000000; font-weight:bold">null</span><span style="color:#339933">;</span><span> </span>  Context c<span> </span><span style="color:#339933">=</span>prepareContext<span>(</span>context<span>)</span><span style="color:#339933">;</span><span> </span>  Resources r<span> </span><span style="color:#339933">=</span><span> </span>c.<span style="color:#202020">getResources</span><span>(</span><span>)</span><span style="color:#339933">;</span><span> </span>LayoutInflater inflater<span> </span><span style="color:#339933">=</span><span>(</span>LayoutInflater<span>)</span><span> </span>c .<span style="color:#202020">getSystemService</span><span>(</span>Context.<span style="color:#202020">LAYOUT_INFLATER_SERVICE</span><span>)</span><span style="color:#339933">;</span><span> </span>  inflater<span> </span><span style="color:#339933">=</span>inflater.<span style="color:#202020">cloneInContext</span><span>(</span>c<span>)</span><span style="color:#339933">;</span><span> </span>inflater.<span style="color:#202020">setFilter</span><span>(</span>this<span>)</span><span style="color:#339933">;</span><span> </span>  result<span> </span><span style="color:#339933">=</span><span> </span>inflater.<span style="color:#202020">inflate</span><span>(</span>mLayoutId<span style="color:#339933">,</span>parent<span style="color:#339933">,</span><span> </span><span style="color:#000000; font-weight:bold">false</span><span>)</span><span style="color:#339933">;</span><span> </span>  performApply<span>(</span>result<span>)</span><span style="color:#339933">;</span><span> </span> <span> </span><span>return</span><span> </span>result<span style="color:#339933">;</span><span> </span><span>}</span>  

Host的实现者

AppWidgetHost和AppWidgetHostView是在框架中定义的两个基类。应用程序可以利用这两个类来实现自己的Host。Launcher是缺省的桌面,它是一个Host的实现者。

LauncherAppWidgetHostView扩展了AppWidgetHostView,实现了对长按事件的处理。

LauncherAppWidgetHost扩展了AppWidgetHost,这里只是重载了onCreateView,创建LauncherAppWidgetHostView的实例。

AppWidgetService

AppWidgetService存在的目的主要是解开AppWidgetProvider和AppWidgetHost之间的耦合。如果AppWidgetProvider和AppWidgetHost的关系固定死了,AppWidget就无法在任意进程里显示了。而有了AppWidgetService,AppWidgetProvider根本不需要知道自己的AppWidget在哪里显示了。

 

LauncherAppWidgetHostView: 扩展了AppWidgetHostView,实现了对长按事件的处理

LauncherAppWidgetHost: 扩展了AppWidgetHost,这里只是重载了onCreateView,创建LauncherAppWidgetHostView的实例

24 /**
25  * Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
26  * which correctly captures all long-press events. This ensures that users can
27  * always pick up and move widgets.
28  */
29 public class LauncherAppWidgetHost extends AppWidgetHost {
30     public LauncherAppWidgetHost(Context context, int hostId) {
31         super(context, hostId);
32     }
33     
34     @Override
35     protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
36             AppWidgetProviderInfo appWidget) {
37         return new LauncherAppWidgetHostView(context);
38     }
39 }

 

首先在Launcher.java中定义了如下两个变量

174     private AppWidgetManager mAppWidgetManager;
175     private LauncherAppWidgetHost mAppWidgetHost;

在onCreate函数中初始化,

224         mAppWidgetManager = AppWidgetManager.getInstance(this);
225         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
226         mAppWidgetHost.startListening();
上述代码,获取mAppWidgetManager的实例,并创建LauncherAppWidgetHost,以及监听
AppWidgetManager只是应用程序与底层Service之间的一个桥梁,是Android中标准的aidl实现方式
应用程序通过AppWidgetManager调用Service中的方法
frameworks/base /  core  /  java  /  android  /  appwidget  /  AppWidgetManager.java
35 /**
36  * Updates AppWidget state; gets information about installed AppWidget providers and other
37  * AppWidget related state.
38  */
39 public class AppWidgetManager {
197    static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache = new WeakHashMap();
198     static IAppWidgetService sService;
204     /**
205      * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
206      * Context} object.
207      */
208     public static AppWidgetManager getInstance(Context context) {
209         synchronized (sManagerCache) {
210             if (sService == null) {
211                 IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
212                 sService = IAppWidgetService.Stub.asInterface(b);
213             }
214
215             WeakReference<AppWidgetManager> ref = sManagerCache.get(context);
216             AppWidgetManager result = null;
217             if (ref != null) {
218                 result = ref.get();
219             }
220             if (result == null) {
221                 result = new AppWidgetManager(context);
222                 sManagerCache.put(context, new WeakReference(result));
223             }
224             return result;
225         }
226     }
227
228     private AppWidgetManager(Context context) {
229         mContext = context;
230         mDisplayMetrics = context.getResources().getDisplayMetrics();
231     }

以上代码是设计模式中标准的单例模式

 

frameworks/base/ core / java / android / appwidget / AppWidgetHost.java

90     public AppWidgetHost(Context context, int hostId) {
91         mContext = context;
92         mHostId = hostId;
93         mHandler = new UpdateHandler(context.getMainLooper());
94         synchronized (sServiceLock) {
95             if (sService == null) {
96                 IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
97                 sService = IAppWidgetService.Stub.asInterface(b);
98             }
99         }
100     }

可以看到AppWidgetHost有自己的HostId,Handler,和sService

93         mHandler = new UpdateHandler(context.getMainLooper());

这是啥用法呢?

参数为Looper,即消息处理放到此Looper的MessageQueue中,有哪些消息呢?

40     static final int HANDLE_UPDATE = 1;
41     static final int HANDLE_PROVIDER_CHANGED = 2;
48
49     class Callbacks extends IAppWidgetHost.Stub {
50         public void updateAppWidget(int appWidgetId, RemoteViews views) {
51             Message msg = mHandler.obtainMessage(HANDLE_UPDATE);
52             msg.arg1 = appWidgetId;
53             msg.obj = views;
54             msg.sendToTarget();
55         }
56
57         public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) {
58             Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED);
59             msg.arg1 = appWidgetId;
60             msg.obj = info;
61             msg.sendToTarget();
62         }
63     }
64
65     class UpdateHandler extends Handler {
66         public UpdateHandler(Looper looper) {
67             super(looper);
68         }
69         
70         public void handleMessage(Message msg) {
71             switch (msg.what) {
72                 case HANDLE_UPDATE: {
73                     updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
74                     break;
75                 }
76                 case HANDLE_PROVIDER_CHANGED: {
77                     onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj);
78                     break;
79                 }
80             }
81         }
82     }

通过以上可以看到主要有两中类型的消息,HANDLE_UPDATE和HANDLE_PROVIDER_CHANGED

处理即通过自身定义的方法

231     /**
232      * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk.
233      */
234     protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
235         AppWidgetHostView v;
236         synchronized (mViews) {
237             v = mViews.get(appWidgetId);
238         }
239         if (v != null) {
240             v.updateAppWidget(null, AppWidgetHostView.UPDATE_FLAGS_RESET);
241         }
242     }
243
244     void updateAppWidgetView(int appWidgetId, RemoteViews views) {
245         AppWidgetHostView v;
246         synchronized (mViews) {
247             v = mViews.get(appWidgetId);
248         }
249         if (v != null) {
250             v.updateAppWidget(views, 0);
251         }
252     }

 

那么此消息是何时由谁发送的呢?

从以上的代码中看到AppWidgetHost定义了内部类Callback,扩展了类IAppWidgetHost.Stub,类Callback中负责发送以上消息

Launcher中会调用本类中的如下方法,

102     /**
103      * Start receiving onAppWidgetChanged calls for your AppWidgets.  Call this when your activity
104      * becomes visible, i.e. from onStart() in your Activity.
105      */
106     public void startListening() {
107         int[] updatedIds;
108         ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
109         
110         try {
111             if (mPackageName == null) {
112                 mPackageName = mContext.getPackageName();
113             }
114             updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews);
115         }
116         catch (RemoteException e) {
117             throw new RuntimeException("system server dead?", e);
118         }
119
120         final int N = updatedIds.length;
121         for (int i=0; i<N; i++) {
122             updateAppWidgetView(updatedIds[i], updatedViews.get(i));
123         }
124     }
最终调用AppWidgetService中的方法startListening方法,并把mCallbacks传过去,由Service负责发送消息
Launcher中添加Widget
在Launcher中添加widget,有两种途径,通过Menu或者长按桌面的空白区域,都会弹出Dialog,让用户选择添加
如下代码是当用户选择
1999         /**
2000          * Handle the action clicked in the "Add to home" dialog.
2001          */
2002         public void onClick(DialogInterface dialog, int which) {
2003             Resources res = getResources();
2004             cleanup();
2005
2006             switch (which) {
2007                 case AddAdapter.ITEM_SHORTCUT: {
2008                     // Insert extra item to handle picking application
2009                     pickShortcut();
2010                     break;
2011                 }
2012
2013                 case AddAdapter.ITEM_APPWIDGET: {
2014                     int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
2015
2016                     Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
2017                     pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
2018                     // start the pick activity
2019                     startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
2020                     break;
2021                 }

当用户在Dialog中选择AddAdapter.ITEM_APPWIDGET时,首先会通过AppWidgethost分配一个appWidgetId,并最终调到AppWidgetService中去

同时发送Intent,其中保存有刚刚分配的appWidgetId,AppWidgetManager.EXTRA_APPWIDGET_ID

139     /**
140      * Get a appWidgetId for a host in the calling process.
141      *
142      * @return a appWidgetId
143      */
144     public int allocateAppWidgetId() {
145         try {
146             if (mPackageName == null) {
147                 mPackageName = mContext.getPackageName();
148             }
149             return sService.allocateAppWidgetId(mPackageName, mHostId);
150         }
151         catch (RemoteException e) {
152             throw new RuntimeException("system server dead?", e);
153         }
154     }
2016                     Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
2017                     pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
2018                     // start the pick activity
2019                     startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
这段代码之后,代码将会怎么执行呢,根据Log信息,可以看到代码将会执行到Setting应用中
packages/apps/Settings/  src /  com /  android /  settings /  AppWidgetPickActivity.java
此类将会通过AppWidgetService获取到当前系统已经安装的Widget,并显示出来
78     /**
79      * Create list entries for any custom widgets requested through
80      * {@link AppWidgetManager#EXTRA_CUSTOM_INFO}.
81      */
82     void putCustomAppWidgets(List<PickAdapter.Item> items) {
83         final Bundle extras = getIntent().getExtras();
84         
85         // get and validate the extras they gave us
86         ArrayList<AppWidgetProviderInfo> customInfo = null;
87         ArrayList<Bundle> customExtras = null;
88         try_custom_items: {
89             customInfo = extras.getParcelableArrayList(AppWidgetManager.EXTRA_CUSTOM_INFO);
90             if (customInfo == null || customInfo.size() == 0) {
91                 Log.i(TAG, "EXTRA_CUSTOM_INFO not present.");
92                 break try_custom_items;
93             }
94
95             int customInfoSize = customInfo.size();
96             for (int i=0; i<customInfoSize; i++) {
97                 Parcelable p = customInfo.get(i);
98                 if (p == null || !(p instanceof AppWidgetProviderInfo)) {
99                     customInfo = null;
100                     Log.e(TAG, "error using EXTRA_CUSTOM_INFO index=" + i);
101                     break try_custom_items;
102                 }
103             }
104
105             customExtras = extras.getParcelableArrayList(AppWidgetManager.EXTRA_CUSTOM_EXTRAS);
106             if (customExtras == null) {
107                 customInfo = null;
108                 Log.e(TAG, "EXTRA_CUSTOM_INFO without EXTRA_CUSTOM_EXTRAS");
109                 break try_custom_items;
110             }
111
112             int customExtrasSize = customExtras.size();
113             if (customInfoSize != customExtrasSize) {
114                 Log.e(TAG, "list size mismatch: EXTRA_CUSTOM_INFO: " + customInfoSize
115                         + " EXTRA_CUSTOM_EXTRAS: " + customExtrasSize);
116                 break try_custom_items;
117             }
118
119
120             for (int i=0; i<customExtrasSize; i++) {
121                 Parcelable p = customExtras.get(i);
122                 if (p == null || !(p instanceof Bundle)) {
123                     customInfo = null;
124                     customExtras = null;
125                     Log.e(TAG, "error using EXTRA_CUSTOM_EXTRAS index=" + i);
126                     break try_custom_items;
127                 }
128             }
129         }
130
131         if (LOGD) Log.d(TAG, "Using " + customInfo.size() + " custom items");
132         putAppWidgetItems(customInfo, customExtras, items);
133     }
从上述代码中可以看到,可以放置用户自己定义的伪Widget
关于伪widget,个人有如下想法:
早期Android版本中的Google Search Bar就属于伪Widget,其实就是把widget做到Launcher中,但是用户体验与真widget并没有区别,个人猜想HTC的sense就是这样实现的。
优点:是不需要进程间的通信,效率将会更高,并且也可以规避点Widget开发的种种限制
缺点:导致Launcher代码庞大,不易于维护
用户选择完之后,代码如下
135     /**
136      * {@inheritDoc}
137      */
138     @Override
139     public void onClick(DialogInterface dialog, int which) {
140         Intent intent = getIntentForPosition(which);
141         
142         int result;
143         if (intent.getExtras() != null) {
144             // If there are any extras, it's because this entry is custom.
145             // Don't try to bind it, just pass it back to the app.
146             setResultData(RESULT_OK, intent);
147         } else {
148             try {
149                 mAppWidgetManager.bindAppWidgetId(mAppWidgetId, intent.getComponent());
150                 result = RESULT_OK;
151             } catch (IllegalArgumentException e) {
152                 // This is thrown if they're already bound, or otherwise somehow
153                 // bogus.  Set the result to canceled, and exit.  The app *should*
154                 // clean up at this point.  We could pass the error along, but
155                 // it's not clear that that's useful -- the widget will simply not
156                 // appear.
157                 result = RESULT_CANCELED;
158             }
159             setResultData(result, null);
160         }
161         finish();
162     }
将会 149                 mAppWidgetManager.bindAppWidgetId(mAppWidgetId, intent.getComponent());
如果此次添加的Widget是intent.getComponent()的第一个实例,将会发送如下广播
171     /**
172      * Sent when an instance of an AppWidget is added to a host for the first time.
173      * This broadcast is sent at boot time if there is a AppWidgetHost installed with
174      * an instance for this provider.
175      * 
176      * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
177      */
178    public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
紧接着会发送UPDATE广播
135     /**
136      * Sent when it is time to update your AppWidget.
137      *
138      * <p>This may be sent in response to a new instance for this AppWidget provider having
139      * been instantiated, the requested {@link AppWidgetProviderInfo#updatePeriodMillis update interval}
140      * having lapsed, or the system booting.
141      *
142      * <p>
143      * The intent will contain the following extras:
144      * <table>
145      *   <tr>
146      *     <td>{@link #EXTRA_APPWIDGET_IDS}</td>
147      *     <td>The appWidgetIds to update.  This may be all of the AppWidgets created for this
148      *     provider, or just a subset.  The system tries to send updates for as few AppWidget
149      *     instances as possible.</td>
150      *  </tr>
151      * </table>
152      * 
153     * @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
154      */
155    public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";

 待用户选择完要添加的widget之后,将会回到Launcher.java中的函数onActivityResult中

538                 case REQUEST_PICK_APPWIDGET:
539                     addAppWidget(data);
540                     break;

上述addAppWidget中做了哪些事情呢?

1174     void addAppWidget(Intent data) {
1175         // TODO: catch bad widget exception when sent
1176         int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
1177         AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
1178
1179         if (appWidget.configure != null) {
1180             // Launch over to configure widget, if needed
1181             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
1182             intent.setComponent(appWidget.configure);
1183             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1184
1185             startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
1186         } else {
1187             // Otherwise just add it
1188             onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
1189         }
1190     }

首先获取appWidgetId,再通过AppWidgetManager获取AppWidgetProviderInfo,最后判断此Widget是否存在ConfigActivity,如果存在则启动ConfigActivity,否则直接调用函数onActivityResult

541                 case REQUEST_CREATE_APPWIDGET:
542                     completeAddAppWidget(data, mAddItemCellInfo);
543                     break;

通过函数completeAddAppWidget把此widget的信息插入到数据库中,并添加到桌面上

873     /**
874      * Add a widget to the workspace.
875      *
876      * @param data The intent describing the appWidgetId.
877      * @param cellInfo The position on screen where to create the widget.
878      */
879     private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) {
880         Bundle extras = data.getExtras();
881         int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
882
883         if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString());
884
885         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
886
887         // Calculate the grid spans needed to fit this widget
888         CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
889         int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
890
891         // Try finding open space on Launcher screen
892         final int[] xy = mCellCoordinates;
893         if (!findSlot(cellInfo, xy, spans[0], spans[1])) {
894             if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId);
895             return;
896         }
897
898         // Build Launcher-specific widget info and save to database
899         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
900         launcherInfo.spanX = spans[0];
901         launcherInfo.spanY = spans[1];
902
903         LauncherModel.addItemToDatabase(this, launcherInfo,
904                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
905                 mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
906
907         if (!mRestoring) {
908             mDesktopItems.add(launcherInfo);
909
910             // Perform actual inflation because we're live
911             launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
912
913             launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
914             launcherInfo.hostView.setTag(launcherInfo);
915
916             mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
917                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
918         }
919     }

 

Launcher中删除widget

 长按一个widget,并拖入到DeleteZone中可实现删除

具体代码在DeleteZone中

92     public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
93             DragView dragView, Object dragInfo) {
94         final ItemInfo item = (ItemInfo) dragInfo;
95
96         if (item.container == -1) return;
97
98         if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
99             if (item instanceof LauncherAppWidgetInfo) {
100                 mLauncher.removeAppWidget((LauncherAppWidgetInfo) item);
101             }
102         } else {
103             if (source instanceof UserFolder) {
104                 final UserFolder userFolder = (UserFolder) source;
105                 final UserFolderInfo userFolderInfo = (UserFolderInfo) userFolder.getInfo();
106                 // Item must be a ShortcutInfo otherwise it couldn't have been in the folder
107                 // in the first place.
108                 userFolderInfo.remove((ShortcutInfo)item);
109             }
110         }
111         if (item instanceof UserFolderInfo) {
112             final UserFolderInfo userFolderInfo = (UserFolderInfo)item;
113             LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo);
114             mLauncher.removeFolder(userFolderInfo);
115         } else if (item instanceof LauncherAppWidgetInfo) {
116             final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
117             final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
118             if (appWidgetHost != null) {
119                 appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
120             }
121         }
122         LauncherModel.deleteItemFromDatabase(mLauncher, item);
123     }

删除时,判断删除的类型是否是AppWidget,如果是的话,要通过AppWidgetHost,删除AppWidetId,并最终从数据库中删除。

 

 

八     Launcher   celllayout介绍

 

           (1) 大家都知道workspace是有celllayout组成

Celllayout被划分为了4行4列的表格,用Boolean类型的mOccupied二维数组来标记每个cell是否被占用。在attrs.xml中定义了shortAxisCells和longAxisCells分别存储x轴和y轴方向的cell个数。在Celllayout构造函数中初始化。

(2) 内部类CellInfo为静态类,实现了ContextMenu.ContextMenuInfo接口,其对象用于存储cell的基本信息

VacantCell类用于存储空闲的cell,用到了同步机制用于管理对空闲位置的操作。所有的空cell都存储在vacantCells中。 

cellX和cellY用于记录cell的位置,起始位0。如:(0,0) (0,1),每一页从新开始编号。 

clearVacantCells作用是将Vacant清空:具体是释放每个cell,将list清空。 

findVacantCellsFromOccupied从存放cell的数值中找到空闲的cell。在Launcher.Java中的restoreState方法中调用。

(3) mPortrait用于标记是横屏还是竖屏,FALSE表示竖屏,默认为FALSE。 

(4)修改CellLayout页面上cell的布局:
 

CellLayout页面上默认的cell为4X4=16个,可以通过修改配置文件来达到修改目的。 

在CellLayout.Java类的CellLayout(Context context, AttributeSet attrs, int defStyle)构造方法中用变量mShortAxisCells和mLongAxisCells存储行和列。

其值是在自定义配置文件attrs.xml中定义的,并在workspace_screen.xml中赋初值的,初值都为4,即4行、4列。可以在workspace_screen.xml修改对应的值。

注意:CellLayout构造方法中从attrs.xml中获取定义是这样的:mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4);当workspace_screen.xml中没有给定义的变量赋值时,上面的4就起作用。

(5)Launcher(主屏/待机) App的BUG: 没有初始化定义CellLayout中屏幕方向的布尔值参数:

  1. Launcher App:\cupcake\packages\apps\Launcher

待机画面分为多层,桌面Desktop Items在\res\layout-*\workspace_screen.xml中置: 

  1. <com.android.launcher.CellLayout 
  2. ... ... 
  3. launcher:shortAxisCells="4" 
  4. launcher:longAxisCells="4" 
  5. ... ... 
  6. />

以上表示4行4列.

再看看com.android.launcher.CellLayout ,其中有定义屏幕方向的参数:

  1. private boolean mPortrait;

但是一直没有初始化,也就是mPortrait=false,桌面的单元格设置一直是以非竖屏(横屏)的设置定义进行初始化。 

再来看看横屏和竖屏情况下的初始化不同之处,就可以看出BUG了:

  1. boolean[][] mOccupied;//二元单元格布尔值数组 
  2.             if (mPortrait) { 
  3.                 mOccupied = new boolean[mShortAxisCells][mLongAxisCells]; 
  4. } else { 
  5. mOccupied = new boolean[mLongAxisCells][mShortAxisCells]; 
  6. }

如果我们满屏显示桌面(横向和纵向的单元格数不一致),而不是默认的只显示4行4列,则mShortAxisCells = 4, mLongAxisCells = 5,数组应该初始化是:new boolean[4][5],但是实际是按照非竖屏处理,初始化成了new boolean[5][4],会产生数组越界异常。

可以在构造函数中,添加通过屏幕方向初始化mPortrait,代码如下: 

  1. public CellLayout(Context context, AttributeSet attrs, int defStyle) 

  2. super(context, attrs, defStyle); 
  3. mPortrait = this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;// 新增代码
  4. ... ...

  好了就写这些,太累了,以后再补上其他跟Launcher有关的


这篇关于Android Launcher全面剖析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/863889

相关文章

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR

STL经典案例(四)——实验室预约综合管理系统(项目涉及知识点很全面,内容有点多,耐心看完会有收获的!)

项目干货满满,内容有点过多,看起来可能会有点卡。系统提示读完超过俩小时,建议分多篇发布,我觉得分篇就不完整了,失去了这个项目的灵魂 一、需求分析 高校实验室预约管理系统包括三种不同身份:管理员、实验室教师、学生 管理员:给学生和实验室教师创建账号并分发 实验室教师:审核学生的预约申请 学生:申请使用实验室 高校实验室包括:超景深实验室(可容纳10人)、大数据实验室(可容纳20人)、物联网实验

如何掌握面向对象编程的四大特性、Lambda 表达式及 I/O 流:全面指南

这里写目录标题 OOP语言的四大特性lambda输入/输出流(I/O流) OOP语言的四大特性 面向对象编程(OOP)是一种编程范式,它通过使用“对象”来组织代码。OOP 的四大特性是封装、继承、多态和抽象。这些特性帮助程序员更好地管理复杂的代码,使程序更易于理解和维护。 类-》实体的抽象类型 实体(属性,行为) -》 ADT(abstract data type) 属性-》成