乐趣区

01.Android之基础组件问题

目录介绍

1.0.0.1 说下 Activity 的生命周期?屏幕旋转时生命周期?异常条件会调用什么方法?
1.0.0.2 后台的 Activity 被系统回收怎么办?说一下 onSaveInstanceState()和 onRestoreInstanceState()方法特点?
1.0.0.3 如何避免配置改变时 Activity 重建?优先级低的 Activity 在内存不足被回收后怎样做可以恢复到销毁前状态?
1.0.0.4 app 切换到后台,当前 activity 会走 onDestory 方法吗?一般在 onstop 方法里做什么?什么情况会导致 app 会被杀死?
1.0.0.5 Activity 的启动过程是有几种方式?从桌面 launcher 上点击应用图标会干啥,调用 startActivty()又会做什么?
1.0.0.6 说下 Activity 的四种启动模式?singleTop 和 singleTask 的区别以及应用场景?任务栈的作用是什么?
1.0.0.7 两个 Activity 之间怎么传递数据?intent 和 bundle 有什么区别?为什么有了 intent 还要设计 bundle?
1.0.0.8 知道哪些 Activity 启动模式的标记位?flag 是干什么用的,什么时候用到?
1.0.1.0 同一程序不同的 Activity 是否可以放在不同的 Task 任务栈中?
1.0.1.1 介绍一下 Service,启动 Service 有几种方式,生命周期是怎样的?说一下 onStartCommand()的作用?service 如何杀不死?
1.0.1.2 一个 Activty 先 start 一个 Service 后,再 bind 时会回调什么方法?此时如何做才能回调 Service 的 destory()方法?
1.0.1.3 bindService 是一个异步的过程吗?绑定 service 大概需要经历那些过程?
1.0.1.4 是否能在 Service 进行耗时操作?如果非要可以怎么做,如何避免 service 线程卡顿?service 里面可以弹土司吗?
1.0.1.5 Activity 如何与 Service 通信?Service 的生命周期与启动方法有什么区别?
1.0.2.0 是否了解 ActivityManagerService,它发挥什么作用,说一下 AMS 启动流程?
1.0.2.1 Android 中哪些事件需要用到广播?广播的生命周期是怎样的?
1.0.2.3 广播有几种形式?他们分别有什么特点,如何使用广播?广播是怎么实现不同进程之间通信的?
1.0.2.8 Fragment 与 Activity 之间是如何传值的?Fragment 与 Fragment 之间是如何传值的?
1.0.2.9 Activity 创建 Fragment 的方式是什么?FragmentPageAdapter 和 FragmentPageStateAdapter 的区别?
1.0.3.0 fragment 特点?说一下 Fragment 的生命周期?如何解决 getActivity 为 null 的异常问题?
1.0.3.1 在 fragment 中为什么有时 getActivity()会为 null?Fragment 试图为什么有的时候会重叠,怎么产生的,又如何解决?
1.0.3.2 为什么 fragment 传递数据不用构造方法传递?FragmentManager , add 和 replace 有什么区别?
1.0.3.9 Activitiy 启动流程中 performLaunchActivity 的作用?
1.0.4.0 Intent 是什么?Intent 可以传递哪些数据?传递对象的时候为什么要实例化?
1.0.4.1 mipmap 系列中 xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi 和 ldpi 存在怎样的关系?
1.0.4.2 res 目录和 assets 目录的区别?R 文件是如何生成的,主要有什么作用?
1.0.4.3 Context 是什么?Context 有哪些类型,分别作用是什么?Context 下有哪些子类?哪些场景只能用 activity 上下文?
1.0.4.4 ActivityThread 的 main()的流程大概是怎么样的?
1.0.5.0 序列化的方式有哪些?效率对比有何优势?如何做性能上分析的?
1.0.5.9 界面的刷新为什么需 16.6ms?画面的显示需要哪些步骤?界面保持不变时还会 16.6ms 刷新一次屏幕吗?
1.0.6.0 Android 中日志级别有哪几种?开发中需要注意什么问题,打印日志源码分析原理是什么?

好消息

博客笔记大汇总【15 年 10 月到至今】,包括 Java 基础及深入知识点,Android 技术博客,Python 学习笔记等等,还包括平时开发中遇到的 bug 汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是 markdown 格式的!同时也开源了生活博客,从 12 年起,积累共计 500 篇[近 100 万字],将会陆续发表到网上,转载请注明出处,谢谢!
链接地址:https://github.com/yangchong2…
如果觉得好,可以 star 一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!所有的笔记将会更新到 GitHub 上,同时保持更新,欢迎同行提出或者 push 不同的看法或者笔记!

1.0.0.1 说下 Activity 的生命周期?屏幕旋转时生命周期?异常条件会调用什么方法?

在 Activity 的生命周期涉及到七大方法,分别是:

onCreate()表示 Activity 正在创建,常做初始化工作,如 setContentView 界面资源、初始化数据
onStart()表示 Activity 正在启动,这时 Activity 可见但不在前台,无法和用户交互
onResume()表示 Activity 获得焦点,此时 Activity 可见且在前台并开始活动
onPause()表示 Activity 正在停止,可做 数据存储、停止动画等操作
onStop()表示 activity 即将停止,可做稍微重量级回收工作,如取消网络连接、注销广播接收器等
onDestroy()表示 Activity 即将销毁,常做回收工作、资源释放
onRestart()表示当 Activity 由后台切换到前台,由不可见到可见时会调用,表示 Activity 重新启动

屏幕旋转时生命周期

屏幕旋转时候,如果不做任何处理,activity 会经过销毁到重建的过程。一般这种效果都不是想要的。比如视频播放器就经常会涉及屏幕旋转场景。技术博客大总结

第一种情况:当前的 Activity 不销毁【设置 Activity 的 android:configChanges=”orientation|keyboardHidden|screenSize” 时,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法】
<activity
android:name=”.activity.VideoDetailActivity”
android:configChanges=”orientation|keyboardHidden|screenSize”
android:screenOrientation=”portrait”/>
执行该方法
// 重写旋转时方法,不销毁 activity
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}

第二种情况:销毁当前的 Activity 后重建,这种也尽量避免。【不设置 Activity 的 android:configChanges 时,切屏会重新调用各个生命周期,默认首先销毁当前 activity, 然后重新加载】

异常条件会调用什么方法

当非人为终止 Activity 时,比如系统配置发生改变时导致 Activity 被杀死并重新创建、资源内存不足导致低优先级的 Activity 被杀死,会调用 onSavaInstanceState() 来保存状态。该方法调用在 onStop 之前,但和 onPause 没有时序关系。
有人会问,onSaveInstanceState()与 onPause()的区别,onSaveInstanceState()适用于对临时性状态的保存,而 onPause()适用于对数据的持久化保存。
当异常崩溃后 App 又重启了,这个时候会走 onRestoreInstanceState()方法,可以在该方法中取出 onSaveInstanceState()保存的状态数据。

什么时候会引起异常生命周期

资源相关的系统配置发生改变或者资源不足:例如屏幕旋转,当前 Activity 会销毁,并且在 onStop 之前回调 onSaveInstanceState 保存数据,在重新创建 Activity 的时候在 onStart 之后回调 onRestoreInstanceState。其中 Bundle 数据会传到 onCreate(不一定有数据)和 onRestoreInstanceState(一定有数据)。技术博客大总结

防止屏幕旋转的时候重建,在清单文件中添加配置:android:configChanges=”orientation”

1.0.0.2 后台的 Activity 被系统回收怎么办?说一下 onSaveInstanceState()和 onRestoreInstanceState()方法特点?

后台的 Activity 被系统回收怎么办?
Activity 中提供了一个 onSaveInstanceState()回调方法,这个方法会保证一定在活动被回收之前调用,可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。onSaveInstanceState()方法会携带一个 Bundle 类型的参数,Bundle 提供了一系列的方法用于保存数据,比如可以使用 putString()方法保存字符串,使用 putInt()方法保存整型数据。每个保存方法需要传入两个参数,第一个参数是键,用于后面从 Bundle 中取值,第二个参数是真正要保存的内容。技术博客大总结

说一下 onSaveInstanceState()和 onRestoreInstanceState()方法特点?

Activity 的 onSaveInstanceState()和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。
// 保存数据
@Override
protected void onSaveInstanceState(Bundle outBundle) {
super.onSaveInstanceState(outBundle);
outBundle.putBoolean(“Change”, mChange);
}

// 取出数据
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mChange = savedInstanceState.getBoolean(“Change”);
}

// 或者在 onCreate 方法取数据也可以
//onCreate()方法其实也有一个 Bundle 类型的参数。这个参数在一般情况下都是 null,
// 但是当活动被系统回收之前有通过 onSaveInstanceState()方法来保存数据的话,这个参就会带有之前所保存的全部数据
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String data = savedInstanceState.getString(“data”);
}
}

什么时候会触发走这两个方法?
当应用遇到意外情况(如:内存不足、用户直接按 Home 键)由系统销毁一个 Activity,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个 Activity 时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。除非该 activity 是被用户主动销毁的,通常 onSaveInstanceState()只适合用于保存一些临时性的状态,而 onPause()适合用于数据的持久化保存。

onSaveInstanceState()被执行的场景有哪些?

系统不知道你按下 HOME 后要运行多少其他的程序,自然也不知道 activityA 是否会被销毁,因此系统都会调用 onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则当用户按下 HOME 键时

长按 HOME 键,选择运行其他的程序时
锁屏时
从 activity A 中启动一个新的 activity 时
屏幕方向切换时

1.0.0.3 如何避免配置改变时 Activity 重建?优先级低的 Activity 在内存不足被回收后怎样做可以恢复到销毁前状态?

如何避免配置改变时 Activity 重建

为了避免由于配置改变导致 Activity 重建,可在 AndroidManifest.xml 中对应的 Activity 中设置 android:configChanges=”orientation|screenSize”。此时再次旋转屏幕时,该 Activity 不会被系统杀死和重建,只会调用 onConfigurationChanged。因此,当配置程序需要响应配置改变,指定 configChanges 属性,重写 onConfigurationChanged 方法即可。
使用场景,比如视频播放器横竖屏切换播放视频,就需要设置这种属性。具体可以看我封装的视频播放器库,地址:https://github.com/yangchong2…

