當前位置:編程學習大全網 - 源碼下載 - 如何第壹次寫Android Launcher Switcher就上手

如何第壹次寫Android Launcher Switcher就上手

Home App and Switcher thereof

有玩 Android 系統手機的人應該都有使用過 Android Launcher 之類的應用程式,它是用於變換桌面環境或操作的應用程式,或可以說是變更使用者經驗 (UX) 的程式。在命名上我個人是比較偏好稱作 Home App,因為它是按下 Home 鍵切換或執行的程式,而且它也是跑在 home screen 的桌面程式;而 Launcher 的狹定義是來自 Android 官方預設的 Home app,即 Launcher.apk。我自己下載 Android source Froyo,裏面預設是 Launcher2.apk (com.android.launcher2),位於 {SourceDir}/packages/apps 下。

Activity 可以在 AndroidManifest.xml 中註冊啟動器,啟動器也就是 launcher,它不代表是 Launcher.apk 或任何壹種 Home App,而是指應用程式進入口會顯示在 Home App (或 Launcher)上。而 Home App 壹般來說都沒有註冊啟動器,因此有人就寫了偵測系統中所有 Home App 的程式,也就是 Home App Switcher。比較好的 Swicher 還會提供清除和設定 default Home App。我在 HTC Incredible S 中安裝了數種 Home App,即 Launcher7、ADW、Zeam、Regina、LauncherPro、Home Sample、Home++,當然預設的 HTC Sense 也有。其中只有 Zeam 是有註冊 launcher 分類,所以在應用程式選單上就能找得到。

Intent and Intent Filters

在 Android 系統中,Intent 是極為重要,也是最基本要知道的東西。對於系統而言,Intent 就像是應用程式的描述,也類似標簽的概念。根據 Intent 內容,系統或應用程式能夠去使用與搜尋其他應用程式與其提供的服務。Intent 資訊是註冊在程式的 AndroidManifest.xml,並包含在 Intent Filters 標簽的內容中。當搜尋系統中的應用程式時,會去比對應用程式的 Intent Filters,而搜尋條件是包裝在 Intent 物件中以找到符合某種條件程式。Intent Filters 的內容就像是應用程式的索引,內容比對成功就會回傳相對應的 ResolveInfo。有關 Intent 的詳細內容可以參考底下提供的官方網頁連結 [1]。

壹個 Activity 基本上要在 AndroidManifest.xml 的 intent-filter 中註冊壹個 action 和 category; action預設是 intent.ACTION_MAIN,而 category 預設是 intent.CATEGORY_LAUNCHER。根據官方網站說明,ACTION_MAIN 指該 Activity 是個可開始啟動的進入點;CATEGORY_LAUNCHER 指該 Activity 在 Home App 上有啟動器,也就是在應用程式選單上會出現該 Activity 的 icon,以方便執行。註冊內容如下:

<activity android:name=".HomeSelector" android:label="@string/app_name">

<intent-filter>

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

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

</intent-filter>

</activity>

壹個應用程式中,可以有多個 Activity 都註冊 CATEGORY_LAUNCHER,那在安裝完後就會有多個 icon 出現在程式選單上,分別能執行各個 Activity。如果 Activity 沒有註冊 CATEGORY_LAUNCHER,那至少要註冊 CATEGORY_DEFAULT,因為它是系統使用 Intent 搜尋的預設條件之壹。註冊 CATEGORY_DEFAULT 後該 Activity 不會有 icon 在應用程式選單上出現,所以壹般都是給非 ACTION_MAIN 的 Activity 註冊,如 intent.ACTION_VIEW 之類的。如果 Activity 只註冊 action,壹般預設 Intent 的搜尋可能會失效,那就需要使用顯形式啟動,以上面的例子來看,就是指定啟動 HomeSelector,例子如下:

view sourceprint?

1 Intent i = new Intent(this, HomeSelector.class);

2 startActivity(i);

如果是指定啟動外部程式,那可以按照下面的做法:

view sourceprint?

1 Intent myIntent = new Intent();

2 myIntent.setClassName("com.KaDaNet", "com.KaDaNet.KaDaNet_MainController");

3 startActivity(myIntent);

那隱含式搜尋呢?如果妳有 Android SDK 有研究,那妳應該看過 SDK 中的 sample code,其中的 API Demo 展示了許多使用 API 功能的 Activity,而 API Demo 是由階層式列表對這些 Activity 分類。這個階層式列表的內容是動態生成出來的,由 loadLabel 和 activityInfo.name 去分類。因為是動態生成,所以就不會把列表寫死,那所有列表上要顯示的 Activity 資訊,就是利用隱含式搜尋獲得,程式碼如下:

view sourceprint?

1 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);

2 mainIntent.addCategory(Intent.CATEGORY_SAMPLE_CODE);

3 PackageManager pm = getPackageManager();

4 List<resolveinfo> list = pm.queryIntentActivities(mainIntent, 0);

API Demo 只顯示 category 註冊為 CATEGORY_SAMPLE_CODE 的 Activity 的資訊。把條件塞進 Intent 後利用 queryIntentActivities 搜尋,其中的壹個 Activity 註冊內容如下:

<activity android:name=".app.CustomDialogActivity"

android:label="@string/activity_custom_dialog"

android:theme="@style/Theme.CustomDialog">

<intent-filter>

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

<category android:name="android.intent.category.SAMPLE_CODE" />

</intent-filter>

</activity>

Home App and Intent Filters

在 Android 中,Home App 需要告訴系統自己是壹個 Home App,那它才會以 Home 的方式去執行,也就是跑在 home screen 上。同樣的,Home App 透過在 AndroidManifest.xml 上註冊 category 為 intent.CATEGORY_HOME 就可以了。以 SDK sample 中的 Home 為例,其註冊內容如下:

<activity android:name="Home" android:theme="@style/Theme"

android:launchMode="singleInstance"

android:stateNotNeeded="true">

<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>

</activity>

搜尋 Home App 的做法與 API Demo 相同,使用 PackageManager 中的 queryIntentActivities 即可。在下面範例中,對搜尋到的結果取出我們想要的資訊包裝成 HomeInfo,並回傳 List。

view sourceprint?

01 private List<homeinfo> queryHomeApp() {

02 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);

03 mainIntent.addCategory(Intent.CATEGORY_HOME);

04 PackageManager pm = getPackageManager();

05 List<resolveinfo> rList = pm.queryIntentActivities(mainIntent, 0);

06 List<homeinfo> homeList = new ArrayList<homeinfo>();

07

08 for(ResolveInfo r : rList) {

09 homeList.add(new HomeInfo(r, pm));

10 }

11 return homeList;

12 }

這裏使用 HomeInfo 類別包裝四種資訊,即類別名稱、封包名稱、Activity 標簽和 Activity icon。

view sourceprint?

01 public class HomeInfo {

02 public String name;

03 public String packageName;

04 public String label;

05 public Drawable icon;

06

07 public HomeInfo(ResolveInfo rInfo, PackageManager pm) {

08 name = rInfo.activityInfo.name;

09 packageName = rInfo.activityInfo.packageName;

10 label = rInfo.loadLabel(pm).toString();

11 icon = rInfo.loadIcon(pm);

12 }

13 }

很簡單吧!這樣就能取得系統中所有的 Home App 資訊,接下來根據這些資訊來切換 Home App。切換的方式,先前也提到過了,那就是顯形式啟動!

view sourceprint?

1 public void switchHome(String packageName) {

2 Intent homeIntent = new Intent("android.intent.action.MAIN");

3 homeIntent.addCategory("android.intent.category.HOME");

4 homeIntent.addCategory("android.intent.category.DEFAULT");

5 homeIntent.setPackage(packageName);

6 this.startActivity(homeIntent);

7 this.finish();

8 }

在程式界面上顯示所有已安裝的 Home App 資訊,最簡單的方式是利用 ListView,再新建壹個 list adapter 進行配接並設定 list item 按下後要處理的事,以上面的程式碼來看就是我們要處理的事,即切換到另壹個 Home App。界面設定的程式碼如下,顯示畫面如 Fig. 1。

view sourceprint?

01 ListView lvHome = (ListView) findViewById(R.id.lvHome);

02 final HomeListAdapter hAdapter = new HomeListAdapter(this);

03 hAdapter.addItemList(queryHomeApp());

04 lvHome.setAdapter(hAdapter);

05 lvHome.setOnItemClickListener(new AdapterView.OnItemClickListener() {

06 @Override

07 public void onItemClick(AdapterView parent, View view, int position, long id) {

08 HomeListItem item = (HomeListItem) hAdapter.getItem(position);

09 switchTo(item.getHomeInfo().packageName);

10 }

11 });

Fig.1. Home App list

Home App Management

壹個較好的 Home App 切換器都會提供相關管理的功能。壹般來說,基本的管理功能就是顯示資訊。除了找出系統中的所有 Home App 之外,也要能夠顯示這些 Home App 相關的資訊,當然資訊的種類要依需求而定。以 Home App 來說,可以分成未執行和執行時的資訊,也就是說使用者或許希望能看到哪些 Home App 是正在執行;如果正在執行,還有額外的資訊可以看到,像是程序相關的資料。根據這些資訊可以讓使用者決定是否要切換或是關掉 Home App。在這節中,我將介紹如何獲知該 Home App 是否正在執行,與執行時所占用記憶體的多寡,當然也包括移除 Home App 安裝和顯示應用程式細節。

Android 中的 ActivityManager 類別 [2] 是用來與系統裏正在執行的程序進行互動的 API;使用這個類別,我們能拿到所有正在執行的 App,並能以特定的 package 名稱去過濾某個 App 是否正在執行,範例如下:

view sourceprint?

1 public boolean isAppRunning() {

2 for(RunningAppProcessInfo info : mAM.getRunningAppProcesses()) {

3 if (info.processName.equals(packageName)) return true;

4 for (String pn : info.pkgList) {

5 if (pn.equals(packageName)) return true;

6 }

7 }

8 return false;

9 }

函式中的 mAM 即 ActivityManager,利用 getRunningAppProcesses 方法取得所有的存活 App,因為 Android 並沒有提供過濾的功能,需要自行處理;其中先比對程序名稱是因為在壹般的情況下,很多 App 的程序名稱會與 package 名稱相同,所以用這種方式加速比對。如果該 Home App 的 package 名稱與程序名稱不相同,只好比對這程序中所有被載進的 package 其名稱。

接下來是取得程序所占用的記憶體,利用 ActivityManager 中的 getProcessMemoryInfo 並餵入程序 ID 即可得到 Debug.MemoryInfo。RunningAppProcessInfo 的欄位包含程序 ID,可以透過剛剛取得的執行中 App 資料獲得。不過實際上,程序所占用的記憶體情況挺復雜的,可以參考 [3-6]。RSS 指包含整個分享函式庫。PSS 是以等比例去切割分享函式庫大小,比例分母為所有使用到該函式庫的程序數。這邊我僅以程序全部的 PSS 來估計記憶體占用,程式碼如下:

view sourceprint?

1 public int getMemoryUsed(ActivityManager am) {

2 Debug.MemoryInfo mInfo = am.getProcessMemoryInfo(new int[]{PID})[0];

3 return mInfo.getTotalPss();

4 }

使用者可能會根據 Home App 占用記憶體的多寡來決定是否要停止該程序,以移出更多的空間給其他比較需要資源的軟體使用。移除程序的 API 為用 ActivityManager 類別中的 killBackgroundProcesses 方法,使用它需要跟系統註冊 KILL_BACKGROUND_PROCESSES 權限,使用方法如下,只消 package 名稱:

view sourceprint?

1 mAM.killBackgroundProcesses(pkgName);

停止某 Home App 之後,Home Selector 需要更新畫面上的 ListView,這只要通知 ListView 的資料源更新即可,Android 有提供了 notifyDataSetChanged 這個方便的函式。不過要註意的是這個函式只是去通知更新,也就是說 Adapter 的 getView 會重新執行壹遍,如果資料綁定處理不是寫在 getView 中就不會發生資料更新,而需要在更新通知前先處理資料綁定。

view sourceprint?

1 public void refresh() {

2 hAdapter.notifyDataSetChanged();

3 }

剩下的移除安裝和檢視應用程式細節功能也很簡單,都是透過 Intent 去啟動系統的 Activity,特別要註意的是設定 Intent 的 flag 和目標 URI,程式碼如下:

view sourceprint?01 public void startHomeInfo(Context cont, String pkgName) {

02 Uri homeURI = getHomeUri(pkgName);

03 Intent i = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, homeURI);

04 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

05 cont.startActivity(i);

06 }

07

08 public void unInstallHome(Context cont, String pkgName) {

09 Uri homeURI = getHomeUri(pkgName);

10 Intent i = new Intent(Intent.ACTION_DELETE, homeURI);

11 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

12 cont.startActivity(i);

}

  • 上一篇:mysql5.7解壓包怎麽安裝
  • 下一篇:大部分的詩都是遊戲。
  • copyright 2024編程學習大全網