Android 四大组件

本文介绍 Android 四大组件,分别是:Activity(活动),BroadcastReceiver 广播接收器,Service 服务,ContentProvider 内容提供者。

一、Activity(活动):基本组件,每个活动就是一个单独的用户界面。

二、BroadcastReceiver 广播接收器:用于让应用程序对一个外部事件作出相应。

监听广播:

  1. 写一个继承BroadcastReceiver的类,重新onReceive()方法,当广播消息到达接收器时,Android 将调用这个方法,并将传递给包含在这消息中的 Intent 对象。广播接收器仅在他执行这个方法时处于活动状态。当onReceive返回后,广播接收器将不再处于活动状态。广 播接收器的功能类似一个回调函数,只在单次运行时处于活动状态。

  2. 注册广播接收者

    1. <!--静态注册广播,priority="20"表示广播的级别先接收高级别的在接收地阶别的广播-->
    2. <receiver android:name=".SmsBroadCastReceiver">
    3. <intent-filter android:priority="20">
    4. <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
    5. </intent-filter>
    6. </receiver>
    1. // 代码动态注册广播
    2. // 生成广播处理
    3. smsBroadCastReceiver = new SmsBroadCastReceiver();
    4. // 实例化过滤器并设置要过滤的广播
    5. IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
    6. // 注册广播
    7. BroadCastReceiverActivity.this.registerReceiver(smsBroadCastReceiver, intentFilter);

注意:

  1. 生命周期只有十秒左右,如果在onReceive()内做超过十秒内的事情,就会报ANR (Application No Response)程序无响应的错误信息,如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service,由 Service 来完成。这里不能使用子线程来解决 , 因为BroadcastReceiver的生命周期很短 , 子线程可能还没有结束BroadcastReceiver就先结束了。BroadcastReceiver一旦结束 , 此时BroadcastReceiver的所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程(没有任何活动组件的进程)。如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死。所以采用子线程来解决是不可靠的。

  2. 动态注册广播接收器还有一个特点,就是当用来注册的 Activity 关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕 app 本身未启动,该 app 订阅的广播在触发时也会对它起作用。

系统常见广播 Intent:开机启动、电池电量变化、时间改变等广播。

三、Service 服务:一个具有很长的生命周期,但是没有用户界面的程序。

Service 使用步骤:

  1. 写一个继承 service 的类,给出服务需要做的事情。

  2. 注册服务:<service name=".MyService" />

  3. 启动服务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 并重写下面方法:

  1. public class PersonContentProvider extends ContentProvider {
  2. public boolean onCreate()
  3. public Uri insert(Uri uri, ContentValues values)
  4. public int delete(Uri uri, String selection, String[] selectionArgs)
  5. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
  6. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
  7. public String getType(Uri uri)
  8. }

2、在AndroidManifest.xml使用<provider>对该 ContentProvider 进行配置

  1. <provider
  2. android:name=".downloads.DownloadProvider"
  3. android:authorities="com.example.xiazai.downloads" />

3、使用 ContentResolver 操作 ContentProvider 中的数据

  1. // 这里以修改通讯录为例
  2. ContentResolver resolver = getContentResolver();
  3. Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person");
  4. // 添加一条记录
  5. ContentValues values = new ContentValues();
  6. values.put("name", "linjiqin");
  7. values.put("age", 25);
  8. resolver.insert(uri, values);
  9. // 获取 person 表中所有记录
  10. Cursor cursor = resolver.query(uri, null, null, null, "personid desc");
  11. while(cursor.moveToNext()) {
  12. Log.i("ContentTest", "personid=" + cursor.getInt(0) + ",name=" + cursor.getString(1));
  13. }
  14. // 把 id 为 1 的记录的 name 字段值更改新为 zhangsan
  15. ContentValues updateValues = new ContentValues();
  16. updateValues.put("name", "zhangsan");
  17. Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
  18. resolver.update(updateIdUri, updateValues, null, null);
  19. // 删除 id 为 2 的记录
  20. Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
  21. resolver.delete(deleteIdUri, null, null);

4、监听 ContentProvider 中数据的变化

如果 ContentProvider 的访问者需要知道 ContentProvider 中的数据发生变化,可以在 ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此 URI 上的访问者,例子如下:

  1. public class PersonContentProvider extends ContentProvider {
  2. public Uri insert(Uri uri, ContentValues values) {
  3. db.insert("person", "personid", values);
  4. getContext().getContentResolver().notifyChange(uri, null);
  5. }
  6. }

如果 ContentProvider 的访问者需要得到数据变化通知,必须使用 ContentObserver 对数据(数据采用 uri 描述)进行监听,当监听到数据变化通知时,系统就会调用 ContentObserver 的onChange()方法:

  1. getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),
  2. true, new PersonObserver(new Handler()));
  3. public class PersonObserver extends ContentObserver {
  4. public PersonObserver(Handler handler) {
  5. super(handler);
  6. }
  7. public void onChange(boolean selfChange) {
  8. // 此处可以进行相应的业务处理
  9. }
  10. }

(完)