优先级低的 Activity 在内存不足被回收后怎样做可以恢复到销毁前状态
优先级低的 Activity 在内存不足被回收后重新打开会引发 Activity 重建。Activity 被重新创建时会调用 onRestoreInstanceState(该方法在 onStart 之后),并将 onSavaInstanceState 保存的 Bundle 对象作为参数传到 onRestoreInstanceState 与 onCreate 方法。因此可通过 onRestoreInstanceState(Bundle savedInstanceState)和 onCreate((Bundle savedInstanceState)来判断 Activity 是否被重建,并取出数据进行恢复。但需要注意的是,在 onCreate 取出数据时一定要先判断 savedInstanceState 是否为空。

如何判断 activity 的优先级?技术博客大总结
除了在栈顶的 activity, 其他的 activity 都有可能在内存不足的时候被系统回收,一个 activity 越处于栈底,被回收的可能性越大. 如果有多个后台进程,在选择杀死的目标时,采用最近最少使用算法(LRU)。

1.0.0.4 app 切换到后台,当前 activity 会走 onDestory 方法吗?一般在 onstop 方法里做什么?什么情况会导致 app 会被杀死,这时候会走 onDestory 吗?

app 切换到后台,当前 activity 会走 onDestory 方法吗?
不会走 onDestory 方法,会先后走 onPause 和 onStop 方法。

一般在 onstop 方法里做什么?

比如。写轮播图的时候,会在 onstop 方法里写上暂停轮播图无限轮播,在 onStart 方法中会开启自动无限轮播。
再比如,写视频播放器的时候,当 app 切换到后台,则需要停止视频播放,也是可以在 onstop 中处理的。关于视频播放器,可以看我这个开源项目:视频播放器

什么情况会导致 app 会被杀死,这时候会走 onDestory 吗?
系统资源不足,会导致 app 意外被杀死。应用只有在进程存活的情况下才会按照正常的生命周期进行执行,如果进程突然被 kill 掉,相当于 System.exit(0); 进程被杀死,根本不会走(activity,fragment)生命周期。只有在进程不被 kill 掉,正常情况下才会执行 ondestory()方法。

activity 被回收如何恢复
当系统内存不足时, activity 会被回收,我们其实可以覆写 onSaveInstanceState()方法。onSaveInstanceState()方法接受一个 Bundle 类型的参数, 开发者可以将状态数据存储到这个 Bundle 对象中, 这样即使 activity 被系统摧毁, 当用户重新启动这个 activity 而调用它的 onCreate()方法时, 上述的 Bundle 对象会作为实参传递给 onCreate()方法, 开发者可以从 Bundle 对象中取出保存的数据, 然后利用这些数据将 activity 恢复到被摧毁之前的状态。

1.0.0.5 Activity 的启动过程是有几种方式?从桌面 launcher 上点击应用图标会干啥,调用 startActivty()又会做什么?

Activity 的启动过程是怎样的,有几种方式?

注意是启动过程,不是生命周期。技术博客大总结

app 启动的过程有两种情况,第一种是从桌面 launcher 上点击相应的应用图标,第二种是在 activity 中通过调用 startActivity 来启动一个新的 activity。

从桌面 launcher 上点击应用图标会干啥,调用 startActivty()又会做什么?

创建一个新的项目,默认的根 activity 都是 MainActivity,而所有的 activity 都是保存在堆栈中的,启动一个新的 activity 就会放在上一个 activity 上面,而我们从桌面点击应用图标的时候,由于 launcher 本身也是一个应用,当我们点击图标的时候,系统就会调用 startActivitySately(), 一般情况下,我们所启动的 activity 的相关信息都会保存在 intent 中,比如 action,category 等等。
我们在安装这个应用的时候,系统也会启动一个 PackaManagerService 的管理服务,这个管理服务会对 AndroidManifest.xml 文件进行解析,从而得到应用程序中的相关信息,比如 service,activity,Broadcast 等等,然后获得相关组件的信息。
当我们点击应用图标的时候,就会调用 startActivitySately()方法,而这个方法内部则是调用 startActivty(), 而 startActivity()方法最终还是会调用 startActivityForResult()这个方法。而在 startActivityForResult()这个方法。因为 startActivityForResult()方法是有返回结果的,所以系统就直接给一个 -1,就表示不需要结果返回了。
而 startActivityForResult()这个方法实际是通过 Instrumentation 类中的 execStartActivity()方法来启动 activity,Instrumentation 这个类主要作用就是监控程序和系统之间的交互。而在这个 execStartActivity()方法中会获取 ActivityManagerService 的代理对象,通过这个代理对象进行启动 activity。启动会就会调用一个 checkStartActivityResult()方法,如果说没有在配置清单中配置有这个组件,就会在这个方法中抛出异常了。
当然最后是调用的是 Application.scheduleLaunchActivity()进行启动 activity,而这个方法中通过获取得到一个 ActivityClientRecord 对象,而这个 ActivityClientRecord 通过 handler 来进行消息的发送,系统内部会将每一个 activity 组件使用 ActivityClientRecord 对象来进行描述,而 ActivityClientRecord 对象中保存有一个 LoaderApk 对象,通过这个对象调用 handleLaunchActivity 来启动 activity 组件,而页面的生命周期方法也就是在这个方法中进行调用。

1.0.0.6 说下 Activity 的四种启动模式?singleTop 和 singleTask 的区别以及应用场景?任务栈的作用是什么?

Activity 的四种启动模式

standard 标准模式:每次启动一个 Activity 就会创建一个新的实例
singleTop 栈顶复用模式:如果新 Activity 已经位于任务栈的栈顶,就不会重新创建,并回调 onNewIntent(intent) 方法
singleTask 栈内复用模式:只要该 Activity 在一个任务栈中存在,都不会重新创建,并回调 onNewIntent(intent) 方法。如果不存在,系统会先寻找是否存在需要的栈,如果不存在该栈,就创建一个任务栈,并把该 Activity 放进去;如果存在,就会创建到已经存在的栈中
singleInstance 单实例模式:具有此模式的 Activity 只能单独位于一个任务栈中,且此任务栈中只有唯一一个实例

singleTop 和 singleTask 的区别以及应用场景

singleTop:同个 Activity 实例在栈中可以有多个,即可能重复创建;该模式的 Activity 会默认进入启动它所属的任务栈,即不会引起任务栈的变更;为防止快速点击时多次 startActivity,可以将目标 Activity 设置为 singleTop
singleTask:同个 Activity 实例在栈中只有一个,即不存在重复创建;可通过 android:taskAffinity 设定该 Activity 需要的任务栈,即可能会引起任务栈的变更;常用于主页和登陆页

singleTop 或 singleTask 的 Activity 在以下情况会回调 onNewIntent()

singleTop:如果新 Activity 已经位于任务栈的栈顶,就不会重新创建,并回调 onNewIntent(intent) 方法
singleTask:只要该 Activity 在一个任务栈中存在,都不会重新创建,并回调 onNewIntent(intent) 方法

任务栈的作用是什么?技术博客大总结
它是存放 Activity 的引用的,Activity 不同的启动模式,对应不同的任务栈的存放;可通过 getTaskId()来获取任务栈的 ID,如果前面的任务栈已经清空,新开的任务栈 ID+1,是自动增长的;首先来看下 Task 的定义,Google 是这样定义 Task 的:Task 实际上是一个 Activity 栈,通常用户感受的一个 Application 就是一个 Task。从这个定义来看,Task 跟 Service 或者其他 Components 是没有任何联系的,它只是针对 Activity 而言的。

1.0.0.7 两个 Activity 之间怎么传递数据?intent 和 bundle 有什么区别?为什么有了 intent 还要设计 bundle?

两个 Activity 之间怎么传递数据?

基本数据类型可以通过 Intent 传递数据

把数据封装至 intent 对象中
Intent intent = new Intent(content, MeActivity.class);
intent.putExtra(“goods_id”, goods_id);
content.startActivity(intent);

把数据封装至 bundle 对象中技术博客大总结

把 bundle 对象封装至 intent 对象中
Bundle bundle = new Bundle();
bundle.putString(“malename”, “ 李志 ”);
intent.putExtras(bundle);
startActivity(intent);

intent 和 bundle 有什么区别?

Intent 传递数据和 Bundle 传递数据是一回事,Intent 传递时内部还是调用了 Bundle。
public @NonNull Intent putExtra(String name, String value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putString(name, value);
return this;
}

为什么有了 intent 还要设计 bundle?

两者比较

Bundle 只是一个信息的载体,内部其实就是维护了一个 Map<String,Object>。
Intent 负责 Activity 之间的交互,内部是持有一个 Bundle 的。

bundle 使用场景
Fragment 之间传递数据;比如,某个 Fragment 中点击按钮弹出一个 DialogFragment。最便捷的方式就是通过 Fragment.setArguments(args)传递参数。
public static void showFragmentDialog(String title, String content, boolean is_open, AppCompatActivity activity) {
ServiceDialogFragment mainDialogFragment = new ServiceDialogFragment();
Bundle bundle = new Bundle();
bundle.putString(“title”, title);
bundle.putString(“content”, content);
bundle.putBoolean(“is_open”,is_open);
mainDialogFragment.setArguments(bundle);
mainDialogFragment.show(activity.getSupportFragmentManager());
}

@Override
public void onCreate(Bundle savedInstanceState) {
setLocal(Local.CENTER);
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null) {
title = bundle.getString(“title”);
content = bundle.getString(“content”);
is_open = bundle.getBoolean(“is_open”);
}
}

