Android開發之接收系統廣播訊息
BroadcastReceiver除了接收使用者所傳送的廣播訊息之外,還有一個重要的用途:接收系統廣播。如果應用需要在系統特定時刻執行某些操作,就可以通過監聽系統廣播來實現。Android的大量系統事件都會對外發送標準廣播。以下是Android常見的廣播Action常量(詳請參考Android API文件中關於Intent的說明)。
Action常量 |
說明 |
ACTION_TIME_CHANGED |
系統時間被改變。 |
ACTION_DATE_CHANGED |
系統日期被改變。 |
ACTION_TIMEZONE_CHANGED |
系統時區被改變。 |
ACTION_BOOT_COMPLETED |
系統啟動完成。 |
ACTION_PACKAGE_ADDED |
新的應用程式被安裝。 |
ACTION_PACKAGE_CHANGED |
應用程式被改變。 |
ACTION_PACKAGE_REMOVED |
應用程式被解除安裝。 |
ACTION_PACKAGE_RESTARTED |
應用程式被重新啟動。 |
ACTION_PACKAGE_DATA_CLEARED |
應用程式資料被清理。 |
ACTION_BATTERY_CHANGED |
電池電量改變。 |
ACTION_SHUTDOWN |
系統被關閉。 |
ACTION_BATTRY_LOW |
電池電量低。 |
ACTION_POWER_CONNECTED |
外接電源被連通。 |
ACTION_POWER_DISCONNECTED |
外接電源被斷開。 |
ACTION_SHUTDOWN |
系統關閉。 |
ACTION_NEW_OUTGOING_CALL |
播出電話。 |
ACTION _PHONE_STATE |
系統通話狀態改變。 |
通過 來監聽特殊的廣播,即可以實現應用跟隨系統執行特定的某些操作。
例項一:開機自動執行的Service
在實際應用總往往會讓一些應用跟隨系統啟動,比如一個黑名單來電自動攔截的APP,監聽垃圾簡訊的APP... ...等等。為了讓Service能跟隨系統啟動,我們需要讓BroadcastReceiver監聽Action名為:ACTION_BOOT_COMPLETED
程式清單:
public class LaunchWithSys extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Intent launchService=new Intent(context, LaunchService.class);
//啟動指定Service
context.startActivity(launchService);
}
}
從上面看該LaunchWithSys非常簡單,只需要啟動指定的Service即可,接下來需要註冊用於監聽系統開機廣播的BroadcastReceiver:因此需要在AndroidManifest.xml註冊一個下面的接收器:
<!—註冊一個監聽系統開機廣播的BroadcastReceiver à
<receiver android:name=”com.jph.monitorbroadcastfromsys.LaunchWithSys”>
<intent-filter>
<action android:name=”android.intent.action.BOOT_COMPLETED”></action>
</intent-filter>
</receiver>
除此之外,為了能夠讓程式訪問開機啟動事件,需要為程式新增如下的許可權:
<pre name="code" class="html"><!—為程式新增訪問系統開機事件的許可權 à
<uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED”/>
例項二:短息提醒
在Android中當系統收到簡訊後會發出一個有序的廣播,該廣播的Intent的Action為android.provider.Telephony.SMS_RECEIVED。因此只需要在程式中開發一個對應的BroadcastReceiver即可監聽到系統接收到的簡訊。
程式清單:
public class SmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//如果收到簡訊
if (intent.getAction().equals(“android.provider.Telephony.SMS_RECEIVED”)) {
//取消這條有序廣播(取消後會讓其它應用收不到簡訊)
abortBroadcast();
Bundle bundle=intent.getExtras();
if (bundle!=null) {//如果資料不為空
//獲得收到的簡訊資料
Object[] objArray=(Object[]) bundle.get(“pdus”);
//根據objArray的大小建立一個SmsMessage陣列,用於封裝簡訊內容
SmsMessage []smsMessage=new SmsMessage[objArray.length];
StringBuffer sb=new StringBuffer();
sb.append(“時間:”+new DateFormat().format(“yyyy-MM-dd hh.mm.ss”, new Date()));
//遍歷smsMessage陣列取出所有簡訊
for (int i = 0; i < smsMessage.length; i++) {
//將每條位元組型別的簡訊資料轉換成SmsMessage物件
smsMessage[i]=SmsMessage.createFromPdu((byte[])objArray[i]);
//獲取簡訊傳送地址
sb.append(“傳送者:”+smsMessage[i].getOriginatingAddress());
//獲取簡訊內容
sb.append(“簡訊內容:”+smsMessage[i].getDisplayMessageBody()+”\n”);
}
Toast.makeText(context, sb.toString(), Toast.LENGTH_LONG).show();
}
}
}
}
例項分析:
Ÿ 由於接收到的簡訊內容是以位元組陣列形式儲存的,為了便於用這些資料,需要使用SmsMessage.createFromPdu方法將這些位元組陣列形式的資料轉換成SmsMessage物件。
Ÿ 由於接收器可能收到多條簡訊,因此,通過“pdus”返回了一個簡訊陣列(byte[])所以需要遍歷這個陣列取出每一條簡訊。
由於需要監測系統發出的接收簡訊的廣播,所以在配置檔案中需要進行如下配置:
<!—註冊一個BroadcastReceiver監聽系統收到簡訊發出的廣播 à
<receiver android:name=”com.jph.monitorbroadcastfromsys.SmsReceiver”>
<!—給接收器設個較高的優先順序 以便能在其它程式收到廣播前結束廣播à
<intent-filter android:priority=”666”>
<action android:name=”android.provider.Telephony.SMS_RECEIVED”></action>
</intent-filter>
</receiver>
另外,由於程式需要接收簡訊,所以需要賦予程式接收簡訊的許可權。
<!—為程式新增接收簡訊的許可權 à
<uses-permission android:name=”android.permission.RECEIVE_SMS”/>
執行效果圖:
例項三:手機電量提醒
當手機電量發生變化是系統會發出,Intent的Action名為ACTION_BATTERY_CHANGED的廣播,當手機電量過低時系統會發出Intent的Action名為ACTION_BATTRY_LOW的廣播。所以,只需監測對應Intent的BroadcastReceiver便可實現手機低電量提醒的應用。
程式清單:
public class MianActivity extends Activity {
TextView show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mian);
show=(TextView)findViewById(R.id.show);
ShowPowerReceiver showPowerReceiver=new ShowPowerReceiver();
IntentFilter filter=new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
//註冊showPowerReceiver
registerReceiver(showPowerReceiver, filter);
}
/**
* Describe:</br>
* 手機電量提醒
* */
class ShowPowerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//判斷接收到的是否為電量變化的BroadCast Action
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
//當前電量
int level=intent.getIntExtra("level", 0);
//總電量
int scale=intent.getIntExtra("scale", 100);
int current=level*100/scale;
show.setText("當前電量:"+current+"%");
}
}
}
}
程式執行效果圖:
例項四:來去電提醒
當通話狀態改變時候系統會發出,Intent的Action名為ACTION _PHONE_STAT的廣播,當手機播出電話時系統會發出Intent的Action名為ACTION_NEW_OUTGOING_CALL的廣播。所以,只需監測對應Intent的BroadcastReceiver便可實現來去電提醒的應用。
程式清單:
來電提醒
public class InCallReceiver extends BroadcastReceiver
{
private static Object obj;
/**
* 建立一個永不關閉的Toast
* @param context Context context上下文
* @param msg String msg 訊息提示資訊
* */
public static void showToast(Context context, String msg)
{
//建立一個Toast物件
Toast toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
//設定Toast的顯示位置
toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0);
try
{
//從toast物件中獲取mTN物件
Field field = toast.getClass().getDeclaredField("mTN");
field.setAccessible(true);
obj = field.get(toast);
//從TN物件中獲得show方法
Method method = obj.getClass().getDeclaredMethod("show", null);
//呼叫TN物件的show方法來顯示Toast資訊提示框
method.invoke(obj, null);
}
catch (Exception e)
{
}
}
/**
* 關閉Toast
* */
public static void closeToast()
{
if (obj != null)
{
try
{
Method method = obj.getClass().getDeclaredMethod("hide", null);
method.invoke(obj, null);
}
catch (Exception e)
{
}
}
}
@Override
public void onReceive(final Context context, final Intent intent)
{
//獲取電話管理服務
TelephonyManager tm = (TelephonyManager) context
.getSystemService(Service.TELEPHONY_SERVICE);
switch (tm.getCallState())
{
case TelephonyManager.CALL_STATE_RINGING://響鈴
String incomingNumber = intent
.getStringExtra("incoming_number");
showToast(context, incomingNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK://接聽電話
Log.d("call_state", "offhook");
break;
case TelephonyManager.CALL_STATE_IDLE://結束通話電話
closeToast();
}
}
}
//去電提醒
public class OutCallReceiver extends BroadcastReceiver
{ @Override
public void onReceive(Context context, Intent intent)
{
//獲取去電的電話號碼
String outcomingNumber = intent
.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
InCallReceiver.showToast(context, outcomingNumber);
}
}
配置檔案:AndroidManifest.xml
<!-- 為程式新增訪問電話狀態的許可權 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 為程式新增允許程式監視,修改或放棄播出電話 -->
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<!-- 註冊一個BroadcastReceiver監聽系發出的電話狀態的廣播 -->
<receiver android:name=".InCallReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
<!-- 註冊一個BroadcastReceiver監聽系統發出的新來電的廣播 -->
<receiver android:name=".OutCallReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
程式執行效果圖:
最後:由於在接收廣播的時候,系統會為每一次接收廣播單獨建立一個廣播接收器,即使是同一個廣播的多次接收。因此,當電話處於不同的通話狀態時,實際上是在不同的接收器物件中發生的,所以需要使用靜態變數來儲存Toast物件,不然closeTost我無法獲取在上一個狀態建立的Toast物件,也就無法關閉Toast資訊框了。