热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《androidframework常用api源码分析》之Launcher程序

《androidframework常用api源码分析》android生态在中国已经发展非常庞大了,一方面是因为手机移动端的覆盖,另一方面是从事android开发的人也月来越多。那么用人单位对an

《android framework常用api源码分析》android生态在中国已经发展非常庞大了,一方面是因为手机移动端的覆盖,另一方面是从事android开发的人也月来越多。那么用人单位对android要求也变了,对android不仅要熟练使用而且要懂得原理。而就程序员自身阅读源码有什么那些?这里我通过自己理解归纳了一下。

  1. 提高程序执行效率,正确理解api可以高效使用,优化内存和执行效率。

  2. 避免八阿哥强势逆袭,android开发同学都知道android找bug比较麻烦,尤其是一下jni底层调用错误信息不够明确地方更加难找。

  3. 帮助自己写出优雅的代码,开发需要规范,而源码中有很多优秀的谷歌规范。

  4. 优秀的设计模式,帮助自己提升程序造诣。

  5. 黑科技,通过反射高一些api不能够达到的功能,例如插件化、热更新。

上面是简单个人理解,有更多补充欢迎留言。所以这里准备出一个系列的文章来分析android framework api, 这些文章也是来自整理于网络,所以要感谢那些具有分享精神的大神们。

文章目录:

  1. apk 打包过程解析。
  2. handler 消息机制。
  3. AsyncTask 异步任务。
  4. HandlerThread handler线程。
  5. IntentService意图服务。
  6. Zygote进程。
  7. SystemServer进程。
  8. Launcher 程序。
  9. app 进程启动流程。
  10. 系统app启动安装流程。
  11. app应用安装流程。
  12. Activity启动流程。
  13. LruCache内存缓存

什么是Launcher app?
做应用开发的同学平时应该接触不多,android系统桌面就是Launcher app,但是他也是一个普通的app应用程序,是用户交互的第一个程序。很多手机厂商都会修改系统的默认Launcher app和系统默认主题样式,就发展成现在百花齐放的android盛世。一个自己开发app应用启动就是通过点击应用图标启动起来,如果要分析启动流程那就先看看Launcher app。
在SystemServer进程的启动过程中会调用其main静态方法,开始执行整个SystemServer的启动流程,在其中通过调用三个内部方法分别启动boot service、core service和other service。在调用startOtherService方法中就会通过调用mActivityManagerService.systemReady()方法,那么我们看一下其具体实现:

// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
// where third party code can really run (but before it has actually
// started launching the initial applications), for us to complete our
// initialization.
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready");
mSystemServiceManager.startBootPhase(
SystemService.PHASE_ACTIVITY_MANAGER_READY);

try {
mActivityManagerService.startObservingNativeCrashes();
} catch (Throwable e) {
reportWtf("observing native crashes", e);
}

Slog.i(TAG, "WebViewFactory preparation");
WebViewFactory.prepareWebViewInSystemServer();

try {
startSystemUi(context);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
try {
if (networkScoreF != null) networkScoreF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Score Service ready", e);
}
try {
if (networkManagementF != null) networkManagementF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Managment Service ready", e);
}
try {
if (networkStatsF != null) networkStatsF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Stats Service ready", e);
}
try {
if (networkPolicyF != null) networkPolicyF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Policy Service ready", e);
}
try {
if (connectivityF != null) connectivityF.systemReady();
} catch (Throwable e) {
reportWtf("making Connectivity Service ready", e);
}
try {
if (audioServiceF != null) audioServiceF.systemReady();
} catch (Throwable e) {
reportWtf("Notifying AudioService running", e);
}
Watchdog.getInstance().start();

// It is now okay to let the various system services start their
// third party code...
mSystemServiceManager.startBootPhase(
SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);

try {
if (wallpaperF != null) wallpaperF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying WallpaperService running", e);
}
try {
if (immF != null) immF.systemRunning(statusBarF);
} catch (Throwable e) {
reportWtf("Notifying InputMethodService running", e);
}
try {
if (locationF != null) locationF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying Location Service running", e);
}
try {
if (countryDetectorF != null) countryDetectorF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying CountryDetectorService running", e);
}
try {
if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying NetworkTimeService running", e);
}
try {
if (commonTimeMgmtServiceF != null) {
commonTimeMgmtServiceF.systemRunning();
}
} catch (Throwable e) {
reportWtf("Notifying CommonTimeManagementService running", e);
}
try {
if (textServiceManagerServiceF != null)
textServiceManagerServiceF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying TextServicesManagerService running", e);
}
try {
if (atlasF != null) atlasF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying AssetAtlasService running", e);
}
try {
// TODO(BT) Pass parameter to input manager
if (inputManagerF != null) inputManagerF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying InputManagerService running", e);
}
try {
if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying TelephonyRegistry running", e);
}
try {
if (mediaRouterF != null) mediaRouterF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying MediaRouterService running", e);
}

try {
if (mmsServiceF != null) mmsServiceF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying MmsService running", e);
}
}
});

可以发现这个方法传递了一个Runnable参数,里面执行了各种其他服务的systemReady方法,这里不是我们关注的重点,我们看一下在ActivityManagerService中systemReady方法的具体实现,方法体比较长,我就不在这里贴出代码了,主要的逻辑就是做一些ActivityManagerService的ready操作

