标签: BroadcastReceiver

Android 两种注册、发送广播的区别

Android 两种注册、发送广播的区别

前言:前面文章记录了Service的使用,这次来记录另一个四个组件之一的BroadcastReceiver。主要介绍两种发送和注册广播的区别。

BroadcastReceiver广播接收者用于接收系统或其他程序(包括自己程序)发送的广播。

一.注册广播

在android中,我们如果想接收到广播信息,必须自定义我们的广播接收者。要写一个类来继承BroadcastReceiver,并且重写其onReceive()方法,实现接收到特定广播所要做的事情。

这是一个自定义的广播接收者:

public class MyBroadCastReceiver extends BroadcastReceiver   
{  
   @Override  
   public void onReceive(Context context, Intent intent)   
   {   
       //在这里可以写相应的逻辑来实现一些功能
       //可以从Intent中获取数据、还可以调用BroadcastReceiver的getResultData()获取数据
   }   
}

我们已经定义好了一个广播接收者。要想使用它接受到广播,就要注册这个广播接收者。

有两种方式注册广播:

(1)代码中动态注册

步骤如下:

  1. 实例化自定义的广播接收者
  2. 实例化意图过滤器,并设置要过滤的广播类型(如,我们接收收到短信系统发出的广播)
  3. 使用Context的registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)方法注册广播

代码:

//new出上边定义好的BroadcastReceiver
MyBroadCastReceiver yBroadCastReceiver = new MyBroadCastReceiver();

//实例化过滤器并设置要过滤的广播  
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");

//注册广播   
myContext.registerReceiver(smsBroadCastReceiver,intentFilter, 
             "android.permission.RECEIVE_SMS", null);

(2)在Manifest.xml中静态注册

直接在Manifest.xml文件的<application>节点中配置广播接收者。

 <receiver android:name=".MyBroadCastReceiver">  
            <!-- android:priority属性是设置此接收者的优先级(从-1000到1000) -->
            <intent-filter android:priority="20">
            <actionandroid:name="android.provider.Telephony.SMS_RECEIVED"/>  
            </intent-filter>  
</receiver>

还要在<application>同级的位置配置可能使用到的权限

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

(3)两种注册广播的不同

  1. *种不是常驻型广播,也就是说广播跟随程序的生命周期。
  2. 第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。

二.发送广播

当我们需要发送一个自定义的广播来通知程序中其他组件一些状态时,就可以使用发送一条广播的方式。

有两种方式分别发送两种不同的广播:
通过mContext.sendBroadcast(Intent)mContext.sendBroadcast(Intent, String)发送的是无序广播(后者加了权限);
通过mContext.sendOrderedBroadcast(Intent, String, BroadCastReceiver, Handler, int, String, Bundle)发送的是有序广播。

区别
无序广播:所有的接收者都会接收事件,不可以被拦截,不可以被修改。
有序广播:按照优先级,一级一级的向下传递,接收者可以修改广播数据,也可以终止广播事件。

(1)无序广播的使用:

定义一个按钮,设置其点击事件,发送一个无序广播。

        Intent intent = new  Intent();
        //设置intent的动作为com.example.broadcast,可以任意定义
        intent.setAction("com.example.broadcast");
        //发送无序广播
        sendBroadcast(intent);

定义一个广播接收者,来接收这个广播事件。通过Toast的打印判断是否收到广播

public class MyReceiver extends BroadcastReceiver {
    public MyReceiver() {
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"收到广播", Toast.LENGTH_SHORT).show();
    }
}

在Manifest.xml中配置该接收者。

<receiver
            android:name=".MyReceiver" >
            <intent-filter>
                <!-- 动作设置为发送的广播动作 -->
                <action android:name="com.example.broadcast"/>
            </intent-filter>
</receiver>

运行结果为:Toast打印出 “收到广播”。

(2)有序广播的使用

和无序广播使用不同的是 通过 mContext.sendOrderedBroadcast(Intent, String, BroadCastReceiver, Handler, int, String, Bundle)和每个接收者设置优先级,就可以在小于自己优先级的接收者得到广播前,修改或终止广播。

定义一个按钮,设置其点击事件,发送一个有序广播。

        Intent intent = new  Intent();
        //设置intent的动作为com.example.broadcast,可以任意定义
        intent.setAction("com.example.broadcast");
        //发送无序广播
        //*个参数:intent
        //第二个参数:String类型的接收者权限
        //第三个参数:BroadcastReceiver 指定的接收者
        //第四个参数:Handler scheduler
        //第五个参数:int 此次广播的标记 
        //第六个参数:String 初始数据
        //第七个参数:Bundle 往Intent中添加的额外数据
        sendOrderedBroadcast(intent, null, null, null, "这是初始数据", );

定义多个广播接收者,来接收这个广播事件。通过Toast的打印判断是否收到广播

public class MyReceiver1 extends BroadcastReceiver {
    public MyReceiver1() {
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        //获取广播中的数据(即得到 "这是初始数据" 字符串)
        String message = getResultData();
        Toast.makeText(context ,message ,Toast.LENGTH_SHORT).show();
        //修改数据
        setResultData("这是修改后的数据");
    }
}
public class MyReceiver2 extends BroadcastReceiver {
    public MyReceiver2() {
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        String message = getResultData();
        Toast.makeText(context ,message ,Toast.LENGTH_SHORT).show();
        //终止广播
        abortBroadcast();
    }
}
public class MyReceiver3 extends BroadcastReceiver {
    public MyReceiver3() {
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        String message = getResultData();
        Toast.makeText(context ,message ,Toast.LENGTH_SHORT).show();
    }
}

