BAT大咖助力 全面升级Android面试-3android基础
一,Activity面试详解
1,activity的四种状态
- running 活动状态。用户点击屏幕,屏幕做出响应。置于栈顶
- paused 失去焦点状态。用户点击屏幕,无响应。(可能被新的非全屏的activity或一个透明的activity覆盖),成员变量,状态信息存在。内存紧张,activity回收
- stopped 该activity被另一个activity完全覆盖的时候。成员变量,状态信息存在。内存紧张,activity回收
- killed activity被回收
2,activity的生命周期
activity启动->onCreate()->onStart()->onResume()
点击home键回到主界面(activity不可见)->onPause()->onStop()
当我们再次回到原activity时->onRestart()->onStart()->onResume()
退出当前activity时->onPause()->onStop()->onDestroy()
-
onCreate :Activity第一次被实例化的时候系统会调用,整个生命周期只调用1次该方法。
通常用于初始化设置: 1、为Activity设置所要使用的布局文件。2、为安卓控件绑定监听器等静态的设置操作。 -
onStart(
可见不可交互
):当Activity可见未获得用户焦点不能交互时系统会调用。 -
onResume(
可见可交互
) :当Activity和用户发生交互的时候,触发该方法。 -
onPause (
有一点可见不能交互
):当一个正在前台运行的Activity因为其他的Activity需要前台运行而转入后台运行的时候,触发该方法。 -
onStop(
不可见不能交互
):当Activity被其他的Activity完全覆盖不可见时,触发该方法,如果内存紧张,系统会直接结束这个Activity,而不会触发 onStop 方法。 -
onRestart (
由不可见到可见
):当处于停止状态的Activity需要再次展现给用户的时候,触发该方法。 -
onDestroy :当Activity(用户调用finish()或系统由于内存不足)被系统销毁时调用,(整个生命周期只调用1次)用来释放onCreate ()方法中创建的资源,如结束线程,清空数据,注销广播等。和 onStop 方法一样,如果内存紧张,系统会直接结束这个Activity而不会触发该方法。
具体区别 https://blog.csdn.net/superjunjin/article/details/44674917
3,android进程优先级
参考自:https://blog.csdn.net/qinxiandiqi/article/details/51744782
android对于所有进程的处理态度都是尽可能不杀死
。然而,资源总共就那么多,要是对所有进程都保持宽容的话,资源总会有消耗殆尽的时候。因此,在内存不足的情况,android系统需要根据一定的策略,选择性的杀死部分进程。这个策略就是对所有的进程标记优先级,优先级低的先杀死
。
android将进程的优先级分为5个层次,按照优先级由高到低排列如下:
(1)前台进程(Foreground process)。
它表明用户正在与该进程进行交互操作
,android系统依据下面的条件来将一个进程标记为前台进程:
- 该进程持有一个用户正在与其交互的Activity(也就是这个activity的生命周期方法走到了onResume()方法)。
- 该进程持有一个Service,并且这个Service与一个用户正在交互中的Activity进行绑定。
- 该进程持有一个前台运行模式的Service(也就是这个Service调用了startForegroud()方法)。
- 该进程持有一个正在执行生命周期方法(onCreate()、onStart()、onDestroy()等)的Service。
- 该进程持有一个正在执行onReceive()方法的BroadcastReceiver。
一般情况下,不会有太多的前台进程
。杀死前台进程是操作系统最后无可奈何的做法。当内存严重不足的时候,前台进程一样会被杀死。
(2)可见进程(Visible process)。
它表明虽然该进程没有持有任何前台组件
,但是它还是能够影响到用户看得到的界面。android系统依据下面的条件将一个进程标记为可见进程:
- 该进程持有一个非前台Activity,但这个Activity依然能被用户看到(也就是这个Activity调用了onPause()方法)。例如,当一个activity启动了一个对话框,这个activity就被对话框挡在后面。
- 该进程持有一个与可见(或者前台)Activity绑定的Service。
(3)服务进程(Service process)。
除了符合前台进程和可见进程条件的Service,其它的Service都会被归类为服务进程。
(4)后台进程(Background process)。
持有不可见Activity(调用了onStop()方法)的进程即为后台进程
。通常情况下都会有很多后台进程,当内存不足的时候,在所有的后台进程里面,会按照LRU(最近使用)规则,优先回收最长时间没有使用过的进程
。
(5)空进程(Empty process)。不持有任何活动组件的进程
。保持这种进程只有一个目的,就是为了缓存,以便下一次启动该进程中的组件时能够更快响应
。当资源紧张的时候,系统会平衡进程缓存和底层的内核缓存情况进行回收。
如果一个进程同时满足上述5种优先级中的多个等级条件,android系统会优先选取其中最高的等级作为该进程的优先级
。例如,一个进程持有一个Service(服务进程等级)和一个前台Activity(前台进程等级),那么操作系统会将这个进程标记为前台进程。
另外需要注意的是,如果一个进程为另外一个进程提供服务,那么这个进程的优先级不会低于享受服务的进程
。例如,假设进程A中的content provider为进程B提供服务,或者进程A中有一个Service与进程B中的组件进程绑定,那么进程A的优先级至少要与进程B一致,或者高于进程B。
4,activity启动模式
参考自:https://blog.csdn.net/mynameishuangshuai/article/details/51491074
实际应用时,取类的TaskId查看所属栈id
,类的hashCode查看类实例id
(1)standard-标准模式
这个模式是默认
的启动模式,即标准模式。每次启动一个Activity都会重写创建一个新的实例
,不管这个实例存不存在,这种模式下,谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中
。这个Activity它的onCreate(),onStart(),onResume()方法都会被调用。
(2)singleTop-栈顶复用模式
这个模式下,如果新的activity已经位于栈顶,那么这个Activity不会被重写创建,同时它的onNewIntent方法会被调用
(onCreate(),onStart()方法不会被调用),通过此方法的参数我们可以去除当前请求的信息。如果栈顶不存在该Activity的实例,则情况与standard模式相同。
taskAffinity
standard和singleTop启动模式都是在原任务栈中新建Activity实例,不会启动新的Task,即使你指定了taskAffinity属性
。
那么什么是taskAffinity属性呢,可以简单的理解为任务相关性。
- 这个参数标识了一个Activity所需任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名
- 我们可以单独指定每一个Activity的taskAffinity属性覆盖默认值
- 一个任务的affinity决定于这个任务的根activity(root activity)的taskAffinity(
没明白
) - 在概念上,具有相同的affinity的activity(即设置了相同taskAffinity属性的activity)属于同一个任务
- 为一个activity的taskAffinity设置一个空字符串,表明这个activity不属于任何task
(3)singleTask-栈内复用模式
这个模式十分复杂,有各式各样的组合。在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity
,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈
,并且会回调该实例的onNewIntent方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定
。如果这个任务栈不存在,则会创建这个任务栈。
singleTask启动模式启动Activity时,首先会根据taskAffinity去寻找当前是否存在一个对应名字的任务栈
- 如果不存在,则会创建一个新的Task,并创建新的Activity实例入栈到新创建的Task中去
- 如果存在,则得到该任务栈,查找该任务栈中是否存在该Activity实例
- 如果存在实例,则将它上面的Activity实例都出栈,然后回调启动的Activity实例的onNewIntent方法
- 如果不存在该实例,则新建Activity,并入栈
此外,我们可以将两个不同App中的Activity设置为相同的taskAffinity,这样虽然在不同的应用中,但是Activity会被分配到同一个Task中去。
(4)singleInstance-全局唯一模式
该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性
,即整个系统中就这么一个实例,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
总结:
standard,创建一个新的Activity。
singleTop,栈顶不是该类型的Activity,创建一个新的Activity。否则,onNewIntent。
singleTask,回退栈中没有该类型的Activity,创建Activity,否则,onNewIntent+ClearTop。
singleInstance,回退栈中,只有这一个Activity,没有其他Activity。
应用场景:
参考自:https://blog.csdn.net/qq_35114086/article/details/52614714
参考自:https://blog.csdn.net/CodeEmperor/article/details/50481726
singleTop
1,适合接收通知启动的内容显示页面。
比如新闻客户端收到了100个推送,你每次点一下推送他都会走一遍某个activiy(显示新闻只用一个activity,只是内容不同而已)的oncreate,onstart,代价就是创建了那么多的activity(不要以为只有推送,比如qq消息提醒)
a,耗内存
b, 走了100次的跳转动画 ,也是醉了
2,某些业务需求是自己的activity跳转到自己的。
比如 歌曲搜索 你又不会用actionbar,toolbar,那就在activity上面弄个假的呗,搜索完了自己跳转自己现实搜索结果,那就直接用该模式,变化都在onNewIntent()里面对控件进行赋值之类的。而且也没有跳转动画,人们以为这个activity没有动了,多high。
singleTask
适合作为程序入口点。
例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。
singleInstance
适合需要与程序分离开的页面。
例如闹铃提醒,将闹铃提醒与闹铃设置分离。打电话 接通的时候。地图显示的时候。
他们都有一个特点 就是你做完这件事了都会返回到上一界面 而没有下一界面。
singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,再次启动,首先打开的是B。(ac在一个栈,退出c后,退出a,退出ac的栈,再退出b,最后b置于栈顶
)
http://stackoverflow.com/questions/2626218/examples-for-android-launch-modes
5,scheme
参考自:android-Scheme与网页跳转原生的三种方式
(1)什么是 URL Scheme?
android中的scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面;通过scheme协议,服务器可以定制化告诉App跳转那个页面,可以通过通知栏消息定制化跳转页面,可以通过H5页面跳转页面等
。
(2)URL Scheme应用场景:
客户端应用可以向操作系统注册一个 URL scheme(在activity中添加scheme的信息
),该 scheme 用于从浏览器或其他应用中启动本应用。通过指定的 URL 字段,可以让应用在被调起后直接打开某些特定页面,比如商品详情页、活动详情页等等。也可以执行某些指定动作,如完成支付等。也可以在应用内通过 html 页来直接调用显示 app 内的某个页面。综上URL Scheme使用场景大致分以下几种:
服务器下发跳转路径,客户端根据服务器下发跳转路径跳转相应的页面
H5页面点击锚点,根据锚点具体跳转路径APP端跳转具体的页面
APP端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面
APP根据URL跳转到另外一个APP指定页面
例:大型公司通过其热门app的一个链接跳转到其公司的其他app(微信-->腾讯新闻)
深入学习:Android产品研发(十一)-->应用内跳转Scheme协议
二,Fragment面试详解
1,Fragment为什么会被称为第五大组件
Fragment有自己的生命周期,但是不能独立使用,要加载到activity中使用。
优点
(1)fragment相比activity更节省内存。
(2)能动态灵活的加载到Activity中去配合viewpager使用,UI切换更方便舒适。
2,Fragment加载到Activity的两种方式
(1)静态加载,fragment作为标签加载到activity的布局文件中
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
(2)动态加载
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
DemoFragment fragment = new DemoFragment();
fragmentTransaction.add(R.id.content, fragment);//容器资源作为标志位,把fragment添加到对应的位置
fragmentTransaction.addToBackStack("DemoFragment");
fragmentTransaction.commit();
3,FragmentPagerAdapter与FragmentStatePagerAdapter(源码分析)
FragmentPagerAdapter适用于页面较少的情况
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment)object).getView());
mCurTransaction.detach((Fragment)object);
}
FragmentStatePagerAdapter适用于页面较多的情况,每次切换回收内存
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
mCurTransaction.remove(fragment);
}
4,Fragment生命周期
[图片上传失败...(image-5dfb7b-1524976453038)]
参考自:https://blog.csdn.net/i_wait_for_you/article/details/70240075
https://www.cnblogs.com/LangZXG/p/6501839.html
https://blog.csdn.net/superjunjin/article/details/44783279
由于Fragment依赖Activity的存在而存在,故在创建时Activity生命周期中的方法均先于Fragment生命周期中的方法执行;相反,在销毁时,是先执行Fragment生命周期中的方法再执行Activity生命周期中的方法。
5,Fragment通信
在Fragment中调用Activity中的方法 getActivity
在Fragment中调用Fragment中的方法 接口回调
在Fragment中调用Fragment中的方法 findFragmentById
在Fragment间利用广播
参考自 https://blog.csdn.net/u012702547/article/details/49786417
6,Fragment部分方法
参考自 https://blog.csdn.net/harvic880925/article/details/44927375(详细清晰 写的太棒了)
add:添加Fragment,置于栈顶
replace:替换Fragment,清空对应容器Fragment,置于栈顶
remove:移除Fragment,显示下层Fragment
addToBackStack:添加事务
到回退栈,置于栈顶
popBackStack:将最上层的事务
弹出回退栈(根据具体参数,可以回退到对应的事务)
Fragment回滚以事务
(FragmentTransaction)为单位,一个事务可能包含一个或多个Fragment
Fragment拓展阅读
Fragment全解析系列(一):那些年踩过的坑
Square:从今天开始抛弃Fragment吧!
三,service面试详解
service是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件,可由其他组件启动,一旦启动可在后台长时间运行,即使启动它的组件已经被销毁,也不会受影响。
1,service与Thread
参考自:https://blog.csdn.net/wei_chong_chong/article/details/52251193
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。thread是由本应用程序托管
。可以用 Thread 来执行一些异步的操作。可做耗时操作
。
2). Service:Service 是android的一种机制,系统的组件,它由系统进程托管(servicemanager)
。当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。不可做耗时操作
,如果要做,service中添加Thread去做
Thread的使用
典型的应用中,Thread可以在以下三个位置被创建,不同的位置,其生命周期不一样,所以,我们应该根据该Thread的目标生命周期来决定是在Service中创建Thread还是在Activity中创建它。
(1) 在Activity中被创建
这种情况下,一般在onCreate时创建,在onDestroy()中销毁,否则,Activity销毁后,Thread是会依然在后台运行着。
这种情况下,Thread的生命周期即为整个Activity的生命周期。所以,在Activity中创建的Thread只适合完成一些依赖Activity本身有关的任务,比如定时更新一下Activity的控件状态等
。
核心特点:该Thread的就是为这个Activity服务的,完成这个特定的Activity交代的任务,主动通知该Activity一些消息和事件,Activity销毁后,该Thread也没有存活的意义了。
(2)在Application中被创建
这种情况下,一般自定义Application类,重载onCreate方法,并在其中创建Thread,当然,也会在onTerminate()方法中销毁Thread,否则,如果Thread没有退出的话,即使整个Application退出了,线程依然会在后台运行着。
这种情况下,Thread的生命周期即为整个Application的生命周期。所以,在Application中创建的Thread,可以执行一些整个应用级别的任务,比如定时检查一下网络连接状态等等
。
核心特点:该Thread的终极目标是为这个APP的各个Activity服务的,包括完成某个Activity交代的任务,主动通知某个Activity一些消息和事件等,APP退出之后该Thread也没有存活的意义了。
以上这两种情况下,Thread的生命周期都不应该超出整个应用程序的生命周期,也就是,整个APP退出之后,Thread都应该完全退出,这样才不会出现内存泄漏或者僵尸线程。那么,如果你希望整个APP都退出之后依然能运行该Thread,那么就应该把Thread放到Service中去创建和启动了。
(3)在Service中被创建
这是保证最长生命周期的Thread的唯一方式,只要整个Service不退出,Thread就可以一直在后台执行,一般在Service的onCreate()中创建,在onDestroy()中销毁。
所以,在Service中创建的Thread,适合长期执行一些独立于APP的后台任务,比较常见的就是:在Service中保持与服务器端的长连接
。
2,service启动
(1),startService
- 定义一个类继承service
- 在manifest.xml文件中配置该service
- 使用context的startService(Intent)方法启动该service
- 不再使用时,调用stopService(Intent)方法停止该服务
- onCreate首次创建服务执行,onStartCommand方法每次重启服务都会调用
(2),bindService
- 创建BindService服务端,继承自Service并在类中,创建一个实现IBinder接口的实例对象并提供公共方法给客户端调用;
- 从onBind()回调方法返回此Binder实例;
- 在客户端中,从onServiceConnected()回调方法中接收Binder,并使用提供的方法调用绑定服务
- 多个activity可以绑定一个服务
- 绑定解除,服务销毁
四,BroadcastReceiver面试详解
1,广播定义
在android中,broadcast是一种广泛运用在应用程序内部或之间传输信息的机制,Android中要发送的广播内容是一个Intent,这个Intent中可以携带我们要传送的数据。
2,广播使用场景
参考自:https://www.cnblogs.com/lwbqqyumidi/p/4168017.html
1.同一app内部的同一组件内的消息通信(单个或多个线程之间);(可用handler解决
)
2.同一app内部的不同组件之间的消息通信(单个进程);(可用EventBus
)
3.同一app具有多个进程的不同组件之间的消息通信;
4.不同app之间的组件之间消息通信;
5.Android系统广播在特定情况下与App之间的消息通信。
后三种常用广播解决
3,广播的种类
参考自:https://blog.csdn.net/carson_ho/article/details/53160580
- 普通广播(Normal Broadcast)
- 系统广播(System Broadcast)
- 有序广播(Ordered Broadcast)
- 粘性广播(Sticky Broadcast)
- App应用内广播(Local Broadcast)
4,广播的注册
参考自:https://blog.csdn.net/u012702547/article/details/46955787
- 静态注册:注册完成就一直运行
- 动态注册:跟随activity的生命周期
5,内部实现机制
(1),自定义广播接收器BroadcastReceiver,并复写onReceive方法
(2),通过Binder机制向AMS(Activity Manager Service
)进行注册
(3),广播发送者通过Binder机制向AMS发送广播
(4),AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般是activity)相应的消息循环队列中
(5),消息循环执行,拿到此广播,回调BroadcastReceiver的onReceive方法
6,LocalBroadcastManager详解
具体说明 https://blog.csdn.net/carson_ho/article/details/53160580
- 高效性,比系统的全局广播更高效
- 安全性,只在自身app内传播,不必担心信息泄露
源码分析
- LocalBroadcastManager高效的原因主要是因为内部是通过Handler发送message实现的,它的sendBroadcast方法含义和我们平时所用的不一样。
- 既然是Handler实现的,所以相比系统广播通过Binder实现更高效,同时使用Handler实现,也使广播控制在应用内部,不会发出去也不会被攻击。
- LocalBroadcastManager内部协作主要是靠这两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要是存储待接收的广播对象