Android 四大组件
本文介绍 Android 四大组件,分别是:Activity(活动),BroadcastReceiver 广播接收器,Service 服务,ContentProvider 内容提供者。
一、Activity(活动):基本组件,每个活动就是一个单独的用户界面。
二、BroadcastReceiver 广播接收器:用于让应用程序对一个外部事件作出相应。
监听广播:
写一个继承
BroadcastReceiver
的类,重新onReceive()
方法,当广播消息到达接收器时,Android 将调用这个方法,并将传递给包含在这消息中的 Intent 对象。广播接收器仅在他执行这个方法时处于活动状态。当onReceive
返回后,广播接收器将不再处于活动状态。广 播接收器的功能类似一个回调函数,只在单次运行时处于活动状态。注册广播接收者
<!--静态注册广播,priority="20"表示广播的级别先接收高级别的在接收地阶别的广播-->
<receiver android:name=".SmsBroadCastReceiver">
<intent-filter android:priority="20">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
// 代码动态注册广播
// 生成广播处理
smsBroadCastReceiver = new SmsBroadCastReceiver();
// 实例化过滤器并设置要过滤的广播
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
// 注册广播
BroadCastReceiverActivity.this.registerReceiver(smsBroadCastReceiver, intentFilter);
注意:
生命周期只有十秒左右,如果在
onReceive()
内做超过十秒内的事情,就会报ANR (Application No Response)
程序无响应的错误信息,如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service,由 Service 来完成。这里不能使用子线程来解决 , 因为BroadcastReceiver
的生命周期很短 , 子线程可能还没有结束BroadcastReceiver
就先结束了。BroadcastReceiver
一旦结束 , 此时BroadcastReceiver
的所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程(没有任何活动组件的进程)。如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死。所以采用子线程来解决是不可靠的。动态注册广播接收器还有一个特点,就是当用来注册的 Activity 关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕 app 本身未启动,该 app 订阅的广播在触发时也会对它起作用。
系统常见广播 Intent:开机启动、电池电量变化、时间改变等广播。
三、Service 服务:一个具有很长的生命周期,但是没有用户界面的程序。
Service 使用步骤:
写一个继承 service 的类,给出服务需要做的事情。
注册服务:
<service name=".MyService" />
启动服务
startService()
和bindService()
startService()
方法启动的服务于调用者没有关系,即使调用者关闭了,服务仍然运行想停止服务要调用Context.stopService()
,此时系统会调用onDestory()
,使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onStart()
,如果服务已经启动再次调用只会触发onStart()
方法。
bindService()
启动的服务与调用者绑定,只要调用者关闭服务就终止,使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onBind()
,如果服务已经启动再次调用不会再触发这 2 个方法,调用者退出时系统会调用服务的onUnbind()-->onDestory()
,想主动解除绑定可使用Contex.unbindService()
,系统依次调用onUnbind()-->onDestory()
。
四、ContentProvider 内容提供者
内容提供者将一些特定的应用程序数据供给其它应用程序使用。内容提供者继承于ContentProvider
基类,为其它应用程序取用和存储它管理的数据实现了一套标准方法。然而,应用程序并不直接调用这些方法,而是使用一个ContentResolver
对象,调用它的方法作为替代。ContentResolver
可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。
1、ContentProvider
Android 提供了一些主要数据类型的 ContentProvider,比如音频、视频、图片和私人通讯录等。可在android.provider
包下面找到一些 Android 提供的 ContentProvider。通过获得这些 ContentProvider 可以查询它们包含的数据,当然前提是已 获得适当的读取权限。
2、ContentResolver
当外部应用需要对 ContentProvider 中的数据进行添加、删除、修改和查询操作时,可以使用 ContentResolver 类来完成,要获取 ContentResolver 对象,可以使用 Context 提供的getContentResolver()
方法。
3、Uri
Uri 指定了将要操作的 ContentProvider,其实可以把一个 Uri 看作是一个网址,我们把 Uri 分为三部分。
第一部分是content://
。可以看作是网址中的http://
。
第二部分是主机名或 authority,用于唯一标识这个 ContentProvider,外部应用需要根据这个标识来找到它。可以看作是网址中的主机名,比如www.123si.org
。
第三部分是路径名,用来表示将要操作的数据。可以看作网址中细分的内容路径。
通过 ContentProvider 对外共享数据步骤:
1、继承 ContentProvider 并重写下面方法:
public class PersonContentProvider extends ContentProvider {
public boolean onCreate()
public Uri insert(Uri uri, ContentValues values)
public int delete(Uri uri, String selection, String[] selectionArgs)
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
public String getType(Uri uri)
}
2、在AndroidManifest.xml
使用<provider>
对该 ContentProvider 进行配置
<provider
android:name=".downloads.DownloadProvider"
android:authorities="com.example.xiazai.downloads" />
3、使用 ContentResolver 操作 ContentProvider 中的数据
// 这里以修改通讯录为例
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person");
// 添加一条记录
ContentValues values = new ContentValues();
values.put("name", "linjiqin");
values.put("age", 25);
resolver.insert(uri, values);
// 获取 person 表中所有记录
Cursor cursor = resolver.query(uri, null, null, null, "personid desc");
while(cursor.moveToNext()) {
Log.i("ContentTest", "personid=" + cursor.getInt(0) + ",name=" + cursor.getString(1));
}
// 把 id 为 1 的记录的 name 字段值更改新为 zhangsan
ContentValues updateValues = new ContentValues();
updateValues.put("name", "zhangsan");
Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
resolver.update(updateIdUri, updateValues, null, null);
// 删除 id 为 2 的记录
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);
4、监听 ContentProvider 中数据的变化
如果 ContentProvider 的访问者需要知道 ContentProvider 中的数据发生变化,可以在 ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)
来通知注册在此 URI 上的访问者,例子如下:
public class PersonContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("person", "personid", values);
getContext().getContentResolver().notifyChange(uri, null);
}
}
如果 ContentProvider 的访问者需要得到数据变化通知,必须使用 ContentObserver 对数据(数据采用 uri 描述)进行监听,当监听到数据变化通知时,系统就会调用 ContentObserver 的onChange()
方法:
getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),
true, new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver {
public PersonObserver(Handler handler) {
super(handler);
}
public void onChange(boolean selfChange) {
// 此处可以进行相应的业务处理
}
}
(完)