1.0.0.8 知道哪些 Activity 启动模式的标记位?flag 是干什么用的,什么时候用到?

常见的标记为:

FLAG_ACTIVITY_SINGLE_TOP: 对应 singleTop 启动模式
FLAG_ACTIVITY_NEW_TASK:对应 singleTask 模式

1.0.1.0 同一程序不同的 Activity 是否可以放在不同的 Task 任务栈中?

同一程序不同的 Activity 是否可以放在不同的 Task 任务栈中?

可以的。比如:启动模式里有个 Singleinstance,可以运行在另外的单独的任务栈里面。用这个模式启动的 activity,在内存中只有一份,这样就不会重复的开启。
也可以在激活一个新的 activity 时候, 给 intent 设置 flag,Intent 的 flag 添加 FLAG_ACTIVITY_NEW_TASK,这个被激活的 activity 就会在新的 task 栈里面

1.0.1.1 介绍一下 Service,启动 Service 有几种方式,生命周期是怎样的?说一下 onStartCommand()的作用?service 如何杀不死?

Service 分为两种

本地服务,属于同一个应用程序,通过 startService 来启动或者通过 bindService 来绑定并且获取代理对象。如果只是想开个服务在后台运行的话,直接 startService 即可,如果需要相互之间进行传值或者操作的话,就应该通过 bindService。
远程服务(不同应用程序之间),通过 bindService 来绑定并且获取代理对象。

对应的生命周期如下:

context.startService() ->onCreate()- >onStartCommand()->Service running– 调用 context.stopService() ->onDestroy()
context.bindService()->onCreate()->onBind()->Service running– 调用 >onUnbind() -> onDestroy()

注意
Service 默认是运行在 main 线程的,因此 Service 中如果需要执行耗时操作(大文件的操作,数据库的拷贝,网络请求,文件下载等)的话应该在子线程中完成。

Service 生命周期解释技术博客大总结

onCreate():服务第一次被创建时调用
onStartComand():服务启动时调用
onBind():服务被绑定时调用
onUnBind():服务被解绑时调用
onDestroy():服务停止时调用

说一下 onStartCommand()的作用?

service 如何杀不死?

1.onStartCommand 方法,返回 START_STICKY(粘性)当 service 因内存不足被 kill,当内存又有的时候,service 又被重新创建
2. 设置优先级,在服务里的 ondestory 里发送广播 在广播里再次开启这个服务, 双进程守护

1.0.1.2 一个 Activty 先 start 一个 Service 后,再 bind 时会回调什么方法?此时如何做才能回调 Service 的 destory()方法?

关于 service 中 onDestroy()什么时候会被执行?

当调用了 startService()方法后,又去调用 stopService()方法,这时服务中的 onDestroy()方法就会执行,表示服务已经销毁了。
类似地,当调用了 bindService()方法后,又去调用 unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。

一个 Activty 先 start 一个 Service 后,再 bind 时会回调什么方法?

先 start 后 bind 操作 service,此时如何做才能回调 Service 的 destory()方法?

完全有可能对一个服务既调用了 startService()方法,又调用了 bindService()方法的,这种情况下该如何才能让服务销毁掉呢?根据 Android 系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。
这种情况下要同时调用 stopService()和 unbindService()方法,onDestroy()方法才会执行这样就把服务的生命周期完整地走了一遍。技术博客大总结

1.0.1.3 bindService 是一个异步的过程吗?绑定 service 大概需要经历那些过程?
1.0.1.4 是否能在 Service 进行耗时操作?如果非要可以怎么做,如何避免 service 线程卡顿?service 里面可以弹土司吗?

是否能在 Service 进行耗时操作?

默认情况, 如果没有显示的指定 service 所运行的进程,Service 和 Activity 是运行在当前 app 所在进程的 mainThread(UI 主线程)里面。
service 里面不能执行耗时的操作(网络请求, 拷贝数据库, 大文件),在 Service 里执行耗时操作,有可能出现主线程被阻塞(ANR)的情况。

如果非要可以怎么做,如何避免 service 线程卡顿?
需要在子线程中执行 new Thread(){}.start();

service 里面可以弹土司吗?

可以,但是有条件。一般很少这样做……技术博客大总结

条件是,service 里面弹 toast 需要添加到主线程里执行。
@Override
public void onCreate(){
handler = new Handler(Looper.getMainLooper());
System.out.println(“service started”);
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), “Test”,Toast.LENGTH_SHORT).show();
}
});
}

1.0.1.5 Activity 如何与 Service 通信?Service 的生命周期与启动方法有什么区别?

Activity 如何与 Service 通信?

方法一:
添加一个继承 Binder 的内部类,并添加相应的逻辑方法。重写 Service 的 onBind 方法,返回我们刚刚定义的那个内部类实例。Activity 中创建一个 ServiceConnection 的匿名内部类,并且重写里面的 onServiceConnected 方法和 onServiceDisconnected 方法,这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用,在 onServiceConnected 方法中,我们可以得到一个刚才那个 service 的 binder 对象,通过对这个 binder 对象进行向下转型,得到我们那个自定义的 Binder 实例,有了这个实例,做可以调用这个实例里面的具体方法进行需要的操作了

方法二
通过 BroadCast(广播)的形式,当我们的进度发生变化的时候我们发送一条广播,然后在 Activity 的注册广播接收器,接收到广播之后更新视图

1.0.2.0 是否了解 ActivityManagerService,它发挥什么作用,说一下 AMS 启动流程?

ActivityManagerService 是 Android 中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块类似。
https://blog.csdn.net/dutedeh…

1.0.2.1 Android 中哪些事件需要用到广播?广播的生命周期是怎样的?

Android 中哪些事件需要用到广播?
Android 中:系统在运行过程中,会产生会多事件,那么某些事件产生时,比如:电量改变、收发短信、拨打电话、屏幕解锁、开机,系统会发送广播,只要应用程序接收到这条广播,就知道系统发生了相应的事件,从而执行相应的代码。使用广播接收者,就可以收听广播

广播的生命周期是怎样的?

a. 广播接收者的生命周期非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁;
b. 广播接收者中不要做一些耗时的工作,否则会弹出 Application No Response 错误对话框;
c. 最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉;
d. 耗时的较长的工作最好放在服务中完成;

1.0.2.3 广播有几种形式?他们分别有什么特点,如何使用广播?广播是怎么实现不同进程之间通信的?

广播有几种形式

普通广播:一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们接收的先后是随机的。
有序广播:一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,所以此时的广播接收器是有先后顺序的,且优先级(priority)高的广播接收器会先收到广播消息。有序广播可以被接收器截断使得后面的接收器无法收到它。
本地广播:发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收本应用程序发出的广播。
粘性广播:这种广播会一直滞留,当有匹配该广播的接收器被注册后,该接收器就会收到此条广播。

广播的两种注册形式技术博客大总结
广播的注册有两种方法:一种在活动里通过代码动态注册,另一种在配置文件里静态注册。两种方式的相同点是都完成了对接收器以及它能接收的广播值这两个值的定义;不同点是动态注册的接收器必须要在程序启动之后才能接收到广播,而静态注册的接收器即便程序未启动也能接收到广播,比如想接收到手机开机完成后系统发出的广播就只能用静态注册了。

动态注册

需要使用广播接收者时,执行注册的代码,不需要时,执行解除注册的代码。安卓中有一些广播接收者,必须使用代码注册,清单文件注册是无效的。
public class MainActivity extends Activity {
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
intentFilter.addAction(“android.net.conn.CONNECTIVITY_CHANGE”);
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, “network changes”,Toast.LENGTH_SHORT).show();
}
}
}

静态注册
可以使用清单文件注册。广播一旦发出,系统就会去所有清单文件中寻找,哪个广播接收者的 action 和广播的 action 是匹配的,如果找到了,就把该广播接收者的进程启动起来。

1.0.2.8 Fragment 与 Activity 之间是如何传值的?Fragment 与 Fragment 之间是如何传值的?

Fragment 与 Activity 之间是如何传值的?

1.Activity 向 Fragment 传值:

步骤:
要传的值,放到 bundle 对象里;
在 Activity 中创建该 Fragment 的对象 fragment,通过调用
fragment.setArguments()传递到 fragment 中;
在该 Fragment 中通过调用 getArguments()得到 bundle 对象,就能得到里面的值。

2.Fragment 向 Activity 传值:

第一种:

在 Activity 中调用 getFragmentManager()得到 fragmentManager,,调用 findFragmentByTag(tag)或者通过 findFragmentById(id)
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(tag);

第二种:
通过回调的方式,定义一个接口(可以在 Fragment 类中定义),接口中有一个空的方法,在 fragment 中需要的时候调用接口的方法,值可以作为参数放在这个方法中,然后让 Activity 实现这个接口,必然会重写这个方法,这样值就传到了 Activity 中

Fragment 与 Fragment 之间是如何传值的?

