android

简介

Broadcast Receiver用于接收并处理广播通知(broadcastannouncements)。

多数的广播是系统发起的,如地域变换、电量不足、来电来信等。

程序可以有任意数量的Broadcast Receivers来响应它觉得重要的通知。

Broadcast Receiver可以通过多种方式通知用户:启动activity、使用NotificationManager、开启背景灯、振动设备、播放声音等,最典型的是在状态栏显示一个图标,这样用户就可以点它打开看通知内容。

通常我们的某个应用或系统本身在某些事件(电池电量不足、来电来短信)来临时会广播一个Intent出去,我们可以利用注册一个Broadcast Receiver来监听到这些Intent并获取Intent中的数据。


在程序中接受系统发的广播

我们举一个例子说明,一个接受系统Date改变的广播的例子。

先新建一个HelloBroadcastReceiver.java类,继承自BroadcastReceiver并复写它的onReceive方法。这样就创建了一个专门用来接收广播的类。

package com.tianjf;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.util.Log;  
  
public class HelloBroadcastReceiver extends BroadcastReceiver {  
  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        Log.d("debug", "Date has been changed!");          
        Log.d("debug", ""+intent.getAction());   
    }  
}

但是上面的Broadcast Receiver没有指定接收哪个广播,我们要指定某个Receiver接收哪个广播,必须在AndroidManifest.xml中注册此Receiver

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="com.tianjf"  
    android:versionCode="1"  
    android:versionName="1.0" >  
  
    <uses-sdk android:minSdkVersion="4" />  
  
    <application  
        android:icon="@drawable/ic_launcher"  
        android:label="@string/app_name" >  
        <activity  
            android:name=".BroadcastDemoActivity"  
            android:label="@string/app_name" >  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
        <!-- 定义Broadcast Receiver指定监听的Action(系统Date改变action) -->  
        <receiver android:name="HelloBroadcastReceiver" >  
            <intent-filter>  
                <action android:name="android.intent.action.DATE_CHANGED" />  
            </intent-filter>  
        </receiver>  
    </application>  
  
</manifest>

OK,运行之后,更改系统Date看看,果然打印了两行Log。(着两行Log有时候打得出来,有时候打不出来,不知道神马原因)


接收自己发的广播

直接上代码

BroadcastDemoActivity.java

package com.tianjf;  
  
import android.app.Activity;  
import android.content.Intent;  
import android.os.Bundle;  
import android.view.View;  
import android.view.View.OnClickListener;  
import android.widget.Button;  
  
public class BroadcastDemoActivity extends Activity implements OnClickListener {  
  
    private Button button;  
  
    /** Called when the activity is first created. */  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
  
        button = (Button) findViewById(R.id.button);  
        button.setOnClickListener(this);  
    }  
  
    @Override  
    public void onClick(View v) {  
        // 定义一个intent  
        Intent intent = new Intent();  
        intent.setAction("MY_BROADCAST_ACTION");  
        intent.putExtra("yaoyao",  
                "yaoyao is 189 days old ,27 weeks -- 2010-08-10");  
        // 广播出去  
        sendBroadcast(intent);  
    }  
}

HelloBroadcastReceiver.java

package com.tianjf;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.util.Log;  
  
public class HelloBroadcastReceiver extends BroadcastReceiver {  
  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        Log.d("debug", "" + intent.getAction());  
        if (intent.getAction().equals("android.intent.action.DATE_CHANGED")) {  
            Log.d("debug", "Date has been changed!");  
            Log.d("debug", "" + intent.getAction());  
        } else if (intent.getAction().equals("MY_BROADCAST_ACTION")) {  
            Log.d("debug", "Say Hello to Yaoyao!");  
            Log.d("debug", intent.getStringExtra("yaoyao"));  
        }  
    }  
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="com.tianjf"  
    android:versionCode="1"  
    android:versionName="1.0" >  
  
    <uses-sdk android:minSdkVersion="4" />  
  
    <application  
        android:icon="@drawable/ic_launcher"  
        android:label="@string/app_name" >  
        <activity  
            android:name=".BroadcastDemoActivity"  
            android:label="@string/app_name" >  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
        <!-- 定义Broadcast Receiver指定监听的Action(系统Date改变action和自动义的广播) -->  
        <receiver android:name="HelloBroadcastReceiver" >  
            <intent-filter>  
                <action android:name="MY_BROADCAST_ACTION" />  
                <action android:name="android.intent.action.DATE_CHANGED" />  
            </intent-filter>  
        </receiver>  
    </application>  
  
</manifest>

注册广播的2种方式

BroadcastReceiver在没有注册广播地址之前是使用不了的。没有注册广播地址的BroadcastReceiver就像一个缺少选台按钮的收音机,虽然功能俱备,但也无法收到电台的信号。有两种注册方法:

  1. 静态注册。在AndroidManifest.xml文件中配置(上文两个例子已经介绍了)

  2. 动态注册。要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service注册一个广播

静态注册的方法参照以上两个例子。

至于动态注册的方法,我们看以下例子:

将以上的BroadcastDemoActivity做如下修改

package com.tianjf;  
  