在Manifest.xml中配置该接收者。并设置优先级:MyReceiver1>MyReceiver2>MyReceiver3。

<!-- 优先级相等的话,写在前面的receiver的优先级大于后面的 -->
<receiver
            android:name=".MyReceiver1" >
            <!-- 定义广播的优先级 -->
            <intent-filter android:priority="1000">                
                <!-- 动作设置为发送的广播动作 -->
                <action android:name="com.example.broadcast"/>
            </intent-filter>
</receiver>
<receiver 
               android:name=".MyReceiver2" >
                   <!-- 定义广播的优先级 -->
                   <intent-filter  android:priority="0">
                   <!-- 动作设置为发送的广播动作 -->
                   <action android:name="com.example.broadcast"/>
            </intent-filter>
</receiver>
<receiver 
               android:name=".MyReceiver3" >
                   <!-- 定义广播的优先级 -->
                   <intent-filter  android:priority="-1000">
                   <!-- 动作设置为发送的广播动作 -->
                   <action android:name="com.example.broadcast"/>
            </intent-filter>
</receiver>

运行结果:MyReceiver1得到广播数据后打印“这是初始数据”,MyReceiver2接收到广播数据打印“这是修改后的数据”,MyReceiver3没有打印。

Android 之使用LocalBroadcastManager解决BroadcastReceiver安全问题

Android 之使用LocalBroadcastManager解决BroadcastReceiver安全问题

Android系统中,BroadcastReceiver的设计初衷就是从全局考虑的,可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言BroadcastReceiver是存在安全性问题的,相应问题及解决如下:

在发送广播时指定接收者必须具备的permission。或通过Intent.setPackage设置广播仅对某个程序有效。

2.  当应用程序注册了某个广播时,即便设置了IntentFilter还是会接收到来自其他应用程序的广播进行匹配判断。对于动态注册的广播可以通过类似registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)的接口指定发送者必须具备的permission,对于静态注册的广播可以通过android:exported=”false”属性表示接收者对外部应用程序不可用,即不接受来自外部的广播。

上面两个问题其实都可以通过LocalBroadcastManager来解决:

Android v4 兼容包提供android.support.v4.content.LocalBroadcastManager工具类,帮助大家在自己的进程内进行局部广播发送与注册,使用它比直接通过sendBroadcast(Intent)发送系统全局广播有以下几点好处。

1    因广播数据在本应用范围内传播,你不用担心隐私数据泄露的问题。

2    不用担心别的应用伪造广播,造成安全隐患。

3    相比在系统内发送全局广播,它更高效。

其使用方法也和正常注册广播类似:

  1.  LocalBroadcastManager mLocalBroadcastManager;
  2.  BroadcastReceiver mReceiver;
  3.  IntentFilter filter = new IntentFilter();    
  4.  filter.addAction(“test”);    
  5.  mReceiver = new BroadcastReceiver() {    
  6.             @Override    
  7.             public void onReceive(Context context, Intent intent) {    
  8.                 if (intent.getAction().equals(“test”)) {    
  9.                     //Do Something  
  10.                 }
  11.             }
  12.         };
  13. mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);  
  14. mLocalBroadcastManager.registerReceiver(mReceiver, filter);
  15. 当然,和正常广播一样,也要在对应的生命周期中反注册掉:
  16.  @Override  
  17. protected void onDestroy() {  
  18.    super.onDestroy();  
  19.    mLocalBroadcastManager.unregisterReceiver(mReceiver);
  20. }
  1. <pre code_snippet_id=“195849” snippet_file_name=”blog_20140220_3_2735095″></pre><pre></pre><pre></pre><pre></pre>  

Android BroadcastReceiver 注册和反注册

Android BroadcastReceiver 注册和反注册

说起来这个问题很简单,只要注册和反注册成对出现就行,好像很多教材都是如此介绍。但实际开发中,对广播注册和反注册的时机把握还是很重要的。

关于广BroadcastReceiver注册和反注册时机,主要有以下几点:

onCreate – onDestroy(即便页面未显示,仍然能接受广播)
onResume – onPause(即只有页面显示时,才能接受广播)
onStart – onStop

https://stackoverflow.com/questions/21136464/when-to-unregister-broadcastreceiver-in-onpause-ondestroy-or-onstop

 

当注册广播时,常会遇到的问题就是重复注销广播处理函数是会报错,而且会让进程奔溃。一般来说,可以通过一个变量来保存广播处理是否被注销,每当注销时,将它标记为false。如果再次注销时遇到false就不对他进行注销处理。

曾经我遇到过几次异常,提示我的BroadcastReceiver多次被反注册,导致系统异常死掉。废了很长时间查看代码找原因。。

比如我之前常在finish方法中反注册广播,但finish方法有可能被多次调用,导致 BroadcastReceiver多次被反注册。

另外,需要注意一点,你用什么contex(上下文)注册的广播,则必须用它来反注册。