public void systemReady(final Runnable goingCallback) {
...
// Start up initial activity.
mBooting = true;
startHomeActivityLocked(mCurrentUserId, "systemReady");
...
}

重点是在这个方法体中调用了startHomeActivityLocked方法,看其名字就是说开始执行启动homeActivity的操作,好了,既然如此,我们再看一下startHomeActivityLocked的具体实现:

boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
// the factory test app, so just sit around displaying the
// error message and don't try to start anything.
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, reason);
}
}

return true;
}

首先是调用getHomeIntent()方法,看一下getHomeIntent是如何实现构造Intent对象的:

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);
}
return intent;
}

可以发现,启动Launcher的Intent对象中添加了Intent.CATEGORY_HOME常量,这个其实是一个launcher的标志,一般系统的启动页面Activity都会在androidmanifest.xml中配置这个标志。比如我们在github中的android launcher源码中查看其androidmanifest.xml文件:
这里写图片描述
可以发现其Activity的定义intentfilter中就是定义了这样的category。不同的手机厂商可能会修改Launcher的源码,但是这个category一般是不会更改的。

继续回到我们的startHomeActivityLocked方法,我们发现经过一系列的判断逻辑之后最后调用了mStackSupervisor.startHomeActivity方法,然后我们可以查看一下该方法的具体实现逻辑:

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo,
null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */,
null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */,
null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */,
0 /* startFlags */, null /* options */, false /* ignoreTargetSecurity */,
false /* componentSpecified */,
null /* outActivity */, null /* container */, null /* inTask */);
if (inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
// again. We need to schedule another resume.
scheduleResumeTopActivities();
}
}

发现其调用的是scheduleResumeTopActivities()方法,这个方法其实是关于Activity的启动流程的逻辑了,这里我们不在详细的说明,关于Activity的启动流程我们在下面的文章中会介绍。

因为我们的Launcher启动的Intent是一个隐士的Intent,所以我们会启动在androidmanifest.xml中配置了相同catogory的activity,android M中配置的这个catogory就是LauncherActivity。

LauncherActivity继承与ListActivity,我们看一下其Layout布局文件:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>


<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>


<TextView
android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/activity_list_empty"
android:visibility="gone"
android:textAppearance="?android:attr/textAppearanceMedium"
/>


FrameLayout>

可以看到我们现实的桌面其实就是一个ListView控件,然后看一下其onCreate方法:

@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);

mPackageManager = getPackageManager();

if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarIndeterminateVisibility(true);
}
onSetContentView();

mIcOnResizer= new IconResizer();

mIntent = new Intent(getTargetIntent());
mIntent.setComponent(null);
mAdapter = new ActivityAdapter(mIconResizer);

setListAdapter(mAdapter);
getListView().setTextFilterEnabled(true);

updateAlertTitle();
updateButtonText();

if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
setProgressBarIndeterminateVisibility(false);
}
}

可以看到在LauncherActivity的onCreate方法中初始化了一个PackageManager,其主要作用就是从中查询出系统所有已经安装的应用列表,应用包名,应用图标等信息。然后将这些信息注入到Adapter中,这样就可以将系统应用图标和名称显示出来了。
在系统的回调方法onListItemClick中

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = intentForPosition(position);
startActivity(intent);
}

这也就是为什么我们点击了某一个应用图标之后可以启动某一项应用的原因了,我们看一下这里的intentForPosition是如何实现的。

protected Intent intentForPosition(int position) {
ActivityAdapter adapter = (ActivityAdapter) mAdapter;
return adapter.intentForPosition(position);
}

这里又调用了adapter的intentForPosition方法:

public Intent intentForPosition(int position) {
if (mActivitiesList == null) {
return null;
}

Intent intent = new Intent(mIntent);
ListItem item = mActivitiesList.get(position);
intent.setClassName(item.packageName, item.className);
if (item.extras != null) {
intent.putExtras(item.extras);
}
return intent;
}

可以看到由于adapter的每一项中都保存了应用的包名可启动Activity名称,所以这里在初始化Intent的时候,直接将这些信息注入到Intent中,然后调用startActivity,就将这些应用启动了(关于startActivity是如何启动的下面的文章中我将介绍)。

总结:

Launcher的启动流程

  • Zygote进程 –> SystemServer进程 –> startOtherService方法 –> ActivityManagerService的systemReady方法 –> startHomeActivityLocked方法 –> ActivityStackSupervisor的startHomeActivity方法 –> 执行Activity的启动逻辑,执行scheduleResumeTopActivities()方法。。。。

  • 因为是隐士的启动Activity,所以启动的Activity就是在AndroidManifest.xml中配置catogery的值为:

public static final String CATEGORY_HOME = "android.intent.category.HOME";

可以发现android M中在androidManifest.xml中配置了这个catogory的activity是LauncherActivity,所以我们就可以将这个Launcher启动起来了

  • LauncherActivity中是以ListView来显示我们的应用图标列表的,并且为每个Item保存了应用的包名和启动Activity类名,这样点击某一项应用图标的时候就可以根据应用包名和启动Activity名称启动我们的App了。

推荐阅读
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 本文介绍了如何使用iptables添加非对称的NAT规则段,以实现内网穿透和端口转发的功能。通过查阅相关文章,得出了解决方案,即当匹配的端口在映射端口的区间内时,可以成功进行端口转发。详细的操作步骤和命令示例也在文章中给出。 ... [详细]
author-avatar
兴霖先玲广_215
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有