一、启动初探
”在android手机上,当我们点击桌面上的按钮启动一个应用,就能打开应用的界面。这里我们所说的桌面其实就是android系统启动后的就已经帮我们运行的第一个程序,launcher程序。
launcher程序可以理解为作为其它应用app入口管理的一个系统自带的app,正常情况下,安装一个新的应用,就会在桌面(laucher)程序中显示一个相应的图标。
上述点击桌面上的图标打开应用的过程,本质上是通过lancher应用的提供的桌面图标启动另一个app的过程,并打开了新应用的首个Activity。
很显然,上述的过程是两个应用之间的一个交互,属于跨进程的通信。
在《Android系统流程解析》文章中,针对于上述场景我们搞清楚了“Android手机系统启动的流程,都干了些什么?”,今天我们搞清楚另一个问题,launcher程序是如何打开桌面上的一个应用的源码流程分析。
二、launcher程序的源码分析(基于Android10.0源码)
桌面程序就是我们平常所说的launcher程序,我们在手机桌面上看到的应用的icon,实际上是在LauncherActivity上实现的。接下来我们看launcherActivity的源代码:
/**
* Displays a list of all activities which can be performed
* for a given intent. Launches when clicked.
* 翻译:显示所有通过给定的intent能被执行的Activity列表。点击的时候启动
*/
public abstract class LauncherActivity extends ListActivity {
Intent mIntent;
PackageManager mPackageManager;//包管理器
IconResizer mIconResizer;
.....
protected void onListItemClick(ListView l, View v, int position, long id) {
//此处就是桌面图标被点击后,执行的核心代码
Intent intent = intentForPosition(position);
//调用了是Activity的startActivity方法
startActivity(intent);
}
...}
《基于android10.0源码分析Activity的启动流程》在之前写过这篇文章中,我们就是从StartActivity入手,分析了启动Activity的源码实现流程。
对于从桌面启动一个Activity本质上也是构建了一个intent去调用startActivity(intent)。这里面我们再看一下intentForPosition(position)的源码,来确定我们的intent构建是大概是如何关联到我们的我们平常在开发自己的应用的时候设置的launcher activity。
/**
* 返回指定位置所对应的app的真实intent
*/
protected Intent intentForPosition(int position) {
ActivityAdapter adapter = (ActivityAdapter) mAdapter;
return adapter.intentForPosition(position);
}
实际上继续调用了LaucherActivity的内容类ActivityAdapter的intentForPosition()
private class ActivityAdapter extends BaseAdapter implements Filterable {
public Intent intentForPosition(int position) {
if (mActivitiesList == null) {
return null;
}
// 通过mIntent构建了intent
Intent intent = new Intent(mIntent);
// 获取确定位置的activity是谁
ListItem item = mActivitiesList.get(position);
intent.setClassName(item.packageName, item.className);
if (item.extras != null) {
intent.putExtras(item.extras);
}
return intent;
}
}
当前要执行的Activity的确定是在mActivitiesList获取的。看一下mActivitiesList的存储的内容:
private class ActivityAdapter extends BaseAdapter implements Filterable {
......
public ActivityAdapter(IconResizer resizer) {
mIconResizer = resizer;
mInflater = (LayoutInflater) LauncherActivity.this.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
mShowIcons = onEvaluateShowIcons();
//在构造方法中,我们找到此方法
mActivitiesList = makeListItems();
}
}
接着看makeListItems()
public List<ListItem> makeListItems() {
// Load all matching activities and sort correctly
List<ResolveInfo> list = onQueryPackageManager(mIntent);
onSortResultList(list);
ArrayList<ListItem> result = new ArrayList<ListItem>(list.size());
int listSize = list.size();
for (int i = 0; i < listSize; i++) {
ResolveInfo resolveInfo = list.get(i);
result.add(new ListItem(mPackageManager, resolveInfo, null));
}
return result;
}
这里面,我们看到实际的ActivitiesList是通过PackageManager加载得到,getPackageManager实现是在contextImpl.java中实现,通过下载android的framework层的源码关联找到,刚开始在android studio中直接关联是找不到具体的方法实现。
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
//非常熟悉的ActivityThread,后续的源码实现,我们能看到类似Ams的binder的接口
//这里也是跨进程的通信
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
上面的有些跑偏了,回到桌面上点击一个应用icon启动应用的流程上来,其实源码流程还是非常简单的:
1.通过PackageManager检索以mIntent为筛选条件的ResolveInfo集合
List<ResolveInfo> list = onQueryPackageManager(mIntent);
2.对ResolveInfo集合二次封装成ListItem集合,成为列表Adapter的数据
3.当点击条目时,通过ListItem创建出跳转Intent,调用startActivity跳转
Intent intent = intentForPosition(position);
startActivity(intent);
当然这里面跑偏了的地方是packageManager获取ActivitiesList的逻辑,因为这部分的源码也非常多,在本篇中主要看Launcher是通过点击桌面icon启动应用,就不详细分析,这部分内容会在PMS的源码分析文章中呈现,会包括PackageManger是如何读取manifest文件的,为什么就能找到我们自己开发的应用的启动启动Activity。