Android-broadcast-详解

http://www.cnblogs.com/playing/archive/2011/03/23/1992030.html

基础用法

BroadcastReceiver:

在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。而BroadcastReceiver是对发送出来的 Broadcast进行过滤接受并响应的一类组件。下面将详细的阐述如何发送Broadcast和使用BroadcastReceiver过滤接收的过程:

首先在需要发送信息的地方,把要发送的信息和用于过滤的信息(如Action、Category)装入一个Intent对象,然后通过调用 Context.sendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把 Intent对象以广播方式发送出去。

当Intent发送以后,所有已经注册的BroadcastReceiver会检查注册时的IntentFilter是否与发送的Intent相匹配,若匹配则就会调用BroadcastReceiver的onReceive()方法。所以当我们定义一个BroadcastReceiver的时候,都需要实现onReceive()方法。

注册BroadcastReceiver有两种方式:

一种方式是,静态的在AndroidManifest.xml中用<receiver>标签生命注册,并在标签内用<intent- filter>标签设置过滤器。

另一种方式是,动态的在代码中先定义并设置好一个 IntentFilter 对象,然后在需要注册的地方调Context.registerReceiver()方法,如果取消时就调用 Context.unregisterReceiver()方法。如果用动态方式注册的BroadcastReceiver的Context对象被销毁时,BroadcastReceiver也就自动取消注册了。(特别注意,有些可能需要后台监听的,如短信消息)

另外,若在使用sendBroadcast()的方法是指定了接收权限,则只有在AndroidManifest.xml中用<uses- permission>标签声明了拥有此权限的BroascastReceiver才会有可能接收到发送来的Broadcast。同样,若在注册BroadcastReceiver时指定了可接收的Broadcast的权限,则只有在包内的AndroidManifest.xml中 用<uses-permission>标签声明了,拥有此权限的Context对象所发送的Broadcast才能被这个 BroadcastReceiver所接收。

1.静态注册BroadcastReceiver:

静态注册比动态注册麻烦点,先新建一个类继承BroadcastReceiver,如:

clsReceiver2.java

复制代码
  
  
package com.testBroadcastReceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; /* * 接收静态注册广播的BroadcastReceiver, * step1:要到AndroidManifest.xml这里注册消息 * <receiver android:name="clsReceiver2"> <intent-filter> <action android:name="com.testBroadcastReceiver.Internal_2"/> </intent-filter> </receiver> step2:定义消息的字符串 step3:通过Intent传递消息来驱使BroadcastReceiver触发 */ public class clsReceiver2 extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Toast.makeText(context, " 静态: " + action, 1000 ).show(); } }
复制代码

然后到AndroidManifest.xml 添加receive标签

  
  
< receiver android:name = " clsReceiver2 " > < intent - filter > < action android:name = " com.testBroadcastReceiver.Internal_2 " /> </ intent - filter > </ receiver >

第一个name是类名,即你的继承BroadcastReceiver的类的名字,里面实现了BroadcastReceive的onReceive()方法,来处理你接到消息的动作。

第二个name是action的名称,即你要监听的消息名字(其它消息都会被过滤不监听)。

2.动态注册BroadcastReceiver:

  主要代码部分为:

     IntentFilter intentFilter = new IntentFilter();

  intentFilter.addAction(String); //为BroadcastReceiver指定action,即要监听的消息名字。

     registerReceiver(MyBroadcastReceiver,intentFilter); //注册监听

     unregisterReceiver(MyBroadcastReceiver); //取消监听

  (一般:在onStart中注册,onStop中取消unregisterReceiver)

     private class MyBroadcastReceive extends BroadcastReceiver

     {

           public void onReceive(Context context, Intent intent)

           {

               String action = intent.getAction();

                if(intent.ACTION_BATTERY_CHANGED.equals(action)) //判断是否接到电池变换消息 

                {

                   //处理内容

                 }

            }

      }

========================================================================

Broadcast:

发送广播消息,把要发送的信息和用于过滤的信息(如Action、Category)装入一个Intent对象,然后通过调用 Context.sendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把 Intent对象以广播方式发送出去。

例如:

  
  
Intent intent = new Intent(INTENAL_ACTION_3); intent.putExtra( " Name " , " hellogv " ); intent.putExtra( " Blog " , " http://blog.csdn.net/hellogv " ); sendBroadcast(intent); // 传递过去

