第47,48,49,50章
第47章 服务
Service是Android中的四大组件,使用它一定要在AndroidManifest.xml中声明,在AndroidManifest.xml中声明是为了让PackageManagerService能解析出该Service, 并建立对应的数据结构。
Service分为如下三类
- foreground service
fg Service执行一些对于用户来说是可感知的操作,如audio应用使用fg service来播放歌曲。 - background service
bg service执行的操作对用户而言是不可感知的。 - bound service
bound service主要是提供c/s接口,允许组件与service进行通信,或者是跨进程的通信。
其实说到底,由于启动方式的不同导致了三种service,
startService -> background service.
startForegroundService -> foreground service
bindService -> bound service
第48章 广播接收器
广播接收器用于响应来自其他应用程序或者系统的广播消息。这些消息有时被称为事件或者意图。例如,应用程序可以初始化广播来让其他的应用程序知道一些数据已经被下载到设备,并可以为他们所用。这样广播接收器可以定义适当的动作来拦截这些通信。
有以下两个重要的步骤来使系统的广播意图配合广播接收器工作。
- 创建广播接收器
- 注册广播接收器
还有一个附加的步骤,要实现自定义的意图,你必须创建并广播这些意图。 - 创建广播接收器
广播接收器需要实现为BroadcastReceiver类的子类,并重写onReceive()方法来接收以Intent对象为参数的消息。
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Intent Detected.", Toast.LENGTH_LONG).show();
}
}
- 注册广播接收器
应用程序通过在AndroidManifest.xml中注册广播接收器来监听制定的广播意图。假设我们将要注册MyReceiver来监听系统产生的ACTION_BOOT_COMPLETED事件。该事件由Android系统的启动进程完成时发出。
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<receiver android:name="MyReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED">
</action>
</intent-filter>
</receiver>
</application>
现在,无论什么时候Android设备被启动,都将被广播接收器MyReceiver所拦截,并且在onReceive()中实现的逻辑将被执行。
有许多系统产生的事件被定义为类Intent中的静态常量值。
下面是一些重要系统事件:
android.intent.action.BATTERY_CHANGED 持久的广播,包含电池的充电状态,级别和其他信息。
android.intent.action.BATTERY_LOW 标识设备的低电量条件。
android.intent.action.BATTERY_OKAY 标识电池在电量低之后,现在已经好了。
android.intent.action.BOOT_COMPLETED 在系统完成启动后广播一次。
android.intent.action.BUG_REPORT 显示报告bug的活动。
android.intent.action.CALL 执行呼叫数据指定的某人。
android.intent.action.CALL_BUTTON 用户点击"呼叫"按钮打开拨号器或者其他拨号的合适界面。
android.intent.action.DATE_CHANGED 日期发生改变。
android.intent.action.REBOOT 设备重启。
- 广播自定义意图
如果你想要应用程序中生成并发送自定义意图,你需要在活动类中通过sendBroadcast()来创建并发送这些意图。如果你使用sendStickyBroadcast(Intent)方法,则意图是持久的(sticky),这意味者你发出的意图在广播完成后一直保持着。
public void broadcastIntent(View view)
{
Intent intent = new Intent();
intent.setAction("cn.uprogrammer.CUSTOM_INTENT");
sendBroadcast(intent);
}
cn.uprogrammer.CUSTOM_INTENT的意图可以像之前我们注册系统产生的意图一样被注册。
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<receiver android:name="MyReceiver">
<intent-filter>
<action android:name="cn.uprogrammer.CUSTOM_INTENT">
</action>
</intent-filter>
</receiver>
</application>
第49章 闹钟服务
AlarmManager这个类提供对系统闹钟服务的访问接口。你可以为你的应用设定一个在未来某个时间唤醒的功能。当闹钟响起,实际上是系统发出了为这个闹钟注册的广播,会自动开启目标应用。注册的闹钟在设备睡眠的时候仍然会保留,可以选择性地设置是否唤醒设备,但是当设备关机和重启后,闹钟将会被清除。
Android Api demos中就有关于闹钟使用的Demo:com.example.android.apis.app.AlarmController其中设定了两种闹钟,一种是一次性的,一种是重复的。
- 一次性闹钟
// When the alarm goes off, we want to broadcast an Intent to our
// BroadcastReceiver. Here we make an Intent with an explicit class
// name to have our own receiver (which has been published in
// AndroidManifest.xml) instantiated and called, and then create an
// IntentSender to have the intent executed as a broadcast.
Intent intent = new Intent(AlarmController.this, OneShotAlarm.class);
PendingIntent sender = PendingIntent.getBroadcast(
AlarmController.this, 0, intent, 0);
// We want the alarm to go off 10 seconds from now.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 10);
// Schedule the alarm!
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
- 重复闹钟
闹钟设置:
// When the alarm goes off, we want to broadcast an Intent to our
// BroadcastReceiver. Here we make an Intent with an explicit class
// name to have our own receiver (which has been published in
// AndroidManifest.xml) instantiated and called, and then create an
// IntentSender to have the intent executed as a broadcast.
// Note that unlike above, this IntentSender is configured to
// allow itself to be sent multiple times.
Intent intent = new Intent(AlarmController.this,
RepeatingAlarm.class);
PendingIntent sender = PendingIntent.getBroadcast(
AlarmController.this, 0, intent, 0);
// We want the alarm to go off 10 seconds from now.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 10);
// Schedule the alarm!
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), 10 * 1000, sender);
闹钟取消:
// Create the same intent, and thus a matching IntentSender, for
// the one that was scheduled.
Intent intent = new Intent(AlarmController.this,
RepeatingAlarm.class);
PendingIntent sender = PendingIntent.getBroadcast(
AlarmController.this, 0, intent, 0);
// And cancel the alarm.
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.cancel(sender);
第50章 内容提供者
内容提供者是应用程序之间共享数据的接口。应用程序创建的数据库,默认情况下是私有的,别的应用程序访问不到数据,如果想把数据对外提供,就要用到内容提供。ContentProvider屏蔽了数据存储的细节,内部实现对用户完全透明, 用户只需要关心操作数据的uri就可以了,ContentProvider可以实现不同app之间共享。 Sql也有增删改查的方法,但是sql只能查询本应用下的数据库。 而ContentProvider 还可以去增删改查本地文件/xml文件的读取等。Android 系统将这种机制应用到方方面面,比如:联系人(通讯录应用程序)Provider 专为不同应用程序提供联系人数据;短信(短信应用程序)Provider 专为不同应用程序提供系统短信信息。当应用继承ContentProvider 类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用SharedPreferences 共享数据,需要使用SharedPreferences API 读写数据。而使用ContentProvider 共享数据的好处是统一了数据访问方式。总之,内容提供者管理了对结构化数据最常见的就是数据库中数据的访问,操作内容提供者是不同进程之间以数据库数据形式交互数据的标准方式。
自定义的内容提供者包括内容提供者和访问者两个部分。
内容提供者,拥有自己的数据库,将数据库暴露出来供访问者修改。ContenProvider的编写基本步骤:
1. 写一个类继承 ContentProvider;
2. 重写一系列的方法,包括数据库操作的空实现;
3. 在内容提供者代码内部定义UriMatcher -用于判断uri是否匹配
static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
mUriMatcher.addURI("在清单文件里面定义的authorities", "自定义匹配字符串", 成功返回的标识);
}
- 在增删改查执行的时候判断uri是否合法,在内容提供者内部实现对数据库的增删改查;
- 清单文件的下面声明provider,这里需要指定主机名,也就是对外提供的Uri,当访问者在内容解析者中传入同一个uri时,才可以访问到数据库;
<provider
android:name="com.itheima.db.BankDBBackdoor"
android:authorities="自定义主机名" >
</provider>
访问者,存在于另外一个工程中,可以对内提供者的数据库进行操作。
- 创建内容提供者解析器
ContentResolver resolver = 上下文.getContentResolver(); - 定义要访问的Uri路径
Uri uri = Uri.parse("content://自定义主机名/自定义匹配字符串") // “content://”是标准写法 - 利用内容提供者解析器进行增删改查,实现对数据库的操作
内容提供者Uri 的书写模板: content:// 主机名authority/path/id。具体的书写规范如下所示:
- "content://" 这是个固定写法,用来说明一个ContentProvider 控制这些数据。
- 主机名或授权Authority:它定义了是哪个ContentProvider 提供这些数据。
- path:路径,URI 下的某一个Item。
- ID:通常定义Uri 时使用”#”号占位符代替, 使用时替换成对应的数字。#表示数据id,#代表任意数字,*用来来匹配任意文本