我在华勤的那些日子-----------Android的laucher3源码剖析
Posted startFromWeb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我在华勤的那些日子-----------Android的laucher3源码剖析相关的知识,希望对你有一定的参考价值。
auncher其实是贯彻于手机的整个系统的,时时刻刻都在运行,要是Launcher不运行了,手机就得黑屏了。Launcher的 LauncherMode=singletask,所以说不管Launcher启动了哪个应用,总有个Launcher的实例在堆栈中,并且位于栈底。点 击Home键进入到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()方法,下面看代码:
@Override
public void onCreate()
super.onCreate();
// set sIsScreenXLarge and sScreenDensity *before* creating icon cache
// 在创建图标缓存之前先设置sIsScreenXLarge和屏幕设备的分辨率
final int screenSize = getResources().getConfiguration().screenLayout &
Configuration.SCREENLAYOUT_SIZE_MASK;
sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE ||
screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE;
sScreenDensity = getResources().getDisplayMetrics().density;
// 实例化图标缓存区的对象
mIconCache = new IconCache(this);
// 实例化一个LauncherModel对象,这个类是保存Launcher的内存启动状态,更新Launcher的数据库的作用
mModel = new LauncherModel(this, mIconCache);
// Register intent receivers
// 注册监听,应用package增加,删除,改变的监听。
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
registerReceiver(mModel, filter);
filter = new IntentFilter();
// 注册application是否可用,语言改变,方向改变的监听。4.0支持横竖屏
filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
registerReceiver(mModel, filter);
filter = new IntentFilter();
filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
registerReceiver(mModel, filter);
filter = new IntentFilter();
filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
registerReceiver(mModel, filter);
// Register for changes to the favorites
// 注册favorites应用程序数据库改变的监听
ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
mFavoritesObserver);
Step 2:在LauncherApplication.java中onTerminate()的方法,解除监听的绑定;
/**
* There's no guarantee that this function is ever called.
*/
@Override
public void onTerminate()
super.onTerminate();
unregisterReceiver(mModel);
ContentResolver resolver = getContentResolver();
resolver.unregisterContentObserver(mFavoritesObserver);
Step 3:Step1中的数据库mFavoritesObserver监听内部类如下:
/**
* Receives notifications whenever the user favorites have changed.
*/
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);
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null)
// If there is already one running, tell it to stop.
// also, don't downgrade isLaunching if we're already running
isLaunching = isLaunching || stopLoaderLocked();
mLoaderTask = new LoaderTask(context, isLaunching);
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
Step 5:接着我们来看看LoaderTask.java的run()方法:
public void run()
// Optimize for end-user experience: if the Launcher is up and // running with the
// All Apps interface in the foreground, load All Apps first. Otherwise, load the
// workspace first (default).
final Callbacks cbk = mCallbacks.get();
final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
keep_running:
// Elevate priority when Home launches for the first time to avoid
// starving at boot time. Staring at a blank home is not cool.
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;
// Whew! Hard work done. Slow us down, and wait until the UI thread has
// settled down.
synchronized (mLock)
if (mIsLaunching)
if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
waitForIdle();
// second step
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();
// Restore the default thread priority after we are done loading items
synchronized (mLock)
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
// Update the saved icons if necessary
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();
// Clear out this reference, otherwise we end up holding it until all of the
// callback runnables are done.
mContext = null;
synchronized (mLock)
// If we are still the last one to be scheduled, remove ourselves.
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 app = ((LauncherApplication)getApplication());
// 得到LauncherModel对象mModel,设置一个mCallbacks = new WeakReference<Callbacks>(callbacks)的
// 回调callbacks
mModel = app.setLauncher(this);
// 得到图标缓存的对象mIconCache
mIconCache = app.getIconCache();
// 得到拖拽控制类DragController的对象
mDragController = new DragController(this);
// 得到一个LayoutInflater布局的对象
mInflater = getLayoutInflater();
// 得到一个AppWidgetManager的对象
mAppWidgetManager = AppWidgetManager.getInstance(this);
// 得到LauncherAppWidgetHost的一个对象
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
// Start receiving onAppWidgetChanged calls for your AppWidgets.
mAppWidgetHost.startListening();
if (PROFILE_STARTUP)
android.os.Debug.startMethodTracing(
Environment.getExternalStorageDirectory() + "/launcher");
// 检查Locale的语言级别,mcc, mnc的改变
checkForLocaleChange();
// 加载Launcher.xml布局文件
setContentView(R.layout.launcher);
// Launcher的布局的初始化
setupViews();
// 第一次启动Android的展示设置向导,
// 这个SharedPreferences中存在
// <boolean name="cling.workspace.dismissed" value="true" />
// 如果值为true,则不显示设置向导,为false,则显示设置向导。
showFirstRunWorkspaceCling();
// 注册数据库观察者
registerContentObservers();
lockAllApps();
mSavedState = savedInstanceState;
restoreState(mSavedState);
// Update customization drawer _after_ restoring the states
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);
// For handling default keys
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;
// If we have a saved version of these external icons, we load them up immediately
// 如果我们已经保存了外部图标的版本,我们立即加载它们
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);
// On large interfaces, we want the screen to auto-rotate based on the current orientation
if (LauncherApplication.isScreenLarge() || Build.TYPE.contentEquals("eng"))
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
Step 7:其中LauncherModel这个类中有个回调接口,具体定义如下:
public interface Callbacks
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void startBinding();
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
public void bindFolders(HashMap<Long,FolderInfo> folders);
public void finishBindingItems();
public void bindAppWidget(LauncherAppWidgetInfo info);
public void bindAllApplications(ArrayList<ApplicationInfo> apps);
public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);
public void bindPackagesUpdated();
public boolean isAllAppsVisible();
public void bindSearchablesChanged();
对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()我研究后再更新
以上是关于我在华勤的那些日子-----------Android的laucher3源码剖析的主要内容,如果未能解决你的问题,请参考以下文章
从苏宁电器到卡巴斯基(第二部)第12篇:我在卡巴的日子 XII
从苏宁电器到卡巴斯基(第二部)第12篇:我在卡巴的日子 XII