http://blog.csdn.net/jiangwei0910410003/article/details/19150705

动态广播注册和静态广播注册的区别:

静态注册广播又叫:常驻型广播,当你的应用程序关闭了,如果有广播信息来,你写的广播接收器同样的能接受到,他的注册方式就是在你的应用程序中的AndroidManifast.xml进行订阅的。

动态注册广播又叫:非常驻型广播,当应用程序结束了,广播自然就没有了,比如你在activity中的onCreate或者onResume中订阅广播,同时你必须在onDestory或者onPause中取消广播订阅。不然会报异常,这样你的广播接收器就一个非常驻型的了。

这里面还有一个细节那就是这两种订阅方式,在发送广播的时候需要注意的是:动态注册的时候使用的是隐式intent方式的,所以在发送广播的时候需要使用隐式Intent去发送,不然是广播接收者是接收不到广播的,这一点要注意。但是静态订阅的时候,因为在AndroidMainfest.xml中订阅的,所以在发送广播的时候使用显示Intent和隐式Intent都可以(当然这个只针对于我们自己定义的广播接收者),所以以防万一,我们一般都采用隐式Intent去发送广播。

下面来做一个案例就是网上很多人弄过的:短信拦截

系统在收到短信的时候,会发送一个:android.provider.Telephony.SMS_RECEIVED这样的广播,而且这是一个有序的广播,所以我们就可以拦截了这条短信,因为系统中的短信接收者的订阅优先级不是1000最高的,所以我们可以自己定义一个短信接收者,将订阅优先级设置成1000,这样我们就可以最先获取到短信内容,然后将截取到知道号码的短信内容上传到服务器上进行保存,然后终止广播。让系统接收不到这条短信。

这里面我们还需要了解的技术就是短信内容的获取,这个我们在代码中解释:

下面来看一下我们定义的短信接收者:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.broadcast.receiver;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5. import android.annotation.SuppressLint;  
  6. import android.content.BroadcastReceiver;  
  7. import android.content.Context;  
  8. import android.content.Intent;  
  9. import android.telephony.SmsMessage;  
  10. import android.util.Log;  
  11. import com.broadcast.service.UploadSMSService;  
  12.   
  13. /** 
  14.  * 广播接收者A 
  15.  * @author weijiang204321 
  16.  * 
  17.  */  
  18. public class SortBroadcastReceiverA extends BroadcastReceiver{  
  19.   
  20.     @SuppressLint("SimpleDateFormat")  
  21.     @Override  
  22.     public void onReceive(Context context, Intent intent) {  
  23.         //获取短信的相关信息,使用字段pdus  
  24.         Object[] pdus = (Object[]) intent.getExtras().get("pdus");  
  25.         StringBuilder content = new StringBuilder();  
  26.         String receiveTime = "";  
  27.         String senderNumber = "";  
  28.         for(Object p : pdus){  
  29.             byte[] pdu = (byte[]) p;  
  30.             SmsMessage message = SmsMessage.createFromPdu(pdu);  
  31.             content.append(message.getMessageBody());  
  32.             Date date = new Date(message.getTimestampMillis());  
  33.             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  34.             receiveTime = format.format(date);  
  35.             senderNumber = message.getOriginatingAddress();  
  36.         }  
  37.           
  38.         Log.e("Demo:","上传短信内容是:"+content.toString());  
  39.         Log.e("Demo:","接收短信的时间是"+receiveTime);  
  40.         Log.e("Demo:","发送短信的号码是:"+senderNumber);  
  41.           
  42.         //拦截的号码是:18910958627,注意前面还有+86是中国地区的编号  
  43.         if("+8618910958627".equals(senderNumber)){  
  44.             //拦截成功 ,上传信息  
  45.             Intent service = new Intent(context,UploadSMSService.class);  
  46.             service.putExtra("content", content.toString());  
  47.             service.putExtra("receiveTime",receiveTime);  
  48.             service.putExtra("senderNumber", senderNumber);  
  49.             context.startService(service);  
  50.         }  
  51.           
  52.     }  
  53.   
  54. }  
在onReceive方法中我们通过intent中的Bundle获取短信的相关信息。

