可以看到当结果为
case ActivityManager.START_INTENT_NOT_RESOLVED:
case ActivityManager.START_CLASS_NOT_FOUND:
就会报 throw new ActivityNotFoundException("Unable to find explicit activity class " + ((Intent) intent).getComponent().toShortString() + "; have you declared this activity in your AndroidManifest.xml?");
这下我们就知道了如果没在清单文件中添加这个注册,报错的位置。
AMS是如何判断activity没有注册的,首先我们得明白startActivity执行的主流程
这个篇幅太多了,可以自己去源码跟一下,这里不作介绍,
我们这里分析主要流程代码
找到在ASR.startActivity (ActivityStarter)中返回了
START_INTENT_NOT_RESOLVED,START_CLASS_NOT_FOUND
private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent, String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int realCallingPid, int realCallingUid, int startFlags, SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity, TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup) { int err = ActivityManager.START_SUCCESS; ... //接下来开始做一些校验判断 if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) { // We couldn't find a class that can handle the given Intent. // That's the end of that! err = ActivityManager.START_INTENT_NOT_RESOLVED; // 从Intent中无法找 到相应的Component } if (err == ActivityManager.START_SUCCESS && aInfo == null) { // We couldn't find the specific class specified in the Intent. // Also the end of the line. err = ActivityManager.START_CLASS_NOT_FOUND; // 从Intent中无法找到相 应的ActivityInfo } ... if (err != START_SUCCESS) { //不能成功启动了,返回err if (resultRecord != null) { resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null); } SafeActivityOptions.abort(options); return err; } //创建出我们的目标ActivityRecord对象,存到传入数组0索引上 ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null, mSupervisor, checkedOptions, sourceRecord); ... return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask, outActivity); }
但是 intent.getComponent(),aInfo又是从哪儿获取的呢,我们回溯到
startActivityMayWait.
看下上面的aInfo哪来的.
ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags, ProfilerInfo profilerInfo) { final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null; if (aInfo != null) { // Store the found target back into the intent, because now that // we have it we never want to do this again. For example, if the // user navigates back to this point in the history, we should // always restart the exact same activity. intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); // Don't debug things in the system process ... } return aInfo; }
发现是从rInfo来的
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags, int filterCallingUid) { synchronized (mService) { try {...final long token = Binder.clearCallingIdentity(); try { return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType, modifiedFlags, userId, true, filterCallingUid); } finally { Binder.restoreCallingIdentity(token); } ... } } }
rInfo的获取
PackageManagerInternal getPackageManagerInternalLocked() { if (mPackageManagerInt == null) { mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); } return mPackageManagerInt; }
具体实现类是PackageManagerService
@Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal(intent, resolvedType, flags, userId, false, Binder.getCallingUid()); }
看resolveIntentInternal
private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,int flags, int userId, boolean resolveForStart, int filterCallingUid) { try {... //获取ResolveInfo列表 final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid, userId, resolveForStart, true /allowDynamicSplits/); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); //找出最好的返回 final ResolveInfo bestChoice = chooseBestActivity(intent, resolvedType, flags, query, userId); return bestChoice; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } }
看 queryIntentActivitiesInternal
private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { ... if (comp != null) { final List list = new ArrayList(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); if (ai != null) { ... } }
原来是从getActivityInfo获取的
@Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId); }
getActivityInfoInternal方法
最后
今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
【Android核心高级技术PDF文档,BAT大厂面试真题解析】
【算法合集】
【延伸Android必备知识点】
【Android部分高级架构视频学习资源】
**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!