第一种:
通过 findFragmentByTag 得到另一个的 Fragment 的对象,这样就可以调用另一个的方法了。

第二种:
通过接口回调的方式。技术博客大总结

第三种:
通过 setArguments,getArguments 的方式。

1.0.2.9 Activity 创建 Fragment 的方式是什么?FragmentPageAdapter 和 FragmentPageStateAdapter 的区别?

Activity 创建 Fragment 的方式是什么?

静态创建具体步骤
首先我们同样需要注册一个 xml 文件,然后创建与之对应的 java 文件,通过 onCreatView()的返回方法进行关联,最后我们需要在 Activity 中进行配置相关参数即在 Activity 的 xml 文件中放上 fragment 的位置。

动态创建具体步骤

(1)创建待添加的碎片实例
(2)获取 FragmentManager,在活动中可以直接通过调用 getSupportFragmentManager()方法得到。
(3)开启一个事务,通过调用 beginTransaction()方法开启。
(4)向容器内添加或替换碎片,一般使用 repalce()方法实现,需要传入容器的 id 和待添加的碎片实例。
(5)提交事务,调用 commit()方法来完成。

FragmentPageAdapter 和 FragmentPageStateAdapter 的区别?

FragmnetPageAdapter 在每次切换页面时,只是将 Fragment 进行分离,适合页面较少的 Fragment 使用以保存一些内存,对系统内存不会多大影响
FragmentPageStateAdapter 在每次切换页面的时候,是将 Fragment 进行回收,适合页面较多的 Fragment 使用,这样就不会消耗更多的内存

1.0.3.0 fragment 特点?说一下 Fragment 的生命周期?如何解决 getActivity 为 null 的异常问题?

fragment 特点

Fragment 可以作为 Activity 界面的一部分组成出现;
可以在一个 Activity 中同时出现多个 Fragment,并且一个 Fragment 也可以在多个 Activity 中使用;
在 Activity 运行过程中,可以添加、移除或者替换 Fragment;
Fragment 可以响应自己的输入事件,并且有自己的生命周期,它们的生命周期会受宿主 Activity 的生命周期影响。

Fragment 从创建到销毁整个生命周期中涉及到的方法依次为:

onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()->onPause()->onStop()->onDestroyView()->onDestroy()->onDetach(),其中和 Activity 有不少名称相同作用相似的方法,而不同的方法有:

onAttach():当 Fragment 和 Activity 建立关联时调用
onCreateView():当 Fragment 创建视图时调用
onActivityCreated():当与 Fragment 相关联的 Activity 完成 onCreate()之后调用
onDestroyView():在 Fragment 中的布局被移除时调用
onDetach():当 Fragment 和 Activity 解除关联时调用

如何解决 getActivity 为 null 的异常问题技术博客大总结
@Override
public void onAttach(Context context) {
super.onAttach(context);
activity = (PhoneNumActivity) context;
}

@Override
public void onDetach() {
super.onDetach();
activity = null;
}

1.0.3.1 在 fragment 中为什么有时 getActivity()会为 null?Fragment 试图为什么有的时候会重叠,怎么产生的,又如何解决?

getActivity()空指针:
这种情况一般发生在在异步任务里调用 getActivity(),而 Fragment 已经 onDetach(),此时就会有空指针,解决方案是在 Fragment 里使用一个全局变量 mActivity,在 onAttach()方法里赋值,这样可能会引起内存泄漏,但是异步任务没有停止的情况下本身就已经可能内存泄漏,相比直接 crash,这种方式显得更妥当一些。

Fragment 视图重叠:
在类 onCreate()的方法加载 Fragment,并且没有判断 saveInstanceState==null 或 if(findFragmentByTag(mFragmentTag) == null),导致重复加载了同一个 Fragment 导致重叠。(PS:replace 情况下,如果没有加入回退栈,则不判断也不会造成重叠,但建议还是统一判断下)
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 在页面重启时,Fragment 会被保存恢复,而此时再加载 Fragment 会重复加载,导致重叠 ;
if(saveInstanceState == null){
// 或者 if(findFragmentByTag(mFragmentTag) == null)
// 正常情况下去 加载根 Fragment
}
}

1.0.3.2 为什么 fragment 传递数据不用构造方法传递?FragmentManager , add 和 replace 有什么区别?

为什么 fragment 传递数据不用构造方法传递?
activity 给 fragment 传递数据一般不通过 fragment 的构造方法来传递,会通过 setArguments 来传递,因为当横竖屏会调用 fragment 的空参构造函数,数据丢失。

FragmentManager , add 和 replace 有什么区别?

使用 FragmentTransaction 的时候,它提供了这样两个方法,一个 add,一个 replace,add 和 replace 影响的只是界面,而控制回退的,是事务。
add 是把一个 fragment 添加到一个容器 container 里。replace 是先 remove 掉相同 id 的所有 fragment,然后在 add 当前的这个 fragment。技术博客大总结

在大部分情况下,这两个的表现基本相同。因为,一般,咱们会使用一个 FrameLayout 来当容器,而每个 Fragment 被 add 或者 replace 到这个 FrameLayout 的时候,都是显示在最上层的。所以你看到的界面都是一样的。但是,使用 add 的情况下,这个 FrameLayout 其实有 2 层,多层肯定要比一层的来得浪费,所以还是推荐使用 replace。当然有时候还是需要使用 add 的。比如要实现轮播图的效果,每个轮播图都是一个独立的 Fragment,而他的容器 FrameLayout 需要 add 多个 Fragment,这样他就可以根据提供的逻辑进行轮播了。而至于返回键的时候,这个跟事务有关,跟使用 add 还是 replace 没有任何关系。
replace()方法会将被替换掉的那个 Fragment 彻底地移除掉,因此最好的解决方案就是使用 hide()和 show()方法来隐藏和显示 Fragment,这就不会让 Fragment 的生命周期重走一遍了。