下面在看一下上传短信信息的服务Service:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.broadcast.service;  
  2.   
  3. import java.net.HttpURLConnection;  
  4. import java.net.URL;  
  5. import java.net.URLEncoder;  
  6.   
  7. import android.app.Service;  
  8. import android.content.Intent;  
  9. import android.os.IBinder;  
  10.   
  11. /** 
  12.  * 上传短信内容的service 
  13.  * @author weijiang204321 
  14.  * 
  15.  */  
  16. public class UploadSMSService extends Service{  
  17.   
  18.     @Override  
  19.     public IBinder onBind(Intent intent) {  
  20.         return null;  
  21.     }  
  22.   
  23.     @Override  
  24.     public int onStartCommand(Intent intent, int flags, int startId) {  
  25.         //获取短信内容和接收时间,发送者的号码  
  26.         final String content = intent.getStringExtra("content");  
  27.         final String receiveTime = intent.getStringExtra("receiveTime");  
  28.         final String senderNumber = intent.getStringExtra("senderNumber");  
  29.           
  30.         new Thread(){  
  31.             @Override  
  32.             public void run(){  
  33.                 sendSMS(content,receiveTime,senderNumber);  
  34.                 //上传完成之后就结束service  
  35.                 stopSelf();  
  36.             }  
  37.         }.start();  
  38.           
  39.         return super.onStartCommand(intent, flags, startId);  
  40.     }  
  41.       
  42.     /** 
  43.      * 上传短信内容 
  44.      * @param content 
  45.      * @param receiveTime 
  46.      * @param senderNumber 
  47.      * @return 
  48.      */  
  49.     private boolean sendSMS(String content, String receiveTime, String senderNumber) {  
  50.         try{  
  51.             String params = "content="+ URLEncoder.encode(content, "UTF-8")+"&receivetime="+ receiveTime+ "&sendernumber="+ senderNumber;  
  52.             byte[] entity = params.getBytes();  
  53.             String path = "http://10.2.86.33:8080/ReceiveSMSContent/ReceiveSMSServlet";  
  54.             HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();  
  55.             conn.setConnectTimeout(5000);  
  56.             conn.setRequestMethod("POST");  
  57.             conn.setDoOutput(true);  
  58.             conn.setRequestProperty("Content-Type""application/x-www-form-urlencoded");  
  59.             conn.setRequestProperty("Content-Length", String.valueOf(entity.length));  
  60.             conn.getOutputStream().write(entity);  
  61.             if(conn.getResponseCode() == 200){  
  62.                 return true;  
  63.             }  
  64.         }catch (Exception e) {  
  65.             e.printStackTrace();  
  66.         }  
  67.         return false;  
  68.     }  
  69.   
  70.   
  71. }  
这个服务的逻辑也很简单的,在获取到短信信息的时候,将内容进行上传。

最后不能忘了在AndroidMainfest.xml中添加权限:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <uses-permission android:name="android.permission.RECEIVE_SMS"/>  
  2. <uses-permission android:name="android.permission.INTERNET"/>  
订阅短信广播:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <receiver android:name="com.broadcast.receiver.SortBroadcastReceiverA">  
  2.             <intent-filter android:priority="999">  
  3.                 <action android:name="android.provider.Telephony.SMS_RECEIVED"/>  
  4.             </intent-filter>  
  5. </receiver>  

当然这里还需要有一个短信内容的接收服务端,在服务端定义一个Servlet来接受数据,具体的服务端的环境搭建,自己上网搜索相关资料进行搭建:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.servlet.receivesms;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import javax.servlet.ServletException;  
  6. import javax.servlet.http.HttpServlet;  
  7. import javax.servlet.http.HttpServletRequest;  
  8. import javax.servlet.http.HttpServletResponse;  
  9.   
  10. public class ReceiveSMSServlet extends HttpServlet{  
  11.   
  12.     private static final long serialVersionUID = 1L;  
  13.   
  14.     @Override  
  15.     protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {  
  16.         req.setCharacterEncoding("utf-8");  
  17.         String content = req.getParameter("content");  
  18.         String receiveTime = req.getParameter("receivetime");  
  19.         String phoneNumber = req.getParameter("sendernumber");  
  20.         System.out.println("发送内容:"+content);  
  21.         System.out.println("发送时间:"+receiveTime);  
  22.         System.out.println("发送号码:"+phoneNumber);  
  23.         super.doGet(req, resp);  
  24.     }  
  25.   
  26.     @Override  
  27.     protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {  
  28.         super.doPost(req, resp);  
  29.     }  
  30.   
  31. }  
测试结果,客户端拦截的号码是18910958627,注意前面有+86是中国区域的编码,服务端接收数据为:


这样就表示截取短信内容成功,并且成功上传到服务器上了。

http://blog.csdn.net/qq_27280457/article/details/51840678
常见的几个系统广播
1.开机启动服务
我们经常会有这样的应用场合,比如消息推送服务,需要实现开机启动的功能。要实现这个功能,我们就可以订阅系统“启动完成”这条广播,接收到这条广播后我们就可以启动自己的服务了。我们来看一下BootCompleteReceiver和MsgPushService的具体实现:

package com.scott.receiver;   
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.util.Log;   
public class BootCompleteReceiver extends BroadcastReceiver {  
private static final String TAG = "BootCompleteReceiver";  
@Override  
public void onReceive(Context context, Intent intent) {  
    Intent service = new Intent(context, MsgPushService.class);  
    context.startService(service);  
    Log.i(TAG, "Boot Complete. Starting MsgPushService...");  
}  

}  

package com.scott.receiver;  
import android.app.Service;  
import android.content.Intent;  
import android.os.IBinder;  
import android.util.Log;  

public class MsgPushService extends Service {  

private static final String TAG = "MsgPushService";  

@Override  
public void onCreate() {  
    super.onCreate();  
    Log.i(TAG, "onCreate called.");  
}  

@Override  
public int onStartCommand(Intent intent, int flags, int startId) {  
    Log.i(TAG, "onStartCommand called.");  
    return super.onStartCommand(intent, flags, startId);  
}  

@Override  
public IBinder onBind(Intent arg0) {  
    return null;  
}  
}

然后我们需要在AndroidManifest.xml中配置相关信息:
<!-- 开机广播接受者 -->  
<receiver android:name=".BootCompleteReceiver">  
    <intent-filter>  
        <!-- 注册开机广播地址-->  
        <action android:name="android.intent.action.BOOT_COMPLETED"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>  
<!-- 消息推送服务 -->  
<service android:name=".MsgPushService"/>  
我们看到BootCompleteReceiver注册了“android.intent.action.BOOT_COMPLETED”这个开机广播地址,从安全角度考虑,系统要求必须声明接收开机启动广播的权限,于是我们再声明使用下面的权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />  
经过上面的几个步骤之后,我们就完成了开机启动的功能,将应用运行在模拟器上,然后重启模拟器,控制台打印如下:   
如果我们查看已运行的服务就会发现,MsgPushService已经运行起来了。

#

2.网络状态变化
在某些场合,比如用户浏览网络信息时,网络突然断开,我们要及时地提醒用户网络已断开。要实现这个功能,我们可以接收网络状态改变这样一条广播,当由连接状态变为断开状态时,系统就会发送一条广播,我们接收到之后,再通过网络的状态做出相应的操作。下面就来实现一下这个功能:
package com.scott.receiver;  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.net.ConnectivityManager;  
import android.net.NetworkInfo;  
import android.util.Log;  
import android.widget.Toast;    
public class NetworkStateReceiver extends BroadcastReceiver {   
private static final String TAG = "NetworkStateReceiver";   
@Override  
public void onReceive(Context context, Intent intent) {  
    Log.i(TAG, "network state changed.");  
    if (!isNetworkAvailable(context)) {  
        Toast.makeText(context, "network disconnected!", 0).show();  
    }  
}  

/** 
 * 网络是否可用 
 *  
 * @param context 
 * @return 
 */  
public static boolean isNetworkAvailable(Context context) {  
    ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);  
    NetworkInfo[] info = mgr.getAllNetworkInfo();  
    if (info != null) {  
        for (int i = 0; i < info.length; i++) {  
            if (info[i].getState() == NetworkInfo.State.CONNECTED) {  
                return true;  
            }  
        }  
    }  
    return false;  
}  

}  
再注册一下这个接收者的信息:
[html] view plain copy
<receiver android:name=".NetworkStateReceiver">  
    <intent-filter>  
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>  
因为在isNetworkAvailable方法中我们使用到了网络状态相关的API,所以需要声明相关的权限才行,下面就是对应的权限声明:
[html] view plain copy
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> 

#

3.电量变化
如果我们阅读软件,可能是全屏阅读,这个时候用户就看不到剩余的电量,我们就可以为他们提供电量的信息。要想做到这一点,我们需要接收一条电量变化的广播,然后获取百分比信息,这听上去挺简单的,我们就来实现以下:
[java] view plain copy
package com.scott.receiver;  

