1. 程式人生 > >Android定製Home,以及launcher的自定義

Android定製Home,以及launcher的自定義

1、想將home screen換成自己寫的activity,該如何實現?

在你要設定為home screen的那個activity的androidManifest.xml中的<intent-filter>標籤中加上這幾句 話<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />執行後,重啟模擬器會彈出一個選擇進入哪個介面的對話方塊

2、怎樣將系統預設的home screen刪除?

重新編譯launcher原始碼,去掉配置檔案中的home屬性和HOME屬性。。
在自己的activity中加入這兩個屬性,然後重新燒系統。 

以下是定製Android的完整思路和步驟:

如果你要定製一個Android系統,你想用你自己的Launcher(Home)作主介面來替換Android自己的Home,而且不希望使用者安裝的Launcher來替換掉你的Launcher.
我們可以通過修改Framework來實現這樣的功能。

這裡以Android2.1的原始碼為例來實際說明。

1)首先了解一下Android的啟動過程。
Android系統的啟動先從Zygote開始啟動,然後……(中間的過程就不說了)…..一直到了SystemServer(framework)這個地方,看到這段程式碼:

  1. /**
  2. * This method is called from Zygote to initialize the system. This will cause the native
  3. * services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back
  4. * up into init2() to start the Android services.
  5. */
  6. nativepublicstaticvoid init1(String[] args);
  7. publicstaticvoid main(String[] args) {
  8. if (SamplingProfilerIntegration.isEnabled()) {
  9. SamplingProfilerIntegration.start();
  10. timer = new Timer();
  11. timer.schedule(new TimerTask() {
  12. @Override
  13. publicvoid run() {
  14. SamplingProfilerIntegration.writeSnapshot(“system_server”);
  15. }
  16. }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
  17. }
  18. // The system server has to run all of the time, so it needs to be
  19. // as efficient as possible with its memory usage.
  20. VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
  21. System.loadLibrary(“android_servers”);
  22. init1(args);
  23. }
  24. publicstaticfinalvoid init2() {
  25. Log.i(TAG, “Entered the Android system server!”);
  26. Thread thr = new ServerThread();
  27. thr.setName(“android.server.ServerThread”);
  28. thr.start();
  29. }
  30. }

從SystemServer的main函式開始啟動各種服務。
首先啟動init1,然後啟動init2.
從上面的註釋可以看到:init1這個方法時被Zygote呼叫來初始化系統的,init1會啟動native的服務如SurfaceFlinger,AudioFlinger等等,這些工作做完以後會回撥init2來啟動Android的service。

這裡我們主要來關注init2的過程。
init2中啟動ServerThread執行緒,
ServerThread中啟動了一系列的服務,比如這些:

  1. ActivityManagerService
  2. EntropyService
  3. PowerManagerService
  4. TelephonyRegistry
  5. PackageManagerService
  6. AccountManagerService
  7. BatteryService
  8. HardwareService
  9. Watchdog
  10. SensorService
  11. BluetoothService
  12. StatusBarService
  13. ClipboardService
  14. InputMethodManagerService
  15. NetStatService
  16. ConnectivityService
  17. AccessibilityManagerService
  18. NotificationManagerService
  19. MountService
  20. DeviceStorageMonitorService
  21. LocationManagerService
  22. SearchManagerService
  23. FallbackCheckinService
  24. WallpaperManagerService
  25. AudioService
  26. BackupManagerService
  27. AppWidgetService

這些大大小小的服務起來以後,開始
((ActivityManagerService)ActivityManagerNative.getDefault()).systemReady()
在systemReady後開始開始啟動Launcher。

在尋找Launcher的時候是根據HOME的filter(在Manifest中定義的<categoryAndroid:name=”android.intent.category.HOME” />)來過濾。
然後根據filter出來的HOME來啟動,如果只有一個HOME,則啟動這個HOME,如果使用者自己裝了HOME,那就會彈出來一個列表供使用者選擇。

我們現在希望從這裡彈出我們自己定製的Launcher,同時也不希望彈出選擇HOME的介面,我們不希望使用者修改我們的home,比如我們的home上放了好多廣告,以及強制安裝的程式,不希望使用者把它幹掉。

我們可以通過這樣來實現:

2) 定義一個私有的filter選項,然後用這個選項來過濾HOME.
一般情況下我們使用Manifest中定義的<category android:name=”android.intent.category.HOME”來過濾的,我們現在增加一個私有的HOME_FIRST過濾。

在Intent.java(frameworks/base/core/java/android/content/Intent.java)中新增兩行程式碼

  1. //新增CATEGORY_HOME_FIRST
  2. @SdkConstant(SdkConstantType.INTENT_CATEGORY)
  3. publicstaticfinal String CATEGORY_HOME_FIRST = “android.intent.category.HOME_FIRST”;

3)修改和CATEGORY_HOME相關的所有的地方,都改成HOME_FIRST,主要是framework中的這幾個地方:

  1. frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中
  2. //intent.addCategory(Intent.CATEGORY_HOME);
  3. 改成intent.addCategory(Intent.CATEGORY_HOME_FIRST);
  4. //if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
  5. 改成if (r.intent.hasCategory(Intent.CATEGORY_HOME_FIRST)) { //Intent.CATEGORY_HOME -> Intent.CATEGORY_HOME_FIRST
  6. frameworks/base/services/java/com/android/server/am/HistoryRecorder.java中
  7. // _intent.hasCategory(Intent.CATEGORY_HOME) &&
  8. 改成 _intent.hasCategory(Intent.CATEGORY_HOME_FIRST) && //Intent.CATEGORY_HOME->Intent.CATEGORY_HOME_FIRST
  9. frameworks/policies/base/mid/com/android/internal/policy/impl/MidWindowManager.java中
  10. //mHomeIntent.addCategory(Intent.CATEGORY_HOME);
  11. 改成 mHomeIntent.addCategory(Intent.CATEGORY_HOME_FIRST);
  12. frameworks/policies/base/mid/com/android/internal/policy/impl/RecentApplicationsDialog.java中
  13. //new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),0);
  14. 改成 new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME_FIRST),0);
  15. frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java中
  16. //mHomeIntent.addCategory(Intent.CATEGORY_HOME);
  17. 改成 mHomeIntent.addCategory(Intent.CATEGORY_HOME_FIRST);
  18. frameworks/policies/base/phone/com/android/internal/policy/impl/RecentApplicationsDialog.java中
  19. //ResolveInfo homeInfo = pm.resolveActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),0);
  20. 改成 ResolveInfo homeInfo = pm.resolveActivity(newIntent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME_FIRST),0);

4) 寫一個自己的Launcher.
可以參考Android sample中的Launcher,或者android原始碼中的 /packages/apps/Launcher 來寫。
在Launcher中標記其是不是Launcher的最關鍵的程式碼時Manifest中的filter:android:name=”android.intent.category.HOME”
現在我們定義了自己的filter,那麼,我們在我們自己寫的Launcher中將Manifest改為:

  1. <application android:process=”android.process.acore3″ android:icon=”@drawable/icon” android:label=”@string/app_name”>
  2. <activity android:name=”.FirstAppActivity”
  3. android:label=”@string/app_name”>
  4. <intent-filter>
  5. <action android:name=”android.intent.action.MAIN” />
  6. <category android:name=”android.intent.category.HOME_FIRST” />
  7. <category android:name=”android.intent.category.DEFAULT” />
  8. <category android:name=”android.intent.category.MONKEY” />
  9. </intent-filter>
  10. </activity>
  11. </application>

然後將編譯好的apk放到/out/target/product/generic/system/app目錄下。

5)將Android自帶的Launcher刪除掉,包括原始碼(packages/apps/Launcher)和apk(/out/target/product/generic/system/app/Launcher.apk)。

6)
做完這些工作,就可以重新編譯Android了,我們可以編譯修改過的幾個相關的包。
如果之前編譯過了Android原始碼,可以用mmm命令來編譯部分的改動。
這裡需要這樣編譯:

  1. $ . build/envsetup.sh
  2. $ mmm frameworks/base
  3. $ mmm frameworks/base/services/java
  4. $ mmm frameworks/policies/base/mid
  5. $ mmm frameworks/policies/base/phone

7)
編譯完成後重新生成img檔案。

  1. $ make snod

現在可以啟動Android模擬器來看效果了。
首先設定環境變數:
$ export ANDROID_PRODUCT_OUT= ./out/target/product/generic
然後切換到
$ cd ./out/host/linux-x86/bin
執行
$ ./emulator

這樣我們啟動的模擬器裡面用的image就是我們剛才編譯好的自己定製的東西了。
從模擬器上可以看到啟動的Launcher是我們自己的Launcher,不會出現預設的Launcher了,也不會出現選擇介面。

9)我們再驗證一下,如果使用者裝上了一個其他的Launcher(Home)會怎麼樣。
從網上找一個一般的Launcher或者自己寫一個一般的Launcher裝上去,重新啟動,不會出現選擇介面。
按HOME鍵也不會出來兩個HOME來選擇。

這樣我們就牢牢控制了使用者的桌面。
只有我們自己定製的HOME才能裝上。 這對於定製Android裝置的廠商很有用處。