最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程。 Launcher其实是贯彻于手机的整个系统的,时时刻刻都在运行,要是Launcher不运行了,手机就得黑屏了。Launcher的 LauncherMode=singletask,所以说不管Launcher启动了哪个应用,总有个Launcher的实例在堆栈中,并且位于栈底。点 击Home键进入到Launcher,上篇中有详细的介绍。大致思路其实就是启动launcher的时候,新启动一个task。大致先说这么多,先看截图:
图(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中做相应的配置,具体参照我前面的博客:
中有详细的介绍:
i f (!convertDatabase(db)) {
// Populate favorites table with initial favorites
loadFavorites(db, R.xml.default_workspace);
}
Step 1:开机后先启动LauncherApplication.java这个类的onCreate()方法,下面看代码:
Step 2:在LauncherApplication.java中onTerminate()的方法,解除监听的绑定;
- @Override
- public void onTerminate() {
- super.onTerminate();
-
- unregisterReceiver(mModel);
-
- ContentResolver resolver = getContentResolver();
- resolver.unregisterContentObserver(mFavoritesObserver);
- }
Step 3:Step1中的数据库mFavoritesObserver监听内部类如下:
- private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- mModel.startLoader(LauncherApplication.this, false);
- }
- };
说明:mModel.startLoader(。。,。。)是开启一个线程,设置线程的优先级NORM_PRIORITY,开始load桌面图标对应的数据库,这个过程是和Launcher.onCreate()同时进行的;
Step 4: 接着我们来看看mModel.startLoader(LauncherApplication.this, false)的方法:
- public void startLoader(Context context, boolean isLaunching) {
- synchronized (mLock) {
- if (DEBUG_LOADERS) {
- Log.d(TAG, "startLoader isLaunching=" + isLaunching);
- }
-
-
- if (mCallbacks != null && mCallbacks.get() != null) {
-
-
- isLaunching = isLaunching || stopLoaderLocked();
- mLoaderTask = new LoaderTask(context, isLaunching);
- sWorkerThread.setPriority(Thread.NORM_PRIORITY);
- sWorker.post(mLoaderTask);
- }
- }
- }
Step 5:接着我们来看看LoaderTask.java的run()方法:
- public void run() {
-
-
-
- final Callbacks cbk = mCallbacks.get();
- final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
-
- keep_running: {
-
-
- synchronized (mLock) {
- if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
- (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
- android.os.Process.setThreadPriority(mIsLaunching
- ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
- }
- if (loadWorkspaceFirst) {
- if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
- loadAndBindWorkspace();
- } else {
- if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
- loadAndBindAllApps();
- }
-
- if (mStopped) {
- break keep_running;
- }
-
-
-
- synchronized (mLock) {
- if (mIsLaunching) {
- if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
- android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- }
- }
- waitForIdle();
-
-
- if (loadWorkspaceFirst) {
- if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
- loadAndBindAllApps();
- } else {
- if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
- loadAndBindWorkspace();
- }
-
-
- synchronized (mLock) {
- android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- }
- }
-
-
-
- if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
- for (Object key : sDbIconCache.keySet()) {
- updateSavedIcon(mContext, (ShortcutInfo) key, sDbIconCache.get(key));
- }
- sDbIconCache.clear();
-
-
-
- mContext = null;
-
- synchronized (mLock) {
-
- if (mLoaderTask == this) {
- mLoaderTask = null;
- }
- }
- }
-
- public void stopLocked() {
- synchronized (LoaderTask.this) {
- mStopped = true;
- this.notify();
- }
- }
加载桌面图标对应的数据库的值,这些值能把这些图标显示在屏幕上。
Step 6:LauncherApplication.onCreate()方法启动完成后,接着开始调用Launcher.java的onCreate()方法。代码如下:
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- LauncherApplication app = ((LauncherApplication)getApplication());
-
-
- mModel = app.setLauncher(this);
-
- mIconCache = app.getIconCache();
-
- mDragController = new DragController(this);
-
- mInflater = getLayoutInflater();
-
-
- mAppWidgetManager = AppWidgetManager.getInstance(this);
-
- mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
-
- mAppWidgetHost.startListening();
-
- if (PROFILE_STARTUP) {
- android.os.Debug.startMethodTracing(
- Environment.getExternalStorageDirectory() + "/launcher");
- }
-
-
- checkForLocaleChange();
-
- setContentView(R.layout.launcher);
-
- setupViews();
-
-
-
-
- showFirstRunWorkspaceCling();
-
-
- registerContentObservers();
-
- lockAllApps();
-
- mSavedState = savedInstanceState;
- restoreState(mSavedState);
-
-
- if (mAppsCustomizeContent != null) {
- mAppsCustomizeContent.onPackagesUpdated();
- }
-
- if (PROFILE_STARTUP) {
- android.os.Debug.stopMethodTracing();
- }
-
- if (!mRestoring) {
- mModel.startLoader(this, true);
- }
-
- if (!mModel.isAllAppsLoaded()) {
- ViewGroup appsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent();
- mInflater.inflate(R.layout.apps_customize_progressbar, appsCustomizeContentParent);
- }
-
-
- mDefaultKeySsb = new SpannableStringBuilder();
- Selection.setSelection(mDefaultKeySsb, 0);
-
-
- IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- registerReceiver(mCloseSystemDialogsReceiver, filter);
-
- boolean searchVisible = false;
- boolean voiceVisible = false;
-
-
- int coi = getCurrentOrientationIndexForGlobalIcons();
- if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null ||
- sAppMarketIcon[coi] == null) {
- updateAppMarketIcon();
- searchVisible = updateGlobalSearchIcon();
- voiceVisible = updateVoiceSearchIcon(searchVisible);
- }
- if (sGlobalSearchIcon[coi] != null) {
- updateGlobalSearchIcon(sGlobalSearchIcon[coi]);
- searchVisible = true;
- }
- if (sVoiceSearchIcon[coi] != null) {
- updateVoiceSearchIcon(sVoiceSearchIcon[coi]);
- voiceVisible = true;
- }
- if (sAppMarketIcon[coi] != null) {
- updateAppMarketIcon(sAppMarketIcon[coi]);
- }
- mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
-
-
- if (LauncherApplication.isScreenLarge() || Build.TYPE.contentEquals("eng")) {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
- }
Step 7:其中LauncherModel这个类中有个回调接口,具体定义如下:
对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()我研究后再更新。
欢迎各界同僚留言指正错误和拍砖!欢迎留言!