import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.os.BatteryManager;  
import android.util.Log;  

public class BatteryChangedReceiver extends BroadcastReceiver {  

private static final String TAG = "BatteryChangedReceiver";  

@Override  
public void onReceive(Context context, Intent intent) {  
    int currLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);  //当前电量  
    int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);      //总电量  
    int percent = currLevel * 100 / total;  
    Log.i(TAG, "battery: " + percent + "%");  
}  

}  
然后再注册一下广播接地址信息就可以了:
[html] view plain copy
<receiver android:name=".BatteryChangedReceiver">  
    <intent-filter>  
        <action android:name="android.intent.action.BATTERY_CHANGED"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>  
当然,有些时候我们是要立即获取电量的,而不是等电量变化的广播,比如当阅读软件打开时立即显示出电池电量。我们可以按以下方式获取:
[java] view plain copy
Intent batteryIntent = getApplicationContext().registerReceiver(null,  
        new IntentFilter(Intent.ACTION_BATTERY_CHANGED));  
int currLevel = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);  
int total = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);  
int percent = currLevel * 100 / total;  
Log.i("battery", "battery: " + percent + "%");  

#

监听SD卡状态
 清单文件中定义广播接收者接收的类型,监听SD卡常见的三种状态,所以广播接收者需要接收三种广播

     <receiver android:name="com.itheima.sdcradlistener.SDCardReceiver">
        <intent-filter >
            <action android:name="android.intent.action.MEDIA_MOUNTED"/>
            <action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
            <action android:name="android.intent.action.MEDIA_REMOVED"/>
            <data android:scheme="file"/>
        </intent-filter>
    </receiver>
 广播接收者的定义

    public class SDCardReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            // 区分接收到的是哪个广播
            String action = intent.getAction();

            if(action.equals("android.intent.action.MEDIA_MOUNTED")){
                System.out.println("sd卡就绪");
            }
            else if(action.equals("android.intent.action.MEDIA_UNMOUNTED")){
                System.out.println("sd卡被移除");
            }
            else if(action.equals("android.intent.action.MEDIA_REMOVED")){
                System.out.println("sd卡被拔出");
            }
        }
    }

#

监听应用的安装、卸载、更新
原理:应用在安装卸载更新时,系统会发送广播,广播里会携带应用的包名
清单文件定义广播接收者接收的类型,因为要监听应用的三个动作,所以需要接收三种广播

    <receiver android:name="com.itheima.app.AppReceiver">
        <intent-filter >
            <action android:name="android.intent.action.PACKAGE_ADDED"/>
            <action android:name="android.intent.action.PACKAGE_REPLACED"/>
            <action android:name="android.intent.action.PACKAGE_REMOVED"/>
            <data android:scheme="package"/>
        </intent-filter>
    </receiver>
广播接收者的定义
    public void onReceive(Context context, Intent intent) {
        //区分接收到的是哪种广播
        String action = intent.getAction();
        //获取广播中包含的应用包名
        Uri uri = intent.getData();
        if(action.equals("android.intent.action.PACKAGE_ADDED")){
            System.out.println(uri + "被安装了");
        }
        else if(action.equals("android.intent.action.PACKAGE_REPLACED")){
            System.out.println(uri + "被更新了");
        }
        else if(action.equals("android.intent.action.PACKAGE_REMOVED")){
            System.out.println(uri + "被卸载了");
        }
    }
异步广播的发送和接收:

sendStickyBroadcast(intent);

当处理完之后的Intent ,依然存在,直到你把它去掉。

发这个广播需要权限<uses-permission android:name="android.permission.BROADCAST_STICKY" />

去掉是用这个方法removeStickyBroadcast(intent); 但别忘了在执行这个方法的应用里面 AndroidManifest.xml同样要加上面的权限;

 

 

sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,

       initialCode, initialData, initialExtras)

这个方法具有有序广播的特性也有异步广播的特性;

 


发送这个广播要: <uses-permission android:name="android.permission.BROADCAST_STICKY" /> 这个权限。才能使用这个方法。如果您并不拥有该权限,将抛出 SecurityException 的。

 

实验现象( sendStickyOrderedBroadcast ()中),在这个方法发来的广播中,代码注册方式中,收到广播先后次序为:注明优先级的、代码注册的、没有优先级的;如果都没有优先级,代码注册收到为最先。

 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值