比如你调用getApplicationContext()注册,用getApplicationContext()删除。而不能直接unregisterReceiver(mFinishReceiver);和registerReceiver(mFinishReceiver, filter);有可能找不到。

BroadcastReceiver详解

BroadcastReceiver详解

 

一、概述

BroadcastReceiver:直译是“广播接收者”,所以它的作用是用来接收发送过来的广播的。

那我们有必要知道:什么是广播。广播,我的理解就是系统中消息的一种变种;就是当一个事件发生时,比如,系统突然断网,系统就发一个广播消息给所有的接收者,所有的接收者在得到这个消息之后,就知道,啊哦,现在没网络了,我的程序应该怎么办,比如显示默认图片、提示用户等。前面,我们说了,BroadcastReceiver就是一个广播消息接收者。

另外我还要提一下,广播之间信息的传递是通过Intent对象来传递的;在《详解Intent》系列文章中,我讲了,Intent调用分为显示调用的隐式调用两种,由于这里能通知到所有的接收者,所以肯定不能利用显示调用,只有利用隐式调用Intent对象了。(这里的隐式调用,并不是真正意义上的Intent隐式调用,因为Intent隐式调用,当出现很多匹配应用时,会以列表形式提示用户选择一个启动,而这里不同的地方在于,当有很多匹配项时,会给所有的匹配项都发一个消息,我说隐式调用,只是方便大家理解构造Intent的方法,即必须利用构造隐式Intent的方法来构造)

 

二、注册相关

 

1、静态注册实例程序

大家可能会问,什么叫静态注册实例程序,先不要管上面的标题,慢慢往下看,后面在讲动态注册时会再提到。

先构造一个接收器:

 

  1. public class MyReceiver extends BroadcastReceiver {  
  2.     private static final String TAG = “MyReceiver”;    
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         // TODO Auto-generated method stub  
  6.         String msg = intent.getStringExtra(“msg”);    
  7.         Log.i(TAG, “MyReceiver:”+msg);    
  8.     }
  9. }

直接派生自BroadcastReceiver,在OnReceive()函数中进行处理即可,我们前面说了,广播的传递是靠Intent的,OnReceive的第二个参数,就是广播传过来的Intent,因为后面我们在发送广播时,会利用PutStringExtra放进去一个标识为msg的字符串,所以这里我们可以利用GetStringExtra把这个字符串取出来。然后用Log标记下这个类接收到了这个消息,以便我们跟踪。
大家可能会想,就这么着,就能收到广播了?当然不是,上面我们说了,通过隐式Intent来发送广播的,我们肯定要匹配这个Intent啊,匹配Intent的术语是Activity中的,在广播这里,叫要注册,也就是要注册一下,什么样的Intent能接收。 

MyReceiver的广播接收 注册代码如下:(静态注册)

 

 

  1. <receiver android:name=“.MyReceiver”>  
  2.     <intent-filter>  
  3.         <action android:name=“android.intent.action.MY_BROADCAST”/>  
  4.         <category android:name=“android.intent.category.DEFAULT” />  
  5.     </intent-filter>  
  6. </receiver>  

android:name:对应接收器的类名;我们自定义的类名叫MyReceiver ,所以这里写”.MyReceiver ” 

intent-filter标签里,同样是必须的两项:action和category;我在这里自定义了action的名字,等下隐式发送通过时,就是利用匹配action来接收通知的。

此时的AndroidManifest.xml全部内容为:

 

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <manifest xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     package=“com.example.test_brocast_blog”  
  4.     android:versionCode=“1”  
  5.     android:versionName=“1.0” >  
  6.     <uses-sdk  
  7.         android:minSdkVersion=“14”  
  8.         android:targetSdkVersion=“14” />  
  9.     <application  
  10.         android:allowBackup=“true”  
  11.         android:icon=“@drawable/ic_launcher”  
  12.         android:label=“@string/app_name”  
  13.         android:theme=“@style/AppTheme” >  
  14.         <activity  
  15.             android:name=“.MainActivity”  
  16.             android:label=“@string/app_name” >  
  17.             <intent-filter>  
  18.                 <action android:name=“android.intent.action.MAIN” />  
  19.                 <category android:name=“android.intent.category.LAUNCHER” />  
  20.             </intent-filter>  
  21.         </activity>  
  22.         <receiver android:name=“.MyReceiver”>  
  23.             <intent-filter>  
  24.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  25.                 <category android:name=“android.intent.category.DEFAULT” />  
  26.             </intent-filter>  
  27.         </receiver>  
  28.     </application>  
  29. </manifest>  

这里特别注意一个<activity> 标签与<receiver>标签的构造。完全相同!!!!!!!
完全相同体现在: 

 

  • 1、所处位置:都直属<application>标签;
  • 2、参数构造基本一样;都有android:name,都有<intent-filter>;
  • 3、都是通过Intent传递参数,也都是通过Intent进行匹配!!!!

 

这说明了一个问题:receiver是activity的变种!!!!!我没有研究源码,但仅从这些相同点来看,他们肯定是从一个共同的类派生出来的。(猜想)

*后是发送广播:

我们在主页面加一个Button,当点击Button时发送广播消息。

