這篇文章我會講以下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的代碼結構。
首先,是一些權限的聲明。例如:
|
這部分可以略過;
其次,Application的構成,如上圖:
(1)Launcher:HomeScreen的Activity。
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME"/>
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY" /> </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME"/>
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY" /> </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME"/>
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY" /> </intent-filter>
上面這段代碼就標誌着它是開機啓動後Home的Activity。通過Launcher.java中onCreat()的分析我們可以大致把握屏幕的主要活動:
|
方法onActivityResult():完成在workspace上增加shortcut,appwidge和Livefolder;
方法onSaveInstantceState()和onRestoreInstanceState():爲了防止Sensor、Land和Port佈局自動切換時數據被置空,通過onSaveInstanceState方法可以保存當前窗口的狀態,在即將佈局切換前將當前的Activity壓入歷史堆棧。如果我們的Activity在後臺沒有因爲運行內存吃緊被清理,則切換時回觸發onRestoreIntanceState()。
(2)WallpaperChooser:設置牆紙。
同理我們從onCreat()作爲入口來分析這個活動的主要功能。
|
(3)default_searchable
對於home中任意的Acitivty,使能系統缺省Search模式,這樣就可以使用android系統默認的search UI。
(4)InstallShortcutReceiver:
繼承自BroadcastReceiver,重寫onReceier()方法,對於發送來的Broadcast(這裏指Intent)進行過濾(IntentFilt)並且響應(這裏是InstallShortcut())。這裏分析下onReceive():
|
其中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目錄裏,由於屏幕放置方式的不同會對桌面造成一定的影響,所以google的Android項目組就決定因地制宜。比如當你橫着放置屏幕的時候就會使用layout-land目錄裏的文件來對系統launcher進行佈局,豎着屏幕的時候會使用layout-port內的佈局文件來對launcher來佈局。
橫豎屏幕切換之際,會重新進行佈局。那我們就以layout-land目錄爲例來看吧。
layout-land/launcuer.xml
- <?xml version="1.0" encoding="utf-8"?>
- <!--
- /*
- **
- ** Copyright 2008, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
- -->
- <manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.zkx_launcher"
- android:sharedUserId="@string/sharedUserId"
- >
- <!-- 應用程序的包名 -->
- <!-- <original-package android:name="com.android.zkx_launcher2" /> -->
- <!-- 對系統資源的訪問權限 -->
- <permission
- android:name="com.android.zkx_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.zkx_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"/>
- <permission
- android:name="com.android.zkx_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"/>
- <permission
- android:name="com.android.zkx_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"/>
- <uses-permission android:name="android.permission.CALL_PHONE" />
- <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
- <uses-permission android:name="android.permission.GET_TASKS" />
- <uses-permission android:name="android.permission.READ_CONTACTS"/>
- <uses-permission android:name="android.permission.SET_WALLPAPER" />
- <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
- <uses-permission android:name="android.permission.VIBRATE" />
- <uses-permission android:name="android.permission.WRITE_SETTINGS" />
- <uses-permission android:name="android.permission.BIND_APPWIDGET" />
- <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
- <!-- 對應用程序的配置 -->
- <application
- android:name="LauncherApplication"
- android:process="@string/process"
- android:label="@string/application_name"
- android:icon="@drawable/ic_launcher_home">
- <!--配置應用程序額的名字,進程,標籤,和圖標
- label的值爲values/strings.xml中application_name 鍵值對的值
- icon爲drawable目錄下名爲的ic_launcher_home的圖片
- 實際上該圖片的位置位於drawable-hdpi(高分辨率)目錄下,是個小房子這個主要是爲了支持多分辨率的.
- hdpi裏面主要放高分辨率的圖片,
- 如WVGA (480x800),FWVGA (480x854)mdpi裏面主要放中等分辨率的圖片,
- 如HVGA (320x480)ldpi裏面主要放低分辨率的圖片,
- 如QVGA (240x320)系統會根據機器的分辨率來分別到這幾個文件夾裏面去找對應的圖片
- 所以在開發程序時爲了兼容不同平臺不同屏幕,建議各自文件夾根據需求均存放不同版本圖片.
- 只需要在res目錄下創建不同的layout文件夾,
- 比如layout-640x360,layout-800x480,
- 所有的layout文件在編譯之後都會寫入R.java裏,
- 而系統會根據屏幕的大小自己選擇合適的layout進行使用 -->
- <!--設置intent-filter可以先啓動該Activity -->
- <activity
- android:name="Launcher"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/Theme"
- android:screenOrientation="nosensor"
- android:windowSoftInputMode="stateUnspecified|adjustPan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY"/>
- </intent-filter>
- </activity>
- <!--設置Wallpapaer的Activity -->
- <activity
- android:name="WallpaperChooser"
- android:label="@string/pick_wallpaper"
- android:icon="@drawable/ic_launcher_wallpaper"
- android:screenOrientation="nosensor"
- android:finishOnCloseSystemDialogs="true">
- <intent-filter>
- <action android:name="android.intent.action.SET_WALLPAPER" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
- <!--安裝快捷方式的Intent -->
- <!-- Intent received used to install shortcuts from other applications -->
- <receiver
- android:name="InstallShortcutReceiver"
- android:permission="com.android.zkx_launcher.permission.INSTALL_SHORTCUT">
- <intent-filter>
- <action android:name="com.android.zkx_launcher.action.INSTALL_SHORTCUT" />
- </intent-filter>
- </receiver>
- <!--卸載快捷方式的Intent -->
- <!-- Intent received used to uninstall shortcuts from other applications -->
- <receiver
- android:name="UninstallShortcutReceiver"
- android:permission="com.android.zkx_launcher.permission.UNINSTALL_SHORTCUT">
- <intent-filter>
- <action android:name="com.android.zkx_launcher.action.UNINSTALL_SHORTCUT" />
- </intent-filter>
- </receiver>
- <!-- 供應商信息-->
- <!-- The settings provider contains Home's data, like the workspace favorites -->
- <provider
- android:name="LauncherProvider"
- android:authorities="com.android.zkx_launcher.settings"
- android:writePermission="com.android.zkx_launcher.permission.WRITE_SETTINGS"
- android:readPermission="com.android.zkx_launcher.permission.READ_SETTINGS" />
- </application>
- </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:widget的packageName
#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>
代碼如下:
<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"/>
說明:
1.
<manifest標籤頭部還應聲明:
android:sharedUserId="android.uid.shared",作用是獲得系統權限,但是這樣的程序屬性只能在build整個系統時放進去(就是系統軟件)才起作用,手動安裝是沒有權限的。
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文件中:- public class SystemServer
- {
- ......
- native public static void init1(String[] args);
- ......
- public static void main(String[] args) {
- ......
- init1(args);
- ......
- }
- ......
- }
- public class SystemServer
- {
- ......
- native public static void init1(String[] args);
- ......
- public static void main(String[] args) {
- ......
- init1(args);
- ......
- }
- ......
- }
- public class SystemServer
- {
- ......
- native public static void init1(String[] args);
- ......
- public static void main(String[] args) {
- ......
- init1(args);
- ......
- }
- ......
- }
Step 2. SystemServer.init1
這個函數是一個JNI方法,實現在 frameworks/base/services/jni/com_android_server_SystemServer.cpp文件中:
- namespace android {
- extern "C" int system_init();
- static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz)
- {
- system_init();
- }
- /*
- * JNI registration.
- */
- static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 },
- };
- int register_android_server_SystemServer(JNIEnv* env)
- {
- return jniRegisterNativeMethods(env, "com/android/server/SystemServer",
- gMethods, NELEM(gMethods));
- }
- }; // namespace android
- namespace android {
- extern "C" int system_init();
- static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz)
- {
- system_init();
- }
- /*
- * JNI registration.
- */
- static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 },
- };
- int register_android_server_SystemServer(JNIEnv* env)
- {
- return jniRegisterNativeMethods(env, "com/android/server/SystemServer",
- gMethods, NELEM(gMethods));
- }
- }; // namespace android
- namespace android {
- extern "C" int system_init();
- static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz)
- {
- system_init();
- }
- /*
- * JNI registration.
- */
- static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 },
- };
- int register_android_server_SystemServer(JNIEnv* env)
- {
- return jniRegisterNativeMethods(env, "com/android/server/SystemServer",
- gMethods, NELEM(gMethods));
- }
- }; // namespace android
Step 3. libsystem_server.system_init
函數system_init實現在libsystem_server庫中,源代碼位於frameworks/base/cmds/system_server/library/system_init.cpp文件中:
- extern "C" status_t system_init()
- {
- LOGI("Entered system_init()");
- sp<ProcessState> proc(ProcessState::self());
- sp<IServiceManager> sm = defaultServiceManager();
- LOGI("ServiceManager: %p\n", sm.get());
- sp<GrimReaper> grim = new GrimReaper();
- sm->asBinder()->linkToDeath(grim, grim.get(), 0);
- char propBuf[PROPERTY_VALUE_MAX];
- property_get("system_init.startsurfaceflinger", propBuf, "1");
- if (strcmp(propBuf, "1") == 0) {
- // Start the SurfaceFlinger
- SurfaceFlinger::instantiate();
- }
- // Start the sensor service
- SensorService::instantiate();
- // On the simulator, audioflinger et al don't get started the
- // same way as on the device, and we need to start them here
- if (!proc->supportsProcesses()) {
- // Start the AudioFlinger
- AudioFlinger::instantiate();
- // Start the media playback service
- MediaPlayerService::instantiate();
- // Start the camera service
- CameraService::instantiate();
- // Start the audio policy service
- AudioPolicyService::instantiate();
- }
- // And now start the Android runtime. We have to do this bit
- // of nastiness because the Android runtime initialization requires
- // some of the core system services to already be started.
- // All other servers should just start the Android runtime at
- // the beginning of their processes's main(), before calling
- // the init function.
- LOGI("System server: starting Android runtime.\n");
- AndroidRuntime* runtime = AndroidRuntime::getRuntime();
- LOGI("System server: starting Android services.\n");
- runtime->callStatic("com/android/server/SystemServer", "init2");
- // If running in our own process, just go into the thread
- // pool. Otherwise, call the initialization finished
- // func to let this process continue its initilization.
- if (proc->supportsProcesses()) {
- LOGI("System server: entering thread pool.\n");
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
- LOGI("System server: exiting thread pool.\n");
- }
- return NO_ERROR;
- }
- extern "C" status_t system_init()
- {
- LOGI("Entered system_init()");
- sp<ProcessState> proc(ProcessState::self());
- sp<IServiceManager> sm = defaultServiceManager();
- LOGI("ServiceManager: %p\n", sm.get());
- sp<GrimReaper> grim = new GrimReaper();
- sm->asBinder()->linkToDeath(grim, grim.get(), 0);
- char propBuf[PROPERTY_VALUE_MAX];
- property_get("system_init.startsurfaceflinger", propBuf, "1");
- if (strcmp(propBuf, "1") == 0) {
- // Start the SurfaceFlinger
- SurfaceFlinger::instantiate();
- }
- // Start the sensor service
- SensorService::instantiate();
- // On the simulator, audioflinger et al don't get started the
- // same way as on the device, and we need to start them here
- if (!proc->supportsProcesses()) {
- // Start the AudioFlinger
- AudioFlinger::instantiate();
- // Start the media playback service
- MediaPlayerService::instantiate();
- // Start the camera service
- CameraService::instantiate();
- // Start the audio policy service
- AudioPolicyService::instantiate();
- }
- // And now start the Android runtime. We have to do this bit
- // of nastiness because the Android runtime initialization requires
- // some of the core system services to already be started.
- // All other servers should just start the Android runtime at
- // the beginning of their processes's main(), before calling
- // the init function.
- LOGI("System server: starting Android runtime.\n");
- AndroidRuntime* runtime = AndroidRuntime::getRuntime();
- LOGI("System server: starting Android services.\n");
- runtime->callStatic("com/android/server/SystemServer", "init2");
- // If running in our own process, just go into the thread
- // pool. Otherwise, call the initialization finished
- // func to let this process continue its initilization.
- if (proc->supportsProcesses()) {
- LOGI("System server: entering thread pool.\n");
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
- LOGI("System server: exiting thread pool.\n");
- }
- return NO_ERROR;
- }
- extern "C" status_t system_init()
- {
- LOGI("Entered system_init()");
- sp<ProcessState> proc(ProcessState::self());
- sp<IServiceManager> sm = defaultServiceManager();
- LOGI("ServiceManager: %p\n", sm.get());
- sp<GrimReaper> grim = new GrimReaper();
- sm->asBinder()->linkToDeath(grim, grim.get(), 0);
- char propBuf[PROPERTY_VALUE_MAX];
- property_get("system_init.startsurfaceflinger", propBuf, "1");
- if (strcmp(propBuf, "1") == 0) {
- // Start the SurfaceFlinger
- SurfaceFlinger::instantiate();
- }
- // Start the sensor service
- SensorService::instantiate();
- // On the simulator, audioflinger et al don't get started the
- // same way as on the device, and we need to start them here
- if (!proc->supportsProcesses()) {
- // Start the AudioFlinger
- AudioFlinger::instantiate();
- // Start the media playback service
- MediaPlayerService::instantiate();
- // Start the camera service
- CameraService::instantiate();
- // Start the audio policy service
- AudioPolicyService::instantiate();
- }
- // And now start the Android runtime. We have to do this bit
- // of nastiness because the Android runtime initialization requires
- // some of the core system services to already be started.
- // All other servers should just start the Android runtime at
- // the beginning of their processes's main(), before calling
- // the init function.
- LOGI("System server: starting Android runtime.\n");
- AndroidRuntime* runtime = AndroidRuntime::getRuntime();
- LOGI("System server: starting Android services.\n");
- runtime->callStatic("com/android/server/SystemServer", "init2");
- // If running in our own process, just go into the thread
- // pool. Otherwise, call the initialization finished
- // func to let this process continue its initilization.
- if (proc->supportsProcesses()) {
- LOGI("System server: entering thread pool.\n");
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
- LOGI("System server: exiting thread pool.\n");
- }
- return NO_ERROR;
- }
Step 4. AndroidRuntime.callStatic
這個函數定義在frameworks/base/core/jni/AndroidRuntime.cpp文件中:
- /*
- * Call a static Java Programming Language function that takes no arguments and returns void.
- */
- status_t AndroidRuntime::callStatic(const char* className, const char* methodName)
- {
- JNIEnv* env;
- jclass clazz;
- jmethodID methodId;
- env = getJNIEnv();
- if (env == NULL)
- return UNKNOWN_ERROR;
- clazz = findClass(env, className);
- if (clazz == NULL) {
- LOGE("ERROR: could not find class '%s'\n", className);
- return UNKNOWN_ERROR;
- }
- methodId = env->GetStaticMethodID(clazz, methodName, "()V");
- if (methodId == NULL) {
- LOGE("ERROR: could not find method %s.%s\n", className, methodName);
- return UNKNOWN_ERROR;
- }
- env->CallStaticVoidMethod(clazz, methodId);
- return NO_ERROR;
- }
- /*
- * Call a static Java Programming Language function that takes no arguments and returns void.
- */
- status_t AndroidRuntime::callStatic(const char* className, const char* methodName)
- {
- JNIEnv* env;
- jclass clazz;
- jmethodID methodId;
- env = getJNIEnv();
- if (env == NULL)
- return UNKNOWN_ERROR;
- clazz = findClass(env, className);
- if (clazz == NULL) {
- LOGE("ERROR: could not find class '%s'\n", className);
- return UNKNOWN_ERROR;
- }
- methodId = env->GetStaticMethodID(clazz, methodName, "()V");
- if (methodId == NULL) {
- LOGE("ERROR: could not find method %s.%s\n", className, methodName);
- return UNKNOWN_ERROR;
- }
- env->CallStaticVoidMethod(clazz, methodId);
- return NO_ERROR;
- }
- /*
- * Call a static Java Programming Language function that takes no arguments and returns void.
- */
- status_t AndroidRuntime::callStatic(const char* className, const char* methodName)
- {
- JNIEnv* env;
- jclass clazz;
- jmethodID methodId;
- env = getJNIEnv();
- if (env == NULL)
- return UNKNOWN_ERROR;
- clazz = findClass(env, className);
- if (clazz == NULL) {
- LOGE("ERROR: could not find class '%s'\n", className);
- return UNKNOWN_ERROR;
- }
- methodId = env->GetStaticMethodID(clazz, methodName, "()V");
- if (methodId == NULL) {
- LOGE("ERROR: could not find method %s.%s\n", className, methodName);
- return UNKNOWN_ERROR;
- }
- env->CallStaticVoidMethod(clazz, methodId);
- return NO_ERROR;
- }
Step 5. SystemServer.init2
這個函數定義在frameworks/base/services/java/com/android/server/SystemServer.java文件中:
- public class SystemServer
- {
- ......
- public static final void init2() {
- Slog.i(TAG, "Entered the Android system server!");
- Thread thr = new ServerThread();
- thr.setName("android.server.ServerThread");
- thr.start();
- }
- }
- public class SystemServer
- {
- ......
- public static final void init2() {
- Slog.i(TAG, "Entered the Android system server!");
- Thread thr = new ServerThread();
- thr.setName("android.server.ServerThread");
- thr.start();
- }
- }
- public class SystemServer
- {
- ......
- public static final void init2() {
- Slog.i(TAG, "Entered the Android system server!");
- Thread thr = new ServerThread();
- thr.setName("android.server.ServerThread");
- thr.start();
- }
- }
Step 6. ServerThread.run
這個函數定義在frameworks/base/services/java/com/android/server/SystemServer.java文件中:
- class ServerThread extends Thread {
- ......
- @Override
- public void run() {
- ......
- IPackageManager pm = null;
- ......
- // Critical services...
- try {
- ......
- Slog.i(TAG, "Package Manager");
- pm = PackageManagerService.main(context,
- factoryTest != SystemServer.FACTORY_TEST_OFF);
- ......
- } catch (RuntimeException e) {
- Slog.e("System", "Failure starting core service", e);
- }
- ......
- }
- ......
- }
- class ServerThread extends Thread {
- ......
- @Override
- public void run() {
- ......
- IPackageManager pm = null;
- ......
- // Critical services...
- try {
- ......
- Slog.i(TAG, "Package Manager");
- pm = PackageManagerService.main(context,
- factoryTest != SystemServer.FACTORY_TEST_OFF);
- ......
- } catch (RuntimeException e) {
- Slog.e("System", "Failure starting core service", e);
- }
- ......
- }
- ......
- }
- class ServerThread extends Thread {
- ......
- @Override
- public void run() {
- ......
- IPackageManager pm = null;
- ......
- // Critical services...
- try {
- ......
- Slog.i(TAG, "Package Manager");
- pm = PackageManagerService.main(context,
- factoryTest != SystemServer.FACTORY_TEST_OFF);
- ......
- } catch (RuntimeException e) {
- Slog.e("System", "Failure starting core service", e);
- }
- ......
- }
- ......
- }
這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- public static final Context main(int factoryTest) {
- AThread thr = new AThread();
- thr.start();
- synchronized (thr) {
- while (thr.mService == null) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
- }
- }
- ActivityManagerService m = thr.mService;
- mSelf = m;
- ActivityThread at = ActivityThread.systemMain();
- mSystemThread = at;
- Context context = at.getSystemContext();
- m.mContext = context;
- m.mFactoryTest = factoryTest;
- m.mMainStack = new ActivityStack(m, context, true);
- m.mBatteryStatsService.publish(context);
- m.mUsageStatsService.publish(context);
- synchronized (thr) {
- thr.mReady = true;
- thr.notifyAll();
- }
- m.startRunning(null, null, null, null);
- return context;
- }
- ......
- }
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- public static final Context main(int factoryTest) {
- AThread thr = new AThread();
- thr.start();
- synchronized (thr) {
- while (thr.mService == null) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
- }
- }
- ActivityManagerService m = thr.mService;
- mSelf = m;
- ActivityThread at = ActivityThread.systemMain();
- mSystemThread = at;
- Context context = at.getSystemContext();
- m.mContext = context;
- m.mFactoryTest = factoryTest;
- m.mMainStack = new ActivityStack(m, context, true);
- m.mBatteryStatsService.publish(context);
- m.mUsageStatsService.publish(context);
- synchronized (thr) {
- thr.mReady = true;
- thr.notifyAll();
- }
- m.startRunning(null, null, null, null);
- return context;
- }
- ......
- }
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- public static final Context main(int factoryTest) {
- AThread thr = new AThread();
- thr.start();
- synchronized (thr) {
- while (thr.mService == null) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
- }
- }
- ActivityManagerService m = thr.mService;
- mSelf = m;
- ActivityThread at = ActivityThread.systemMain();
- mSystemThread = at;
- Context context = at.getSystemContext();
- m.mContext = context;
- m.mFactoryTest = factoryTest;
- m.mMainStack = new ActivityStack(m, context, true);
- m.mBatteryStatsService.publish(context);
- m.mUsageStatsService.publish(context);
- synchronized (thr) {
- thr.mReady = true;
- thr.notifyAll();
- }
- m.startRunning(null, null, null, null);
- return context;
- }
- ......
- }
Step 8. PackageManagerService.main
這個函數定義在frameworks/base/services/java/com/android/server/PackageManagerService.java文件中:- class PackageManagerService extends IPackageManager.Stub {
- ......
- public static final IPackageManager main(Context context, boolean factoryTest) {
- PackageManagerService m = new PackageManagerService(context, factoryTest);
- ServiceManager.addService("package", m);
- return m;
- }
- ......
- }
- class PackageManagerService extends IPackageManager.Stub {
- ......
- public static final IPackageManager main(Context context, boolean factoryTest) {
- PackageManagerService m = new PackageManagerService(context, factoryTest);
- ServiceManager.addService("package", m);
- return m;
- }
- ......
- }
- class PackageManagerService extends IPackageManager.Stub {
- ......
- public static final IPackageManager main(Context context, boolean factoryTest) {
- PackageManagerService m = new PackageManagerService(context, factoryTest);
- ServiceManager.addService("package", m);
- return m;
- }
- ......
- }
- class PackageManagerService extends IPackageManager.Stub {
- ......
- public PackageManagerService(Context context, boolean factoryTest) {
- ......
- synchronized (mInstallLock) {
- synchronized (mPackages) {
- ......
- File dataDir = Environment.getDataDirectory();
- mAppDataDir = new File(dataDir, "data");
- mSecureAppDataDir = new File(dataDir, "secure/data");
- mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- ......
- mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
- mDalvikCacheDir = new File(dataDir, "dalvik-cache");
- ......
- // Find base frameworks (resource packages without code).
- mFrameworkInstallObserver = new AppDirObserver(
- mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
- mFrameworkInstallObserver.startWatching();
- scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanMode | SCAN_NO_DEX, 0);
- // Collect all system packages.
- mSystemAppDir = new File(Environment.getRootDirectory(), "app");
- mSystemInstallObserver = new AppDirObserver(
- mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
- mSystemInstallObserver.startWatching();
- scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
- // Collect all vendor packages.
- mVendorAppDir = new File("/vendor/app");
- mVendorInstallObserver = new AppDirObserver(
- mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
- mVendorInstallObserver.startWatching();
- scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
- mAppInstallObserver = new AppDirObserver(
- mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
- mAppInstallObserver.startWatching();
- scanDirLI(mAppInstallDir, 0, scanMode, 0);
- mDrmAppInstallObserver = new AppDirObserver(
- mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
- mDrmAppInstallObserver.startWatching();
- scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
- scanMode, 0);
- ......
- }
- }
- }
- ......
- }
- class PackageManagerService extends IPackageManager.Stub {
- ......
- public PackageManagerService(Context context, boolean factoryTest) {
- ......
- synchronized (mInstallLock) {
- synchronized (mPackages) {
- ......
- File dataDir = Environment.getDataDirectory();
- mAppDataDir = new File(dataDir, "data");
- mSecureAppDataDir = new File(dataDir, "secure/data");
- mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- ......
- mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
- mDalvikCacheDir = new File(dataDir, "dalvik-cache");
- ......
- // Find base frameworks (resource packages without code).
- mFrameworkInstallObserver = new AppDirObserver(
- mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
- mFrameworkInstallObserver.startWatching();
- scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanMode | SCAN_NO_DEX, 0);
- // Collect all system packages.
- mSystemAppDir = new File(Environment.getRootDirectory(), "app");
- mSystemInstallObserver = new AppDirObserver(
- mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
- mSystemInstallObserver.startWatching();
- scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
- // Collect all vendor packages.
- mVendorAppDir = new File("/vendor/app");
- mVendorInstallObserver = new AppDirObserver(
- mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
- mVendorInstallObserver.startWatching();
- scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
- mAppInstallObserver = new AppDirObserver(
- mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
- mAppInstallObserver.startWatching();
- scanDirLI(mAppInstallDir, 0, scanMode, 0);
- mDrmAppInstallObserver = new AppDirObserver(
- mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
- mDrmAppInstallObserver.startWatching();
- scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
- scanMode, 0);
- ......
- }
- }
- }
- ......
- }
- class PackageManagerService extends IPackageManager.Stub {
- ......
- public PackageManagerService(Context context, boolean factoryTest) {
- ......
- synchronized (mInstallLock) {
- synchronized (mPackages) {
- ......
- File dataDir = Environment.getDataDirectory();
- mAppDataDir = new File(dataDir, "data");
- mSecureAppDataDir = new File(dataDir, "secure/data");
- mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- ......
- mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
- mDalvikCacheDir = new File(dataDir, "dalvik-cache");
- ......
- // Find base frameworks (resource packages without code).
- mFrameworkInstallObserver = new AppDirObserver(
- mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
- mFrameworkInstallObserver.startWatching();
- scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanMode | SCAN_NO_DEX, 0);
- // Collect all system packages.
- mSystemAppDir = new File(Environment.getRootDirectory(), "app");
- mSystemInstallObserver = new AppDirObserver(
- mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
- mSystemInstallObserver.startWatching();
- scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
- // Collect all vendor packages.
- mVendorAppDir = new File("/vendor/app");
- mVendorInstallObserver = new AppDirObserver(
- mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
- mVendorInstallObserver.startWatching();
- scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
- mAppInstallObserver = new AppDirObserver(
- mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
- mAppInstallObserver.startWatching();
- scanDirLI(mAppInstallDir, 0, scanMode, 0);
- mDrmAppInstallObserver = new AppDirObserver(
- mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
- mDrmAppInstallObserver.startWatching();
- scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
- scanMode, 0);
- ......
- }
- }
- }
- ......
- }
/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文件中:
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- public static void setSystemProcess() {
- try {
- ActivityManagerService m = mSelf;
- ServiceManager.addService("activity", m);
- ServiceManager.addService("meminfo", new MemBinder(m));
- if (MONITOR_CPU_USAGE) {
- ServiceManager.addService("cpuinfo", new CpuBinder(m));
- }
- ServiceManager.addService("permission", new PermissionController(m));
- ApplicationInfo info =
- mSelf.mContext.getPackageManager().getApplicationInfo(
- "android", STOCK_PM_FLAGS);
- mSystemThread.installSystemApplicationInfo(info);
- synchronized (mSelf) {
- ProcessRecord app = mSelf.newProcessRecordLocked(
- mSystemThread.getApplicationThread(), info,
- info.processName);
- app.persistent = true;
- app.pid = MY_PID;
- app.maxAdj = SYSTEM_ADJ;
- mSelf.mProcessNames.put(app.processName, app.info.uid, app);
- synchronized (mSelf.mPidsSelfLocked) {
- mSelf.mPidsSelfLocked.put(app.pid, app);
- }
- mSelf.updateLruProcessLocked(app, true, true);
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(
- "Unable to find android system package", e);
- }
- }
- ......
- }
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- public static void setSystemProcess() {
- try {
- ActivityManagerService m = mSelf;
- ServiceManager.addService("activity", m);
- ServiceManager.addService("meminfo", new MemBinder(m));
- if (MONITOR_CPU_USAGE) {
- ServiceManager.addService("cpuinfo", new CpuBinder(m));
- }
- ServiceManager.addService("permission", new PermissionController(m));
- ApplicationInfo info =
- mSelf.mContext.getPackageManager().getApplicationInfo(
- "android", STOCK_PM_FLAGS);
- mSystemThread.installSystemApplicationInfo(info);
- synchronized (mSelf) {
- ProcessRecord app = mSelf.newProcessRecordLocked(
- mSystemThread.getApplicationThread(), info,
- info.processName);
- app.persistent = true;
- app.pid = MY_PID;
- app.maxAdj = SYSTEM_ADJ;
- mSelf.mProcessNames.put(app.processName, app.info.uid, app);
- synchronized (mSelf.mPidsSelfLocked) {
- mSelf.mPidsSelfLocked.put(app.pid, app);
- }
- mSelf.updateLruProcessLocked(app, true, true);
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(
- "Unable to find android system package", e);
- }
- }
- ......
- }
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- public static void setSystemProcess() {
- try {
- ActivityManagerService m = mSelf;
- ServiceManager.addService("activity", m);
- ServiceManager.addService("meminfo", new MemBinder(m));
- if (MONITOR_CPU_USAGE) {
- ServiceManager.addService("cpuinfo", new CpuBinder(m));
- }
- ServiceManager.addService("permission", new PermissionController(m));
- ApplicationInfo info =
- mSelf.mContext.getPackageManager().getApplicationInfo(
- "android", STOCK_PM_FLAGS);
- mSystemThread.installSystemApplicationInfo(info);
- synchronized (mSelf) {
- ProcessRecord app = mSelf.newProcessRecordLocked(
- mSystemThread.getApplicationThread(), info,
- info.processName);
- app.persistent = true;
- app.pid = MY_PID;
- app.maxAdj = SYSTEM_ADJ;
- mSelf.mProcessNames.put(app.processName, app.info.uid, app);
- synchronized (mSelf.mPidsSelfLocked) {
- mSelf.mPidsSelfLocked.put(app.pid, app);
- }
- mSelf.updateLruProcessLocked(app, true, true);
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(
- "Unable to find android system package", e);
- }
- }
- ......
- }
Step 10. ActivityManagerService.systemReady
這個函數是在上面的Step 6中的ServerThread.run函數在將系統中的一系列服務都初始化完畢之後才調用的,它定義在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- public void systemReady(final Runnable goingCallback) {
- ......
- synchronized (this) {
- ......
- mMainStack.resumeTopActivityLocked(null);
- }
- }
- ......
- }
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- public void systemReady(final Runnable goingCallback) {
- ......
- synchronized (this) {
- ......
- mMainStack.resumeTopActivityLocked(null);
- }
- }
- ......
- }
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- public void systemReady(final Runnable goingCallback) {
- ......
- synchronized (this) {
- ......
- mMainStack.resumeTopActivityLocked(null);
- }
- }
- ......
- }
Step 11. ActivityStack.resumeTopActivityLocked
這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:
- public class ActivityStack {
- ......
- final boolean resumeTopActivityLocked(ActivityRecord prev) {
- // Find the first activity that is not finishing.
- ActivityRecord next = topRunningActivityLocked(null);
- ......
- if (next == null) {
- // There are no more activities! Let's just start up the
- // Launcher...
- if (mMainStack) {
- return mService.startHomeActivityLocked();
- }
- }
- ......
- }
- ......
- }
- public class ActivityStack {
- ......
- final boolean resumeTopActivityLocked(ActivityRecord prev) {
- // Find the first activity that is not finishing.
- ActivityRecord next = topRunningActivityLocked(null);
- ......
- if (next == null) {
- // There are no more activities! Let's just start up the
- // Launcher...
- if (mMainStack) {
- return mService.startHomeActivityLocked();
- }
- }
- ......
- }
- ......
- }
- public class ActivityStack {
- ......
- final boolean resumeTopActivityLocked(ActivityRecord prev) {
- // Find the first activity that is not finishing.
- ActivityRecord next = topRunningActivityLocked(null);
- ......
- if (next == null) {
- // There are no more activities! Let's just start up the
- // Launcher...
- if (mMainStack) {
- return mService.startHomeActivityLocked();
- }
- }
- ......
- }
- ......
- }
Step 12. ActivityManagerService.startHomeActivityLocked
這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- boolean startHomeActivityLocked() {
- ......
- Intent intent = new Intent(
- mTopAction,
- mTopData != null ? Uri.parse(mTopData) : null);
- intent.setComponent(mTopComponent);
- if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- intent.addCategory(Intent.CATEGORY_HOME);
- }
- ActivityInfo aInfo =
- intent.resolveActivityInfo(mContext.getPackageManager(),
- STOCK_PM_FLAGS);
- if (aInfo != null) {
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
- // Don't do this if the home app is currently being
- // instrumented.
- ProcessRecord app = getProcessRecordLocked(aInfo.processName,
- aInfo.applicationInfo.uid);
- if (app == null || app.instrumentationClass == null) {
- intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
- null, null, 0, 0, 0, false, false);
- }
- }
- return true;
- }
- ......
- }
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- boolean startHomeActivityLocked() {
- ......
- Intent intent = new Intent(
- mTopAction,
- mTopData != null ? Uri.parse(mTopData) : null);
- intent.setComponent(mTopComponent);
- if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- intent.addCategory(Intent.CATEGORY_HOME);
- }
- ActivityInfo aInfo =
- intent.resolveActivityInfo(mContext.getPackageManager(),
- STOCK_PM_FLAGS);
- if (aInfo != null) {
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
- // Don't do this if the home app is currently being
- // instrumented.
- ProcessRecord app = getProcessRecordLocked(aInfo.processName,
- aInfo.applicationInfo.uid);
- if (app == null || app.instrumentationClass == null) {
- intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
- null, null, 0, 0, 0, false, false);
- }
- }
- return true;
- }
- ......
- }
- public final class ActivityManagerService extends ActivityManagerNative
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- ......
- boolean startHomeActivityLocked() {
- ......
- Intent intent = new Intent(
- mTopAction,
- mTopData != null ? Uri.parse(mTopData) : null);
- intent.setComponent(mTopComponent);
- if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- intent.addCategory(Intent.CATEGORY_HOME);
- }
- ActivityInfo aInfo =
- intent.resolveActivityInfo(mContext.getPackageManager(),
- STOCK_PM_FLAGS);
- if (aInfo != null) {
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
- // Don't do this if the home app is currently being
- // instrumented.
- ProcessRecord app = getProcessRecordLocked(aInfo.processName,
- aInfo.applicationInfo.uid);
- if (app == null || app.instrumentationClass == null) {
- intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
- null, null, 0, 0, 0, false, false);
- }
- }
- return true;
- }
- ......
- }
- <manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.launcher"
- android:sharedUserId="@string/sharedUserId"
- >
- ......
- <application
- android:name="com.android.launcher2.LauncherApplication"
- android:process="@string/process"
- android:label="@string/application_name"
- android:icon="@drawable/ic_launcher_home">
- <activity
- android:name="com.android.launcher2.Launcher"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/Theme"
- android:screenOrientation="nosensor"
- android:windowSoftInputMode="stateUnspecified|adjustPan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY"/>
- </intent-filter>
- </activity>
- ......
- </application>
- </manifest>
- <manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.launcher"
- android:sharedUserId="@string/sharedUserId"
- >
- ......
- <application
- android:name="com.android.launcher2.LauncherApplication"
- android:process="@string/process"
- android:label="@string/application_name"
- android:icon="@drawable/ic_launcher_home">
- <activity
- android:name="com.android.launcher2.Launcher"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/Theme"
- android:screenOrientation="nosensor"
- android:windowSoftInputMode="stateUnspecified|adjustPan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY"/>
- </intent-filter>
- </activity>
- ......
- </application>
- </manifest>
- <manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.launcher"
- android:sharedUserId="@string/sharedUserId"
- >
- ......
- <application
- android:name="com.android.launcher2.LauncherApplication"
- android:process="@string/process"
- android:label="@string/application_name"
- android:icon="@drawable/ic_launcher_home">
- <activity
- android:name="com.android.launcher2.Launcher"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/Theme"
- android:screenOrientation="nosensor"
- android:windowSoftInputMode="stateUnspecified|adjustPan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY"/>
- </intent-filter>
- </activity>
- ......
- </application>
- </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文件中:
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- ......
- if (!mRestoring) {
- mModel.startLoader(this, true);
- }
- ......
- }
- ......
- }
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- ......
- if (!mRestoring) {
- mModel.startLoader(this, true);
- }
- ......
- }
- ......
- }
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- ......
- if (!mRestoring) {
- mModel.startLoader(this, true);
- }
- ......
- }
- ......
- }
Step 15. LauncherModel.startLoader
這個函數定義在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
- public class LauncherModel extends BroadcastReceiver {
- ......
- public void startLoader(Context context, boolean isLaunching) {
- ......
- synchronized (mLock) {
- ......
- // Don't bother to start the thread if we know it's not going to do anything
- if (mCallbacks != null && mCallbacks.get() != null) {
- // If there is already one running, tell it to stop.
- LoaderTask oldTask = mLoaderTask;
- if (oldTask != null) {
- if (oldTask.isLaunching()) {
- // don't downgrade isLaunching if we're already running
- isLaunching = true;
- }
- oldTask.stopLocked();
- }
- mLoaderTask = new LoaderTask(context, isLaunching);
- sWorker.post(mLoaderTask);
- }
- }
- }
- ......
- }
- public class LauncherModel extends BroadcastReceiver {
- ......
- public void startLoader(Context context, boolean isLaunching) {
- ......
- synchronized (mLock) {
- ......
- // Don't bother to start the thread if we know it's not going to do anything
- if (mCallbacks != null && mCallbacks.get() != null) {
- // If there is already one running, tell it to stop.
- LoaderTask oldTask = mLoaderTask;
- if (oldTask != null) {
- if (oldTask.isLaunching()) {
- // don't downgrade isLaunching if we're already running
- isLaunching = true;
- }
- oldTask.stopLocked();
- }
- mLoaderTask = new LoaderTask(context, isLaunching);
- sWorker.post(mLoaderTask);
- }
- }
- }
- ......
- }
- public class LauncherModel extends BroadcastReceiver {
- ......
- public void startLoader(Context context, boolean isLaunching) {
- ......
- synchronized (mLock) {
- ......
- // Don't bother to start the thread if we know it's not going to do anything
- if (mCallbacks != null && mCallbacks.get() != null) {
- // If there is already one running, tell it to stop.
- LoaderTask oldTask = mLoaderTask;
- if (oldTask != null) {
- if (oldTask.isLaunching()) {
- // don't downgrade isLaunching if we're already running
- isLaunching = true;
- }
- oldTask.stopLocked();
- }
- mLoaderTask = new LoaderTask(context, isLaunching);
- sWorker.post(mLoaderTask);
- }
- }
- }
- ......
- }
Step 16. LoaderTask.run
這個函數定義在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
- public class LauncherModel extends BroadcastReceiver {
- ......
- private class LoaderTask implements Runnable {
- ......
- public void run() {
- ......
- keep_running: {
- ......
- // second step
- if (loadWorkspaceFirst) {
- ......
- loadAndBindAllApps();
- } else {
- ......
- }
- ......
- }
- ......
- }
- ......
- }
- ......
- }
- public class LauncherModel extends BroadcastReceiver {
- ......
- private class LoaderTask implements Runnable {
- ......
- public void run() {
- ......
- keep_running: {
- ......
- // second step
- if (loadWorkspaceFirst) {
- ......
- loadAndBindAllApps();
- } else {
- ......
- }
- ......
- }
- ......
- }
- ......
- }
- ......
- }
- public class LauncherModel extends BroadcastReceiver {
- ......
- private class LoaderTask implements Runnable {
- ......
- public void run() {
- ......
- keep_running: {
- ......
- // second step
- if (loadWorkspaceFirst) {
- ......
- loadAndBindAllApps();
- } else {
- ......
- }
- ......
- }
- ......
- }
- ......
- }
- ......
- }
Step 17. LoaderTask.loadAndBindAllApps
這個函數定義在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
- public class LauncherModel extends BroadcastReceiver {
- ......
- private class LoaderTask implements Runnable {
- ......
- private void loadAndBindAllApps() {
- ......
- if (!mAllAppsLoaded) {
- loadAllAppsByBatch();
- if (mStopped) {
- return;
- }
- mAllAppsLoaded = true;
- } else {
- onlyBindAllApps();
- }
- }
- ......
- }
- ......
- }
- public class LauncherModel extends BroadcastReceiver {
- ......
- private class LoaderTask implements Runnable {
- ......
- private void loadAndBindAllApps() {
- ......
- if (!mAllAppsLoaded) {
- loadAllAppsByBatch();
- if (mStopped) {
- return;
- }
- mAllAppsLoaded = true;
- } else {
- onlyBindAllApps();
- }
- }
- ......
- }
- ......
- }
- public class LauncherModel extends BroadcastReceiver {
- ......
- private class LoaderTask implements Runnable {
- ......
- private void loadAndBindAllApps() {
- ......
- if (!mAllAppsLoaded) {
- loadAllAppsByBatch();
- if (mStopped) {
- return;
- }
- mAllAppsLoaded = true;
- } else {
- onlyBindAllApps();
- }
- }
- ......
- }
- ......
- }
Step 18. LoaderTask.loadAllAppsByBatch
這個函數定義在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:
- public class LauncherModel extends BroadcastReceiver {
- ......
- private class LoaderTask implements Runnable {
- ......
- private void loadAllAppsByBatch() {
- ......
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- final PackageManager packageManager = mContext.getPackageManager();
- List<ResolveInfo> apps = null;
- int N = Integer.MAX_VALUE;
- int startIndex;
- int i=0;
- int batchSize = -1;
- while (i < N && !mStopped) {
- if (i == 0) {
- mAllAppsList.clear();
- ......
- apps = packageManager.queryIntentActivities(mainIntent, 0);
- ......
- N = apps.size();
- ......
- if (mBatchSize == 0) {
- batchSize = N;
- } else {
- batchSize = mBatchSize;
- }
- ......
- Collections.sort(apps,
- new ResolveInfo.DisplayNameComparator(packageManager));
- }
- startIndex = i;
- for (int j=0; i<N && j<batchSize; j++) {
- // This builds the icon bitmaps.
- mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
- i++;
- }
- final boolean first = i <= batchSize;
- final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- final ArrayList<ApplicationInfo> added = mAllAppsList.added;
- mAllAppsList.added = new ArrayList<ApplicationInfo>();
- mHandler.post(new Runnable() {
- public void run() {
- final long t = SystemClock.uptimeMillis();
- if (callbacks != null) {
- if (first) {
- callbacks.bindAllApplications(added);
- } else {
- callbacks.bindAppsAdded(added);
- }
- ......
- } else {
- ......
- }
- }
- });
- ......
- }
- ......
- }
- ......
- }
- ......
- }
- public class LauncherModel extends BroadcastReceiver {
- ......
- private class LoaderTask implements Runnable {
- ......
- private void loadAllAppsByBatch() {
- ......
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- final PackageManager packageManager = mContext.getPackageManager();
- List<ResolveInfo> apps = null;
- int N = Integer.MAX_VALUE;
- int startIndex;
- int i=0;
- int batchSize = -1;
- while (i < N && !mStopped) {
- if (i == 0) {
- mAllAppsList.clear();
- ......
- apps = packageManager.queryIntentActivities(mainIntent, 0);
- ......
- N = apps.size();
- ......
- if (mBatchSize == 0) {
- batchSize = N;
- } else {
- batchSize = mBatchSize;
- }
- ......
- Collections.sort(apps,
- new ResolveInfo.DisplayNameComparator(packageManager));
- }
- startIndex = i;
- for (int j=0; i<N && j<batchSize; j++) {
- // This builds the icon bitmaps.
- mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
- i++;
- }
- final boolean first = i <= batchSize;
- final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- final ArrayList<ApplicationInfo> added = mAllAppsList.added;
- mAllAppsList.added = new ArrayList<ApplicationInfo>();
- mHandler.post(new Runnable() {
- public void run() {
- final long t = SystemClock.uptimeMillis();
- if (callbacks != null) {
- if (first) {
- callbacks.bindAllApplications(added);
- } else {
- callbacks.bindAppsAdded(added);
- }
- ......
- } else {
- ......
- }
- }
- });
- ......
- }
- ......
- }
- ......
- }
- ......
- }
- public class LauncherModel extends BroadcastReceiver {
- ......
- private class LoaderTask implements Runnable {
- ......
- private void loadAllAppsByBatch() {
- ......
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- final PackageManager packageManager = mContext.getPackageManager();
- List<ResolveInfo> apps = null;
- int N = Integer.MAX_VALUE;
- int startIndex;
- int i=0;
- int batchSize = -1;
- while (i < N && !mStopped) {
- if (i == 0) {
- mAllAppsList.clear();
- ......
- apps = packageManager.queryIntentActivities(mainIntent, 0);
- ......
- N = apps.size();
- ......
- if (mBatchSize == 0) {
- batchSize = N;
- } else {
- batchSize = mBatchSize;
- }
- ......
- Collections.sort(apps,
- new ResolveInfo.DisplayNameComparator(packageManager));
- }
- startIndex = i;
- for (int j=0; i<N && j<batchSize; j++) {
- // This builds the icon bitmaps.
- mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
- i++;
- }
- final boolean first = i <= batchSize;
- final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- final ArrayList<ApplicationInfo> added = mAllAppsList.added;
- mAllAppsList.added = new ArrayList<ApplicationInfo>();
- mHandler.post(new Runnable() {
- public void run() {
- final long t = SystemClock.uptimeMillis();
- if (callbacks != null) {
- if (first) {
- callbacks.bindAllApplications(added);
- } else {
- callbacks.bindAppsAdded(added);
- }
- ......
- } else {
- ......
- }
- }
- });
- ......
- }
- ......
- }
- ......
- }
- ......
- }
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- final PackageManager packageManager = mContext.getPackageManager();
- final PackageManager packageManager = mContext.getPackageManager();
- 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文件中:
- class PackageManagerService extends IPackageManager.Stub {
- ......
- public List<ResolveInfo> queryIntentActivities(Intent intent,
- String resolvedType, int flags) {
- ......
- synchronized (mPackages) {
- String pkgName = intent.getPackage();
- if (pkgName == null) {
- return (List<ResolveInfo>)mActivities.queryIntent(intent,
- resolvedType, flags);
- }
- ......
- }
- ......
- }
- ......
- }
- class PackageManagerService extends IPackageManager.Stub {
- ......
- public List<ResolveInfo> queryIntentActivities(Intent intent,
- String resolvedType, int flags) {
- ......
- synchronized (mPackages) {
- String pkgName = intent.getPackage();
- if (pkgName == null) {
- return (List<ResolveInfo>)mActivities.queryIntent(intent,
- resolvedType, flags);
- }
- ......
- }
- ......
- }
- ......
- }
- class PackageManagerService extends IPackageManager.Stub {
- ......
- public List<ResolveInfo> queryIntentActivities(Intent intent,
- String resolvedType, int flags) {
- ......
- synchronized (mPackages) {
- String pkgName = intent.getPackage();
- if (pkgName == null) {
- return (List<ResolveInfo>)mActivities.queryIntent(intent,
- resolvedType, flags);
- }
- ......
- }
- ......
- }
- ......
- }
系統在啓動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文件中:
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- private AllAppsView mAllAppsGrid;
- ......
- public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
- mAllAppsGrid.setApps(apps);
- }
- ......
- }
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- private AllAppsView mAllAppsGrid;
- ......
- public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
- mAllAppsGrid.setApps(apps);
- }
- ......
- }
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- private AllAppsView mAllAppsGrid;
- ......
- public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
- mAllAppsGrid.setApps(apps);
- }
- ......
- }
Step 21. AllApps2D.setApps
這個函數定義在packages/apps/Launcher2/src/com/android/launcher2/AllApps2D.java文件中:
- public class AllApps2D
- extends RelativeLayout
- implements AllAppsView,
- AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener,
- View.OnKeyListener,
- DragSource {
- ......
- public void setApps(ArrayList<ApplicationInfo> list) {
- mAllAppsList.clear();
- addApps(list);
- }
- public void addApps(ArrayList<ApplicationInfo> list) {
- final int N = list.size();
- for (int i=0; i<N; i++) {
- final ApplicationInfo item = list.get(i);
- int index = Collections.binarySearch(mAllAppsList, item,
- LauncherModel.APP_NAME_COMPARATOR);
- if (index < 0) {
- index = -(index+1);
- }
- mAllAppsList.add(index, item);
- }
- mAppsAdapter.notifyDataSetChanged();
- }
- ......
- }
- public class AllApps2D
- extends RelativeLayout
- implements AllAppsView,
- AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener,
- View.OnKeyListener,
- DragSource {
- ......
- public void setApps(ArrayList<ApplicationInfo> list) {
- mAllAppsList.clear();
- addApps(list);
- }
- public void addApps(ArrayList<ApplicationInfo> list) {
- final int N = list.size();
- for (int i=0; i<N; i++) {
- final ApplicationInfo item = list.get(i);
- int index = Collections.binarySearch(mAllAppsList, item,
- LauncherModel.APP_NAME_COMPARATOR);
- if (index < 0) {
- index = -(index+1);
- }
- mAllAppsList.add(index, item);
- }
- mAppsAdapter.notifyDataSetChanged();
- }
- ......
- }
- public class AllApps2D
- extends RelativeLayout
- implements AllAppsView,
- AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener,
- View.OnKeyListener,
- DragSource {
- ......
- public void setApps(ArrayList<ApplicationInfo> list) {
- mAllAppsList.clear();
- addApps(list);
- }
- public void addApps(ArrayList<ApplicationInfo> list) {
- final int N = list.size();
- for (int i=0; i<N; i++) {
- final ApplicationInfo item = list.get(i);
- int index = Collections.binarySearch(mAllAppsList, item,
- LauncherModel.APP_NAME_COMPARATOR);
- if (index < 0) {
- index = -(index+1);
- }
- mAllAppsList.add(index, item);
- }
- mAppsAdapter.notifyDataSetChanged();
- }
- ......
- }
到了這裏,系統默認的Home應用程序Launcher就把PackageManagerService中的應用程序加載進來了,當我們在屏幕上點擊下面這個圖標時,就會把剛纔加載好的應用程序以圖標的形式展示出來了:
點擊這個按鈕時,便會響應Launcher.onClick函數:
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- public void onClick(View v) {
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- ......
- } else if (tag instanceof FolderInfo) {
- ......
- } else if (v == mHandleView) {
- if (isAllAppsVisible()) {
- ......
- } else {
- showAllApps(true);
- }
- }
- }
- ......
- }
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- public void onClick(View v) {
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- ......
- } else if (tag instanceof FolderInfo) {
- ......
- } else if (v == mHandleView) {
- if (isAllAppsVisible()) {
- ......
- } else {
- showAllApps(true);
- }
- }
- }
- ......
- }
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- public void onClick(View v) {
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- ......
- } else if (tag instanceof FolderInfo) {
- ......
- } else if (v == mHandleView) {
- if (isAllAppsVisible()) {
- ......
- } else {
- showAllApps(true);
- }
- }
- }
- ......
- }
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- void showAllApps(boolean animated) {
- mAllAppsGrid.zoom(1.0f, animated);
- ((View) mAllAppsGrid).setFocusable(true);
- ((View) mAllAppsGrid).requestFocus();
- // TODO: fade these two too
- mDeleteZone.setVisibility(View.GONE);
- }
- ......
- }
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- void showAllApps(boolean animated) {
- mAllAppsGrid.zoom(1.0f, animated);
- ((View) mAllAppsGrid).setFocusable(true);
- ((View) mAllAppsGrid).requestFocus();
- // TODO: fade these two too
- mDeleteZone.setVisibility(View.GONE);
- }
- ......
- }
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
- ......
- void showAllApps(boolean animated) {
- mAllAppsGrid.zoom(1.0f, animated);
- ((View) mAllAppsGrid).setFocusable(true);
- ((View) mAllAppsGrid).requestFocus();
- // TODO: fade these two too
- mDeleteZone.setVisibility(View.GONE);
- }
- ......
- }
當點擊上面的這些應用程序圖標時,便會響應AllApps2D.onItemClick函數:
- public class AllApps2D
- extends RelativeLayout
- implements AllAppsView,
- AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener,
- View.OnKeyListener,
- DragSource {
- ......
- public void onItemClick(AdapterView parent, View v, int position, long id) {
- ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
- mLauncher.startActivitySafely(app.intent, app);
- }
- ......
- }
- public class AllApps2D
- extends RelativeLayout
- implements AllAppsView,
- AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener,
- View.OnKeyListener,
- DragSource {
- ......
- public void onItemClick(AdapterView parent, View v, int position, long id) {
- ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
- mLauncher.startActivitySafely(app.intent, app);
- }
- ......
- }
- public class AllApps2D
- extends RelativeLayout
- implements AllAppsView,
- AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener,
- View.OnKeyListener,
- DragSource {
- ......
- public void onItemClick(AdapterView parent, View v, int position, long id) {
- ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
- mLauncher.startActivitySafely(app.intent, app);
- }
- ......
- }
七 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的事件:
- 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>
- 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。
- 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>
- 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。
- 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>
- 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裏做的:
- 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="FONT-STYLE: italic; COLOR: #666666">// 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>
- 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,下面代碼可以看出:
- 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>
- 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的實例
首先在Launcher.java中定義瞭如下兩個變量
在onCreate函數中初始化,
以上代碼是設計模式中標準的單例模式
frameworks/base/ core / java / android / appwidget / AppWidgetHost.java
可以看到AppWidgetHost有自己的HostId,Handler,和sService
93 mHandler = new UpdateHandler(context.getMainLooper());
這是啥用法呢?
參數爲Looper,即消息處理放到此Looper的MessageQueue中,有哪些消息呢?
通過以上可以看到主要有兩中類型的消息,HANDLE_UPDATE和HANDLE_PROVIDER_CHANGED
處理即通過自身定義的方法
那麼此消息是何時由誰發送的呢?
從以上的代碼中看到AppWidgetHost定義了內部類Callback,擴展了類IAppWidgetHost.Stub,類Callback中負責發送以上消息
Launcher中會調用本類中的如下方法,
當用戶在Dialog中選擇AddAdapter.ITEM_APPWIDGET時,首先會通過AppWidgethost分配一個appWidgetId,並最終調到AppWidgetService中去
同時發送Intent,其中保存有剛剛分配的appWidgetId,AppWidgetManager.EXTRA_APPWIDGET_ID
待用戶選擇完要添加的widget之後,將會回到Launcher.java中的函數onActivityResult中
上述addAppWidget中做了哪些事情呢?
首先獲取appWidgetId,再通過AppWidgetManager獲取AppWidgetProviderInfo,最後判斷此Widget是否存在ConfigActivity,如果存在則啓動ConfigActivity,否則直接調用函數onActivityResult
通過函數completeAddAppWidget把此widget的信息插入到數據庫中,並添加到桌面上
Launcher中刪除widget
長按一個widget,並拖入到DeleteZone中可實現刪除
具體代碼在DeleteZone中
刪除時,判斷刪除的類型是否是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中屏幕方向的布爾值參數:
- Launcher App:\cupcake\packages\apps\Launcher
待機畫面分爲多層,桌面Desktop Items在\res\layout-*\workspace_screen.xml中置:
- <com.android.launcher.CellLayout
- ... ...
- launcher:shortAxisCells="4"
- launcher:longAxisCells="4"
- ... ...
- />
以上表示4行4列.
再看看com.android.launcher.CellLayout ,其中有定義屏幕方向的參數:
- private boolean mPortrait;
但是一直沒有初始化,也就是mPortrait=false,桌面的單元格設置一直是以非豎屏(橫屏)的設置定義進行初始化。
再來看看橫屏和豎屏情況下的初始化不同之處,就可以看出BUG了:
- boolean[][] mOccupied;//二元單元格布爾值數組
- if (mPortrait) {
- mOccupied = new boolean[mShortAxisCells][mLongAxisCells];
- } else {
- mOccupied = new boolean[mLongAxisCells][mShortAxisCells];
- }
如果我們滿屏顯示桌面(橫向和縱向的單元格數不一致),而不是默認的只顯示4行4列,則mShortAxisCells = 4, mLongAxisCells = 5,數組應該初始化是:new boolean[4][5],但是實際是按照非豎屏處理,初始化成了new boolean[5][4],會產生數組越界異常。
可以在構造函數中,添加通過屏幕方向初始化mPortrait,代碼如下:
- public CellLayout(Context context, AttributeSet attrs, int defStyle)
- {
- super(context, attrs, defStyle);
- mPortrait = this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;// 新增代碼
- ... ...