Android---BroadcastReceiver
广播接收者(BroadcastReceiver)用于异步接收广播Intent,广播Intent的发送是通过调用Context.sendBroadcast()、Context.sendOrderedBroadcast()或者Context.sendStickyBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收,广播接收者和JMS中的Topic消息接收者很相似。
有两种可以接收广播两大类:
- 正常广播 (与发送
Context.sendBroadcast
)是完全异步的。 广播接收机,所有运行在一个未定义的顺序,通常是在同一时间。 这是更有效,但意味着接收者不能使用的结果或中止的API包括在这里。 - 有序广播 (与发送
Context.sendOrderedBroadcast
)被发送到一个接收器后,一时间。由于每个接收器打开执行中,因此它可以传播到下一个接收器,也可以完全中止播出,以便它不会获得通过其他接收器。 在接收器上运行的顺序可以控制的android :priority过滤属性匹配的意向,优先接收机,同样会在一个任意的顺序运行。
即使在正常的广播案件,该系统在某些情况下可能会恢复到交付一次播出一个接收器。 特别是接收器,可能需要创建,一个进程只有一个将运行一次,以避免超载系统。与新工艺在这种情况下,然而,非有序的语义保持:这些接收器仍不能返回结果或中止他们的广播。
BroadcastReceiver生命周期
广播接收器只有一个回调方法:
void onReceive(Context curContext, Intent broadcastMsg)
当广播消息抵达接收器时,Android调用它的onReceive() 方法并将包含消息的Intent对象传递给它。广播接收器仅在它执行这个方法时处于活跃状态。当onReceive()返回后,它即为失活状态。
拥有一个活跃状态的广播接收器的进程被保护起来而不会被杀死。但仅拥有失活状态组件的进程则会在其它进程需要它所占有的内存的时候随时被杀掉。
这种方式引出了一个问题:如果响应一个广播信息需要很长的一段时间,我们一般会将其纳入一个衍生的线程中去完成,而不是在主线程内完成它,从而保证用户交互过程的流畅。如果onReceive()衍生了一个线程并且返回,则包涵新线程在内的整个进程都被会判为失活状态(除非进程内的其它应用程序组件仍处于活跃状态),于是它就有可能被杀掉。这个问题的解决方法是令onReceive()启动一个新服务,并用其完成任务,于是系统就会知道进程中仍然在处理着工作。
要实现一个广播接收者过程:第一步:继承BroadcastReceiver,并重写onReceive()方法。
public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { } }
第二步:订阅感兴趣的广播Intent,订阅方法有两种:
第一种:使用代码进行订阅 IntentFilter filter = newIntentFilter("android.provider.Telephony.SMS_RECEIVED"); AlarmReceiver receiver = new AlarmReceiver(); registerReceiver(receiver, filter); 第二种:在AndroidManifest.xml文件中的<application>节点里进行订阅: <receiver android:name=".AlarmReceiver"> </receiver>
写一个BroadcastReceiver 的子类别,然后撰写一个Activity子类别来发出Intent 给Android , 由Android 去启动适当的BroadcastReceiver 子类别的对象,并反向呼叫其onReceive()函数来进行相关的动作。
(1) 在activity中点击repeat_alarm按钮BroadcastReceiver 对象就重复接到有AlarmMananger 所发出的Intent,屏幕的Title 区也显示出:内容
(2)如果按下<stop_alarm>按钮,Alarm Manager 就停止发出Intent 了
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConActivity app = ConActivity.getApp();
app.btEvent("AlarmReceiver广播接收器使用");
}
}
当Android 依据Intent 对象的内容而找到AlarmReceiver 时,就拿AlarmReceiver類别來诞生对象,并反向呼叫其onReceive()函數:
public void onReceive(Context context, Intent intent)
{
ConActivity app = ConActivity.getApp();app.btEvent("AlarmReceiver广播接收器使用");
}
为了让你看到这个反向呼叫动作,此函數送回字串给ac01,显示于画面上。当onReceive()函數执行完毕,此对象就被删除了。
import android.app.Activity; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Intent; import android.os.Bundle; import android.os.SystemClock; import android.widget.Button; import android.view.View; import android.view.View.OnClickListener; public class ConActivity extends Activity implements OnClickListener{ private static ConActivity appRe= null;//内部对象 private Button btn, btn2; boolean k = false; // @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); appRe = this; setContentView(R.layout.repeat_alarm); btn = (Button)findViewById(R.id.repeat_al); btn.setBackgroundResource(R.drawable.bk); btn.setOnClickListener(this); btn2 = (Button)findViewById(R.id.stop_al); btn2.setBackgroundResource(R.drawable.bk); btn2.setOnClickListener(this); setTitle("waiting ... Alarm=15"); Intent intent = new Intent(ConActivity.this, AlarmReceiver.class); PendingIntent p_intent = PendingIntent.getBroadcast(ConActivity.this, 0, intent, 0); //创建PendingIntent存储对象//设置定时器Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis()); calendar.add(Calendar.SECOND, 15); AlarmManager am= (AlarmManager)getSystemService(ALARM_SERVICE); am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), p_intent);//获取时钟管理器在设定的时间发出p_intent对象 } public static ConActivity getApp() {return appRe;} public void btEvent( String data ) { k = !k; if(k) setTitle(data); else setTitle("wait..."); } public void onClick(View v) { if(v == btn) { setTitle("Repeating..."); Intent intent = new Intent(ConActivity.this, AlarmReceiver.class); PendingIntent p_intent = PendingIntent.getBroadcast(ConActivity.this, 0, intent, 0); // We want the alarm to go off 30 seconds from now. long firstTime = SystemClock.elapsedRealtime(); firstTime += 800; // Schedule the alarm! AlarmManager am= (AlarmManager)getSystemService( ALARM_SERVICE); am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,firstTime, 800, p_intent);//使用时钟管理器定期重复发出p_Intent } if(arg0 == btn2){ Intent intent = new Intent(ConActivity.this, AlarmReceiver.class); PendingIntent p_intent = PendingIntent.getBroadcast(ConActivity.this, 0, intent, 0); AlarmManager am= (AlarmManager)getSystemService(ALARM_SERVICE); am.cancel(p_intent); finish(); } }}修改AndroidManifest.xml 的内容,更改为: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.misoo.kx01"> <application android:icon="@drawable/icon"> <activity android:name=".ConActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".AlarmReceiver"> </receiver> </application> </manifest>
使用广播接收者窃听短信
当系统收到短信时,会发出一个action名称为android.provider.Telephony.SMS_RECEIVED的广播Intent,该Intent存放了接收到的短信内容,使用名称“pdus”即可从Intent中获取短信内容。 public class IncomingSMSReceiver extends BroadcastReceiver { private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"; @Override
public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(SMS_RECEIVED)) { SmsManager sms = SmsManager.getDefault(); Bundle bundle = intent.getExtras(); if (bundle != null) { Object[] pdus = (Object[]) bundle.get("pdus"); SmsMessage[] messages = new SmsMessage[pdus.length]; for (int i = 0; i < pdus.length; i++) messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); for (SmsMessage message : messages){ String msg = message.getMessageBody(); String to = message.getOriginatingAddress(); sms.sendTextMessage(to, null, msg, null, null); }}}}} 在AndroidManifest.xml文件中的<application>节点里对接收到短信的广播Intent进行订阅: <receiver android:name=".IncomingSMSReceiver"> <intent-filter><action android:name="android.provider.Telephony.SMS_RECEIVED"/></intent-filter></receiver> 在AndroidManifest.xml文件中添加以下权限: <uses-permission android:name="android.permission.RECEIVE_SMS"/><!-- 接收短信权限 --> <uses-permission android:name="android.permission.SEND_SMS"/><!-- 发送短信权限 -->
除了短信到来广播Intent,Android还有很多广播Intent,如:开机启动、电池电量变化、时间已经改变等广播Intent。
接收电池电量变化广播Intent ,在AndroidManifest.xml文件中的<application>节点里订阅此
Intent:
<receiver android:name=".IncomingSMSReceiver">
<intent-filter>
<action android:name="android.intent.action.BATTERY_CHANGED"/>
</intent-filter>
</receiver>
接收开机启动广播Intent,在AndroidManifest.xml文件中的<application>节点里订阅此Intent:
<receiver android:name=".IncomingSMSReceiver">
<intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter></receiver>
并且要进行权限声明:<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
通常一个BroadcastReceiver对象的生命周期不超过5秒,所以在BroadcastReceiver里不能做一些比较耗时的操作,
如果需要完成一项比较耗时的工作,可以通过发送Intent给Activity或Service,由Activity或Service来完成。
public class IncomingSMSReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
//发送Intent启动服务,由服务来完成比较耗时的操作
Intent service = new Intent(context, XxxService.class);
context.startService(service);
//发送Intent启动Activity,由Activity来完成比较耗时的操作
Intent newIntent = new Intent(context, XxxActivity.class);
context.startActivity(newIntent);}}