布局文件如下:(activity_main.xml)

 

  1. <RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     xmlns:tools=“http://schemas.android.com/tools”  
  3.     android:layout_width=“match_parent”  
  4.     android:layout_height=“match_parent”  
  5.     tools:context=“com.example.test_brocast_blog.MainActivity” >  
  6.    <Button  
  7.         android:id=“@+id/sent_btn”  
  8.         android:layout_width=“wrap_content”  
  9.         android:layout_height=“wrap_content”  
  10.         android:text=“发送Broadcast” />  
  11. </RelativeLayout>  

代码如下:(MainActivity.java)

  1. public class MainActivity extends Activity {  
  2.     @Override  
  3.     protected void onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.activity_main);
  6.         Button btn= (Button)findViewById(R.id.sent_btn);
  7.         btn.setOnClickListener(new View.OnClickListener() {  
  8.             @Override  
  9.             public void onClick(View v) {  
  10.                 // TODO Auto-generated method stub  
  11.                 send();
  12.             }
  13.         });
  14.     }
  15.     public void send() {  
  16.         Intent intent = new Intent(“android.intent.action.MY_BROADCAST”);  
  17.         intent.putExtra(“msg”, “hello receiver.”);  
  18.         sendBroadcast(intent);
  19.     }
  20. }

真正的发送代码在send()函数中,在这个函数中可以看到,我们通过传进去刚才注册的MyRecevier的action,来构造一个隐式Intent,然后利用PutExtra放进去一个额外信息(这个不是必须的,我们仅仅是为了跟踪这个消息传到了哪里去,在《详解Intnent》系统文章中有讲怎样构造一个隐式Intent),与StartActivity不同的是,这里利用的是sendBroadcast(intent)来发送这个Intent;
效果图: 

 

%title插图%num

%title插图%num

这里注意一下应用名!!!!(com.example.test_brocast_blog)后面会用到。

 

2、动态注册实例程序

前面,我们说了静态注册,什么叫静态注册呢,就是利用XML来注册。

相反,利用代码来注册的就叫动态注册。

静态注册和动态注册是有区别的,主要体现在接收上。

静态注册的程序,无论该程序是否启动,都会当广播到来时接收,并处理。而动态注册的程序只有在程序运行时才会收到广播消息,程序不运行了,它就收不到了。

动态注册的代码如下:

 

  1. MyReceiver receiver = new MyReceiver();  
  2. IntentFilter filter = new IntentFilter();  
  3. filter.addAction(“android.intent.action.MY_BROADCAST”);  
  4. registerReceiver(receiver, filter);

同样,首先生成我们要接收的类的实例,然后利用IntentFilter来声明他可以匹配的广播类型(这里利用动作来匹配),*后利用registerReceiver(receiver, filter);来注册,即利用当哪种类型的Intent广播到来时,要调用MyReceiver类。 

注意:发送广播之前,要先注册,不然根本没有接收者匹配,当然,不注册接收者也不会出现任何错误或警告,只是发送一个没有任何接收者的广播播毫无意义。
下面我们新建一个项目,取名叫:Test_Brocast_Blog_Dynamic

同样,写一个MyRecever类来接收广播,MyRecever类内容不变:

 

  1. public class MyReceiver extends BroadcastReceiver {  
  2.     private static final String TAG = “MyReceiver”;    
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         // TODO Auto-generated method stub  
  6.         String msg = intent.getStringExtra(“msg”);    
  7.         Log.i(TAG, msg);
  8.     }
  9. }

然后同样,给MainActivity布局里添加一个Button,当点击Button时发送广播。布局文件与上面一样,这里我只写代码:

  1. public class MainActivity extends Activity {  
  2.     @Override  
  3.     protected void onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.activity_main);
  6.       //在发送之前,确定在代码的某个位置已经动态注册  
  7.         MyReceiver receiver = new MyReceiver();  
  8.         IntentFilter filter = new IntentFilter();  
  9.         filter.addAction(“android.intent.action.MY_BROADCAST”);  
  10.         registerReceiver(receiver, filter);
  11.         //发送广播  
  12.         Button btn= (Button)findViewById(R.id.sent_btn);
  13.         btn.setOnClickListener(new View.OnClickListener() {  
  14.             @Override  
  15.             public void onClick(View v) {  
  16.                 // TODO Auto-generated method stub  
  17.                 send();
  18.             }
  19.         });
  20.     }
  21.     public void send() {  
  22.         Intent intent = new Intent(“android.intent.action.MY_BROADCAST”);  
  23.         intent.putExtra(“msg”, “hello receiver.”);  
  24.         sendBroadcast(intent);
  25.     }
  26. }

在OnCreate()里在注册广播接收者,即告诉系统,当这个广播到来时,用MyReciver来接收。然后点击发送按钮来发送广播。
注:(在运行这个程序之前,先把静态注册的APP装到手机上,这是下面得出结论的前提) 

结果如下:

操作界面

%title插图%num

结果:

%title插图%num

咦?怎么出来两条信息?

也就是说有两个接收者接收到了这条广播,但我们这里只注册了一个MyRecever实例啊。

对的,看应用名称就可以看得出,这是两个不同的应用,有一个是静态注册的(com.example.test_brocast_blog),所以静态注册的程序不管是否启动,都可以收得到匹配的广播的,并对这个广播操作。

