1.Activity
说下Activity的生命周期?
答:
onStart()和onResume()/onPause()和onStop()的区别?并且这几个方法在那种场景下用到,具体有什么作用?
答:onStart和onStop是从Activity是否可见这个角度来回调的,而onResume和onPause是从Activity是否位于前台这个角度来回调的。
onstart表示Activity可见,但是还不能与用户进行交互,可以理解为Activity已经显示出来了,但是我们还看不见。
onStop表示Activity即将停止,此时可以做一些稍微重量级的回收工作,但不能太耗时,此时Activity已经变得不可见。
onResume表示此时Activity从后台切换到前台,可以与用户进行交互,于onstart相比,onStart和onResume都表示Activity可见,但onstart的时候Activity还在后台,而onResume时Activity从后台切换到前台。onPause表示Activity正在停止,此时Activity切换到后台,不能与用户进行交互。不能再onPause中做重量级的操作。
从Activity的整个生命周期来看,onCreate和ondestory是配对的,分别标识着Activity的创建与销毁,并且只能有一次被调用。
从Activity的可见来说,onstart和onStop是配对的,随着用户的操作或者设备屏幕的点亮与熄灭,这两个方法可能被多次调用。
从Activit是否在前台来说,onResume和onPause是配对的,随着用户的操作或者设备屏幕的点亮与熄灭,这两个方法可能多次被调用。
有两个Activity,分别为Activity A和Activity B,用A启动B ,B 的onResume和A的onPause哪个先执行?
答:追踪android的源码可知,在新的activity启动之前,栈顶的Activity需要先onPause后,新的Activity才能执行。因此在启动ActivityB之前先执行Activity A的onPause,然后启动Activity B 执行Activity B的onResume。
Activity异常生命周期,以及简单说一下这些方法是做什么用的?
答:情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建
在默认情况下,如果我们的Activity不做特殊处理,那么当系统配置发生改变后,Activity就会被销毁并重新创建,其生命周期如下图:
当系统配置发生改变后,Activity会被销毁,其onPause,onStop,onDestroy均会被调用,由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。
如图:当竖屏切换到横屏时,测试log如下:
当由横屏切换到竖屏的时候,测试log如下:
由此我们可以看出,当系统配置发生改变后,Activity会被销毁,其中onPause,onStop,onDestory均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。由上图我们可以看出,onSaveInstanceState调用时机是在onStop之前,需要说明的是这个方法只会出现在Activity被异常终止的情况下,正常情况下系统不会回调这个方法。当系统重建的时候会调用onRestoreInstanceState这个方法,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法,因为我们可以通过onCreate和onRestoreInstanceState方法来判断Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从上图我们可以看出,onRestoreInstanceState的调用时机是在onStart之后。
同时,我们知道onSaveInstanceState和onRestoreInstanceState方法当中,系统为我们做了一定得恢复工作。当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前的Activity的视图结构,并且在Activity重启后为我们恢复这些数据。比如文本框中用户输入的数据,ListVIew滚动的位置等。这些View相关的状态系统都能够默认为我们恢复。
关于保存和恢复View层次结构,系统的工作流程是这样的:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着Window再委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说它可能是DecorView。最后顶层容器再去意义通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这就是一种典型的委托思想,上层委托下次,父容器委托子元素去处理一件事情。
针对onSaveInstanceState方法还需要有一点说明,那就是系统只会在Activity即将被销毁并且有机会重新显示的情况下才会调用它。当Activity正常销毁的时候,系统不会调用onSaveInstanceState,因为被销毁的Activity不可能再次被显示。比如我们上文提到的旋转屏幕所造成的Activity异常销毁,这个过程和正常停止Activity是不一样的,因为旋转屏幕后,Activity被销毁的同时会立刻创建Activity实例,这个时候Activity有机会再次立刻展示,所以系统要进行数据存储。这里可以简单地这么理解,系统只在Activity异常终止的时候才会调用onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,其他情况不会触发这个过程。
情况2:资源内存不足导致低优先级的Activity被杀死
这种情况,不是很好模拟,但是其数据存储和恢复过程和情况1完全一致,这里我们描述一下Activity的优先级情况,Activity按照优先级从高到低,可以分为如下三种:
1)前台Activity——正在和用户交互的Activity,优先级最高
2)可见但是非前台Activity——比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互
3)后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低
当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并且后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。
onSaveInstanceState()和onRestoreInstanceState()何时会调用?
答:1.onSaveInstanceState(Bundle outState)在什么时机会被调用呢?
答案是当activity有可能被系统回收的情况下,而且是在onStop()之前。注意是有可能,如果是已经确定会被销毁,比如用户按下了返回键,或者调用了finish()方法销毁activity,则onSaveInstanceState不会被调用。
或者也可以说,此方法只有在activity被异常终止的情况下会被调用。
总结下,onSaveInstanceState(Bundle outState)会在以下情况被调用:
1、当用户按下HOME键时。
2、从最近应用中选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从当前activity启动一个新的activity时。
5、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。
在前4种情况下,当前activity的生命周期为:
onPause -> onSaveInstanceState -> onStop。
2.onRestoreInstanceState什么时机被调用?
onRestoreInstanceState(Bundle savedInstanceState)只有在activity确实是被系统回收,重新创建activity的情况下才会被调用。
比如第5种情况屏幕方向切换时,activity生命周期如下:
onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
在这里onRestoreInstanceState被调用,是因为屏幕切换时原来的activity确实被系统回收了,又重新创建了一个新的activity。
而按HOME键返回桌面,又马上点击应用图标回到原来页面时,activity生命周期如下:
onPause -> onSaveInstanceState -> onStop -> onRestart -> onStart -> onResume
因为activity没有被系统回收,因此onRestoreInstanceState没有被调用。
如果onRestoreInstanceState被调用了,则页面必然被回收过,则onSaveInstanceState必然被调用过。
onSaveInstanceState()与onPause()的区别?如何避免配置改变时Activity重建?
答:前者适用于对临时性状态的保存,而后者适用于对数据的持久化保存。
如果不手动配置Activity configChanges属性,系统默认对Activity进行销毁重建操作。
API13以下,Activity configChanges属性中含有orientation即可避免Activity销毁重建。
API13及以上,Activity configChanges属性中同时含有orientation和screenSize即可避免Activity销毁重建。
Activity启动模式有哪些?具体说一下使用场景,并绘制进出栈的图形?
答:1.standard
在Activity的栈中无论该活动有没有加入栈,活动就会被创建。
standard加载模式的栈如下所示,无论栈中有没有该对象的实例,都会被创建。
2.singleTop模式
只要被创建的活动不位于栈的顶部,该活动就会被创建入栈。如果将要被创建的活动位于栈的顶部,该活动的实例就不会被创建。测试方法,把上面的模式直接改成singleTop模式,MainActivty往自己身上跳转就不会从新创建一个新的实例,会重用之前在栈顶中的实例。如果是MainActivty跳转到SecondActivty, 然后SecondActivity再次跳转到MainActivty, 那么此时的MainActivity将会被创建,因为栈顶是SecondActivity。如下所示:
应用场景一:可以解决重复打开activity的问题,例如 点击注册按钮(500毫秒你点击了两次) , 如果你用系统默认的启动模式, 就会打开2个注册页面,singTop启动模式的意思是开启actiivty的时候系统会先判断此activity是否存在栈顶, 如果存在就激活此实例 因此就可以解决上述的问题。
应用场景二:在浏览器的书签 特点:检查栈顶是否存在这个实例 如果存在则不重新创建
3.singleTask模式
单任务模式,这个也不难理解,如果从MainActivty跳转到SecondActivity, 如果再从SecondActivty跳转到MainActivity, 在单任务模式下MainActivity已经在栈中,就会把它之前的Activity出栈,使其处于在栈顶活跃的位置。原理如下图所示:
应用场景:浏览器 特点:该实例在任务栈只能存在一个,如果再启动,则把上面的Activity实例全部清除。A --> B --> C -->D在D点击返回键,就返回到A。
4.singleInstance
可以看成单例模式,这个比较特殊,被设置成singleInstance的Activity将会放入另一个栈中,因为这样为了便于共用。上面3中模式位于同一个栈中。下方ThirdActivity跳转到一个加载模式为singleInstance的Activity中。
应用场景:来电显示界面 特点:该实例Activity会创建一个单独的任务栈,且与用户正在交互的界面的任务栈在 前端,直到全部Activity退出.
从非activity中开启activity为什么要添加flag,不添加会出现什么问题?
答:如果不是在Activity中启动的,那就可以看做不是用户主动的行为,也就说这个界面可能出现在任何APP之上,如果不用Intent.FLAG_ACTIVITY_NEW_TASK将其限制在自己的Task中,那用户可能会认为该Activity是当前可见APP的页面,这是不合理的。举个例子:我们在听音乐,这个时候如果邮件Service突然要打开一个Activity,如果不用Intent.FLAG_ACTIVITY_NEW_TASK做限制,那用户可能认为这个Activity是属于音乐APP的,因为用户点击返回的时候,可能会回到音乐,而不是邮件(如果邮件之前就有界面)。
onNewIntent()调用时机?onNewIntent()具体有什么作用?
答:singleInstance:
第一次进入:onCreate onStart
在栈顶再次进入: onNewIntent
不在栈顶再次进入:onNewIntent onRestart onStart
按home键再次进入:onRestart onStart
按返回键:onRestart onStart
standard:
第一次进入:onCreate onStart
在栈顶再次进入: onCreate onStart
不在栈顶再次进入:onCreate onStart
按home键再次进入:onRestart onStart
按返回键:onRestart onStart
singleTop:
第一次进入:onCreate onStart
在栈顶再次进入:onNewIntent
不在栈顶再次进入:onCreate onStart
按home键再次进入:onRestart onStart
按返回键:onRestart onStart
singleTask:
第一次进入:onCreate onStart
在栈顶再次进入:onNewIntent
不在栈顶再次进入:onNewIntent onRestart onStart
按home键再次进入:onRestart onStart
按返回键:onRestart onStart
具体作用:
1. standard
默认启动模式,每次激活Activity时都会创建Activity,并放入任务栈中,永远不会调用onNewIntent()。
2. singleTop
如果在任务的栈顶正好存在该Activity的实例, 就重用该实例,并调用其onNewIntent(),否者就会创建新的实例并放入栈顶(即使栈中已经存在该Activity实例,只要不在栈顶,都会创建实例,而不会调用onNewIntent(),此时就跟standard模式一样)。
3. singleTask
如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的onNewIntent())。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移除栈。如果栈中不存在该实例,将会创建新的实例放入栈中(此时不会调用onNewIntent())。
4. singleInstance
在一个新栈中创建该Activity实例,并让多个应用共享该栈中的该Activity实例。一旦改模式的Activity的实例存在于某个栈中,任何应用再激活改Activity时都会重用该栈中的实例,其效果相当于多个应用程序共享一个应用,不管谁激活该Activity都会进入同一个应用中。
当调用到onNewIntent(intent)的时候,需要在onNewIntent() 中使用setIntent(intent)赋值给Activity的Intent.否则,后续的getIntent()都是得到老的Intent。
2.Service
Service的生命周期
答:
onCreate():
首次创建服务时,系统将调用此方法。如果服务已在运行,则不会调用此方法,该方法只调用一次。
onStartCommand():
当另一个组件通过调用startService()请求启动服务时,系统将调用此方法。
onDestroy():
当服务不再使用且将被销毁时,系统将调用此方法。
onBind():
当另一个组件通过调用bindService()与服务绑定时,系统将调用此方法。
onUnbind():
当另一个组件通过调用unbindService()与服务解绑时,系统将调用此方法。
onRebind():
当旧的组件与服务解绑后,另一个新的组件与服务绑定,onUnbind()返回true时,系统将调用此方法。
1)启动Service服务
单次:startService() —> onCreate() —> onStartCommand()
多次:startService() —> onCreate() —> onStartCommand() —> onStartCommand()
2)停止Service服务
stopService() —> onDestroy()
3)绑定Service服务
bindService() —> onCreate() —> onBind()
4)解绑Service服务
unbindService() —> onUnbind() —> onDestroy()
5)启动绑定Service服务
startService() —> onCreate() —> onStartCommand() —> bindService() —> onBind()
6)解绑停止Service服务
unbindService() —> onUnbind() —> stopService() —> onDestroy()
7)解绑绑定Service服务
unbindService() —> onUnbind(ture) —> bindService() —> onRebind()
Activity如与Service通信?
Activity与Service进行通信的三种方式
第一种 简单通信
直接通过Intent进行传值,我们在启动一个Service的时候通过Intent的对象向Service进行传值,这种方式传递值比较不方便,性能不是很高。
(1)在MainActivity中通过启动服务和终止服务的按钮分别调用startService(intent)和stopService(intent)来启动和停止服务
(2)在Myservice中,我们通过onStartCommand(finalIntent intent, int flags, intstartId)这个函数来接收从Activity传过来的值
第二种 Binder
我们在绑定服务的时候,首先要做的就是让我们的MainActivity实现ServiceConnection类,实现这个类之后,我们还需要重写ServiceConnection类中的两个方法onServiceConnected和onServiceDisconnected,这两个方法分别是在绑定成功和服务所在进程崩溃的时候被调用,如果绑定成功了,那么onServiceConnected(ComponentName componentName, IBinder iBinder) 就会被执行,然后第二个参数IBinder正是MyService中onBind()方法的返回值,因此我们可以通过这个返回值来想MyService传递数据。
(1)MyService 中我们创建一个Binder类,让其实现android.os.Binder类,并且定义一个方法setData,然后我们通过onBind()方法将其对象返回MainActivity。
(2)在MainActivity中,首先添加一个Binder对象,然后在ServiceConnection中获取MyService中返回的Binder对象,接着我们通过Binder对象调用它的方法setData向其传递数据
第三种,监听服务中的进程的变化
在MyService中
(1)添加一个公开的接口Callback
(2)在MyService内部添加一个变量
(3)向外界派发信息
(4)在Binder中返回一个当前的MyService对象,然外部可以添加事件的绑定
在MainActivity中我们可以通过onServiceConnected方法中的iBinder对象来实现MyService中Callback的接口,由于 onDataChange() 是执行在子线程中的,因此我们需要再定义一个Hander对象,将任务由子线程切换到主线程中,让主线程来进行 UI 操作
startService,bindService交叉使用时的生命周期
答:(1)针对startService和bindService:
a.先startService,则调用onCreate,onStartCommand;当bindService时,再调用onBind(不会再调用onCreate)
b.先bindServcie,则调用onCreate,onBind;当startService时,再调用onStartCommand
(2)针对stopService和unbindService:
a.先stopService,则不会调用任何方法,然后当unbindService时,依次调用onUnbind和onDestroy方法
b.先unbindService,则会调用onUnbind,然后当stopService时,会调用onDestroy
详细的log记录如下,其中,MyService中定义MyServiceBinder类(继承自IBinder),该类中定义了start方法(当Activity与service建立连接时,onServiceConnected中调用),end方法(当连接异常断开时,onServiceDisconnected中调用):
1.StartService
(1)start
12-02 07:39:50.986 24755-24755/com.example.wtx.service D/MyService: onCreate
12-02 07:39:50.986 24755-24755/com.example.wtx.service D/MyService: onStartCommand
(2)stop
12-02 07:40:31.731 24755-24755/com.example.wtx.service D/MyService: onDestroy
2.BindService
(1)bind
12-02 07:42:12.212 24755-24755/com.example.wtx.service D/MyService: onCreate
12-02 07:42:12.212 24755-24755/com.example.wtx.service D/MyService: onBind
12-02 07:42:12.226 24755-24755/com.example.wtx.service D/MyService: MyServiceBinder.start
(2)unbind
12-02 07:42:25.559 24755-24755/com.example.wtx.service D/MyService: onUnbind
12-02 07:42:25.559 24755-24755/com.example.wtx.service D/MyService: onDestroy
3.先Start再bind
(1)start
12-02 07:44:34.809 24755-24755/com.example.wtx.service D/MyService: onCreate
12-02 07:44:34.809 24755-24755/com.example.wtx.service D/MyService: onStartCommand
(2)bind
12-02 07:44:47.059 24755-24755/com.example.wtx.service D/MyService: onBind
12-02 07:44:47.075 24755-24755/com.example.wtx.service D/MyService: MyServiceBinder.start
(3)stop
无新log打印
(4)unbind
12-02 07:45:19.796 24755-24755/com.example.wtx.service D/MyService: onUnbind
12-02 07:45:19.796 24755-24755/com.example.wtx.service D/MyService: onDestroy
4.先Start再bind
(1)start
12-02 07:46:05.332 24755-24755/com.example.wtx.service D/MyService: onCreate
12-02 07:46:05.332 24755-24755/com.example.wtx.service D/MyService: onStartCommand
(2)bind
12-02 07:46:06.395 24755-24755/com.example.wtx.service D/MyService: onBind
12-02 07:46:06.409 24755-24755/com.example.wtx.service D/MyService: MyServiceBinder.start
(3)unbind
12-02 07:46:18.284 24755-24755/com.example.wtx.service D/MyService: onUnbind
(4)stop
12-02 07:46:56.315 24755-24755/com.example.wtx.service D/MyService: onDestroy
5.先Bind再start
(1)bind
12-02 07:48:04.821 24755-24755/com.example.wtx.service D/MyService: onCreate
12-02 07:48:04.821 24755-24755/com.example.wtx.service D/MyService: onBind
12-02 07:48:04.832 24755-24755/com.example.wtx.service D/MyService: MyServiceBinder.start
(2)start
12-02 07:48:18.636 24755-24755/com.example.wtx.service D/MyService: onStartCommand
(3)unbind
12-02 07:48:34.132 24755-24755/com.example.wtx.service D/MyService: onUnbind
(4)stop
12-02 07:48:45.197 24755-24755/com.example.wtx.service D/MyService: onDestroy
6.先Bind再start
(1)bind
12-02 07:51:01.932 11090-11090/com.example.wtx.service D/MyService: onCreate
12-02 07:51:01.932 11090-11090/com.example.wtx.service D/MyService: onBind
12-02 07:51:01.944 11090-11090/com.example.wtx.service D/MyService: MyServiceBinder.start
(2)start
12-02 07:51:09.613 11090-11090/com.example.wtx.service D/MyService: onStartCommand
(3)stop
无新log打印
(4)unbind
12-02 07:51:23.592 11090-11090/com.example.wtx.service D/MyService: onUnbind
12-02 07:51:23.592 11090-11090/com.example.wtx.service D/MyService: onDestroy
前台服务是什么?和普通服务的不同?如何去开启一个前台服务?如何去做保活?
前台服务是那些被认为用户知道(用户所认可的)且在系统内存不足的时候不允许系统杀死的服务。前台服务必须给状态栏提供一个通知,它被放到正在运行(Ongoing)标题之下——这就意味着通知只有在这个服务被终止或从前台主动移除通知后才能被解除。
前台服务与普通服务的区别:
前台Service的系统优先级更高、不易被回收;
前台Service会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。
实现前台服务的步骤:
其实很简单就是将服务创建后创建一个Notification就好,利用提供好的方法进行显示和移除就好
1.创建一个Service,在这个Service的onCreate方法中创建一个Notification
要请求让服务运行于前台,请调用 startForeground()。此方法采用两个参数:唯一标识通知的整型数和状态栏的 Notification。
注意:提供给 startForeground() 的整型 ID 不得为 0。
如何保活后台服务:
1. 提高Service的优先级:
android:priority="1000"
2.把service写成系统服务,将不会被回收:
在Manifest.xml文件中设置persistent属性为true,则可使该服务免受out-of-memory killer的影响。但是这种做法一定要谨慎,系统服务太多将严重影响系统的整体运行效率。
3.将服务改成前台服务foreground service:
重写onStartCommand方法,使用StartForeground(int,Notification)方法来启动service。
注:一般前台服务会在状态栏显示一个通知,最典型的应用就是音乐播放器,只要在播放状态下,就算休眠也不会被杀,如果不想显示通知,只要把参数里的int设为0即可。
同时,对于通过startForeground启动的service,onDestory方法中需要通过stopForeground(true)来取消前台运行状态。
4.利用Android的系统广播
利用ANDROID的系统广播检查Service的运行状态,如果被杀掉,就再起来,系统广播是Intent.ACTION_TIME_TICK,这个广播每分钟发送一次,我们可以每分钟检查一次Service的运行状态,如果已经被结束了,就重新启动Service。
3.Broadcast Receiver
广播有哪些注册方式?有什么区别?
答:1、在应用程序的代码中注册(动态注册)
注册BroadcastReceiver:
registerReceiver(receiver,filter);
取消注册BroadcastReceiver:
unregisterReceiver(receiver);
当BroadcastReceiver更新UI,通常会使用这样的方法注册。启动Activity时候注册BroadcastReceiver,Activity不可见时候,取消注册。
2、在androidmanifest.xml当中注册(静态注册)
1)第一种不是常驻型广播,也就是说广播跟随程序的生命周期。
2)第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
使用这样的方法注册弊端:它会始终处于活动状态,毕竟是手机开发,cpu和电源资源比较少,一直处于活动耗费大,不利。
BroadcastReceiver与LocalBroadcastReceiver有什么区别?
答:BroadcastReceiver是针对应用间、应用与系统间、应用内部进行通信的一种方式
LocalBroadcastReceiver仅在自己的应用内发送接收广播,也就是只有自己的应用能收到,数据更加安全广播只在这个程序里,而且效率更高。
BroadcastReceiver 使用
1.制作intent(可以携带参数)
2.使用sendBroadcast()传入intent;
3.制作广播接收器类继承BroadcastReceiver重写onReceive方法(或者可以匿名内部类啥的)
4.在java中(动态注册)或者直接在Manifest中注册广播接收器(静态注册)使用registerReceiver()传入接收器和intentFilter
5.取消注册可以在OnDestroy()函数中,unregisterReceiver()传入接收器
LocalBroadcastReceiver 使用
LocalBroadcastReceiver不能静态注册,只能采用动态注册的方式。
在发送和注册的时候采用,LocalBroadcastManager的sendBroadcast方法和registerReceiver方法
4.ContentProvider
ContentProvider、ContentResolver与ContentObserver之间的关系是什么?
答:ContentProvider:Android应用程序运行在不同的进程空间中,因此不同应用程序的数据是不能够直接访问的。为了增强程序之间的数据共享能力,Android系统提供了像SharedPreferences这类简单的跨越程序边界的访问方法,但这些方法都存在一定的局限性,提供数据的能力有限,安卓系统提供了另一种跨进程提供数据的方式也就ContentProvider翻译过来叫做:数据提供者,是应用程序之间共享数据的一种接口机制,其他应用程序则可以在不知道数据来源的情况下,对共享数据进行增删改查等操作。在Android系统中,许多系统内置的数据也是通过ContentProvider提供给用户使用,例如通讯录、音视频图像文件等。
调用者不能直接调用ContentProvider的接口函数,需要通过ContentResolver对象,通过URI间接调用ContentProvider,Android系统根据URI确定处理这个查询的ContentProvider。
ContentObserver:内容观察者,观察)特定Uri引起的数据库的变化,继而做一些相应的处理,当ContentObserver所观察的Uri发生变化时,便会触发它回调onChange方法。ContentObserver的编写:创建一个类继承自ContentObserver,重写onChange,监听的的url数据发生变化时就会回调此方法。
简单说一下ContentProvider的权限管理
答:在AndroidManifest.xml中provider标签中有三个额外的参数permission、readPermission、writePermission;
先看下面这段代码:
android:name=".PeopleContentProvider"
android:authorities="com.harvic.provider.PeopleContentProvider"
android:exported="true"
android:permission="com.harvic.contentProviderBlog"
android:readPermission="com.harvic.contentProviderBlog.read"
android:writePermission="com.harvic.cotentProviderBlog.write"/>
在这段代码中有几个参数要特别注意一下:
exported:这个属性用于指示该服务是否能被其他程序应用组件调用或跟他交互; 取值为(true | false),如果设置成true,则能够被调用或交互,否则不能;设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。具体参见:《Permission Denial: opening provider 隐藏的android:exported属性的含义
readPermission:使用Content Provider的查询功能所必需的权限,即使用ContentProvider里的query()函数的权限;
writePermission:使用ContentProvider的修改功能所必须的权限,即使用ContentProvider的insert()、update()、delete()函数的权限;
permission:客户端读、写 Content Provider 中的数据所必需的权限名称。 本属性为一次性设置读和写权限提供了快捷途径。 不过,readPermission和writePermission属性优先于本设置。 如果同时设置了readPermission属性,则其将控制对 Content Provider 的读取。 如果设置了writePermission属性,则其也将控制对 Content Provider 数据的修改。也就是说如果只设置permission权限,那么拥有这个权限的应用就可以实现对这里的ContentProvider进行读写;如果同时设置了permission和readPermission那么具有readPermission权限的应用才可以读,拥有permission权限的才能写!也就是说只拥有permission权限是不能读的,因为readPermission的优先级要高于permission;如果同时设置了readPermission、writePermission、permission那么permission就无效了。