android面试题
activity相关
1 .activity启动流程****
Oncreate()---onstart()---onresume()--onpause()--onstop--ondestory()
2.onSaveInstanceState(),onRestoreInstanceState的 调用 时机
onSaveInstanceState()是当activity有可能被系统回收的情况下,而且是在onStop()之前。注意是有可能,如果是已经确定会被销毁,比如用户按下了返回键,或者调用了finish()方法销毁activity,则onSaveInstanceState不会被调用。
总结下,onSaveInstanceState(Bundle outState)会在以下情况被调用:
1、当用户按下HOME键时。
2、从最近应用中选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从当前activity启动一个新的activity时。
5、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。
在前4种情况下,当前activity的生命周期为:
onPause -> onSaveInstanceState -> onStop。
onRestoreInstanceState(Bundle savedInstanceState)只有在activity确实是被系统回收,重新创建activity的情况下才会被调用。
onCreate()里也有Bundle参数,可以用来恢复数据,它和onRestoreInstanceState有什么区别?
因为onSaveInstanceState 不一定会被调用,所以onCreate()里的Bundle参数可能为空,如果使用onCreate()来恢复数据,一定要做非空判断。
而onRestoreInstanceState的Bundle参数一定不会是空值,因为它只有在上次activity被回收了才会调用
而且onRestoreInstanceState是在onStart()之后被调用的。有时候我们需要onCreate()中做的一些初始化完成之后再恢复数据,用onRestoreInstanceState会比较方便。下面是官方文档对onRestoreInstanceState的说明:
它是说,用onRestoreInstanceState方法恢复数据,你可以决定是否在方法里调用父类的onRestoreInstanceState方法,即是否调用super.onRestoreInstanceState(savedInstanceState);
而用onCreate()恢复数据,你必须调用super.onCreate(savedInstanceState);
否则运行会报错误
3 . Activity的四种启动模式应用场景*
standard标准模式:如果在mainfest中不设置就默认standard;standard就是新建一个Activity就在栈中新建一个activity实例
singleTop栈顶复用模式:这种启动模式下,如果要启动的Activity已经处于栈的顶部,那么此时系统不会创建新的实例,而是直接打开此页面,同时它的onNewIntent()方法会被执行,我们可以通过Intent进行传值,而且它的onCreate(),onStart()方法不会被调用,因为它并没有发生任何变化。场景:登录、WXPayEntryActivity、WXEntryActivity、
推送
Singletask栈内单例模式:栈内只有一个activity实例,栈内已存activity实例,在其他activity中start这个activity,Android直接把这个实例上面其他activity实例踢出栈GC掉;场景:主页面(Fragment的containerActivity)
Singleinstance堆内单例:整个手机操作系统里面只有一个实例存在就是内存单例;场景:系统Launcher、锁屏键、来电显示等系统应用
4 . Activity A跳转Activity B,再按返回键,生命周期执行的顺序
备注:只要 activity 处于可见的状态,就不会执行 onStop 方法!
在activityA跳转activityB是:执行的是A onpause,B oncreate,B onstart, B onresume,A onstop
在Activity B返回activityA是;执行 B onpause, A onrestart ,A onstart,A onresume,B onstop,B ondestroy;
如果打开的activityB是一个弹窗,那么activityA打开弹窗activityB执行的生命周期是 :A onpause,B oncreate ,B onstart, B onresume
ActivityB返回activityA时执行的是B onpause, A onResume, B onstop, B ondestory.
5. 横竖屏切换,按home键,按返回键,锁屏与解锁屏幕,跳转透明Activity界面,启动一个 Theme 为 Dialog 的 Activity,弹出Dialog时Activity的生命周期
竖屏切横屏:onpause,onstop,ondestroy,oncreate,onstart,onresume
按Home键:Onpause onstop
按返回键:onpause,onstop,ondestroy,
按手机开关键时,屏幕锁屏:Onpause,onstop
解锁:onrestart,onstart,onresume
6 . onStart 和 onResume、onPause 和 onStop 的区别
onstart当activity可见时调用
onresume当activity可交互时调用
onpause当activity不可交互时调用
Onstop 当activity不可见时调用
7.Activity之间传递数据的方式Intent是否有大小限制,如果传递的数据量偏大,有哪些方案
有限制 , Intent携带信息的大小其实是受Binder限制,Binder传递缓存有一个限定大小,通常是1Mb,但同一个进程中所有的传输共享缓存空间,在使用Intent传递数据时,1Mb并不是安全上限。因为Binder中可能正在处理其它的传输工作。 不同的机型和系统版本,这个上限值也可能会不同。一般大于500kb就会出现异常。
如果传输的数据量过大,会报Transaction TooLarge Exception
可以使用eventBus()的粘性事件,可以使用静态把数据设置为全局(待确认)
8 . Activity的onNewIntent()方法什么时候会执行
当我们在activity的启动模式中设置为栈内唯一时,也就是android:launchMode=”singleTask”或android:launchMode=”signleTop”时,会用到这个方法。
9.显示启动和隐式启动
显示启动可直接使用intent传入对应的类名startactivity或startservice
隐士启动需要在androidmainfest对应的activity里面配置intentfilter , 通过指定的action跳转 。 可跨进程调用 , 比如打电话
10.scheme使用场景,协议格式,如何使用
使用场景
服务器下发跳转路径,客户端根据服务器下发跳转路径跳转相应的页面
H5页面点击锚点,根据锚点具体跳转路径APP端跳转具体的页面
APP端收到服务器端下发的PUSH通知栏消息,根据消息的点击跳转路径跳转相关页面
APP根据URL跳转到另外一个APP指定页面
协议格式 xl://goods:8888/goodsDetail?goodsId=10011002
通过上面的路径 Scheme、Host、port、path、query全部包含,基本上平时使用路径就是这样子的。
· xl代表该Scheme 协议名称
· goods代表Scheme作用于哪个地址域
· goodsDetail代表Scheme指定的页面
· goodsId代表传递的参数
· 8888代表该路径的端口号
URL Scheme如何使用:
1.)在AndroidManifest.xml中对标签增加设置Scheme
<activity android:name=".GoodsDetailActivity" android:theme="@style/AppTheme">
2.)获取Scheme跳转的参数
Uri uri = getIntent().getData();
if (uri != null) {
// 完整的url信息
String url = uri.toString();
Log.e(TAG, "url: " + uri);
// scheme部分
String scheme = uri.getScheme();
Log.e(TAG, "scheme: " + scheme);
// host部分
String host = uri.getHost();
Log.e(TAG, "host: " + host);
//port部分
int port = uri.getPort();
Log.e(TAG, "host: " + port);
// 访问路劲
String path = uri.getPath();
Log.e(TAG, "path: " + path);
List pathSegments = uri.getPathSegments();
// Query部分
String query = uri.getQuery();
Log.e(TAG, "query: " + query);
//获取指定参数值
String goodsId = uri.getQueryParameter("goodsId");
Log.e(TAG, "goodsId: " + goodsId);
}
3.)调用方式
Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse("xl://goods:8888/goodsDetail?goodsId=10011002"));
startActivity(intent); 11.ANR 的四种场景
· KeyDispatchTimeout(最常见类型)—— input事件5s内未处理完成导致ANR发生,主要为按键和触摸事件;(InputDispatching Timeout)
· BroadcastTimeout:—— BroadcastReceiver在特定时间内未处理完成导致ANR发生(限制:前台广播10s;后台广播60s);(BroadcastQueue TimeOut)ServiceTimeout)
· Service在特定的时间内未处理完成导致ANR发生。(限制:前台服务20s;后台服务200s);(Sercive TimeOut)
· ContentProviderTimeout —— 内容提供者,在10s内未处理完成导致ANR发生;(ContentProvider TimeOut) 12.activty间传递数据的方式****
1. 使用putextra
2 .使用bundle****
3 .使用activity销毁时传递 ( startActivityForResult ,onActivityResult(), setResult ())****
4. SharedPreferences传递数据****
****5. 使用 序列 化对象Seriazable
6.使用静态变量传递数据****
13 . 跨App启动Activity的方式,注意事项
使用隐士意图调用 ,
AppB 在 Mainfest 中声明权限
在 AppA 中需要也声明 AppB 的这个权限
AppB 必须必 AppA 先安装才可以访问
1) 需要注意防止被外部app调用(在androidmanifest文件中设置android:exported=“false”)
2) 拒绝服务漏洞,我们可以在intent.getextra的时候加上try{}catch{}
3) 尽量不要将activity暴露出来,如果需要暴露(exported=“true”),最好加上权限
§ 总结:跨 App 启动 Activity 首先要明确 App 之间的关系
§ 外部可启动 exported 或有 intentFilter 的 Activity
§ 可外部启动的 Activity 需要拒绝服务漏洞
§ 尽量不暴露 Activity,为暴露的 Activity 加权限控制
§ Intent 的 Extras 读取时要捕获异常****
14.Activity任务栈是 什么
android任务栈又称为Task,它是一个栈结构,具有后进先出的特性,用于存放我们的Activity组件
我们每次打开一个新的Activity或者退出当前Activity都会在一个称为任务栈的结构中添加或者减少一个Activity组件,因此一个任务栈包含了一个activity的集合, android系统可以通过Task有序地管理每个activity,并决定哪个Activity与用户进行交互:只有在任务栈栈顶的activity才可以跟用户进行交互****
在我们退出应用程序时,必须把所有的任务栈中所有的activity清除出栈时,任务栈才会被销毁。当然任务栈也可以移动到后台, 并且保留了每一个activity的状态. 可以有序的给用户列出它们的任务, 同时也不会丢失Activity的状态信息。****
需要注意的是,一个App中可能不止一个任务栈,某些特殊情况下,单独一个Actvity可以独享一个任务栈。还有一点就是一个Task中的Actvity可以来自不同的App,同一个App的Activity也可能不在一个Task中。**** 按下Home键回到桌面,再启动另一个应用,这时候之前那个Task就被移到后台,成为后台任务栈,而刚启动的那个Task就被调到前台,成为前台任务栈,Android系统显示的就是前台任务栈中的Top实例Activity。
AndroidManifest文件中的属性android:launchMode 或 通过Intent的flag 15 : 有哪些Activity常用的标记位Flags****
FLAG_ ACTIVITY_NEW_TASK
这个标记位的作用是指定Activity启动模式为”singleTask“,其作用等同于在AndroidManifest中指定android:launchMode="singleTask"相同。
FLAG_ACTIVITY_SINGLE_TOP
这个标记位的作用是指定Activity启动模式为”singleTop“,其作用等同于在AndroidManifest中指定android:launchMode="singleTop"相同。
FLAG_ACTIVITY_CLEAR_TOP
具有此标记位的Activity,当它启动的时候,在同一个任务栈中所有位于它上面的Activity都要出栈。这个模式一般需要和FLAG_ ACTIVITY_NEW_TASK配合使用,在这种情况下,如果调用的Activity的实例已存在,那么系统就会回调方法onNewIntent()。
如果被启动的Activity采用的是standard,那么它连同它之上的Activity都要出栈,系统会重新创建Activity实例并放入栈顶。singleTask模式其实默认就具有此标记位的效果。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标记的Activity不会出现在历史Activity的列表中,当某些情况我们不希望用户通过历史列表回到我们这个Activity的时候,这个标记将起作用。它等同于在AndroidManifest中为Activity添加属性:android:excludeFromRecents=“true” Activity的数据是怎么保存的,进程被Kill后,保存的数据怎么恢复的****
在onsaveinstancestate保存数据,在onrestoreinstancestate恢复数据
Service相关
1 . service 的生命周期,两种启动方式的区别
StartService的生命周期为:onCreate –> onStartCommand(可多次调用) –> onDestroy。 BindService的生命周期为:onCreate –> onBind(只一次,不可多次绑定) –> onUnbind –> onDestory。
通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。
1.bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
2.client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
3.bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁。
2.Service启动流程
不了解
3.Service与Activity怎么实现通信
- 通过Binder对象
当Activity通过调用bindService(Intent service, ServiceConnection conn,int flags),我们可以得到一个Service的一个对象实例,然后我们就可以访问Service中的方法
- 通过broadcast(广播)的形式
当我们的进度发生变化的时候我们发送一条广播,然后在Activity的注册广播接收器,接收到广播之后更新activity
4.IntentService是什么,IntentService原理,应用场景及其与Service的区别
IntentService是继承Service的,那么它包含了Service的全部特性,当然也包含service的生命周期,那么与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去你执行你的耗时操作。另外IntentService中在onHandleIntent方法中处理耗时操作,处理完成之后会自动的结束自己。无需调用stopSelf()方法停止Service;
原理:intentService 就是封装好的Service + Handler + Thread,一般用于后台执行耗时任务,拥有Service的特点(比如优先级比较高,不容易被系统杀死)。 源码分析:在intentService的onCreate方法中开启了一个handleThread(子线程 + 初始化好looper等),同时用handler绑定该子线程的Looper。在onStartCommand中会将intent封装进Message中,发送,最后在handler的handMessage方法中,调用onHandleIntent(因此该方法是在handleThread线程操作)。
- 通过
HandlerThread单独开启一个名为IntentService的线程 - 创建一个名叫
ServiceHandler的内部Handler - 把内部Handler与HandlerThread所对应的子线程进行绑定
- 通过
onStartCommand()传递给服务intent,依次插入到工作队列中,并逐个发送给onHandleIntent() - 通过
onHandleIntent()来依次处理所有Intent请求对象所对应的任务
使用intentService与service有什么不同呢
(1)直接 创建一个默认的工作线程,该线程执行所有的intent传递给onStartCommand()区别于应用程序的主线程。
(2)直接创建一个工作队列,将一个意图传递给你onHandleIntent()的实现,所以我们就永远不必担心多线程。
(3)当请求完成后自己会调用stopSelf(),所以你就不用调用该方法了。
(4)提供的默认实现onBind()返回null,所以也不需要重写这个方法。so easy啊
(5)提供了一个默认实现onStartCommand(),将意图工作队列,然后发送到你onHandleIntent()实现。真是太方便了
5.Service 的 onStartCommand 方法有几种返回值?各代表什么意思? 在service中,onStartCommand()方法有三种返回值: START_STICKY(常量值:1):sticky的意思是“粘性的”。使用这个返回值时,我们启动的服务跟应用程序"粘"在一起,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务。当再次启动服务时,传入的第一个参数将为null; START_NOT_STICKY(常量值:2):“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。 START_REDELIVER_INTENT(常量值:3):重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
6.bindService和startService混合使用的生命周期以及怎么关闭
BroadcastReceiver相关
1.广播的分类和使用场景 广播的类型主要分为:
- 普通广播 普通广播对于多个接收者来说是完全异步的,通常每个接收者都无需等待即可以接收到广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他接收者的 接收动作。
- 系统广播 Android中内置了很多系统广播:只要涉及到手机的基本操作(开机、网络变化、插入耳机等),都可以通过发送系统广播来监听变化,通过发送对应的intent-filter(包括action)
- 无序广播
无序广播即为我们平时经常使用的广播,其主要是通过public abstract void sendBroadcast (Intent intent)方法进行发送,并通过intent传递数据。无序广播会被注册了的相应的感兴趣(intent-filter匹配)接收,且顺序是无序的。如果发送广播时有相应的权限要求,BroadCastReceiver如果想要接收此广播,也需要有相应的权限。
无序广播不可以被拦截,不可以被终止,不可以被修改,无序广播任何接收者只要匹配条件都可以接收到,无优先级问题。 - 有序广播 有序广播比较特殊,每次发送广播会先发送到优先者高的地方,然后再通过优先者高的往低的发送,优先者高的可以截断广播,那么之后的接收者就接收不到广播了,可以在广播注册时使用intent-filter里面的android: priority=”xxx”去解决或在java代码中用setPriority(xxx)来设置。
- 粘性广播 粘性消息在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理,如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态,粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据。
- App应用内广播
具体使用1 - 将全局广播设置成局部广播
1.注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
2.在广播发送和接收时,增设相应权限permission,用于权限验证;
3.发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
广播的两种注册方式的区别 区别:
在AndroidManifest中进行注册后,不管该应用程序是否处于活动状态,都会进行监听,比如某个程序时监听 内存的使用情况的,当在手机上安装好后,不管改应用程序是处于什么状态,都会执行改监听方法中的内容。
在代码中进行注册后,当应用程序关闭后,就不再进行监听。我们读知道,应用程序是否省电,决定了该应用程序的受欢迎程度,所以,对于那些没必要在程序关闭后仍然进行监听的Receiver,在代码中进行注册,无疑是一个明智的选择
广播发送和接收的原理 1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
2.广播发送者通过binder机制向AMS发送广播;
3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
4.消息循环执行拿到此广播,回调给BroadcastReceiver中的onReceive()方法。
本地广播和全局广播的区别 本地广播是广播事件的发送和接收都在本应用,不影响其他应用也不受其他应用影响,只能被动态注册,不能静态注册,主要用法都在LocalBroadcastManager类中。数据更加安全广播只在这个程序里,而且效率更高。 全局广播是可以接收其他应用发的广播,也可以发送广播让其他应用接收,全局广播既可以动态注册,也可以静态注册,接受其他应用和系统广播是全局广播的一个重要应用点。总体来说两者应用场景不同。
4.ContentProvider
- 1.什么是ContentProvider及其使用 实现各个应用程序之间的(跨应用)数据共享,比如联系人应用中就使用了ContentProvider,你在自己的应用中可以读取和修改联系人的数据,不过需要获得相应的权限。其实它也只是一个中间人,真正的数据源是文件或者SQLite等
一个应用实现ContentProvider来提供内容给别的应用来操作,通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以。
- 2.ContentProvider,ContentResolver,ContentObserver之间的关系 ContentResolver:
- 内容解析者,用于获取内容提供者提供的数据
- ContentResolver.notifyChange(uri)发出消息
- 内容监听器,可以监听数据的改变状态 ContentObserver:
- 目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,
简单一句话来描述就是:使用ContentResolver来获取ContentProvider提供的数据,同时注册ContentObserver监听Uri数据的变化
-
3.ContentProvider的实现原理
-
ContentProvider是通过IBinder实现通信过程的
-
getContentResolver获得到的是ApplicationContentResolver(在ContextImpt中实现的)
-
Client端ApplicationContentResolver使用ContentProviderProxy作为IBinder的Proxy(ContentProviderNative中实现)
-
Provider端通过Transport作为IBinder的实现端(ContentProvider中实现)
-
4.ContentProvider的优点 1.ContentProvider提供了对底层数据存储方式的抽象。如底层可以采用SQLite方式存储数据,使用 ContentProvider封装之后,即便底层换成XML存储也不会对上层应用代码产生影响。
2.Android框架中的一些类需要ContentProvider类型数据。如想让数据可以使用在SyncAdapter, Loader,CursorAdapter等类上,那么就需要为数据做一层ContentProvider封装。
3.ContentProvider为应用间的数据交互提供了一个安全的环境。它准许你把自己的应用数据根据需求 开放给其它应用进行增、删、改、查,而不用担心直接开放数据库权限而带来的安全问题。 参考:www.jianshu.com/p/147169640… -
5.Uri 是什么 是统一资源标识符(URI),外界进程通过
URI找到对应的ContentProvider& 其中的数据,再进行数据操作