一、广播机制简介
作为Android四大组件之一的Broadcast,同样被intent传递,通常情况下,自定义和Android预定义的广播加载到intent中被广播出去,注册的BroadcastReceiver就可以监听到这些Intent,并获得其中的数据
例如连接网络,电池充上电,来短信,这些预定义的广播,会被Android的intent自动的广播
也就是说,发送广播通过intent,接受广播通过BroadcastReceiver
1.广播分类
-
标准广播:异步执行的广播,广播发出后,所有的接收器会在同一时间接收到广播的内容,没有先后顺序,效率较高,缺点也显而易见,无返回值,不能截断,通过sendBroadcast()函数发送 -
有序广播:同步执行的广播,广播发出后,同一时刻只有一个接收器能接受到,该接收器接收到之后才能传递给下一个,广播可截断,且广播接收器有优先级,优先级高的先接收到广播,如果优先度高的广播截断,则优先度低的无法收到广播,通过sendOrderedBroadcast()发送 优先级取值范围为-1000到1000,通过abortBroadcast的方法终止广播的传递,先接收到广播的接收器可以通过setResultExtras(Bundle)方法传递值给下一个接收者,下一个接收者通过getResultExtras(true).var获得bundle
2.广播注册
和Activity以及service一样,广播需要通过注册,注册分为动态注册和静态注册
- 动态注册:动态注册广播为java代码内部实现,我们可以自由的控制该接收器的注册与取消。但是动态注册的广播接收器和Activity的生命周期相同
- 静态注册:在清单文件中实现,不受程序的约束,应用程序关闭后仍然可以接收到广播
二、系统广播
1.动态注册网络监听器
新建内部类来设置广播接收器
class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
if (info != null && info.isAvailable()){
Toast.makeText(context, "网络已连接", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, "网络已断开", Toast.LENGTH_SHORT).show();
}
}
}
onReceive方法为每次状态改变时执行的方法
getSystemService为Activity的方法(所以是内部类),通过该方法获得实例,为管理网络连接的系统服务类,调用它的getinfo方法获得info再进行判断
接着在清单文件内添加权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
代码采用动态注册的方式如下
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver,intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
registerReceiver放入了intentfilter实例,别忘了在onDestroy方法中解注册
2.静态注册开启启动
静态注册可以在程序未启动时接受广播
package com.thundersoft.session9;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "已开机", Toast.LENGTH_SHORT).show();
}
}
静态注册如下
<receiver android:name=".BootCompleteReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
开启启动后系统就会发送BOOT_COMPLETED这个广播
权限同样需要申请使用
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
三、自定义广播
发送自定义广播可以用来传递数据
1.发送自定义有序广播
创建四个接收器
public class Receiver1 extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接收者1接受广播", Toast.LENGTH_SHORT).show();
}
}
在接收器3中添加拦截广播代码abortBroadcast();
package com.thundersoft.session9;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
public class SenderActivity extends Activity {
private Receiver1 receiver1;
private Receiver2 receiver2;
private Receiver3 receiver3;
private Receiver4 receiver4;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout linearLayout = new LinearLayout(this);
Button button = new Button(this);
linearLayout.addView(button);
setContentView(linearLayout);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("com.test.broadcasttest.My_TestBroadcast");
sendOrderedBroadcast(intent,null);
}
});
receiver1 = new Receiver1();
IntentFilter intentFilter1 = new IntentFilter();
intentFilter1.addAction("com.test.broadcasttest.My_TestBroadcast");
intentFilter1.setPriority(88);
registerReceiver(receiver1,intentFilter1);
receiver2 = new Receiver2();
IntentFilter intentFilter2 = new IntentFilter();
intentFilter2.addAction("com.test.broadcasttest.My_TestBroadcast");
intentFilter2.setPriority(100);
registerReceiver(receiver2,intentFilter2);
receiver3 = new Receiver3();
IntentFilter intentFilter3 = new IntentFilter();
intentFilter3.addAction("com.test.broadcasttest.My_TestBroadcast");
registerReceiver(receiver3,intentFilter3);
receiver4 = new Receiver4();
IntentFilter intentFilter4 = new IntentFilter();
intentFilter4.addAction("com.test.broadcasttest.My_TestBroadcast");
registerReceiver(receiver4,intentFilter4);
}
}
一、优先级顺序值越高越先接受,如果没有设定优先级,则顺序接受
二、自定义广播的接收器必须要动态注册,静态注册的android:priority属性失效
四、本地广播
前面的广播应用于整个系统,其他的应用程序也可以被接受,有很大的安全性问题,例如垃圾广播被接受,或者发送的关键广播被其他接收器获取,而本地广播机制,使得广播和接收器只能在应用程序内部使用
通过LocalBroadcastManager注册处理
package com.thundersoft.session9;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class LocalBroadcastActivity extends Activity {
private LocalBroadcastTest localBroadcastTest;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout linearLayout = new LinearLayout(this);
setContentView(linearLayout);
Button button = new Button(this);
linearLayout.addView(button);
localBroadcastManager = LocalBroadcastManager.getInstance(LocalBroadcastActivity.this);
localBroadcastTest = new LocalBroadcastTest();
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("com.thundersoft.session9.MY_LOCALBROADCAST");
localBroadcastManager.sendBroadcast(intent);
}
});
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.thundersoft.session9.MY_LOCALBROADCAST");
localBroadcastManager.registerReceiver(localBroadcastTest,intentFilter);
}
class LocalBroadcastTest extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "本地广播接收器已接受", Toast.LENGTH_SHORT).show();
}
}
}
五、实践
1.要求
实现一个登录页面,提供记住密码和用户名功能,登陆后显示列表视图必须包含登录用户名,且点击列表会显示点击那一项
登录后主界面列表选择第一个选项后,显示一组图片,图片加载完成前需要显示进度条,加载完成后,长按某一个图片弹出提示框是否删除,如果确定就不再显示该图片
登录后主界面列表选择第二个选项后,跳转到另一个Activity,并且传过去一个字符串,在新的Activity中包含2个Fragment,左边的Fragment类似手机设置,含一个亮度设置选项,点击后右边的Fragment显示具体的亮度设置界面
登录后主界面列表选择第三个选项后,跳转到另一个Activity,并且传过去int/byte/Serializable等多种类型的数据,在新的Activity中显示传入的数据,检查是否所传所有类型的数据都正确接收,同时在Activity启动的时候开始监听android.net.conn.CONNECTIVITY_CHANGE,Activity退出后不再监听,当网络状态改变的时候提示用户网络状态改变情况。
登录后主界面列选择第四个选项后,跳转到另一个Activity,运用本章学习的资源、样式等知识,实现Activity中显示类似自己手机设置列表的效果(跟进到自己手机的设置界面一样)
登录后主界面列表选择第五个选项后,跳转到另一个Activity显示记账本,其含一个 记账项输入、金额输入、添加按钮和一个列表,添加记账功能往自定义Content Provider写入,数据可以存储在自定义数据库,或者xml文件里,或者最简单的存在列表变量里面;
登陆后主页面列表选择第六个选项后,通过输入网址下载MP3,显示下载进度,下载完成后立即播放,实现停止功能,并且显示播放的次数
登录后主界面列表选择第七个选项后,跳转到另一个Activity,其含音乐文件选择控件、启动和停止按钮、状态显示区,当选择一个音乐启动播放后,Activity可以退出,音乐在后台不停,再次进去应用,能正确显示播放状态,能停止音乐播放。
(以上是Android 十 当中的实践要求)
登录后主界面列表选择第八个选项后,跳转到另一个Activity,显示系统内存显示并和strings判断,检测系统的内存使用情况
2.代码实现
package com.thundersoft.login;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
public class ListenerActivity extends Activity {
private LinearLayout linearLayout;
private ListenerReceiver listenerReceiver;
private TextView textView0,textView1,textView2,textView3;
int x = 0;
String maxMemory = "";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL);
setContentView(linearLayout);
maxMemory = getResources().getString(R.string.current_memory);
textView0 = new TextView(this);
textView1 = new TextView(this);
textView2 = new TextView(this);
textView3 = new TextView(this);
textView0.setText("预定最大分配内存为:"+maxMemory);
linearLayout.addView(textView0);
linearLayout.addView(textView1);
linearLayout.addView(textView2);
linearLayout.addView(textView3);
listenerReceiver = new ListenerReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
intentFilter.addAction("com.test.broadcast.MEMORY_CHANGE");
intentFilter.setPriority(100);
registerReceiver(listenerReceiver,intentFilter);
}
class ListenerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
float maxMemory1 = (float) (Runtime.getRuntime().maxMemory() * 1.0/ (1024 * 1024));
textView1.setText("最大分配内存为:"+maxMemory1);
float totalMemory = (float) (Runtime.getRuntime().totalMemory() * 1.0/ (1024 * 1024));
textView2.setText("当前分配的总内存为:"+totalMemory);
float freeMemory = (float) (Runtime.getRuntime().freeMemory() * 1.0/ (1024 * 1024));
textView3.setText("剩余内存为:"+freeMemory);
TextView textView = new TextView(ListenerActivity.this);
textView.setText(x+"s系统剩余内存为:" + String.valueOf(memoryInfo.availMem / (1024 * 1024)) + "MB");
linearLayout.addView(textView);
if (x == 5){
if (maxMemory.equals(String.valueOf(maxMemory1))){
textView0.setText("最大分配内存相同");
}
}
try {
Thread.sleep(1000);
x++;
} catch (InterruptedException e) {
e.printStackTrace();
}
{
if (x < 20){
intent.setAction("com.test.broadcast.MEMORY_CHANGE");
context.sendOrderedBroadcast(intent,null);
}else{
abortBroadcast();
}
};
}
}
}
代码没啥难理解的地方,因为实现的方式很简单,通过更改网络发送广播,接收器收到广播,收到后继续发送广播,接收器通过对系统内存的检测,不断地更新内存显示界面
原本是想接受开机广播,但是电脑配置很差,AVD运行起来卡爆,另外开机广播为全局广播,需要静态注册接收器,且静态注册的接收器是一个独立类(或许有办法不独立?),传值就成了难点,尝试了很多办法,例如接口,实现接口的方式,getset方式,bundle等方式,均会出现空指针等异常,迫不得已退而求其次这样写,也希望能有大佬指点指点
3.实现效果
|