Android Launcher 分析
1. Launcher的啟動(dòng)過程
從網(wǎng)絡(luò)上找了一段關(guān)于Launcher的啟動(dòng)過程的文章,作為學(xué)習(xí)Launcher的背景知識(shí):
Linux kernel啟動(dòng)以后會(huì)通過app_main進(jìn)程來初始化android Runtime Java運(yùn)行環(huán)境,而zygote是Android的第一個(gè)進(jìn)程。所有的android的應(yīng)用以及大部分系統(tǒng)服務(wù)都是通過zygote fork出來的子進(jìn)程(我現(xiàn)在看到的只有native的service manager不是zygote fork出來的)。在system server中啟動(dòng)的若干系統(tǒng)服務(wù)中與我們啟動(dòng)進(jìn)程相關(guān)的就是Acitivity Manager。
? 當(dāng)systerm server啟動(dòng)好所有服務(wù)以后,系統(tǒng)就進(jìn)入”system ready”狀態(tài),這個(gè)時(shí)候Activity Manager就登場了。Activity Manager光看代碼行就知道是一個(gè)重量級(jí)的服務(wù),它主要管理Activity之間的跳轉(zhuǎn),以及進(jìn)程的生命周期。當(dāng)Activity Manager發(fā)現(xiàn)系統(tǒng)已經(jīng)啟動(dòng)好以后它就會(huì)發(fā)出一個(gè)intent:
? 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);
? }
通過這個(gè)category類型為home的intent,Activity?Manager就會(huì)通過:
?
?startActivityLocked(null, intent, null, null, 0, aInfo,
? null, null, 0, 0, 0, false, false);
啟動(dòng)Home進(jìn)程了。而這個(gè)啟動(dòng)Home進(jìn)程的過程實(shí)際上還是去通過zygote?fork出的一個(gè)子進(jìn)程。
因此只要在manifest中具備這樣的intent-filter都可以在開機(jī)的時(shí)候作為Home啟動(dòng):
? <intent-filter>
? <action android:name="android.intent.action.MAIN" />
? <category android:name="android.intent.category.HOME"/>
? <category android:name="android.intent.category.DEFAULT" />
? </intent-filter>
多個(gè)home之間的switch會(huì)在開始的時(shí)候有個(gè)選擇,至于這個(gè)選擇好像是package?manager來實(shí)現(xiàn)的,沒有仔細(xì)研究過。
2.UI結(jié)構(gòu)
通過launcher/Res/Layout-land/launcher.xml分析可以得到主屏幕的UI結(jié)構(gòu):
?
整個(gè)homescreen是一個(gè)包含三個(gè)child?view的FrameLayout(com.android.launcher.DragLayer)。
第一個(gè)child就是桌面com.android.launcher.Workspace。這個(gè)桌面又包含三個(gè)child。每個(gè)child就對(duì)應(yīng)一個(gè)桌面。這就是你在Android上看到的三個(gè)桌面。每個(gè)桌面上可以放置下列對(duì)象:應(yīng)用快捷方式,appwidget和folder。
第二個(gè)child是一個(gè)SlidingDrawer控件,這個(gè)控件由兩個(gè)子控件組成。一個(gè)是com.android.launcher.HandleView,就是Android桌面下方的把手,當(dāng)點(diǎn)擊這個(gè)把手時(shí),另一個(gè)子控件,com.android.launcher.AllAppsGridView就會(huì)彈出,這個(gè)子控件列出系統(tǒng)中當(dāng)前安裝的所有類型為category.launcher的Activity。
第三個(gè)child是com.android.launcher.DeleteZone。當(dāng)用戶在桌面上長按一個(gè)widget時(shí),把手位置就會(huì)出現(xiàn)一個(gè)垃圾桶形狀的控件,就是這個(gè)控件。
3.應(yīng)用程序代碼分析
由Launcher中的AndroidManifest.xml可以看出整個(gè)Launcher的代碼結(jié)構(gòu)。
首先,是一些權(quán)限的聲明。例如:
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
這部分可以略過;
其次,Application的構(gòu)成,如上圖:
?
(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>
上面這段代碼就標(biāo)志著它是開機(jī)啟動(dòng)后Home的Activity。通過Launcher.java中onCreat()的分析我們可以大致把握屏幕的主要活動(dòng):
protected void onCreate(Bundle savedInstanceState) {
??????? super.onCreate(savedInstanceState);
//把xml文件的內(nèi)容實(shí)例化到View中
??????? mInflater = getLayoutInflater();
//監(jiān)聽?wèi)?yīng)用程序控件改變事件
??????? mAppWidgetManager = AppWidgetManager.getInstance(this);
??????? mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
??????? mAppWidgetHost.startListening();
// 用于調(diào)試????
??????? if (PROFILE_STARTUP) {
??????????? android.os.Debug.startMethodTracing("/sdcard/launcher");
??????? }
? //監(jiān)聽locale,mcc,mnc是否改變,如果改變,則重寫新配置
????? //mcc:mobile country code(國家代碼China 460); mnc:mobile network code(網(wǎng)絡(luò)代碼)
??????? checkForLocaleChange();
?????? /*This allows such applications to have a virtual wallpaper that is larger than the physical screen, matching the size of their workspace.*/
??????? setWallpaperDimension();
//顯示主屏幕UI元素,workspace,slidingdrawer(handleview and appgridview),deletezone
??????? setContentView(R.layout.launcher);
//Finds all the views we need and configure them properly.
//完成workspace,slidingdrawer,deletezone的各種事件操作和監(jiān)聽
??????? setupViews();
//Registers various intent receivers.
//允許其他應(yīng)用對(duì)本應(yīng)用的操作
??????? registerIntentReceivers();
//Registers various content observers.
//例如,注冊一個(gè)內(nèi)容觀察者跟蹤喜愛的應(yīng)用程序
??????? registerContentObservers();
//重新保存前一個(gè)狀態(tài)(目的??)
??????? mSavedState = savedInstanceState;
??????? restoreState(mSavedState);
//調(diào)試?
??????? if (PROFILE_STARTUP) {
??????????? android.os.Debug.stopMethodTracing();
??????? }
//Loads the list of installed applications in mApplications.
??????? if (!mRestoring) {
??????????? startLoaders();
??????? }
??????? // For handling default keys??
??????? mDefaultKeySsb = new SpannableStringBuilder();
??????? Selection.setSelection(mDefaultKeySsb, 0);
??? }
方法onActivityResult():完成在workspace上增加shortcut,appwidge和Livefolder;
方法onSaveInstantceState()和onRestoreInstanceState():為了防止Sensor、Land和Port布局自動(dòng)切換時(shí)數(shù)據(jù)被置空,通過onSaveInstanceState方法可以保存當(dāng)前窗口的狀態(tài),在即將布局切換前將當(dāng)前的Activity壓入歷史堆棧。如果我們的Activity在后臺(tái)沒有因?yàn)檫\(yùn)行內(nèi)存吃緊被清理,則切換時(shí)回觸發(fā)onRestoreIntanceState()。
(2)WallpaperChooser:設(shè)置墻紙。
同理我們從onCreat()作為入口來分析這個(gè)活動(dòng)的主要功能。
public void onCreate(Bundle icicle) {
??????? super.onCreate(icicle);
//設(shè)置允許改變的窗口狀態(tài),需在 setContentView 之前調(diào)用
??????? requestWindowFeature(Window.FEATURE_NO_TITLE);
/ /添加墻紙資源,將資源標(biāo)識(shí)符加入到動(dòng)態(tài)數(shù)組中
??????? findWallpapers();
//顯示墻紙?jiān)O(shè)置屏幕的UI元素,Imageview,Gallery and Button(LinearLayout)
??????? setContentView(R.layout.wallpaper_chooser);
//圖片查看功能的實(shí)現(xiàn)
??????? mGallery = (Gallery) findViewById(R.id.gallery);
??????? mGallery.setAdapter(new ImageAdapter(this));
??????? mGallery.setOnItemSelectedListener(this);
??????? mGallery.setCallbackDuringFling(false);
//Button事件監(jiān)聽,點(diǎn)擊選擇setWallpaper(Resid)
??????? findViewById(R.id.set).setOnClickListener(this);
??????? mImageView = (ImageView) findViewById(R.id.wallpaper);
??? }
(3)default_searchable
對(duì)于home中任意的Acitivty,使能系統(tǒng)缺省Search模式,這樣就可以使用android系統(tǒng)默認(rèn)的search?UI。
?
(4)InstallShortcutReceiver:
繼承自BroadcastReceiver,重寫onReceier()方法,對(duì)于發(fā)送來的Broadcast(這里指Intent)進(jìn)行過濾(IntentFilt)并且響應(yīng)(這里是InstallShortcut())。這里分析下onReceive():
<!-- Enable system-default search mode for any activity in Home -->
<!-- Intent received used to install shortcuts from other applications -->
public void onReceive(Context context, Intent data) {
?????? //接受并過濾Intent
if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
??????????? return;
??????? }
????? //獲取屏幕
??????? int screen = Launcher.getScreen();
//安裝快捷方式
??????? if (!installShortcut(context, data, screen)) {
??????????? //如果屏幕已滿,搜尋其他屏幕
??????????? for (int i = 0; i < Launcher.SCREEN_COUNT; i++) {
??????????????? if (i != screen && installShortcut(context, data, i)) break;
??????????? }
??????? }
??? }
其中IntallShortcut()方法:首先,對(duì)傳入的坐標(biāo)進(jìn)行判斷(findEmptyCell()),如果是空白位置,則可以放置快捷方式;其次,缺省情況下,我們允許創(chuàng)建重復(fù)的快捷方式,具體創(chuàng)建過程(addShortcut())就是把快捷方式的信息傳入數(shù)據(jù)庫(addItemToDatabase())。
(5)UninstallShortcutReceiver:
同理,UninstallShortcutReceiver()繼承自BroadcastReceiver(),實(shí)現(xiàn)onReceiver()方法。定義一個(gè)ContentResolver對(duì)象完成對(duì)數(shù)據(jù)庫的訪問和操作(通過URI定位),進(jìn)而刪除快捷方式 。
?
(6)LauncherProvider:
繼承自ContentProvider(),主要是建立一個(gè)數(shù)據(jù)庫來存放HomeScreen中的數(shù)據(jù)信息,并通過內(nèi)容提供者來實(shí)現(xiàn)其他應(yīng)用對(duì)launcher中數(shù)據(jù)的訪問和操作。
重寫了ContentProvider()中的方法:
getType():返回?cái)?shù)據(jù)類型。如果有自定義的全新類型,通過此方法完成數(shù)據(jù)的訪問。
query():查詢數(shù)據(jù)。傳入URI,返回一個(gè)Cursor對(duì)象,通過Cursor完成對(duì)數(shù)據(jù)庫數(shù)據(jù)的遍歷訪問。
Insert():插入一條數(shù)據(jù)。
bulkInsert():大容量數(shù)據(jù)的插入。
delete():刪除一條數(shù)據(jù)。
update():更改一條數(shù)據(jù)。
sendNotify():發(fā)送通知。
類DatabaseHelper繼承自一個(gè)封裝類SQLiteOpenHelper(),方便了數(shù)據(jù)庫的管理和維護(hù)。
重寫的方法:
onCreate():創(chuàng)建一個(gè)表。其中db.execSQL()方法執(zhí)行一條SQL語句,通過一條字符串執(zhí)行相關(guān)的操作。當(dāng)然,對(duì)SQL基本語句應(yīng)該了解。
onUpgrade():升級(jí)數(shù)據(jù)庫。
對(duì)HomeScreen數(shù)據(jù)庫操作的一些方法:
addClockWidget(),addSearchWidget,addShortcut,addAppShortcut,
loadFavorites(),launcherAppWidgetBinder(),convertWidget(),updateContactsShortcuts(),
copyFromCursor()
補(bǔ)充:
類AddAdapter(AddAdapter.java)列出了這四個(gè)類型對(duì)象。當(dāng)用戶在桌面空白處長按時(shí),下列函數(shù)序列被執(zhí)行:
Launcher::onLongClick?-->
Launcher::showAddDialog?-->
Launcher::showDialog(DIALOG_CREATE_SHORTCUT);?-->
Launcher::onCreateDialog?-->
Launcher::CreateShortcut::createDialog:這個(gè)函數(shù)創(chuàng)建一個(gè)彈出式對(duì)話框,詢問用戶是要添加什么(快捷方式,appwidget,?文件夾和墻紙)其內(nèi)容就來自AddAdapter。
類DesktopItemsLoader負(fù)責(zé)將桌面上所有的對(duì)象從content?provider中提取。
線程private?ApplicationsLoader?mApplicationsLoader負(fù)責(zé)從包管理器中獲取系統(tǒng)中安裝的應(yīng)用列表。(之后顯示在AllAppsGridView上)。ApplicationsLoader::run實(shí)現(xiàn):
1)通過包管理器列出系統(tǒng)中所有類型為Launcher,action為MAIN的activity;
2)對(duì)每一個(gè)Activity,
??? ???a)?將Activity相關(guān)元數(shù)據(jù)信息,如title,?icon,?intent等緩存到appInfoCache;
????? ???b)?填充到ApplicationsAdapter?中。填充過程中用到了一些小技巧,每填充4(UI_NOTIFICATION_RATE)個(gè)activity更新一下相應(yīng)view。
在Launcher::onCreate中,函數(shù)startLoaders被調(diào)用。而該函數(shù)接著調(diào)用loadApplications和loadUserItems,分別獲取系統(tǒng)的應(yīng)用列表,以及顯示在桌面上的對(duì)象列表(快捷方式,appwidget,folder等)。
Launcher上排列的所有應(yīng)用圖標(biāo)由AllAppsGridView對(duì)象呈現(xiàn)。這個(gè)對(duì)象是一個(gè)GridView。其對(duì)應(yīng)的Adapter是ApplicationsAdapter,對(duì)應(yīng)的model則是ApplicationInfo數(shù)組。數(shù)組內(nèi)容是由ApplicationsLoader裝載的。
?
?
?
總結(jié)
以上是生活随笔為你收集整理的Android Launcher 分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ActivityGroup中的子Acti
- 下一篇: Android library proj