1.0.3.9 Activitiy 启动流程中 performLaunchActivity 的作用?Activity 启动流程中 handleResumeActivity 的作用?

Activitiy 启动流程中 performLaunchActivity 的作用?

从 ActivityClientRecord 中获取到待启动的 Activity 的组件信息
使用类加载器创建 Activity 对象
通过 LoadedApk 的方法创建 Applicayiton 对象,该对象唯一,不会重复创建。
会创建 ContextImpl 并且建立 Context 和 Activity 的联系,以及创建 PhoneWindow,建立 Window 和 Activity 的联系。
调用 Activity 的 onCreate()

Activity 启动流程中 handleResumeActivity 的作用?

执行 onStart()、onResume()—利用 Instrucmentation
获取 Window
创建 DecorView、设置为不可见 INVISIBLE、建立 DecorView 和 Activity 的联系。
获取 Activity 的 WindowManager
调用 WindowManager.addView(decorView, …)将 DecorView 添加到 WM 中,完成显示的工作。

何时将 DecorView 设置为 VISIBLE? 并且显示出来?技术博客大总结

也是在 handleResumeActivity 中
现将 DecorView 设置为不可见
wm.addView(): 将 DecorView 添加到 Window 总
然后执行 makeVisible 让 DecorView 可见

1.0.4.0 Intent 是什么?Intent 可以传递哪些数据?传递对象的时候为什么要实例化?

Intent 是一种运行时绑定(run-time binding)机制,它能在程序运行过程中连接两个不同的组件。

举例:比如,有一个 Activity 希望打开网页浏览器查看某一网页的内容,那么这个 Activity 只需要发出 WEB_SEARCH_ACTION 给 Android
Android 就会根据 Intent 的请求内容,查询各组件注册时声明的 IntentFilter,找到网页浏览器的 Activity 来浏览网页

Intent 可以传递的数据基本数据类型的数据,数组,还有集合,还有序列化的对象

序列化:表示将一个对象转换成可存储或可传输的状态

Android 中序列化对象方式:技术博客大总结

第一种:JAVA 中的 Serialize 机制,译成串行化、序列化……,其作用是能将数据对象存入字节流当中,在需要时重新生成对象。主要应用是利用外部存储设 备保存对象状 态,以及通过网络传输对象等。
第二种:在 Android 系统中,定位为针对内存受限的设备,因此对性能要求更高,另外系统中采用了新的 IPC(进程间通信)机制,必然要求使用性能更出色的对象传输方式。

1.0.1.2 Activity 如与 Service 通信?Service 的生命周期与启动方法由什么区别?
可以通过 bindService 的方式,先在 Activity 里实现一个 ServiceConnection 接口,并将该接口传递给 bindService()方法,在 ServiceConnection 接口的 onServiceConnected()方法
里执行相关操作。

Service 的生命周期与启动方法由什么区别?
startService():开启 Service,调用者退出后 Service 仍然存在。
bindService():开启 Service,调用者退出后 Service 也随即退出。

Service 生命周期:
只是用 startService()启动服务:onCreate() -> onStartCommand() -> onDestory
只是用 bindService()绑定服务:onCreate() -> onBind() -> onUnBind() -> onDestory
同时使用 startService()启动服务与 bindService()绑定服务:onCreate() -> onStartCommnad() -> onBind() -> onUnBind() -> onDestory
1.1.0.4 广播有哪些注册方式?有什么区别?广播发送和接收原理是什么[binder 如何运作的]?

广播有哪些注册方式?

静态注册:常驻系统,不受组件生命周期影响,即便应用退出,广播还是可以被接收,耗电、占内存。
动态注册:非常驻,跟随组件的生命变化,组件结束,广播结束。在组件结束前,需要先移除广播,否则容易造成内存泄漏。

广播发送和接收原理是什么[binder 如何运作的]?

继承 BroadcastReceiver,重写 onReceive()方法。
通过 Binder 机制向 ActivityManagerService 注册广播。
通过 Binder 机制向 ActivityMangerService 发送广播。
ActivityManagerService 查找符合相应条件的广播(IntentFilter/Permission)的 BroadcastReceiver,将广播发送到 BroadcastReceiver 所在的消息队列中。
BroadcastReceiver 所在消息队列拿到此广播后,回调它的 onReceive()方法。

1.0.4.1 mipmap 系列中 xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi 和 ldpi 存在怎样的关系?
表示不同密度的图片资源,像素从高到低依次排序为 xxxhdpi>xxhdpi>xhdpi>hdpi>mdpi>ldpi,根据手机的 dpi 不同加载不同密度的图片
1.0.4.2 res 目录和 assets 目录的区别?

assets:不会在 R 文件中生成相应标记,存放到这里的资源在打包时会打包到程序安装包中。(通过 AssetManager 类访问这些文件)
res:会在 R 文件中生成 id 标记,资源在打包时如果使用到则打包到安装包中,未用到不会打入安装包中。
res/anim:存放动画资源
res/raw:和 asset 下文件一样,打包时直接打入程序安装包中(会映射到 R 文件中)

1.0.4.3 Context 是什么?Context 有哪些类型,分别作用是什么?Context 下有哪些子类?哪些场景只能用 activity 上下文?

Context 是什么?
Context 是一个抽象基类。在翻译为上下文,也可以理解为环境,是提供一些程序的运行环境基础信息。

Context 有哪些类型,分别作用是什么?

Context 下有两个子类,ContextWrapper 是上下文功能的封装类,而 ContextImpl 则是上下文功能的实现类。
ContextWrapper 又有三个直接的子类,ContextThemeWrapper、Service 和 Application。其中,ContextThemeWrapper 是一个带主题的封装类,而它有一个直接子类就是 Activity,所以 Activity 和 Service 以及 Application 的 Context 是不一样的,只有 Activity 需要主题,Service 不需要主题。