import android.app.Activity;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.os.Bundle;  
import android.view.View;  
import android.view.View.OnClickListener;  
import android.widget.Button;  
  
public class BroadcastDemoActivity extends Activity implements OnClickListener {  
  
    private Button button;  
    HelloBroadcastReceiver receiver;  
  
    /** Called when the activity is first created. */  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
  
        button = (Button) findViewById(R.id.button);  
        button.setOnClickListener(this);  
  
        receiver = new HelloBroadcastReceiver();  
        IntentFilter filter = new IntentFilter();  
        filter.addAction("android.intent.action.DATE_CHANGED");  
        filter.addAction("MY_BROADCAST_ACTION");  
        registerReceiver(receiver, filter);  
    }  
  
    @Override  
    public void onClick(View v) {  
        // 定义一个intent  
        Intent intent = new Intent();  
        intent.setAction("MY_BROADCAST_ACTION");  
        intent.putExtra("yaoyao",  
                "yaoyao is 189 days old ,27 weeks -- 2010-08-10");  
        // 广播出去  
        sendBroadcast(intent);  
    }  
  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        unregisterReceiver(receiver);  
    }  
}

注意:动态注册广播的方法必须在onDestory方法里面注销,如果不注销的话,系统会报一个异常(如下)

12-19 07:16:04.186: E/ActivityThread(23244): Activity com.tianjf.BroadcastDemoActivity has leaked IntentReceiver com.tianjf.HelloBroadcastReceiver@40cef150 that was originally registered here. Are you missing a call to unregisterReceiver()?

异常也提示了要注销广播。

两种广播注册方式的区别:

  • 静态注册的方法,不管应用处于什么状态,甚至于退出应用,都能接收广播并作相应的处理。当然,相对来说是比较耗电的

  • 动态注册的方法,因为动态注册必须要注销广播,所以,应用退出也就不接收广播了。所以是省电的


有序广播(Ordered Broadcast)

有序广播比较特殊,它每次只发送到优先级较高的接收者那里,然后由优先级高的接受者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播

为了演示有序广播的流程,我们创建三个接收者的代码,如下:

package com.tianjf;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.os.Bundle;  
import android.util.Log;  
  
public class FirstBroadcastReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "TestOrderdBroadcastReceiver";  
  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = intent.getStringExtra("msg");  
        Log.i(TAG, "FirstBroadcastReceiver: " + msg);  
  
        Bundle bundle = new Bundle();  
        bundle.putString("msg", msg + "@FirstBroadcastReceiver");  
        setResultExtras(bundle);  
    }  
}
package com.tianjf;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.os.Bundle;  
import android.util.Log;  
  
public class SecondBroadcastReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "TestOrderdBroadcastReceiver";  
  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = getResultExtras(true).getString("msg");  
        Log.i(TAG, "SecondBroadcastReceiver: " + msg);  
  
        Bundle bundle = new Bundle();  
        bundle.putString("msg", msg + "@SecondBroadcastReceiver");  
        setResultExtras(bundle);  
    }  
}
package com.tianjf;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.util.Log;  
  
public class ThirdBroadcastReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "TestOrderdBroadcastReceiver";  
  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = getResultExtras(true).getString("msg");  
        Log.i(TAG, "ThirdBroadcastReceiver: " + msg);  
    }  
}

我们注意到,在FirstReceiver和SecondReceiver中最后都使用了setResultExtras方法将一个Bundle对象设置 为结果集对象,传递到下一个接收者那里,这样以来,优先级低的接收者可以用getResultExtras获取到最新的经过处理的信息集合。

之后,我们需要为三个接收者注册广播地址,我们修改一下AndroidMainfest.xml文件:

<receiver android:name=".FirstBroadcastReceiver" >  
    <intent-filter android:priority="1000" >  
        <action android:name="android.intent.action.MY_BROADCAST" />  
  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>  
<receiver android:name=".SecondBroadcastReceiver" >  
    <intent-filter android:priority="999" >  
        <action android:name="android.intent.action.MY_BROADCAST" />  
  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>  
<receiver android:name=".ThirdBroadcastReceiver" >  
    <intent-filter android:priority="998" >  
        <action android:name="android.intent.action.MY_BROADCAST" />  
  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>

我们看到,现在这三个接收者的<intent-filter>多了一个android:priority属性,并且依次减小。这个属性的范围在-1000到1000,数值越大,优先级越高。

现在,我们需要写一下发送广播的代码,如下:

Intent intent = new Intent("android.intent.action.MY_BROADCAST");    
   intent.putExtra("msg", "hello receiver.");    
   sendOrderedBroadcast(intent, "myAndroid.permission.MY_BROADCAST_PERMISSION");

注意,使用sendOrderedBroadcast方法发送有序广播时,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为 null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有拦截垃圾短信 的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。

所以我们在AndroidMainfest.xml中定义一个权限:

<permission  
    android:name="myAndroid.permission.MY_BROADCAST_PERMISSION"  
    android:protectionLevel="normal" />

然后声明使用了此权限:

<uses-permission android:name="myAndroid.permission.MY_BROADCAST_PERMISSION" />