如果想试一下,动态注册的代码能不能收到广播,可以反过来一下,运行静态注册的程序,把动态注册的程序关掉,看出来几条Log?答案肯定是一条!因为我们都知道动态注册的代码在程序不运行时是收不到广播的。

 

三、普通广播与有序广播

普通广播是指大家等级都是一样的,当广播到来时,都能一块接收到,并没有接收的先后顺序。由于是一同接收到的,所以一个接收者是没有办法阻止另一个接收者接收这个广播的。

有序广播是指接收是按一定的优先级顺序来接收的,优先级高的先收到,并可以对广播进行操作后,再传给下一个接收者,当然也可以不传,如果不传的话,后面的接收者就都收不到这个广播了。

普通广播

(在运行这个程序之前,先把手机上前两个APP全部删除,以免影响结果)

首先我们建三个接收者,并对他们全部静态注册。三个接收者的代码分别如下:

FirstRecever:

 

  1. public class FirstRecever extends BroadcastReceiver {  
  2.     private static final String TAG = “MyReceiver”;    
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         // TODO Auto-generated method stub  
  6.         String msg = intent.getStringExtra(“msg”);    
  7.         Log.i(TAG, “FirstRecever:”+msg);    
  8.     }
  9. }

SecondRecever: 

 

  1. public class SecondRecever extends BroadcastReceiver {  
  2.     private static final String TAG = “MyReceiver”;    
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         // TODO Auto-generated method stub  
  6.         String msg = intent.getStringExtra(“msg”);    
  7.         Log.i(TAG, “SecondRecever:”+msg);    
  8.     }
  9. }

 

MyReceiver

 

  1. public class MyReceiver extends BroadcastReceiver {  
  2.     private static final String TAG = “MyReceiver”;    
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         // TODO Auto-generated method stub  
  6.         String msg = intent.getStringExtra(“msg”);    
  7.         Log.i(TAG, “MyReceiver:”+msg);    
  8.     }
  9. }

注册代码如下:(AndroidManifest.xml)

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <manifest xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     package=“com.example.test_brodcast”  
  4.     android:versionCode=“1”  
  5.     android:versionName=“1.0” >  
  6.     <uses-sdk  
  7.         android:minSdkVersion=“14”  
  8.         android:targetSdkVersion=“14” />  
  9.     <application  
  10.         android:allowBackup=“true”  
  11.         android:icon=“@drawable/ic_launcher”  
  12.         android:label=“@string/app_name”  
  13.         android:theme=“@style/AppTheme” >  
  14.         <activity  
  15.             android:name=“.MainActivity”  
  16.             android:label=“@string/app_name” >  
  17.             <intent-filter>  
  18.                 <action android:name=“android.intent.action.MAIN” />  
  19.                 <category android:name=“android.intent.category.LAUNCHER” />  
  20.             </intent-filter>  
  21.         </activity>  
  22.         <!– 分别注册这三个接收器 –>  
  23.          <receiver android:name=“.MyReceiver”>  
  24.             <intent-filter>  
  25.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  26.                 <category android:name=“android.intent.category.DEFAULT” />  
  27.             </intent-filter>  
  28.         </receiver>  
  29.          <receiver android:name=“.FirstRecever”>  
  30.             <intent-filter>  
  31.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  32.                 <category android:name=“android.intent.category.DEFAULT” />  
  33.             </intent-filter>  
  34.         </receiver>  
  35.          <receiver android:name=“.SecondRecever”>  
  36.             <intent-filter>  
  37.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  38.                 <category android:name=“android.intent.category.DEFAULT” />  
  39.             </intent-filter>  
  40.         </receiver>  
  41.     </application>  
  42. </manifest>  

同样,我们在MainActivity中添加一个Button,当点击按钮时发送广播,MainActivity代码如下: 

 

  1. public class MainActivity extends Activity {
  2.     @Override
  3.     protected void onCreate(Bundle savedInstanceState) {
  4.         super.onCreate(savedInstanceState);
  5.         setContentView(R.layout.activity_main);
  6.         Button btn= (Button)findViewById(R.id.sent_btn);  
  7.         btn.setOnClickListener(new View.OnClickListener() {
  8.             @Override
  9.             public void onClick(View v) {
  10.                 // TODO Auto-generated method stub
  11.                 send();
  12.             }
  13.         });
  14.     }
  15.     public void send() {
  16.         Intent intent = new Intent(“android.intent.action.MY_BROADCAST”);  
  17.         intent.putExtra(“msg”, “hello receiver.”);
  18.         sendBroadcast(intent);
  19.     }
  20. }

运行程序,结果:
%title插图%num
可见,三个全都收到了广播。 

有序广播(无访问权限版)

首先有序广播与普通广播的不同点在发送和接收都有不同。

首在发送有序广播:

 

  1. public class MainActivity extends Activity {
  2.     @Override
  3.     protected void onCreate(Bundle savedInstanceState) {
  4.         super.onCreate(savedInstanceState);
  5.         setContentView(R.layout.activity_main);
  6.         Button btn= (Button)findViewById(R.id.sent_btn);  
  7.         btn.setOnClickListener(new View.OnClickListener() {
  8.             @Override
  9.             public void onClick(View v) {
  10.                 // TODO Auto-generated method stub
  11.                 send();
  12.             }
  13.         });
  14.     }
  15.     public void send() {
  16.         Intent intent = new Intent(“android.intent.action.MY_BROADCAST”);    
  17.         intent.putExtra(“msg”, “hello receiver.”);
  18.         sendOrderedBroadcast(intent, null);  //没有添加权限
  19.     }
  20. }

在前面的各个例子中,我们发送广播都是用的:sendBroadcast(intent); 而这里却用的是:sendOrderedBroadcast(intent, null); 

对这个函数的官方解释是:

 

public abstract void sendOrderedBroadcast (Intent intent, String receiverPermission)

Added in API level 1

Broadcast the given intent to all interested BroadcastReceivers, delivering them one at a time to allow more preferred receivers to consume the broadcast before it is delivered to less preferred receivers. This call is asynchronous; it returns immediately, and you will continue executing while the receivers are run.

See BroadcastReceiver for more information on Intent broadcasts.

Parameters
intent The Intent to broadcast; all receivers matching this Intent will receive the broadcast.
receiverPermission (optional) String naming a permissions that a receiver must hold in order to receive your broadcast. If null, no permission is required.

其中第二个参数是指定接收者必须拥有的接收权限,如果设为NUll,就是不需要接收权限,所有匹配的Receiver都能接收到。我们这里先不需要权限试试看,下面再举个需要权限的例子。
接收端 

我们上面说了,接收端必须是有序的,是有优先级的,这种优先级是在注册时配置的,比如:

 

  1. <receiver android:name=“.FirstRecever”>  
  2. <intent-filter android:priority=“10”>  
  3.     <action android:name=“android.intent.action.MY_BROADCAST”/>  
  4.     <category android:name=“android.intent.category.DEFAULT” />  
  5. </intent-filter>  
  6. lt;/receiver>  

与上面静态注册的不同在于,在Intent-filter中添加一个android:priority=”10″属性,这个就是接收器优先级,数字越大的接收器,优先级越高,越先接到广播。
同样,上面我们三个类FirstRecever、SecondRecever和MyReceiver 的注册文件代码如下 : 

 

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <manifest xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     package=“com.example.testbroast_order”  
  4.     android:versionCode=“1”  
  5.     android:versionName=“1.0” >  
  6.     <uses-sdk  
  7.         android:minSdkVersion=“14”  
  8.         android:targetSdkVersion=“14” />  
  9.     <application  
  10.         android:allowBackup=“true”  
  11.         android:icon=“@drawable/ic_launcher”  
  12.         android:label=“@string/app_name”  
  13.         android:theme=“@style/AppTheme” >  
  14.         <activity  
  15.             android:name=“.MainActivity”  
  16.             android:label=“@string/app_name” >  
  17.             <intent-filter>  
  18.                 <action android:name=“android.intent.action.MAIN” />  
  19.                 <category android:name=“android.intent.category.LAUNCHER” />  
  20.             </intent-filter>  
  21.         </activity>  
  22.          <receiver android:name=“.FirstRecever”>  
  23.             <intent-filter android:priority=“10”>  
  24.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  25.                 <category android:name=“android.intent.category.DEFAULT” />  
  26.             </intent-filter>  
  27.         </receiver>  
  28.          <receiver android:name=“.SecondRecever”>  
  29.             <intent-filter android:priority=“9”>  
  30.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  31.                 <category android:name=“android.intent.category.DEFAULT” />  
  32.             </intent-filter>  
  33.         </receiver>  
  34.         <receiver android:name=“.MyReceiver”>  
  35.             <intent-filter android:priority=“8”>  
  36.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  37.                 <category android:name=“android.intent.category.DEFAULT” />  
  38.             </intent-filter>  
  39.         </receiver>  
  40.         <!– 接收优先级逐级降低 –>  
  41.     </application>  
  42. </manifest>  

*后是代码部分
前面我也曾提到,在一个接收器收到发来的Intent后,可以对其进行更改,对发送来的广播Intent进行修改是利用setResultExtras(bundle);  函数来实现的。 

 

public final void setResultExtras (Bundle extras)

Added in API level 1

Change the current result extras of this broadcast; only works with broadcasts sent through Context.sendOrderedBroadcast. This is a Bundle holding arbitrary data, whose interpretation is up to the broadcaster. Can be set to null. Calling this method completely replaces the current map (if any).

This method does not work with non-ordered broadcasts such as those sent with Context.sendBroadcast

 

Parameters
extras The new extra data map; may be null.

翻译一下: 

这个函数是用来改变当前广播传来的Extra额外信息的;它只能通过Context.sendOrderedBroadcast.发送过来的广播有效;它使用Bundle来传递任意的数据,而这些数据只有接收器(broadcaster)才能解析。当然也可以把它设置为NULL,这样,它就把传来的数据映射全部清空了。

参数:

extras:新的数据映射,可以为空。

从上面的优先级可以看出,这里三个类的接收顺序是这样的:FirstRecever-》SecondRecever-》MyReceiver

下面看看FirstRecever如何利用setResultExtras来改变传来的Msg信息:

 

  1. public class FirstRecever extends BroadcastReceiver {  
  2.     private static final String TAG = “MyReceiver”;    
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         // TODO Auto-generated method stub  
  6.         //先获得传过来的MSG  
  7.         String msg = intent.getStringExtra(“msg”);    
  8.         Log.i(TAG, “FirstRecever:”+msg);    
  9.         //更改广播数据  
  10.          Bundle bundle = new Bundle();    
  11.          bundle.putString(“msg”, msg + “@FirstReceiver”);    
  12.          setResultExtras(bundle);
  13.     }
  14. }

首先,我们利用getStringExtra()获得传过来的msg消息,然后利用bundle重新封装一个以“msg”为key的消息,把”@FirstReceiver”添加到消息里,表示经过了这里。
*后利用setResultExtras(bundle);  修改当前的结果集。

如果想终止消息往下一个接收器传递,可以使用:abortBroadcast();  //终止消息再传递

这里有一个疑问:我利用setResultExtras(bundle); 修改传送结果,对原来广播过来的数据有影响吗?下面我们就在SecondRecever中做个测试

 

下面看看另外两个接收器代码:SecondRecever

 

 

  1. public class SecondRecever extends BroadcastReceiver {  
  2.     private static final String TAG = “MyReceiver”;    
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         // TODO Auto-generated method stub          
  6.        //先获得广播过来的MSG  
  7.         String broadcast_msg = intent.getStringExtra(“msg”);    
  8.         Log.i(TAG, “SecondRecever–broadcast_msg:”+broadcast_msg);    
  9.         //接收通过setResultExtras传过来的msg  
  10.         String msg = getResultExtras(true).getString(“msg”);    
  11.         Log.i(TAG, “SecondReceiver: ” + msg);    
  12.         //修改setResultExtras传来的结果  
  13.         Bundle bundle = new Bundle();    
  14.         bundle.putString(“msg”, msg + “@SecondReceiver”);    
  15.         setResultExtras(bundle);
  16.     }
  17. }

这里先通过intent.getStringExtra(“msg”);  获得广播过来的数据; 

然后再利用getResultExtras(true).getString(“msg”);  获得上一级传过来的setResultExtras(bundle);  里的数据;
*后重新将bundle里的数据中添加”@SecondReceiver”做个标记;

*后,MyReceiver: 

 

  1. public class MyReceiver extends BroadcastReceiver {  
  2.     private static final String TAG = “MyReceiver”;    
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         // TODO Auto-generated method stub  
  6.         String msg = getResultExtras(true).getString(“msg”);    
  7.         Log.i(TAG, “MyReceiver: ” + msg);    
  8.     }
  9. }

 

到这就所有就序了,运行下代码:
%title插图%num
从结果可以看出:通过setResultExtras(bundle);  传递的数据是不会更改原生广播的数据的。也只是原来广播数据中额外添加的数据。

 

有序广播(添加访问权限版)

前面我们看到在sendOrderedBroadcast(intent, null);  中,第二个参数可以设定访问权限,在上个例子中,我们并没有加入访问权限,下面我们就发送一个带权限的广播:

 

  1. public class MainActivity extends Activity {  
  2.     @Override  
  3.     protected void onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.activity_main);
  6.         Button btn= (Button)findViewById(R.id.sent_btn);
  7.         btn.setOnClickListener(new View.OnClickListener() {  
  8.             @Override  
  9.             public void onClick(View v) {  
  10.                 // TODO Auto-generated method stub  
  11.                 send();
  12.             }
  13.         });
  14.     }
  15.     public void send() {  
  16.         Intent intent = new Intent(“android.intent.action.MY_BROADCAST”);    
  17.         intent.putExtra(“msg”, “hello receiver.”);    
  18.         sendOrderedBroadcast(intent, “harvic.broadcast.perssion”);   
  19.     }
  20. }

这段代码中,我们利用 sendOrderedBroadcast(intent, “harvic.broadcast.perssion”); 发送一个必须拥有”harvic.broadcast.perssion”权限的接收器才能接收到我们的广播;
然后我们要在接收器中加入声明使用权限的代码: 

有关权限的声明与使用,可以参考这篇文章:《声明、使用与自定义权限》

首先创建一个”harvic.broadcast.perssion”权限

 

  1. <permission android:name=“harvic.broadcast.perssion” android:protectionLevel=”normal”></permission>   

然后是底部声明,我们要使用这个权限: 

 

  1. <uses-permission  android:name=“harvic.broadcast.perssion”/>  

所以总体的代码如下:

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <manifest xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     package=“com.example.testbroadcast_order_perssion”  
  4.     android:versionCode=“1”  
  5.     android:versionName=“1.0” >  
  6.     <uses-sdk
  7.         android:minSdkVersion=“14”  
  8.         android:targetSdkVersion=“14” />  
  9.     <permission android:name=“harvic.broadcast.perssion” android:protectionLevel=”normal”></permission>   
  10.     <application
  11.         android:allowBackup=“true”  
  12.         android:icon=“@drawable/ic_launcher”  
  13.         android:label=“@string/app_name”  
  14.         android:theme=“@style/AppTheme” >  
  15.         <activity
  16.             android:name=“.MainActivity”  
  17.             android:label=“@string/app_name” >  
  18.             <intent-filter>
  19.                 <action android:name=“android.intent.action.MAIN” />  
  20.                 <category android:name=“android.intent.category.LAUNCHER” />  
  21.             </intent-filter>
  22.         </activity>
  23.         <receiver android:name=“.FirstRecever” >  
  24.             <intent-filter android:priority=“10”>  
  25.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  26.                 <category android:name=“android.intent.category.DEFAULT” />  
  27.             </intent-filter>
  28.         </receiver>
  29.          <receiver android:name=“.SecondRecever” >  
  30.             <intent-filter android:priority=“9”>  
  31.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  32.                 <category android:name=“android.intent.category.DEFAULT” />  
  33.             </intent-filter>
  34.         </receiver>
  35.         <receiver android:name=“.MyReceiver” >  
  36.             <intent-filter android:priority=“8”>  
  37.                 <action android:name=“android.intent.action.MY_BROADCAST”/>  
  38.                 <category android:name=“android.intent.category.DEFAULT” />  
  39.             </intent-filter>
  40.         </receiver>
  41.         <!– 接收优先级逐级降低 –>
  42.     </application>
  43.     <!– 如果不添加使用权限声明,那么接收器会拒*接受消息的,所以在Log中不会有任何显示 –>
  44.     <uses-permission  android:name=“harvic.broadcast.perssion”/>  
  45. </manifest>

其它接收器代码不变,运行之后: 

%title插图%num
有个地方要注意:即便像我们现在这样,自己的应用发出广播给自己接收,但如果不声明使用权限,是不能接收到广播的。这点与Activity的权限机制不一样,在Activity中,只要在同一个应用中相互App跳转,是不需要声明使用权限的,权限的限制只针对其它应用调用此Activity。

好啦,这篇文章到这就结束了。本篇内容比较多,涉及到的代码工程也比较多,现将相关源码列表如下:

1、静态注册源码
2、动态注册源码
3、普通接收源码
4、有序广播(无访问权限)源码
5、有序广播(添加访问权限)源码
6、本文所用图片

 

注意:在OnReceive中保存传过来值的问题:

如果有下面一段伪代码:

class xxxx{

private int mData=1;

public void onReceive(Context context, Intent intent)

Log.d(“tag”,mData + “”);

mData = intent.getIntExtra(“intdata”,-1);

}

如果我们通过intent传过来的值保存在mData中,在下次再来的时候先打出来mData的值,会发现,每次打出来的都是1,所以根本没有办法通过成员变量保存onReceive中传过来的值。

BroadcastReceiver 怎么访问 local 变量?

BroadcastReceiver 怎么访问 local 变量?

尝试用 react native 调用 android 代码,基本功能是发送短信,然后读取短信发送报告,成功或者失败

但我在看代码时,对一点非常困惑,安卓在广播context.registerReceiver(new BroadcastReceiver()时,会告知是那条短信发送成功吗?

假设我非常密集的发送了 10 条短信,sendEvent(mReactContext, "sms_onDelivery", "SMS delivered");能传递 sms 的 id 或者 phoneNumber 出来吗? 我尝试直接sendEvent(mReactContext, "sms_onDelivery", phoneNumber + "SMS delivered");说无法读取本地变量,必须全局变量才行

但是全局变量的话每次调用 autoSend,不是立即将 phoneNumber 改写为*新发送的 phoneNumber 吗?

代码源 :https://github.com/briankabiro/react-native-get-sms-

android/blob/f40a861ce85bb54ee0b4bd7241b302e4114a91b9/android/src/main/java/com/react/SmsModule.java#L232

@ReactMethod
    public void autoSend(String phoneNumber, String message, final Callback errorCallback,
                         final Callback successCallback) {

        cb_autoSend_succ = successCallback;
        cb_autoSend_err = errorCallback;

        try {
           // 无关代码,略过

            //---when the SMS has been delivered---
            context.registerReceiver(new BroadcastReceiver() {
                @Override
                public void onReceive(Context arg0, Intent arg1) {
                    switch (getResultCode()) {
                        case Activity.RESULT_OK:
                            sendEvent(mReactContext, "sms_onDelivery", "SMS delivered"); // 这怎么知道发送成功的是哪个短信?能传递 phoneNumber 出来吗?
                            break;
                        case Activity.RESULT_CANCELED:
                            sendEvent(mReactContext, "sms_onDelivery", "SMS not delivered");
                            break;
                    }
                }
            }, new IntentFilter(DELIVERED));  

4 条回复    2021-03-14 22:18:37 +08:00

1 susunus   16 天前 via iPhone 广播监听到短信变化后,contentprovider 读下?
2 MapHacker   16 天前 PendingIntent sentPI = PendingIntent.getBroadcast(context, 0, new Intent(SENT), 0);
这个 new Intent(SENT)可以调用 putExtra 方法存一些数据,可以试试呢 然后在 onReceive 通过 arg1.getExtra 取到对应的值,不知道是否可行
3 gamesover   15 天前 @MapHacker 服! https://stackoverflow.com/a/4640066/2251303 后面有人评论里提到和你一样的办法
还有一个就是 PendingIntent.getBroadcast(context, `0`, new Intent(SENT), 0),第二个参数可以改为任意数字,那么我直接 pass sms 的数据库 id 就可以了
4 gamesover   15 天前 不好意思,继续更正一下,https://stackoverflow.com/a/36408330/2251303 requstcode 无法在接受广播时提取,但是不同的 requstcode 还是有意义的
友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速