一、Broadcast简述
Android广播机制的三要素:
广播(Broadcast):用于广播发送;
广播接收器(BroadcastReceiver):用于广播接收;
意图内容(Intent):用于保存广播相关信息的媒介。
二、自定义广播及其发送与接收demo
1.新建一个类存储自定义广播内容:
package com.example.broadcastapp;
public class MyConstants {
public static final String CUSTOM_BROADCAST_ACTION = "com.example.broadcastapp.CUSTOM_BROADCAST_ACTION";
}
"com.example.broadcastapp":创建的包名;
"CUSTOM_BROADCAST_ACTION":自定义广播动作,用于Intent和IntentFilter匹配对应动作。
2.定义广播接收器,接收指定Action的广播:
package com.example.broadcastapp;
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.Toast;
// 自定义广播接收器
// Note:定义和注册是两回事,这里单独定义接收器,再在主活动中注册
public class Broadtext extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent){
if(intent.getAction().equals(MyConstants.CUSTOM_BROADCAST_ACTION)){
Toast.makeText(context, "Broadcast has been received!", Toast.LENGTH_SHORT).show();
}
}
}
当Intent获得的Action与自定义的广播动作(MyConstants.CUSTOM_BROADCAST_ACTION)匹配时(equals用于判断是否相同),表示接收到广播并Toast弹窗提醒。
3.在activity_main.xml简单定义一个Button,用于主活动类中的点击发送广播事件:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/sendBroadcastButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
tools:layout_editor_absoluteX="143dp"
tools:layout_editor_absoluteY="300dp"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
4.MainActivity.java中设计OnClick事件,并注册、注销广播接收器:
package com.example.broadcastapp;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Broadtext broadtext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 自定义广播接收器的初始化
broadtext = new Broadtext();
// 发送广播的按钮点击事件
Button sendBroadcastButton = findViewById(R.id.sendBroadcastButton);
sendBroadcastButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建Intent并设置Action
Intent intent = new Intent(MyConstants.CUSTOM_BROADCAST_ACTION);
// 发送自定义广播
sendBroadcast(intent);
}
});
}
// 动态注册广播接收器
@Override
protected void onStart(){
super.onStart();
IntentFilter fliter = new IntentFilter(MyConstants.CUSTOM_BROADCAST_ACTION);
registerReceiver(broadtext, fliter);
}
@Override
protected void onStop(){
super.onStop();
unregisterReceiver(broadtext);
}
}
注册前在onCreate()方法中初始化自定义的广播接收器。再定义Button的点击事件监听,通过Intent匹配Action信息,将其发送。
采用动态注册广播接收器的方式,在onStart()注册,在onStop()注销,广播接收器的Intent需要筛选带有指定动作的广播(IntentFilter)。
5.AndroidManifest.xml文件无需修改:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.broadcastapp">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Broadcastapp">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
6.执行结果:成功执行后点击Button能够收到"Broadcast has been received!"的弹窗提醒。
三、飞行模式系统广播的监听demo
1.定义广播接收器:
package com.example.broadcastdemo1;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
public class AirplaneModeChangeReceiver extends BroadcastReceiver {
private static final String TAG = "AirplaneModeReceiver";
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
// 当收到飞行模式变化的广播时打印日志
Log.d(TAG, "Airplane mode changed, is on: " + isAirplaneModeOn);
// 显示Toast消息
Toast.makeText(context, "Airplane mode changed, is on: " + isAirplaneModeOn, Toast.LENGTH_SHORT).show();
}
}
}
判断Intent获取Action是否与系统广播动作ACTION_AIRPLANE_MODE_CHANGED匹配,匹配后利用boolean、getBooleanExtra("state". false)获取飞行模式的状态(开启-true,关闭-false),再Log打印日志和Toast弹窗提醒。
2.广播接收器注册与注销:
package com.example.broadcastdemo1;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private AirplaneModeChangeReceiver airplaneModeChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化广播接收器
airplaneModeChangeReceiver = new AirplaneModeChangeReceiver();
}
@Override
protected void onStart() {
super.onStart();
// 注册飞行模式变化的接收器
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(airplaneModeChangeReceiver, filter);
}
@Override
protected void onStop() {
super.onStop();
// 注销飞行模式变化的接收器
unregisterReceiver(airplaneModeChangeReceiver);
}
}
在MainActivity,onCreate()方法中初始化广播接收器,在onStart注册、onStop()注销广播接收器。
3.activity_main.xml和AndroidManifest.xml文件中无需额外修改,执行结果根据飞行模型的进入与否进行弹窗提示,也可以在Log中搜索airplane查询。
四、Executors模拟耗时任务,完成后发送本地广播
1.新建类以自定义广播内容:
package com.example.broadcastdemo2;
public class MyConstants {
public static final String All_DONE_ACTION = "com.example.broadcastdemo2.All_DONE_ACTION";
}
2.新建类以定义广播接收器:
package com.example.broadcastdemo2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyBoradcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent){
if(intent.getAction().equals(MyConstants.All_DONE_ACTION)){
Toast.makeText(context, "Timetask has been done--by Broadcast!", Toast.LENGTH_SHORT).show();
}
}
}
当Intent获取的Action匹配到MyConstants.All_DONE_ACTION后,Toast弹窗提醒。
3.新建类定义耗时任务(Service):
package com.example.broadcastdemo2;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.app.IntentService;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.widget.Toast;
import java.util.concurrent.Executors;
public class TimeService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId ){
// 使用Executor来在后台线程执行耗时任务
Executors.newSingleThreadExecutor().execute(() -> {
try {
// 模拟耗时任务
Thread.sleep(10000); // 10秒
} catch (InterruptedException e) {
e.printStackTrace();
}
// // 如果Toast显示的代码放置在了Executor的execute()方法之外,或者没有使用Handler来确保它在主线程中执行,Toast会立即显示,而不管耗时任务是否完成。
// // 执行完耗时任务后切回主线程实现弹窗提醒
// // 弹窗Toast或者AlertDialog的显示操作是在主线程(UI线程)中执行的,因为UI操作必须在主线程中进行
// // 创建的Handler,它与主线程的Looper关联,使用Handler的post()方法来将显示弹窗的操作切换回主线程
// new Handler(Looper.getMainLooper()).post(() -> {
// Toast.makeText(getApplicationContext(), "任务完成!", Toast.LENGTH_SHORT).show();
// });
// 在Executor中耗时任务后执行,避免直接发送广播
Intent intent1 = new Intent(MyConstants.All_DONE_ACTION);
// 发送本地广播
LocalBroadcastManager.getInstance(this).sendBroadcast(intent1);
// sendBroadcast(intent1);
});
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
// // 通过IntentService自动创建线程中执行onHandleIntent方法,并销毁
// public class MyIntentService extends IntentService {
//
// public MyIntentService() {
// super("MyIntentService");
// }
//
// @Override
// protected void onHandleIntent(@Nullable Intent intent) {
// // 在这里执行耗时任务
// try {
// // 模拟耗时任务,例如线程睡眠
// Thread.sleep(10000); // 10秒
// } catch (InterruptedException e) {
// Thread.currentThread().interrupt();
// }
// }
// }
}
继承Service类的TimeService,在服务onStartCommand()阶段使用Executor线程池在后台线程执行耗时任务。需要注意的是,执行完耗时任务随即利用Intent发送本地广播,在同一个线程内,否则会出现无论执行耗时任务与否都会发送广播(Noting:Toast与Dialog方法的显示操作在主线程执行,这里需要Handler切回)。
不绑定活动的Service,在onBind方法中不返回值。
也能通过IntentService自动管理线程执行耗时任务。
4.在AndroidManifest.xml文件中注册Service:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcastdemo2">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Broadcastdemo2">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 服务注册 -->
<service android:name=".TimeService" />
</application>
</manifest>
5.MainActivity中定义耗时任务的执行、结束和广播接收器的注册、注销:
package com.example.broadcastdemo2;
import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private MyBoradcast myBoradcast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化自定义的广播接收器
myBoradcast = new MyBoradcast();
}
public void StartTimeService(View view){
Intent timeservice = new Intent(this, TimeService.class);
startService(timeservice);
}
public void StopTimeService(View view){
Intent timeservice = new Intent(this, TimeService.class);
stopService(timeservice);
}
@Override
protected void onStart(){
super.onStart();
IntentFilter filter = new IntentFilter(MyConstants.All_DONE_ACTION);
// registerReceiver(myBoradcast, filter);
// 注册本地广播接收器
// Note:广播类型在注册注销接收器和发送广播时要记得一一对应
LocalBroadcastManager.getInstance(this). registerReceiver(myBoradcast, filter);
}
@Override
protected void onStop(){
super.onStop();
// unregisterReceiver(myBoradcast);
// 注销本地广播接收器
LocalBroadcastManager.getInstance(this). unregisterReceiver(myBoradcast);
}
}
类似的在onCreate()中初始化广播接收器,在onStart()和onStop()方法注册、注销本地广播接收器。
这里定义了耗时任务的开启StartTimeService(View view)和结束StopTimeService(View view)方法,联动activity_main.xml文件中的Button按钮完成点击事件。
6.在activity_main.xml文件中定义两个Button:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="StartTimeService"
android:text="Start Service"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.484"
tools:ignore="MissingConstraints,UsingOnClickInXml" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="StopTimeService"
android:text="Stop Service"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.603"
tools:ignore="MissingConstraints,OnClick,UsingOnClickInXml" />
</androidx.constraintlayout.widget.ConstraintLayout>
在这里的Button中,使用了android:onClick属性,代替MainActivity中的点击事件代码。
android:onClick="StartTimeService"
android:onClick="StopTimeService"
对应MainActivity中定义的耗时任务开启、结束方法,当主活动类中有类似的需要点击事件完成的方法时,可以采用在xml文件中定义Button的onClick属性来简化代码。
五、发送权限广播与接收权限广播
1.发送权限广播:
在activity_main.xml文件中,添加权限permission:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.broadcasdemo3">
<!--发送广播时添加权限-->
<permission android:name="com.example.broadcasdemo3.MY_PERMISSION"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Broadcasdemo3">
<activity
android:name=".MainActivity"
android:exported="true"
android:noHistory="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
permission的位置是有要求的,需要定义在application之前。
在发送广播是,添加广播权限:
Intent sendintent = new Intent(MyBroadcast.MY_SEND_ACTION);
sendBroadcast(sendintent, "com.example.broadcasdemo3.MY_PERMISSION");
MyBroadcast.MY_SEND_ACTION是自定义的动作,snedBroadcast()在原来的基础上,添加了"com.example.broadcasdemo3.MY_PERMISSION"的权限,”com.example.broadcasdemo3“是自己的包名,"MY_PERMISSION"是自定义的权限。
在使用广播接收器时,则需要添加使用权限,例如在另一个应用中要接收广播时:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.app2">
<!-- // 添加广播的使用/接收权限-->
<uses-permission android:name="com.example.broadcasdemo3.MY_PERMISSION"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Broadcasdemo3">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="com.example.app2.ACTION_START" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
一般不在广播接收器的其他地方添加权限,例如在注册时。
2.接收广播权限:
给广播接收添加权限时情况与发送相反,在接受的app中添加权限:
<permission android:name="com.example.broadcasdemo3.MY_PERMISSION"/>
在发送对应广播的添加使用权限:
<uses-permission android:name="com.example.broadcasdemo3.MY_PERMISSION"/>
注册广播接收器时,在原有基础上添加权限:
registerReceiver(BroadcastReceiver, IntentFilter(MyBroadcast.MY_SEND_ACTION),"com.example.broadcasdemo3.MY_PERMISSION", null)
BroadcastReceiver为定义的广播接收器(初始化后)。
六、设计两个app,制作发送广播权限的demo
在五权限广播的基础上,设计两个app之间的广播发送、接受。
目标:app1通过Button按钮发送权限广播,并切换到app2,在app2中Toast弹窗提示广播已收到。
1.自定义广播内容:
package com.example.broadcasdemo3;
public class MyBroadcast {
public static final String MY_SEND_ACTION = "com.example.broadcasdemo3.MY_SEND_ACTION";
}
2.在app1的activity_main.xml文件中定义一个Button:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.329"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
3.在app1的MainActivity中,设计Button的点击事件,包括权限广播的发送和切换到app2:
package com.example.broadcasdemo3;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mybutton = findViewById(R.id.mybutton);
mybutton.setOnClickListener(new View.OnClickListener() {
@SuppressLint("QueryPermissionsNeeded")
@Override
public void onClick(View v) {
// // 发送广播
// Intent sendintent = new Intent(MyBroadcast.MY_SEND_ACTION);
// Log.d("BroadcastSender", "Sending broadcast to APP2");
// // 添加权限时,发送广播也要注意加上权限参数
// sendBroadcast(sendintent, "com.example.broadcasdemo3.MY_PERMISSION");
sendBroadcast(sendintent);
// Handler延迟发送广播,以便app2能在启动后收到
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent sendintent = new Intent(MyBroadcast.MY_SEND_ACTION);
Log.d("BroadcastSender", "Sending broadcast to APP2");
// 添加权限时,发送广播也要注意加上权限参数
sendBroadcast(sendintent, "com.example.broadcasdemo3.MY_PERMISSION");
}
}, 2000); // 延迟2秒发送广播
// // 启动并切换到app2 由于没有新建任务栈,所以这里的切换只能在管理中显示一个应用
// Intent app2Intent = new Intent(Intent.ACTION_VIEW);
// app2Intent.setComponent(new ComponentName("com.example.app2", "com.example.app2.MainActivity"));
// // 检查是否有应用可以处理这个Intent
// if (app2Intent.resolveActivity(getPackageManager()) != null) {
// startActivity(app2Intent);
// } else {
// // 没有应用可以处理这个Intent,可以提示用户或者做其他处理
// Toast.makeText(MainActivity.this, "无法打开APP2", Toast.LENGTH_SHORT).show();
// }
// 新建任务栈,能够实现切换时同时保留app1-2在应用管理界面
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setClassName("com.example.app2", "com.example.app2.MainActivity");
// 检查是否有应用可以处理这个Intent
if(intent.resolveActivity(getPackageManager())!=null){
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else {
// 没有应用可以处理这个Intent,可以提示用户或者做其他处理
Toast.makeText(MainActivity.this, "无法打开APP2", Toast.LENGTH_SHORT).show();
}
}
});
}
}
注意这里没有自定义的方法可以供xml文件中的Button使用,不能简化onClick代码。
正常发送时,由于app1切换到app2的时机不足以让app2接受到广播(这个问题实测存在,具体原因有待考究),这里采用了延迟发送广播的原理,利用new Handler().postDelayed(new Runnable())方法实现。
注意发送广播添加权限(app1的AndroidManifest.xml文件中已经定义了permission)
如果不新建任务栈,会出现app1切换到app2后,只能回退操作查看app1。为了方便,这里新建了任务栈,实现app1只跳转app2,可见界面app2不会覆盖app1。通过intent.addFlags方法实现。
4.在app2中新建类,定义广播接收器:
package com.example.app2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
public class MyBroadReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent){
// 除了添加广播权限外,还判断动作类型是否相同
if(intent.getAction().equals("com.example.broadcasdemo3.MY_SEND_ACTION")){
Log.d("BroadcastReceiver", "Received broadcast from APP1");
Toast.makeText(context, "收到来自app1的广播!", Toast.LENGTH_SHORT).show();
// // 利用saveBroadcastReceived方法,简单存储广播接收与否的判断信息,收到将改变键对应的值为true
// ((MainActivity) context).saveBroadcastReceived(true);
}
}
}
注意在app2的AndroidManif.xml文件中定义使用权限。
5.在app2的MainActivity中注册广播接收器:
package com.example.app2;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private MyBroadReceiver myBroadReceiver;
// 广播接收器的注册时机与广播发送、接收时间的判断,根据生命周期调整
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化自定义的广播接收器
myBroadReceiver = new MyBroadReceiver();
//
// IntentFilter filter=new IntentFilter("com.example.broadcasdemo3.MY_SEND_ACTION");
// // 注册时添加权限存在问题,不能正常接收广播
registerReceiver(myBroadReceiver,filter, "com.example.broadcasdemo3.MY_PERMISSION", null);
// //正常不加权限时可以收到
// registerReceiver(myBroadReceiver,filter);
}
// @Override
// public void onDestroy(){
// super.onDestroy();
// unregisterReceiver(myBroadReceiver);
// }
@Override
public void onStart(){
super.onStart();
IntentFilter filter=new IntentFilter("com.example.broadcasdemo3.MY_SEND_ACTION");
// 注册时添加权限存在问题,不能正常接收广播
// registerReceiver(myBroadReceiver,filter, "com.example.broadcasdemo3.MY_PERMISSION", null);
//正常不加权限时可以收到
registerReceiver(myBroadReceiver,filter);
}
// @Override
// public void onResume(){
// super.onResume();
//
// if(wasBroadcastReceived()){
// Toast.makeText(this, "Broadcast has been received", Toast.LENGTH_SHORT).show();
// // 完成提示后将状态修改为false,返回初始状态
// saveBroadcastReceived(false);
// }
// }
@Override
public void onStop(){
super.onStop();
unregisterReceiver(myBroadReceiver);
}
// 创建SharedPreferences存储信号,收到广播将key键对应值设为true,处理后改为初始false
public static final String PREFS_NAME = "MyAppPreferences";
public static final String KEY_BROADCAST_RECEIVED = "broadcast_received";
// 利用可编辑的键值对来存储信息
public void saveBroadcastReceived(boolean received) {
SharedPreferences preferences = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); // MODE_PRIVATE参数表示PREFS_NAME指定的SharedPreferences文件只能被当前文件访问
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(KEY_BROADCAST_RECEIVED, received); //edit键值
editor.apply();// 应用edit内容
}
public boolean wasBroadcastReceived() {
SharedPreferences preferences = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); // 获取SharedPreferences文件
return preferences.getBoolean(KEY_BROADCAST_RECEIVED, false); // 获取SharedPreferences文件下的键值对(KEY_BROADCAST_RECEIVED-值)
}
}
尤其要注意广播接收器的注册、注销时间,由于app1切换到app2的点击事件,最好是在onResume之前注册,对应周期注销。
注册时不用使用权限。
问题:
(1)在onCreate中注册,第一次打开app2会出现Toast两次弹窗显示,后续正常;
(2)在onResume中注册,会出现app1切换到app2无法正常接收广播的问题,只有app2小窗口化才能收到Toast提醒;
(3)在onStart中注册,需要结合Handler延迟发送广播处理,否则出现与(2)相同的问题。
6.正常执行结果,在app1中点击Button跳转到app2的同时发送广播,由于新建了任务栈,app2不会在应用管理界面覆盖app1,跳转app2后2s内正常收到Toast提醒即成功。