现在的位置: 首页 > 综合 > 正文

Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】

2013年11月03日 ⁄ 综合 ⁄ 共 18944字 ⁄ 字号 评论关闭

最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程。Launcher其实是贯彻于手机的整个系统的,时时刻刻都在运行,要是Launcher不运行了,手机就得黑屏了。Launcher的LauncherMode=singletask,所以说不管Launcher启动了哪个应用,总有个Launcher的实例在堆栈中,并且位于栈底。点击Home键进入到Launcher,上篇Android的全局键(home键/长按耳机键)详解【android源码解析八】
中有详细的介绍。大致思路其实就是启动launcher的时候,新启动一个task。大致先说这么多,先看截图:

大明原创,转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/7585649

图(1)

上图是4.0的Launcher界面,下面我们分步来解析一下Launcher的启动过程。

Step 0:首先要给大家介绍一下Launcher的数据库,这个数据库中存放着待机界面的图标,主屏底部的应用程序图标和桌面folder中各应用程序的图标,ICS4.0的folder中只能放应用程序的快捷方式,shortcut不能放到这个folder中,先看截图:

图(2)

说说各字段的含义:

title:表示桌面应用程序的名字,有的title为空,表示是widget的快捷方式;

intent:表示启动这个图标的intent放到数据库中,当click的时候就会调用这个字段,启动相应的应用程序;

container:表示应用程序的容器,folder的容器为整数,-100:表示在桌面的程序,-101:表示是主屏底部的程序;

screen:表示在第几个屏,folder的screen都是0, container=-101的为0,1,3,4;2为allapp的按钮;

cellX:表示在屏幕X轴的位置,(0,1,2,3),左上角为0点,往右依次增加;

cellY:表示在屏幕Y轴的位置,(0,1,2,3),左上角为0点,往下依次增加;

spallX:表示占X轴几个格;

spallY:表示占Y轴几个格;

itemType:应用程序用0表示,shortcut用1表示,folder用2表示,widget用4表示;

appWidgetId:-1表示不是widget,数字大于0表示才是widget;

isShortCut:值为0表示不是应用程序的ShortCut,值为1表示是应用程序的ShortCut;

iconType:值为0表示图标的名字被定义为包名的资源id,值为1表示图标用bitmap保存;

icon:表示应用程序的图标,二进制的;显示为一张图片;

说明:folder中的应用快捷方式绑定folder---->是用container的值绑定folder的id的;

详细的讲解请参考LauncherSettings.java这个类,有数据库字段的详细讲解;

手机是在第一次烧机完成后,数据库的值还没有,这时候launcher解析default_workspace.xml把这个值存到数据库中;所以说想定制什么样的开机桌面就在default_workspace.xml中做相应的配置,具体参照我前面的博客:

Android中源码Launcher主屏幕程序排列详解【安卓Launcher进化一】中有详细的介绍:

i f (!convertDatabase(db)) {
// Populate favorites table with initial favorites
loadFavorites(db, R.xml.default_workspace);
}

Step 1:开机后先启动LauncherApplication.java这个类的onCreate()方法,下面看代码:

  1. @Override
  2. public void onCreate() {
  3. super.onCreate();
  4. // set sIsScreenXLarge and sScreenDensity *before* creating icon cache
  5. // 在创建图标缓存之前先设置sIsScreenXLarge和屏幕设备的分辨率
  6. final int screenSize = getResources().getConfiguration().screenLayout &
  7. Configuration.SCREENLAYOUT_SIZE_MASK;
  8. sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE ||
  9. screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE;
  10. sScreenDensity = getResources().getDisplayMetrics().density;
  11. // 实例化图标缓存区的对象
  12. mIconCache = new IconCache(this);
  13. // 实例化一个LauncherModel对象,这个类是保存Launcher的内存启动状态,更新Launcher的数据库的作用
  14. mModel = new LauncherModel(this, mIconCache);
  15. // Register intent receivers
  16. // 注册监听,应用package增加,删除,改变的监听。
  17. IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
  18. filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
  19. filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
  20. filter.addDataScheme("package");
  21. registerReceiver(mModel, filter);
  22. filter = new IntentFilter();
  23. // 注册application是否可用,语言改变,方向改变的监听。4.0支持横竖屏
  24. filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
  25. filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
  26. filter.addAction(Intent.ACTION_LOCALE_CHANGED);
  27. filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
  28. registerReceiver(mModel, filter);
  29. filter = new IntentFilter();
  30. filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
  31. registerReceiver(mModel, filter);
  32. filter = new IntentFilter();
  33. filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
  34. registerReceiver(mModel, filter);
  35. // Register for changes to the favorites
  36. // 注册favorites应用程序数据库改变的监听
  37. ContentResolver resolver = getContentResolver();
  38. resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI,
    true,
  39. mFavoritesObserver);
  40. }

Step 2:在LauncherApplication.java中onTerminate()的方法,解除监听的绑定;

  1. /**
  2. * There's no guarantee that this function is ever called.
  3. */
  4. @Override
  5. public void onTerminate() {
  6. super.onTerminate();
  7. unregisterReceiver(mModel);
  8. ContentResolver resolver = getContentResolver();
  9. resolver.unregisterContentObserver(mFavoritesObserver);
  10. }

Step 3:Step1中的数据库mFavoritesObserver监听内部类如下:

  1. /**
  2. * Receives notifications whenever the user favorites have changed.
  3. */
  4. private final ContentObserver mFavoritesObserver =
    new ContentObserver(new Handler()) {
  5. @Override
  6. public void onChange(boolean selfChange) {
  7. mModel.startLoader(LauncherApplication.this,
    false);
  8. }
  9. };

说明:mModel.startLoader(。。,。。)是开启一个线程,设置线程的优先级NORM_PRIORITY,开始load桌面图标对应的数据库,这个过程是和Launcher.onCreate()同时进行的;

Step 4: 接着我们来看看mModel.startLoader(LauncherApplication.this, false)的方法:

  1. public void startLoader(Context context,
    boolean isLaunching) {
  2. synchronized (mLock) {
  3. if (DEBUG_LOADERS) {
  4. Log.d(TAG, "startLoader isLaunching=" + isLaunching);
  5. }
  6. // Don't bother to start the thread if we know it's not going to do anything
  7. if (mCallbacks != null && mCallbacks.get() !=
    null) {
  8. // If there is already one running, tell it to stop.
  9. // also, don't downgrade isLaunching if we're already running
  10. isLaunching = isLaunching || stopLoaderLocked();
  11. mLoaderTask = new LoaderTask(context, isLaunching);
  12. sWorkerThread.setPriority(Thread.NORM_PRIORITY);
  13. sWorker.post(mLoaderTask);
  14. }
  15. }
  16. }

Step 5:接着我们来看看LoaderTask.java的run()方法:

  1. public void run() {
  2. // Optimize for end-user experience: if the Launcher is up and // running with the
  3. // All Apps interface in the foreground, load All Apps first. Otherwise, load the
  4. // workspace first (default).
  5. final Callbacks cbk = mCallbacks.get();
  6. final boolean loadWorkspaceFirst = cbk !=
    null ? (!cbk.isAllAppsVisible()) :
    true;
  7. keep_running: {
  8. // Elevate priority when Home launches for the first time to avoid
  9. // starving at boot time. Staring at a blank home is not cool.
  10. synchronized (mLock) {
  11. if (DEBUG_LOADERS) Log.d(TAG,
    "Setting thread priority to " +
  12. (mIsLaunching ? "DEFAULT" :
    "BACKGROUND"));
  13. android.os.Process.setThreadPriority(mIsLaunching
  14. ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
  15. }
  16. if (loadWorkspaceFirst) {
  17. if (DEBUG_LOADERS) Log.d(TAG,
    "step 1: loading workspace");
  18. loadAndBindWorkspace();
  19. } else {
  20. if (DEBUG_LOADERS) Log.d(TAG,
    "step 1: special: loading all apps");
  21. loadAndBindAllApps();
  22. }
  23. if (mStopped) {
  24. break keep_running;
  25. }
  26. // Whew! Hard work done. Slow us down, and wait until the UI thread has
  27. // settled down.
  28. synchronized (mLock) {
  29. if (mIsLaunching) {
  30. if (DEBUG_LOADERS) Log.d(TAG,
    "Setting thread priority to BACKGROUND");
  31. android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  32. }
  33. }
  34. waitForIdle();
  35. // second step
  36. if (loadWorkspaceFirst) {
  37. if (DEBUG_LOADERS) Log.d(TAG,
    "step 2: loading all apps");
  38. loadAndBindAllApps();
  39. } else {
  40. if (DEBUG_LOADERS) Log.d(TAG,
    "step 2: special: loading workspace");
  41. loadAndBindWorkspace();
  42. }
  43. // Restore the default thread priority after we are done loading items
  44. synchronized (mLock) {
  45. android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
  46. }
  47. }
  48. // Update the saved icons if necessary
  49. if (DEBUG_LOADERS) Log.d(TAG,
    "Comparing loaded icons to database icons");
  50. for (Object key : sDbIconCache.keySet()) {
  51. updateSavedIcon(mContext, (ShortcutInfo) key, sDbIconCache.get(key));
  52. }
  53. sDbIconCache.clear();
  54. // Clear out this reference, otherwise we end up holding it until all of the
  55. // callback runnables are done.
  56. mContext = null;
  57. synchronized (mLock) {
  58. // If we are still the last one to be scheduled, remove ourselves.
  59. if (mLoaderTask == this) {
  60. mLoaderTask = null;
  61. }
  62. }
  63. }
  64. public void stopLocked() {
  65. synchronized (LoaderTask.this) {
  66. mStopped = true;
  67. this.notify();
  68. }
  69. }

加载桌面图标对应的数据库的值,这些值能把这些图标显示在屏幕上

Step 6:LauncherApplication.onCreate()方法启动完成后,接着开始调用Launcher.java的onCreate()方法。代码如下:

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. // 得到LauncherApplication的对象app
  5. LauncherApplication app = ((LauncherApplication)getApplication());
  6. // 得到LauncherModel对象mModel,设置一个mCallbacks = new WeakReference<Callbacks>(callbacks)的
  7. // 回调callbacks
  8. mModel = app.setLauncher(this);
  9. // 得到图标缓存的对象mIconCache
  10. mIconCache = app.getIconCache();
  11. // 得到拖拽控制类DragController的对象
  12. mDragController = new DragController(this);
  13. // 得到一个LayoutInflater布局的对象
  14. mInflater = getLayoutInflater();
  15. // 得到一个AppWidgetManager的对象
  16. mAppWidgetManager = AppWidgetManager.getInstance(this);
  17. // 得到LauncherAppWidgetHost的一个对象
  18. mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
  19. // Start receiving onAppWidgetChanged calls for your AppWidgets.
  20. mAppWidgetHost.startListening();
  21. if (PROFILE_STARTUP) {
  22. android.os.Debug.startMethodTracing(
  23. Environment.getExternalStorageDirectory() + "/launcher");
  24. }
  25. // 检查Locale的语言级别,mcc, mnc的改变
  26. checkForLocaleChange();
  27. // 加载Launcher.xml布局文件
  28. setContentView(R.layout.launcher);
  29. // Launcher的布局的初始化
  30. setupViews();
  31. // 第一次启动Android的展示设置向导,
  32. // 这个SharedPreferences中存在
  33. // <boolean name="cling.workspace.dismissed" value="true" />
  34. // 如果值为true,则不显示设置向导,为false,则显示设置向导。
  35. showFirstRunWorkspaceCling();
  36. // 注册数据库观察者
  37. registerContentObservers();
  38. lockAllApps();
  39. mSavedState = savedInstanceState;
  40. restoreState(mSavedState);
  41. // Update customization drawer _after_ restoring the states
  42. if (mAppsCustomizeContent !=
    null) {
  43. mAppsCustomizeContent.onPackagesUpdated();
  44. }
  45. if (PROFILE_STARTUP) {
  46. android.os.Debug.stopMethodTracing();
  47. }
  48. if (!mRestoring) {
  49. mModel.startLoader(this,
    true);
  50. }
  51. if (!mModel.isAllAppsLoaded()) {
  52. ViewGroup appsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent();
  53. mInflater.inflate(R.layout.apps_customize_progressbar, appsCustomizeContentParent);
  54. }
  55. // For handling default keys
  56. mDefaultKeySsb = new SpannableStringBuilder();
  57. Selection.setSelection(mDefaultKeySsb, 0);
  58. // 注册系统对话框消失的监听
  59. IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
  60. registerReceiver(mCloseSystemDialogsReceiver, filter);
  61. boolean searchVisible =
    false;
  62. boolean voiceVisible =
    false;
  63. // If we have a saved version of these external icons, we load them up immediately
  64. // 如果我们已经保存了外部图标的版本,我们立即加载它们
  65. int coi = getCurrentOrientationIndexForGlobalIcons();
  66. if (sGlobalSearchIcon[coi] ==
    null || sVoiceSearchIcon[coi] ==
    null ||
  67. sAppMarketIcon[coi] == null) {
  68. updateAppMarketIcon();
  69. searchVisible = updateGlobalSearchIcon();
  70. voiceVisible = updateVoiceSearchIcon(searchVisible);
  71. }
  72. if (sGlobalSearchIcon[coi] !=
    null) {
  73. updateGlobalSearchIcon(sGlobalSearchIcon[coi]);
  74. searchVisible = true;
  75. }
  76. if (sVoiceSearchIcon[coi] !=
    null) {
  77. updateVoiceSearchIcon(sVoiceSearchIcon[coi]);
  78. voiceVisible = true;
  79. }
  80. if (sAppMarketIcon[coi] !=
    null) {
  81. updateAppMarketIcon(sAppMarketIcon[coi]);
  82. }
  83. mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
  84. // On large interfaces, we want the screen to auto-rotate based on the current orientation
  85. if (LauncherApplication.isScreenLarge() || Build.TYPE.contentEquals("eng")) {
  86. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
  87. }
  88. }

Step 7:其中LauncherModel这个类中有个回调接口,具体定义如下:

  1. public interface Callbacks {
  2. public boolean setLoadOnResume();
  3. public int getCurrentWorkspaceScreen();
  4. public void startBinding();
  5. public void bindItems(ArrayList<ItemInfo> shortcuts,
    int start, int end);
  6. public void bindFolders(HashMap<Long,FolderInfo> folders);
  7. public void finishBindingItems();
  8. public void bindAppWidget(LauncherAppWidgetInfo info);
  9. public void bindAllApplications(ArrayList<ApplicationInfo> apps);
  10. public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
  11. public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
  12. public void bindAppsRemoved(ArrayList<ApplicationInfo> apps,
    boolean permanent);
  13. public void bindPackagesUpdated();
  14. public boolean isAllAppsVisible();
  15. public void bindSearchablesChanged();
  16. }

对LauncherModel进行初始化的时候mModel = app.setLauncher(this);---->mModel.initialize(launcher);----->

public void initialize(Callbacks callbacks) {
synchronized (mLock) {
mCallbacks = new WeakReference<Callbacks>(callbacks);
}
}

这个callbacks就是定义的接口回调,具体实现是在Launcher.java中定义的,启动Launcher的过程中,这些实现是异步来实现的。还有Launcher.java的onResume()方法没有讲解,到这儿基本上Android的Launcher已经启动起来了,这个onResume()我研究后再更新。

欢迎各界同僚留言指正错误和拍砖!欢迎留言!

抱歉!评论已关闭.