- 启动入口分析:
ActivityManagerService.java中
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME); // 构建一个category为CATEGORY_HOME的Intent
}
return intent;
}
Android系统启动后,PMS会扫描apk信息,并保存 Manifest.xml 中相关的信息,这里的intent就是Intent.CATEGORY_HOME = "android.intent.category.HOME",这个category会在Launcher3的 AndroidManifest.xml中配置,然后PMS找到category为HOME的activity并启动它;
boolean startHomeActivityLocked(int userId) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL && mTopAction == null) {
return false;
}
Intent intent = getHomeIntent();
ActivityInfo aInfo =
resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mStackSupervisor.startHomeActivity(intent, aInfo);
}
}
return true;
}
这里简单介绍一下intent.setComponent():
// 启动自己写的应用程序,一个参数是应用程序的包名,后一个是这个应用程序的主Activity名
Intent intent=new Intent();
intent.setComponent(
new ComponentName("com.aaa.bbb.ccc.ddd","com.aaa.bbb.ccc.ddd.MainActivity"));
startActivity(intent);
// 启动系统自带的应用程序
Intent intent=new Intent();
intent.setComponent(new ComponentName("com.android.calendar","com.android.calendar.LaunchActivity"));
startActivity(intent);
startHomeActivity:启动桌面的activity -> 分为两步,如果没有进程先创建进程,然后再启动activity;
- 获取所有 app 应用信息:Launcher.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
if (!mRestoring) {
if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(true, mWorkspace.getRestorePage());
}
}
}
public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
synchronized (mLock) {
if (DEBUG_LOADERS) {
Log.d(TAG, "startLoader isLaunching=" + isLaunching);
}
// Clear any deferred bind-runnables from the synchronized load process
// We must do this before any loading/binding is scheduled below.
synchronized (mDeferredBindRunnables) {
mDeferredBindRunnables.clear();
}
// 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(mApp.getContext(), isLaunching, loadFlags);
if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
&& mAllAppsLoaded && mWorkspaceLoaded) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
}
}
}
}
这里new了一个LoaderTask,startLoader主要是启动了一个线程,用于加载数据;sWorker是一个Handle对象,用于启动线程的run方法。接下来看看LoaderTask中的run()方法:简化代码
//LauncherModel内部类LoaderTask中的run()方法:
public void run() {
boolean isUpgrade = false;
synchronized (mLock) {
mIsLoaderTaskRunning = true;
}
keep_running: {
isUpgrade = loadAndBindWorkspace();
if (mStopped) {
break keep_running;
}
waitForIdle();
loadAndBindAllApps(); //主要在这个方法中
// Restore the default thread priority after we are done loading items
synchronized (mLock) {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
}
synchronized (sBgLock) {
for (Object key : sBgDbIconCache.keySet()) {
updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
}
sBgDbIconCache.clear();
}
if (LauncherAppState.isDisableAllApps()) {
if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
verifyApplications();
}
}
mContext = null;
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
if (mLoaderTask == this) {
mLoaderTask = null;
}
mIsLoaderTaskRunning = false;
}
}
//loadAndBindAllApps()方法:
private void loadAndBindAllApps() {
if (DEBUG_LOADERS) {
Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
}
if (!mAllAppsLoaded) {
loadAllApps(); //调用了loadAllApps()方法
synchronized (LoaderTask.this) {
if (mStopped) {
return;
}
mAllAppsLoaded = true;
}
} else {
onlyBindAllApps();
}
}
我们在看看LoaderTask类中的loadAllApps()方法中做了哪些事情:
private void loadAllApps() {
final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final Callbacks oldCallbacks = mCallbacks.get();
if (oldCallbacks == null) {
// This launcher has exited and nobody bothered to tell us. Just bail.
Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
return;
}
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List profiles = mUserManager.getUserProfiles();
// Clear the list of apps
mBgAllAppsList.clear();
for (UserHandleCompat user : profiles) {
// Query for the set of apps
final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
// 获取launcher中所有ActivityInfo的信息;注释1
List apps = mLauncherApps.getActivityList(null, user); //注释2
// Fail if we don't have any apps
if (apps == null || apps.isEmpty()) {
return;
}
// Sort the applications by name
final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Collections.sort(apps,
new LauncherModel.ShortcutNameComparator(mLabelCache));
// Create the ApplicationInfos
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfoCompat app = apps.get(i);
// This builds the icon bitmaps.
mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
}
}
// Huh? Shouldn't this be inside the Runnable below?
final ArrayList added = mBgAllAppsList.added;
mBgAllAppsList.added = new ArrayList();
// Post callback on main thread
mHandler.post(new Runnable() {
public void run() {
final long bindTime = SystemClock.uptimeMillis();
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAllApplications(added); //点击事件,见3
} else {
Log.i(TAG, "not binding apps: no Launcher activity");
}
}
});
}
注释1:获取桌面app的基本信息,例如微信桌面图标中显示小红点,就是根据这个获取的;
public abstract class LauncherActivityInfoCompat {
LauncherActivityInfoCompat() {
}
// 包名,主activity的class名字
public abstract ComponentName getComponentName();
public abstract UserHandleCompat getUser();
public abstract CharSequence getLabel();
public abstract Drawable getIcon(int density);
// 应用的信息,PMS解析,点击图标启动activity就需要这些信息
public abstract ApplicationInfo getApplicationInfo();
public abstract long getFirstInstallTime();
public abstract Drawable getBadgedIcon(int density);
}
注释2:mLauncherApps 是 LauncherAppsCompat,我们看看它的初始化:
// LauncherModel 中:
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
Context context = app.getContext();
// ...
// 在LauncherModel的构造函数中初始化
mLauncherApps = LauncherAppsCompat.getInstance(context);
mUserManager = UserManagerCompat.getInstance(context);
}
// LauncherAppsCompat类中:单例模式
public static LauncherAppsCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
if (Utilities.isLmpOrAbove()) {
sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
} else { // 都行,就看这个吧
sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
}
}
return sInstance;
}
}
这里比较关键,mainIntent 中添加了 Intent.ACTION_MAIN 和 Intent.CATEGORY_LAUNCHER,这两条信息会在PackageManagerService查询activity时起到关键的筛查作用,这两条信息也就是App启动时开启的acitivity;
public List getActivityList(String packageName,
UserHandleCompat user) {
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); //查询每个app启动的主界面;
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mainIntent.setPackage(packageName);
List infos = mPm.queryIntentActivities(mainIntent, 0); //PMS查询,跨进程通信;
List list =
new ArrayList(infos.size());
for (ResolveInfo info : infos) {
list.add(new LauncherActivityInfoCompatV16(mContext, info));
}
return list;
}
- 填充桌面数据处理点击:callbacks.bindAllApplications(added);
LoaderTask类中的tryGetCallbacks()
Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
synchronized (mLock) {
if (mStopped) {
return null;
}
if (mCallbacks == null) {
return null;
}
final Callbacks callbacks = mCallbacks.get();
if (callbacks != oldCallbacks) {
return null;
}
if (callbacks == null) {
Log.w(TAG, "no mCallbacks");
return null;
}
return callbacks;
}
}
//LauncherModel类中对mCallbacks进行初始化的:initialize()方法中
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
mCallbacks = new WeakReference(callbacks);
}
}
Laucher.java中的点击事件:
public void onClick(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) {
return;
}
if (!mWorkspace.isFinishedSwitchingState()) {
return;
}
if (v instanceof Workspace) {
if (mWorkspace.isInOverviewMode()) {
mWorkspace.exitOverviewMode(true);
}
return;
}
if (v instanceof CellLayout) {
if (mWorkspace.isInOverviewMode()) {
mWorkspace.exitOverviewMode(mWorkspace.indexOfChild(v), true);
}
}
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
onClickAppShortcut(v);
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
onClickFolderIcon(v);
}
} else if (v == mAllAppsButton) {
onClickAllAppsButton(v); // 所有app展示页的点击事件
} else if (tag instanceof AppInfo) {
startAppShortcutOrInfoActivity(v); // 具体app图标的点击事件:启动app
} else if (tag instanceof LauncherAppWidgetInfo) {
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v);
}
}
}
具体app图标的点击事件:
private void startAppShortcutOrInfoActivity(View v) {
// ... 省略部分代码
boolean success = startActivitySafely(v, intent, tag); // 真正启动应用activity 注释3
mStats.recordLaunch(intent, shortcut);
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
}
// 这里的 intent 是AppInfo中的intent
public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user,
IconCache iconCache, HashMap
注释3:startActivitySafely()
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
}
try {
success = startActivity(v, intent, tag); // startActivity
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
}
return success;
}
boolean startActivity(View v, Intent intent, Object tag) {
//代码重复?这里我也有一点不太懂为什么这么做
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
UserManagerCompat userManager = UserManagerCompat.getInstance(this);
UserHandleCompat user = null;
if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
user = userManager.getUserForSerialNumber(serialNumber);
}
Bundle optsBundle = null;
if (useLaunchAnimation) {
ActivityOptions opts = Utilities.isLmpOrAbove() ?
ActivityOptions.makeCustomAnimation(this, R.anim.task_open_enter, R.anim.no_anim) :
ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
optsBundle = opts.toBundle();
}
if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
} else {
// TODO Component can be null when shortcuts are supported for secondary user
//这里最终也是mContext.startActivity(launchIntent, opts);
launcherApps.startActivityForProfile(intent.getComponent(), user,
intent.getSourceBounds(), optsBundle);
}
return true;
} catch (SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Launcher does not have the permission to launch " + intent +
". Make sure to create a MAIN intent-filter for the corresponding activity " +
"or use the exported attribute for this activity. "
+ "tag="+ tag + " intent=" + intent, e);
}
return false;
}