然后我们点击发送按钮发送一条广播,控制台打印如下:

12-20 08:13:45.978: I/TestOrderdBroadcastReceiver(29914): FirstBroadcastReceiver: hello receiver.  
12-20 08:13:46.088: I/TestOrderdBroadcastReceiver(29914): SecondBroadcastReceiver: hello receiver.@FirstBroadcastReceiver  
12-20 08:13:46.138: I/TestOrderdBroadcastReceiver(29914): ThirdBroadcastReceiver: hello receiver.@FirstBroadcastReceiver@SecondBroadcastReceiver

我们看到接收是按照顺序的,第一个和第二个都在结果集中加入了自己的标记,并且向优先级低的接收者传递下去。

既然是顺序传递,试着终止这种传递,看一看效果如何,我们修改FirstReceiver的代码,在onReceive的最后一行添加以下代码:

abortBroadcast();

然后再次运行程序,控制台打印如下:

12-20 08:13:45.978: I/TestOrderdBroadcastReceiver(29914): FirstBroadcastReceiver: hello receiver.

此次,只有第一个接收者执行了,其它两个都没能执行,因为广播被第一个接收者终止了。

注:只有有序广播可以用abortBroadcast();来终止,其他广播是没有效果的。


BroadcastReceiver的生命周期

BroadcastReceiver的生命周期很短,也就是onReceive的执行时间,onReceive执行结束,BroadcastReceiver的实例就被销毁(不管那种注册方法)


Broadcast Action 大全

android.intent.action.BATTERY_CHANGED
充电状态,或者电池的电量发生变化

android.intent.action.BOOT_COMPLETED
在系统启动后,这个动作被广播一次(只有一次)

android.intent.action.CFF
语音电话的呼叫转移状态已经改变

android.intent.action.CONFIGURATION_CHANGED
设备的配置信息已经改变,参见 Resources.Configuration

android.intent.action.DATA_ACTIVITY
电话的数据活动(data activity)状态(即收发数据的状态)已经改变

android.intent.action.DATA_STATE
电话的数据连接状态已经改变

android.intent.action.DATE_CHANGED
日期被改变

android.server.checkin.FOTA_CANCEL
取消所有被挂起的 (pending) 更新下载

android.server.checkin.FOTA_INSTALL
更新已经被确认,马上就要开始安装

android.server.checkin.FOTA_READY
更新已经被下载,可以开始安装

android.server.checkin.FOTA_RESTART
恢复已经停止的更新下载

android.server.checkin.FOTA_UPDATE
通过 OTA 下载并安装操作系统更新

android.intent.action.MEDIABUTTON
用户按下了"Media Button"

android.intent.action.MEDIA_BAD_REMOVAL
扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)

android.intent.action.MEDIA_EJECT
用户想要移除扩展介质(拔掉扩展卡)

android.intent.action.MEDIA_MOUNTED
扩展介质被插入,而且已经被挂载

android.intent.action.MEDIA_REMOVED
扩展介质被移除

android.intent.action.MEDIA_SCANNER_FINISHED
已经扫描完介质的一个目录

android.intent.action.MEDIA_SCANNER_STARTED
开始扫描介质的一个目录

android.intent.action.MEDIA_SHARED
扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享

android.intent.action.MEDIA_UNMOUNTED
扩展介质存在,但是还没有被挂载 (mount)

android.intent.action.MWI
电话的消息等待(语音邮件)状态已经改变

android.intent.action.NETWORK_TICKLE_RECEIVED
设备收到了新的网络 "tickle" 通知

android.intent.action.PACKAGE_ADDED
设备上新安装了一个应用程序包

android.intent.action.PACKAGE_REMOVED
设备上删除了一个应用程序包

android.intent.action.PHONE_STATE
电话状态已经改变

android.intent.action.PROVIDER_CHANGED
更新将要(真正)被安装

android.intent.action.PROVISIONING_CHECK
要求 polling of provisioning service 下载最新的设置

android.intent.action.SCREEN_OFF
屏幕被关闭

android.intent.action.SCREEN_ON
屏幕已经被打开

android.intent.action.SERVICE_STATE
电话服务的状态已经改变

android.intent.action.SIG_STR
电话的信号强度已经改变

android.intent.action.STATISTICS_REPORT
要求 receivers 报告自己的统计信息

android.intent.action.STATISTICS_STATE_CHANGED
统计信息服务的状态已经改变

android.intent.action.TIMEZONE_CHANGED
时区已经改变

android.intent.action.TIME_SET
时间已经改变(重新设置)

android.intent.action.TIME_TICK
当前时间已经变化(正常的时间流逝)

android.intent.action.UMS_CONNECTED
设备进入 USB 大容量存储模式。

android.intent.action.UMS_DISCONNECTED
设备从 USB 大容量存储模式退出

android.intent.action.WALLPAPER_CHANGED
系统的墙纸已经改变

android.intent.action.XMPP_CONNECTED
XMPP 连接已经被建立

android.intent.action.XMPP_DI  
XMPP 连接已经被断开


你可能感兴趣的:(android)