一、Android的应用程序的入口定义在AndroidManifest.xml文件中可以找出:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher">
<original-package android:name="com.android.launcher2" />
...
<application android:name="com.android.launcher2.LauncherApplication"
android:label="@string/application_name"
android:icon="@drawable/ic_launcher_home"
android:hardwareAccelerated="@bool/config_hardwareAccelerated" --硬加速
android:largeHeap="@bool/config_largeHeap"> --运行时最小堆内存,避免内存out of memory错误的出现 >
<activity
android:name="com.android.launcher2.Launcher"
...
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
</intent-filter>
</activity>
...
</application>
</manifest>
二、
LauncherApplication----onCreate 应用入口
1)获取屏幕的显示尺寸、来判断是否是大屏幕,同时得到它的屏幕密度全局变量。
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;
2)建立应用图标缓存器;创建LauncherModel 对象,在launcher数据变更时操作数据库。
mIconCache = new IconCache(this);
mModel = new LauncherModel(this, mIconCache);
//LauncherModel主要用于加载桌面的图标、插件和文件夹,同时LaucherModel是一个广播接收器,在程序包发生改变、区域、或者配置文件发生改变时,都会发送广播给LaucherModel,LaucherModel会根据不同的广播来做相应加载操作
3)注册(Intent.ACTION_PACKAGE_ADDED Intent.ACTION_PACKAGE_REMOVED Intent.ACTION_PACKAGE_CHANGED)应用添加、删除、改变监听等;
LauncherModel提供接收器对上面事件进行监听。
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);
4)注册本地化配置变化监听,搜寻相关变化监听,外部存储上的应用变化监听。接收器同上
filter = new IntentFilter();
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);
5)注册对桌面favorites content provider 数据变化监听器,触发后执行onChang方法。
ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
mFavoritesObserver);Launcher Activity:实现了点击、长按、触屏、LauncherModel,AllAppViews接口。
protected void onCreate(Bundle savedInstanceState) {
...
mModel = app.setLauncher(this);
mIconCache = app.getIconCache();
...
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
...
//检查本地保存的配置是否需要更新
checkForLocaleChange();
setContentView(R.layout.launcher);
//对UI控件进行初始化和配置
setupViews();
//向用户展示指导的页面
showFirstRunWorkspaceCling();
registerContentObservers();
...
if (!mRestoring) {
//为Launcher加载数据
mModel.startLoader(this, true);
}
...
}
可以将Launcher.onCreate()所执行的操作大概分为七步:
1、LauncherAppliaction.setLauncher()。
2、AppWidgetHost.startListening(),对widget事件进行监听
3、checkForLocaleChange(),检查更新本地保存的配置文件
4、setupViews(),配置UI控件
5、showFirstRunWorkspaceCling(),第一次启动时显示的指导画面
6、registerContentObservers(),设置内容监听器
7、LauncherModel.startLoader(),为Launcher加载Workspace和AllApps中的内容
三、下面具体来看启动过程中到底做了些什么
1、LauncherModel setLauncher(Launcher launcher) {
mModel.initialize(launcher);
return mModel;
}/**
* Set this as the current Launcher activity object for the loader.
*/
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
mCallbacks = new WeakReference<Callbacks>(callbacks);
}
}
由于Launcher实现了Callback接口。在mModel中,将传入的Launcher对象向下转型为Callback赋值给mCallbacks变量。并在LauncherModel中获得了一个Callbacks的软引用。通过这一过程,将Launcher对象作为Callback与mModel进行绑定,当mModel后续进行操作时,Launcher可以通过回调得到结果。
2、mAppWidgetHost.startListening()
LauncherAppWidgetHost继承自AppWidgetHost,它的作用就是帮助Launcher管理AppWidget,并且能够捕获长按事件,使得应用可以正常的删除、添加AppWidget。通过调用mAppWidgetHost.startListening()方法,开启监听。
3、checkForLocaleChange()
private void checkForLocaleChange() {
if (sLocaleConfiguration == null) {
new AsyncTask<Void, Void, LocaleConfiguration>() {
@Override
protected LocaleConfiguration doInBackground(Void... unused) {
LocaleConfiguration localeConfiguration = new LocaleConfiguration();
readConfiguration(Launcher.this, localeConfiguration);
return localeConfiguration;
} @Override
protected void onPostExecute(LocaleConfiguration result) {
sLocaleConfiguration = result;
checkForLocaleChange(); // recursive, but now with a locale configuration
}
}.execute();
return;
} final Configuration configuration = getResources().getConfiguration();
final String previousLocale = sLocaleConfiguration.locale;
final String locale = configuration.locale.toString(); final int previousMcc = sLocaleConfiguration.mcc;
final int mcc = configuration.mcc; final int previousMnc = sLocaleConfiguration.mnc;
final int mnc = configuration.mnc; boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
if (localeChanged) {
sLocaleConfiguration.locale = locale;
sLocaleConfiguration.mcc = mcc;
sLocaleConfiguration.mnc = mnc; mIconCache.flush();
final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
new Thread("WriteLocaleConfiguration") {
@Override
public void run() {
writeConfiguration(Launcher.this, localeConfiguration);
}
}.start();
}
}
在这个方法中,先是检查了本地文件的配置与当前设备的配置是否一致,如果不一致,则更新配置,并且清空IconCache,因为配置的改变可能会改变语言环境,所以需要清空IconCache中的内容重新加载。
4、setupViews() 这个方法中简单的对所有的UI控件进行加载和配置
I、mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
mQsbDivider = (ImageView) findViewById(R.id.qsb_divider);
mDockDivider = (ImageView) findViewById(R.id.dock_divider); // Setup the drag layer
mDragLayer.setup(this, dragController);DragLayer继承自FrameLayout,是整个Launcher的根容器。当快捷图标或者AppWidget被拖拽时,事件的处理就在DragLayer进行操作的。
public void setup(Launcher launcher, DragController controller) {
mLauncher = launcher;
mDragController = controller;
}
只是简单的做了赋值操作,使DragLayer持有Launcher和DragController对象的引用。DragController可以帮助其实现拖拽操作。
II、Hotseat也是FrameLayout的直接子类,代表主屏幕下方的dock栏,可以放置4个快捷图标和一个进入AllApps的按钮。
mHotseat = (Hotseat) findViewById(R.id.hotseat);
if (mHotseat != null) {
mHotseat.setup(this);
}public void setup(Launcher launcher) {
mLauncher = launcher;
setOnKeyListener(new HotseatIconKeyEventListener());
}mHotseat.setup()方法调用之后,Hotseat持有Launcher对象的引用,并且用HotseatIconKeyEvenListener对自身的按键进行监听,进入HotseatIconKeyEvenListener
/**
* A keyboard listener we set on all the hotseat buttons.
*/
class HotseatIconKeyEventListener implements View.OnKeyListener {
public boolean onKey(View v, int keyCode, KeyEvent event) {
final Configuration configuration = v.getResources().getConfiguration();
return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event, configuration.orientation);
}
}调用方法handleHotseatButtonKeyEvent()来处理相应的事件
/**
* Handles key events in the workspace hotseat (bottom of the screen).
*/
static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e, int orientation) {
final ViewGroup parent = (ViewGroup) v.getParent();
final ViewGroup launcher = (ViewGroup) parent.getParent();
final Workspace workspace = (Workspace) launcher.findViewById(R.id.workspace);
final int buttonIndex = parent.indexOfChild(v);
final int buttonCount = parent.getChildCount();
final int pageIndex = workspace.getCurrentPage(); // NOTE: currently we don't special case for the phone UI in different
// orientations, even though the hotseat is on the side in landscape mode. This
// is to ensure that accessibility consistency is maintained across rotations. final int action = e.getAction();
final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
boolean wasHandled = false;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (handleKeyEvent) {
// Select the previous button, otherwise snap to the previous page
if (buttonIndex > 0) {
parent.getChildAt(buttonIndex - 1).requestFocus();
} else {
workspace.snapToPage(pageIndex - 1);
}
}
wasHandled = true;
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (handleKeyEvent) {
// Select the next button, otherwise snap to the next page
if (buttonIndex < (buttonCount - 1)) {
parent.getChildAt(buttonIndex + 1).requestFocus();
} else {
workspace.snapToPage(pageIndex + 1);
}
}
wasHandled = true;
break;
case KeyEvent.KEYCODE_DPAD_UP:
if (handleKeyEvent) {
// Select the first bubble text view in the current page of the workspace
final CellLayout layout = (CellLayout) workspace.getChildAt(pageIndex);
final CellLayoutChildren children = layout.getChildrenLayout();
final View newIcon = getIconInDirection(layout, children, -1, 1);
if (newIcon != null) {
newIcon.requestFocus();
} else {
workspace.requestFocus();
}
}
wasHandled = true;
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
// Do nothing
wasHandled = true;
break;
default: break;
}
return wasHandled;
}
III、未完待续