Context 下有哪些子类,主要是干什么的?

Context 一共有三种类型,分别是 Application、Activity 和 Service。
这三个类虽然分别各种承担着不同的作用,但它们都属于 Context 的一种,而它们具体 Context 的功能则是由 ContextImpl 类去实现的,因此在绝大多数场景下,Activity、Service 和 Application 这三种类型的 Context 都是可以通用的。
不过有几种场景比较特殊,比如启动 Activity,还有弹出 Dialog。出于安全原因的考虑,Android 是不允许 Activity 或 Dialog 凭空出现的,一个 Activity 的启动必须要建立在另一个 Activity 的基础之上,也就是以此形成的返回栈。而 Dialog 则必须在一个 Activity 上面弹出(除非是系统级别吐司),因此在这种场景下,我们只能使用 Activity 类型的 Context,否则将会出错。

1.0.4.4 ActivityThread 的 main()的流程大概是怎么样的?

ActivityThread 的 main()的流程大概是怎么样的?

1.0.5.0 序列化的方式有哪些?效率对比有何优势?如何做性能上分析的?

序列化的方式有哪些

Parcelable
Parcelable 是 Android 特有的一个实现序列化的接口,在 Parcel 内部包装了可序列化的数据,可以在 Binder 中自由传输。序列化的功能由 writeToParcel 方法来完成,最终通过 Parcel 的一系列 write 方法完成。反序列化功能由 CREAOR 来完成,其内部标明了如何创建序列化对象和数组,并通过 Parcel 的一系列 read 方法来完成反序列化的过程。

Serializable
Serializable 是 Java 提供的一个序列化接口,是一个空接口,用于标示对象是否可以支持序列化,通过 ObjectOutputStrean 及 ObjectInputStream 实现序列化和反序列化的过程。注意可以为需要序列化的对象设置一个 serialVersionUID,在反序列化的时候系统会检测文件中的 serialVersionUID 是否与当前类的值一致,如果不一致则说明类发生了修改,反序列化失败。因此对于可能会修改的类最好指定 serialVersionUID 的值。

1.0.5.9 界面的刷新为什么需 16.6ms?画面的显示需要哪些步骤?界面保持不变时还会 16.6ms 刷新一次屏幕吗?

界面的刷新为什么需 16.6ms?

系统每 16.6ms 会发出一个 VSYNC 信号,发出信号后,才会开始进行测量、布局和绘制。
发出 VSYNC 信号时,还会将此时显示器的 buffer 缓冲区的数据取出,并显示在屏幕上。

画面的显示需要哪些步骤?

CPU 计算数据(View 树遍历并执行三大流程:测量、布局和绘制),然后将数据交给 GPU“
GPU 渲染处理,然后将数据放到 Buffer 中。
显示屏 (display) 从 buffer 中取出数据,并进行显示。

界面保持不变时还会 16.6ms 刷新一次屏幕吗?技术博客大总结
对于底层显示器,每间隔 16.6ms 接收到 VSYNC 信号时,就会用 buffer 中数据进行一次显示。所以一定会刷新。

界面刷新的本质流程

通过 ViewRootImpl 的 scheduleTraversals()进行界面的三大流程。
调用到 scheduleTraversals()时不会立即执行,而是将该操作保存到待执行队列中。并给底层的刷新信号注册监听。
当 VSYNC 信号到来时,会从待执行队列中取出对应的 scheduleTraversals()操作,并将其加入到主线程的消息队列中。
主线程从消息队列中取出并执行三大流程: onMeasure()-onLayout()-onDraw()

1.0.6.0 Android 中日志级别有哪几种?开发中需要注意什么问题,打印日志源码分析原理是什么?

Android 中日志级别有哪几种?

1.Log.v 的输出颜色为黑色的,输出大于或等于 VERBOSE 日志级别的信息,也就是可见级别,一般是最低的信息提示
2.Log.d 的输出颜色是蓝色的,也就是调式级别,一般不会中止程序,一般是程序员为了调试而打印的 log
3.Log.i 的输出为绿色,输出大于或等于 INFO 日志级别的信息,也就是信息界级别,不会中止程序,一般是系统中执行操作的信息提示
4.Log.w 的输出为橙色, 输出大于或等于 WARN 日志级别的信息,也就是警告级别,一般不会中止程序,但是可能会影响程序执行结果
5.Log.e 的输出为红色,仅输出 ERROR 日志级别的信息,也就是错误级别,一般会中止程序运行,是最严重的 Log 级别。

解释:

verbose
debug 调试
info 信息
warn 警告
error 误差

通过查看源代码我们发现 Log 类中所有的静态日志方法 Log.v(),Log.d(),Log.i(),Log.w(),Log.e()等方法都是底层都是调用了 println 方法,然后在源码中查看,其实其内部调用的是 println_native 方法,也就是通过 JNI 调用底层的 c ++ 输出日志。

关于其他内容介绍
01. 关于博客汇总链接

1. 技术博客汇总

2. 开源项目汇总

3. 生活博客汇总

4. 喜马拉雅音频汇总

5. 其他汇总

02. 关于我的博客

我的个人站点:www.yczbj.org,www.ycbjie.cn
github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/…

简书:http://www.jianshu.com/u/b7b2…

csdn:http://my.csdn.net/m0_37700275

喜马拉雅听书:http://www.ximalaya.com/zhubo…

开源中国:https://my.oschina.net/zbj161…

泡在网上的日子:http://www.jcodecraeer.com/me…

邮箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/a… 239.headeruserinfo.3.dT4bcV
segmentfault 头条:https://segmentfault.com/u/xi…

掘金:https://juejin.im/user/593943…

退出移动版