最后
在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
frameworks\base\core\java\android\content\ContextWrapper.java
代码:
public void startActivity(Intent intent, Bundle options) {
mBase.startActivity(intent, options);
}
ContextWrapper.startActivity的话,是直接调用的
mBase.startActivity(intent, options);
那么这个mBase是什么呢?又是什么时候赋值的呢?其实mBase是在ContextWrapper的attachBaseContext的时候初始化的。如下:
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
那又是谁调用attachBaseContext的呢?
是在service创建的时候,在ActivityThread里面调用,如下:
3. 代码文件
frameworks\base\core\java\android\app\ActivityThread.java
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
....
}
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
....
} catch (Exception e) {
...
}
}
抽出主要代码分析ActivityThread. handleCreateService()方法里面主要做这几件事
3.1 通过pms找到要启动的Service配置信息,然后通过反射生成Service对象
3.2 创建ContextImpl对象,然后调用service.attach方法设置到ContextWrapper.java的mBaseContext变量里面。
那现在就明白了,service.startActivity()->ContextWrapper.startActivity()->ContextImpl.startActivity()
然后再ContextImpl.startActivity里面会检查Intent的参数是否包含FLAG_ACTIVITY_NEW_TASK,从而出现这个异常。
三. 解决这个异常后会出现问题?
有些同学就会说了,在Service里面启动Activity必须要有FLAG_ACTIVITY_NEW_TASK参数,那么我们添加上不就可以了?
如下:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
那么这样会带来什么问题呢?
这样带来的问题就是在最近任务列表里面会出现两个相同的应用程序,比如你是在电话本里面启动的,那么最近任务列表就会出现两个电话本;因为有两个Task嘛!
那怎么解决呢?
其实也非常好解决,只要在新的Task里面的Activity里面配置android:excludeFromRecents=”true”就可以了。表示这个Activity不会显示在最近列表里面。
四. Activity.startActivity()为什么不出现这个异常呢?
要回答这个问题,需要看下Activity.startActivity()调用到哪里去了
代码文件:
frameworks\base\core\java\android\app\Activity.java
代码:
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
接下来会调用startActivityForResult()->然后一路调用到Ams去启动Activity;
原来如此,Activity重写了startActivity()方法…
五. Android 为什么要这么设计?
那现在来回答这个问题,为什么Android在Service 里面启动Activity要强制规定使用参数FLAG_ACTIVITY_NEW_TASK呢?
我们可以来做这样一个假设,我们有这样一个需求:
我们在电话本里面启动一个Service,然后它执行5分钟后,启动一个Activity
那么很有可能用户在5分钟后已经不在电话本程序里面操作了,有可能去上网,打开浏览器程序了。
5分钟后,此时当前的Task是浏览器的task,那么弹出Activity,如果这个Activity在当前Task的话,也就是浏览器的Task;那么用户就会觉得莫名其妙;因为弹出的Activity和浏览器在一个Task,本来这个Activity应该属于电话本的。
所以,对于Service而言,干脆强制定义启动的Activity要创建一个新的Task.
这种设计,我觉得还是比较合理的。
六. 转载地址 :
最后
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
最后针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
享动态给身边好友一起学习!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!