该文章涉及的内容主要是:
BroadCast
、内容提供者
;
Android基础问题——四大组件之BroadCast、ContentProvider 内容提供者
- 1、BroadCast
- 1.1、Android的广播分类
- 1.2、Android的广播注册方式
- 1.3、广播作用域
- 2、内容提供者`Content provider`
- 2.1、什么是内容提供者?
- 2.2、ContentProvider 是如何实现数据共享的?
- 2.3、说说 ContentProvider、ContentResolver、ContentObserver 之间的关系?
- 2.4、ContentProvider的权限管理
- 2.5、为什么要使用通过ContentResolver类从而与ContentProvider类进行交互,而不直接访问ContentProvider类?
- 2.6、ContentResolver和binder差异
1、BroadCast
1.1、Android的广播分类
广播在执行方式的角度上,可以分类为:
标准广播-Normal broadcast
有序广播-Ordered broadcast
标准广播:一种完全异步执行的广播,所有的广播接收器都可以同时接收到这条广播,彼此没有先后之分,所有的接收者都是同等对待。
注意:标准广播无法被截断,例如:若A、B、C三人同时买彩票就是标准广播,彩票中心发出的中奖号码,A、B、C同时收到消息,无法阻止对方去拦截消息。
有序广播:按照顺序执行的广播方式,同一个时刻只有一个广播接收器能够接收到广播,并且优先级高的广播接收器最先接受到广播,除此之外,接收到广播的接收器还可以截断正在传递的广播,后续的广播接收器就无法收到这条广播了。
1.2、Android的广播注册方式
广播在注册方式上可以分为:
动态注册
静态注册
动态注册:指的是在程序代码中进行广播注册的过程。如下展示了基于动态注册方式的广播代码结构。
@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 onReceiver(Context context, Intent intent) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActivityNetworkInfo();
if (networkInfo != null && networkInfo.isAvailable()) {
Toast.makeText(context, "network is available...", Toast.LENGTH_SHORT);
} else {
Toast.makeText(context, "network is unavailable...", Toast.LENGTH_SHORT);
}
}
}
- 首先,使用
IntentFilter()
设置自身期望捕获到的广播信号,这个信号要和发出的广播信号相同才会有响应。 - 其次,内部类
NetworkChangeReceiver()
继承自BroadcastReceiver
,需要监听广播信号的,都必须要继承该类,并且实现其中的onReceive
方法。 - 紧接着,
registerReceiver
把NetworkChangeReceiver
的对象和IntentFilter
的对象进行注册,方便接收传来的广播信号。 - 最后,
unregisterReceiver
取消了注册,任何注册了广播信号的对象都必须要实现取消注册的功能。
上面的代码实现在onCreate方法中,因此app需要打开才可以接收广播。
静态注册:指的是在AndroidManifest.XML
中注册广播接收的过程,如下所示:
- 外层大框:利用
<receiver>
标签,表明这是一个广播的接收器,在AS中,可以通过新建一个Broadcast Receiver
来自动完成该部分的编写。 - 里层小框“:同样使用了
intent-filter
和action
,表明自身设置的广播信号,在使用权限时,务必记得声明自己需要的权限,否则程序会崩溃。
1.3、广播作用域
主要分为如下:
- 全局广播:指的是可以被其他任何应用程序都能接收到的广播,比如说Android系统发出的电量不足的广播,所有应用程序都能收到。
- 本地广播:指的是只能在应用程序内部传递的广播信号,并且广播接收器也只能接收来自本程序发出的广播。
全局广播的代码实现:
//定义一个继承BroadcastReceiver的MyReceiver类,实现onReceive方法
//onReceive方法会在匹配成功后调用,这里让它简单的显示一下即可
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Toast.makeText(context, "响应了自身设置的广播", Toast.LENGTH_SHORT).show();
}
}
//这个是在AndroidManifest文件中,设置一个自定义的广播"com.example.globalbroadcast.MY_TEST"
//当接收到这条广播时,就会进行匹配,如果匹配成功,就会调用上面的onReceive方法
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.globalbroadcastdemo.MY_TEST"/>
</intent-filter>
</receiver>
//这个是在MainActivity中实现的,设置了一个Button,当做广播信号的发送方。
//广播也仍然是使用intent来发送的,当按下Button,就会通过intent发送一条广播
//注意,这里用到了sendBroadcast()方法,这将发出一个全局广播,所有广播接收器都能收到这条广播
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.testMyBroadcast);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.globalbroadcastdemo.MY_TEST");
sendBroadcast(intent);
}
});
}
}
由于全局广播存在一定的安全问题,因此Android提供了一种仅在App内部传递的广播机制:本地广播。
本地广播实现和全局广播类似,但是本地广播需要调用LocalBroadcastManager
来进行管理,细节存在不同:
// 本地广播的实现,调用了LocalBroadcastManager
// 本地广播仅在本应用程序内传播,外部其他的应用程序接收不到该广播发出的信号
public class MainActivity extends AppCompatActivity {
/**
* intentFilter 意图过滤器,设置自身需要监听的广播信号
*/
private IntentFilter intentFilter;
/**
* localBroadcastManager 本地广播管理
*/
private LocalBroadcastManager localBroadcastManager;
/**
* localReceiver 本地广播接收器
*/
private LocalReceiver localReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取一个本地广播管理的对象
localBroadcastManager = LocalBroadcastManager.getInstance(this);
// 本地广播的发送者
Button button = findViewById(R.id.testMyBroadcast);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.globalbroadcastdemo.MY_LOCAL");
localBroadcastManager.sendBroadcast(intent);
}
});
// 设置自身需要监听的广播信号
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.globalbroadcastdemo.MY_LOCAL");
// 注册本地广播监听器
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter);
}
//LocalReceiver 内部类,实现了onReceive方法
class LocalReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "收到了本地发出的广播", Toast.LENGTH_SHORT).show();
}
}
}
2、内容提供者Content provider
首先给出下图综视:
2.1、什么是内容提供者?
ContentProvider
是Android的四大组件之一。ContentProvider 用来在不同的app之间实现数据共享,并且提供统一的接口。
ContentProvider
是类似数据库中表的方式将数据暴露,也就是说ContentProvider
就像一个数据库,外界获取其数据,类似于数据库的增删改查,只不过是采用URI来表示外界需要访问的“数据库”。
ContentProvider
定义了一个对外开放的接口,从而可以让其他app使用我们自己应用中的文件、数据库内存储的信息。
2.2、ContentProvider 是如何实现数据共享的?
在Android中如果想将自己应用的数据,一般都是数据库中的数据,提供给第三发的应用,那么我们只能通过ContentProvider
来实现。
ContentProvider
是app之间共享数据的接口,使用的时候需要先定义一个类继承ContentProvider
,然后覆写query、insert、update、delete
等方法,因为其四大组件之一因此必须在AndroidManifest
文件中进行注册,把自己的数据通过url的形式共享出去。
第三方可以通过ContentResolver
来访问该Provider。
URI介绍:
每一个ContentResolver
都拥有一个公共的URI,只可以通过这个访问该URI来获得数据。
Android所提供的ContentResolver
都存放在android.provider包中,分为A、B、C、D4个部分:
- A:标准前缀,用来说明一个
ContentResolver
控制这些数据,无法改变。 - B:URI的标识,用于唯一标识这个
ContentResolver
,外部调用者可以根据这个标识来找到它,它定义了哪一个ContentResolver
提供的这些数据,对于第三方的App,为了保证URI的唯一性,它必须是一个完整的、小写的类名,这个标识在元素的authorities
属性中说明:一般是定义该ContentProvider
的包,类的名称。 - C:路径–path,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就行了,content://com.bing.provider.myprovider/tablename
- D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部;content://com.bing.provider.myprovider/tablename/# #表示数据id。
2.3、说说 ContentProvider、ContentResolver、ContentObserver 之间的关系?
首先给出如下图:
1、ContentProvider
:在Android中作用就是对外共享数据,就是说可以通过ContentProvider
把app中的数据共享给其他的app访问,其他应用可以进行增删改查操作;
若采用文件操作模式对外共享数据,数据的访问方式会因为数据存储的方式而不同,从而无法统一数据访问的接口,例如:采用xml
文件对外共享数 据,需要进行xml
解析才能读取数据;采用sharedpreferences
共享数据,需要使用sharedpreferences API
读取数据。使用ContentProvider
对外共享数据的好处是统一了数据的访问方式。
归纳如下:
- 一个app实现
ContentProvider
来提供内容给别的应用来操作,通过ContentProvider
来操作别的app数据,当然在自己的app中也可以。 - 负责管理结构化数据的访问。
- 封装数据并且提供一套定义数据安全的机制。
- 不同进程之间数据访问的接口。
- 为数据跨进程访问提供了一套安全的访问机制,对数据组织和安全访问提供了可靠的保证。
2、ContentResolver
:内容解析者,可以以不同的URI
操作不同的ContentProvider
中的数据,外部进程可以通过ContentResolver
与ContentProvider
进行交互。
- 内容解析者,用于获取内容提供者提供的数据
ContentResolver.notifyChange(uri)
发出消息
3、ContentObserver
:观察ContentObserver
中的数据变化,并将变化通知给外界。
- 内容监听器,可以监听数据的改变状态。
- 目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(
Trigger
),当ContentObserver
所观察的Uri发生变化时,便会触发它。触发器分为表触发器、行触发器,相应地ContentObsever也分为表ContentObserver、行ContentObserver
,当然这是与它所监听的Uri MIME Type
有关的 ContentResolver.registerContentObserver()
方法来监听消息。
2.4、ContentProvider的权限管理
对于ContentProvider
暴露出来的数据,应该是存储自己引用内存中的数据,对于一些存储在外部存储器上的数据,并不能限制访问权限,使用ContentProvider
就没有意义了,对于ContentProvider
而言,有很多权限控制,可以在AndroidManifest
文件中对节点属性进行配置,一般有如下设置:
- android:grantUriPermssions:临时许可标志。
- android:permission:Provider读写权限。
- android:readPermission:Provider的读权限。
- android:writePermission:Provider的写权限。
- android:enabled:标记允许系统启动Provider。
- android:exported:标记允许其他应用程序使用这个Provider。
- android:multiProcess:标记允许系统启动Provider相同的进程中调用客户端。
2.5、为什么要使用通过ContentResolver类从而与ContentProvider类进行交互,而不直接访问ContentProvider类?
一般来说,一款应用要使用多个ContentProvider
,若需要了解每个ContentProvider
的不同实现从而再完成数据交互,操作成本高 & 难度大,所以再ContentProvider
类上加多了一个 ContentResolver
类对所有的ContentProvider
进行统一管理。
// 外部进程向 ContentProvider 中添加数据
public Uri insert(Uri uri, ContentValues values)
// 外部进程 删除 ContentProvider 中的数据
public int delete(Uri uri, String selection, String[] selectionArgs)
// 外部进程更新 ContentProvider 中的数据
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
// 外部应用 获取 ContentProvider 中的数据
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
2.6、ContentResolver和binder差异
如下图所示: