您的位置:首页 > 移动开发 > Android开发

浅谈Android四大组件之BroadcastReceiver

2017-12-27 22:33 369 查看

1、介绍

Android中定件义了很多很多的事件如,电话打出去了,电量变化了,信号变化了,应用安装了,屏幕开启了关闭了,当这些事件到来的时候;android就会发送广播如果想到监听到这些事件,就需要注册广播接收者,主要为了方便开发者进行开发。

内部实现机制:

1、自定义广播接收者BroadcastReceiver,并复写onReceive()方法;

2、通过Binder机制向AMS(Activity Manager Service)进行注册;

3、广播发送者通过Binder机制向AMS发送广播;

4、AMS查找符合相应条件(IntentFilter / Permission等)的BrocastReceiver,将广播发送到BrocastReceiver

(一般情况下为Activity)相应的消息循环队列中;

5、消息循环队列拿到此广播,回调BrocastReceiver中的onReceiver()方法。

2、简单案列-sd卡状态监听(系统广播)

广播接收者的用法

public class SdcradReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//         <action android:name="android.intent.action.MEDIA_MOUNTED" />
//           <action android:name="android.intent.action.MEDIA_UNMOUNTED" />

//一个广播接收者  可以同时注册多个广播的接受事件
//通过action去区分
//获取action  通过intent
String action = intent.getAction();
if (action.equals("android.intent.action.MEDIA_MOUNTED")) {
System.out.println("sd卡 安装成功");
}
if (action.equals("android.intent.action.MEDIA_UNMOUNTED")) {
System.out.println("sd卡 卸载了");
}

Uri data = intent.getData();
System.out.println(data);

}

}


清单代码:

<receiver android:name="cn.test.sdcard.SdcradReceiver" >
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED" />
<action android:name="android.intent.action.MEDIA_UNMOUNTED" />
<data android:scheme="file" />
</intent-filter>
</receiver>


3、简单案列-无序广播(开发者自己手动发广播)

无序广播:相当于新闻联播7点钟准时开始放不能配置优先权,广播不能被中断,系统广播一般是无序广播

实现广播接收类:

public class NormalReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {

String data = intent.getStringExtra("news");
Toast.makeText(context, data, 0).show();

}

}


清单文件注册广播接收:

<receiver android:name="cn.test.receivenormal.NormalReceiver">
<intent-filter >
<action android:name="cn.test.news"/>
</intent-filter>
</receiver>


发送广播:

Intent intent=new Intent();
//为了让广播接收者  有接收到的action
intent.setAction("cn.test.news");
intent.putExtra("news", "新闻联播每天晚上7点准时与您相约,不见不散哦");
sendBroadcast(intent);


4、简单案列-有序广播

有序广播:红头文件 可以配置优先权.广播是可以中断的

背景介绍:

发送一个有序广播,有多个广播接收者,分别为 CityReceiver TownReceiver FarmerReceiver 以及最终的广播接收者ResultReceiver(返回有序广播的接受情况)

申明多个广播接受着:

public class CityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String data = getResultData();
Toast.makeText(context, "市政府收到的信息:"+data, 0).show();

setResultData("皇上给灾民赈灾粮20万石");
}

}


public class TownReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String data = getResultData();
Toast.makeText(context, "镇政府收到的信息:"+data, 0).show();
setResultData("皇上给灾民赈灾粮10万石");
}

}


public class FarmerReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String data = getResultData();
Toast.makeText(context, "灾民收到的信息:" + data, 0).show();
}

}


再定义这三个广播接收者的清单:

<receiver android:name="cn.test.receiveorder.CityReceiver">
//优先级 0-1000
<intent-filter android:priority="1000">
<action android:name="cn.daqing.rice"/>
</intent-filter>
</receiver>

<receiver android:name="cn.test.receiveorder.TownReceiver">
<intent-filter android:priority="666">
<action android:name="cn.daqing.rice"/>
</intent-filter>
</receiver>

<receiver android:name="cn.test.receiveorder.FarmerReceiver">
<intent-filter android:priority="110">
<action android:name="cn.daqing.rice"/>
</intent-filter>
</receiver>


定义最终的广播接收者,自己的广播接收者,该接受者不需要在清单文件中配置,主要由于向发送广播者反应发送的广播最终情况,这个可以根据自己发送有序广播参数选择是否添加:

public class ResultReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String data = getResultData();
Toast.makeText(context, "报告皇上灾民收到信息是:"+data, 0).show();

}

}


发送有序广播

Intent intent=new Intent();
intent.setAction("cn.daqing.rice");

ResultReceiver resultReceiver = new ResultReceiver();

//receiverPermission  接受的权限
//resultReceiver  结果接收者  最终的广播接收者  自己的广播接收者  不需要在清单文件中配置
//广播被优先级高的 中断  依然可以收到广播   最后收到广播
//scheduler handler
//initialData  初始化的数据
//initialExtras  额外的数据
// sendOrderedBroadcast(Intent intent,
//                                 String receiverPermission,
//                                 BroadcastReceiver resultReceiver,
//                                 Handler scheduler,
//                                 int initialCode,
//                                 String initialData,
//                                 Bundle initialExtras)
sendOrderedBroadcast(intent, null, resultReceiver, null, RESULT_OK, "皇上给灾民赈灾粮50万石",
null);


注意:abortBroadcast(),进行广播拦截的时候 是不能拦截最终的广播(resultReceiver )。

- 优先级相同

不同应用 谁先在手机中安装 谁先接受到信息

同一应用 谁先在清单文件中注册 谁先接收到信息

- 优先级不同

不管应用是否相同 都是 按优先级的顺序接受广播

5、动态注册广播

以上广播注册都是在清单文件中进行注册(静态注册)的,而操作非常频繁广播,比如屏幕的关闭和开启电池电量的变化不能直接在清单文件中注册

,这时需要动态的在代码中注册一般在 onCreate()方法中注册,并把receiver声明成全局变量,最后在onDestory()中注销

即:

public class MainActivity extends Activity {
private ScreenReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//代码  动态的注册广播接收者
//      /receiver  要注册  广播接收者
//filter  过滤器
receiver = new ScreenReceiver();
IntentFilter filter=new IntentFilter();
filter.addAction("android.intent.action.SCREEN_ON");
filter.addAction("android.intent.action.SCREEN_OFF");
registerReceiver(receiver, filter);
}
//当activity销毁的时候调用
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
unregisterReceiver(receiver);
}

}


广播接收者:

public class ScreenReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals("android.intent.action.SCREEN_ON")) {
System.out.println("屏幕亮了");
}
if (action.equals("android.intent.action.SCREEN_OFF")) {
System.out.println("屏幕灭了");
}
}

}


6 LocalBroadcastManager

LocalBroadcastManager是Android Support包提供了一个工具,用于在同一个应用内的不同组件间发送Broadcast。LocalBroadcastManager也称为局部通知管理器,这种通知的好处是安全性高,效率也高,适合局部通信,可以用来代替Handler更新UI

好处:

1、因广播数据在本应用范围内传播,你不用担心隐私数据泄露的问题。
2、不用担心别的应用伪造广播,造成安全隐患。
3、相比在系统内发送全局广播,它更高效。


LocalBroadcastManager用法

1、LocalBroadcastManager对象的创建

LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;


2、注册广播接收器

LocalBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );


3 、发送广播

LocalBroadcastManager.sendBroadcast( intent ) ;


4、取消注册广播接收器

LocalBroadcastManager.unregisterReceiver( broadcastReceiver );


LocalBroadcastManager部分源码解析

private static LocalBroadcastManager mInstance;

public static LocalBroadcastManager getInstance(Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}

private LocalBroadcastManager(Context context) {
mAppContext = context;
mHandler = new Handler(context.getMainLooper()) {

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EXEC_PENDING_BROADCASTS:
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
};
}


从这个部分源码可以看出两点:

1、在获取LocalBroadcastManager对象实例的时候,这里用了单例模式。并且把外部传进来的Context 转化成了ApplicationContext,有效的避免了当前Context的内存泄漏的问题。这一点我们在设计单例模式框架的时候是值得学习的,看源码可以学习到很多东西。

2、在LocalBroadcastManager构造函数中创建了一个Handler.可见 LocalBroadcastManager 的本质上是通过Handler机制发送和接收消息的。

3、在创建Handler的时候,用了 context.getMainLooper() , 说明这个Handler是在Android 主线程中创建的,广播接收器的接收消息的时候会在Android 主线程,所以我们决不能在广播接收器里面做耗时操作,以免阻塞UI。

案例:

public class MainActivity extends AppCompatActivity {

private LocalBroadcastManager localBroadcastManager ;
private MyBroadcastReceiver broadcastReceiver ;
private IntentFilter intentFilter ;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//注册广播接收器
localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;
broadcastReceiver = new MyBroadcastReceiver() ;
intentFilter = new IntentFilter( "myaction") ;
localBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );

//在主线程发送广播
Intent intent = new Intent( "myaction" ) ;
intent.putExtra( "data" , "主线程发过来的消息" ) ;
localBroadcastManager.sendBroadcast( intent ) ;

new Thread(new Runnable() {
@Override
public void run() {
//在子线程发送广播
Intent intent = new Intent( "myaction" ) ;
intent.putExtra( "data" , "子线程发过来的消息" ) ;
localBroadcastManager.sendBroadcast( intent ) ;
}
}).start(); ;

}

private class MyBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction() ;
if ( "myaction".equals( action )){
Log.d( "tttt 消息:" + intent.getStringExtra( "data" )  , "线程: " + Thread.currentThread().getName() ) ;
}
}
}

@Override
protected void onDestroy() {
super.onDestroy();

//取消注册广播,防止内存泄漏
localBroadcastManager.unregisterReceiver( broadcastReceiver );
}
}


运行结果

D/tttt 消息:主线程发过来的消息: 线程: main

D/tttt 消息:子线程发过来的消息: 线程: main

可以看出,广播接收器的onReceive方法运行在主线程。

注意事项

虽然LocalBroadcastManager也通过BroadcastReceiver来接收消息,但是他们两个之间还是有很多区别的。

LocalBroadcastManager注册广播只能通过代码注册的方式。传统的广播可以通过代码和xml两种方式注册。

LocalBroadcastManager注册广播后,一定要记得取消监听。这一步可以有效的解决内存泄漏的问题。

LocalBroadcastManager注册的广播,您在发送广播的时候务必使用LocalBroadcastManager.sendBroadcast(intent); 否则您接收不到广播。传统的发送广播的方法:context.sendBroadcast( intent );

7、注意

原因:随着android版本的提高,android的安全性,隐私性不断增强。

4.0以上系统,在广播接收者第一安装的时候必须要有界面,没有界面,广播接收者不起作用,如果第一次安装有界面,第二次安装没有界面,起作用;

2.3系统,不管安装有没有界面 广播接收者都会起作用

4.0以上 如果 应用程序被 apps===>app 点击强行停止 广播接收者不在起作用

2.3系统 点击强行停止以后仍然起作用

相同点: 如果进程被系统杀掉 广播接收者 在广播到来的时候依然起作用 会重启进程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: