Android广播

文章目录

1.收发应用广播

1.标准广播

广播的收发过程分为三个步骤:

  1. 发送标准广播
  2. 定义广播接收器
  3. 开关广播接收器
java 复制代码
public class BroadStandardActivity extends AppCompatActivity implements View.OnClickListener {

    private StandardReceiver standardReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broad_standard);
        findViewById(R.id.btn_send_standard).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        Intent intent = new Intent("com.zzzjian.demo7.standard");
        sendBroadcast(intent);
    }


    @Override
    protected void onStart() {
        super.onStart();
        standardReceiver = new StandardReceiver();
        // 创建意图过滤器 只处理 com.zzzjian.demo7.action.standard 这个动作
        IntentFilter filter = new IntentFilter(StandardReceiver.STANDARD_ACTION);
        // 注册接收器 注册之后才能正常接受广播
        registerReceiver(standardReceiver, filter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 注销接收器 注销之后就不再接受广播
        unregisterReceiver(standardReceiver);
    }
}
java 复制代码
public class StandardReceiver extends BroadcastReceiver {

    private static final String TAG = "StandardReceiver";

    public   static final String STANDARD_ACTION = "com.zzzjian.demo7.standard";
    /**
     * 一旦收到标准广播,马上触发接收器的onReceive方法
     * @param context
     * @param intent
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent != null && intent.getAction().equals(STANDARD_ACTION)){
            Log.d(TAG, "收到一个标准广播");
        }

    }
}

2.有序广播

  • 一个广播存在多个接收器,这些接收器需要排队收听广播,这意味着该广播是条有序广播。
  • 先收到广播的接收器,既可以让其他接收器继续收听广播,也可以中断广播不让其他接收器收听。
java 复制代码
public class BroadOrderActivity extends AppCompatActivity implements View.OnClickListener {

    public static final String ORDER_ACTION = "com.zzzjian.demo7.order";


    private OrderAReceiver orderAReceiver;


    private OrderBReceiver orderBReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broad_order);
        findViewById(R.id.btn_send_order).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(ORDER_ACTION);
        sendOrderedBroadcast(intent,null);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // 多个接收器处理有序广播的顺序规则
        // 1. 先处理优先级最高的接收器
        // 2. 优先级相同的接收器,先注册先处理
        orderAReceiver = new OrderAReceiver();
        IntentFilter filterA = new IntentFilter(ORDER_ACTION);
        filterA.setPriority(8);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            registerReceiver(orderAReceiver, filterA, Context.RECEIVER_EXPORTED);
        }

        orderBReceiver = new OrderBReceiver();
        IntentFilter filterB = new IntentFilter(ORDER_ACTION);
        filterB.setPriority(10);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            registerReceiver(orderBReceiver, filterB, Context.RECEIVER_EXPORTED);
        }
    }


    @Override
    protected void onStop() {
        super.onStop();
        unregisterReceiver(orderAReceiver);
        unregisterReceiver(orderBReceiver);
    }
}
java 复制代码
public class OrderAReceiver extends BroadcastReceiver {

    private static final String TAG = "OrderReceiver";


    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent != null && intent.getAction().equals(BroadOrderActivity.ORDER_ACTION)){
            Log.d(TAG, "收到一个有序广播A");
        }
    }
}
java 复制代码
public class OrderBReceiver extends BroadcastReceiver {


    private static final String TAG = "OrderReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent != null && intent.getAction().equals(BroadOrderActivity.ORDER_ACTION)){
            Log.d(TAG, "收到一个有序广播B");
            // 中断广播
            abortBroadcast();
        }
    }
}

3.广播的静态注册

java 复制代码
public class BroadStaticActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broad_static);
        findViewById(R.id.btn_send_shock).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
    	// 8.0之后必须指定完整类名
        String fullName = "com.zzzjian.demo7.receiver.ShockReceiver";
        Intent intent = new Intent(ShockReceiver.SHOCK_ACTION);
        // 创建一个ComponentName对象,指定当前上下文和接收器全名
        ComponentName componentName = new ComponentName(this, fullName);
        intent.setComponent(componentName);
        sendBroadcast(intent);
    }
}
java 复制代码
public class ShockReceiver extends BroadcastReceiver {

    public static final String SHOCK_ACTION = "com.zzzjian.demo7.shock";

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent != null && intent.getAction().equals(SHOCK_ACTION)){
            Log.d("ShockReceiver", "震动");
            // 从系统服务获取震动管理器
            Vibrator vb = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
            // 震动 500ms
            vb.vibrate(500);
        }
    }
}
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

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


    <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.MyApplication">
        <receiver
            android:name=".receiver.ShockReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.zzzjian.demo7.shock" />
            </intent-filter>
        </receiver>
        <activity
            android:name=".BroadStaticActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

2.监听系统广播

1.接受分钟到达广播

  • Intent.ACTION_TIME_TICK
java 复制代码
public class SystemMinteActivity extends AppCompatActivity {

    private TimeReceiver timeReceiver;

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

    @Override
    protected void onStart() {
        super.onStart();
        timeReceiver = new TimeReceiver();
        IntentFilter filter = new IntentFilter(Intent.ACTION_TIME_TICK);
        registerReceiver(timeReceiver,filter);
    }


    @Override
    protected void onStop() {
        super.onStop();
        unregisterReceiver(timeReceiver);
    }
}
java 复制代码
public class TimeReceiver extends BroadcastReceiver {

    private static final String TAG = "TimeReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent != null){
            Log.d(TAG,"收到一个分钟到达的广播");
        }
    }
}

2.接受网络变更广播

  • android.net.conn.CONNECTIVITY_CHANGE

getType:获取网络类型

网络类型 说明
TYPE_WIFI 无线热点WIFI
TYPE_MOBILE 数据连接
TYPE_WIMAX WiMAX
TYPE_ETHERNET 以太网
TYPE_BLUETOOTH 蓝牙
TYPE_VPN 虚拟专用网络VPN

getTypeName:获取网络类型名称

getSubtype:获取网络的子类型。如果为数据连接时,子类型为2G/3G/4G等细分。

3.定时管理器AlarmManager

Android提供了专门的定时管理器AlarmManager,它利用系统闹钟定时发送广播,常见方法有:

  • set:设置一次性定时器。
  • setAndAllowWhileIdle:设置一次性定时器,即使设备处于空闲状态,也会保证执行定时器。
  • setRepeating:设置重复定时器,但系统不保证按时发送广播。
  • cancel:取消延迟意图的定时器。
java 复制代码
public class AlarmActivity extends AppCompatActivity implements View.OnClickListener {

    private AlarmReceiver alarmReceiver;

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

        findViewById(R.id.btn_alarm).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        alarmReceiver.sendAlarm();
    }

    protected void onStart() {
        // 调用父类的onStart方法
        super.onStart();

        // 创建AlarmReceiver实例,并传入当前应用的上下文
        // 这里假设AlarmReceiver是一个自定义的BroadcastReceiver子类
        alarmReceiver = new AlarmReceiver(getApplicationContext());

        // 创建IntentFilter对象,用于过滤接收到的广播
        // 指定接收的广播动作为AlarmReceiver.ALARM_ACTION
        IntentFilter filter = new IntentFilter(AlarmReceiver.ALARM_ACTION);

        // 检查Android SDK版本是否大于等于Oreo(8.0)
        // Android Oreo引入了一些新的API和行为变化
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // 注册BroadcastReceiver
            // 参数分别为:要注册的BroadcastReceiver实例、IntentFilter对象、注册标志
            // Context.RECEIVER_EXPORTED表示此BroadcastReceiver可以被其他应用访问
            registerReceiver(alarmReceiver, filter, Context.RECEIVER_EXPORTED);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        unregisterReceiver(alarmReceiver);
    }
}
java 复制代码
public class AlarmReceiver extends BroadcastReceiver {

    public static final String ALARM_ACTION = "com.zzzjian.demo7.alarm";

    private final Context mContext;


    public AlarmReceiver(Context context){
        super();
        this.mContext = context;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent != null && intent.getAction().equals(ALARM_ACTION)){
            Log.d("AlarmReceiver","收到一个闹钟的广播");
        }
    }

    public void sendAlarm(){
        // 创建一个Intent对象,指定动作类型为ALARM_ACTION
        Intent intent = new Intent(ALARM_ACTION);

        // 创建一个PendingIntent对象,用于稍后触发Intent
        // 第二个参数为请求码,在此设置为0;第三个参数为Intent对象;第四个参数为标志位,设置为FLAG_IMMUTABLE表示PendingIntent不可变
        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);

        // 获取系统服务AlarmManager,用于管理定时任务
        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);

        // 设置定时任务,当设备空闲时也可以唤醒设备执行任务
        // 参数分别为:AlarmManager设置类型、触发时间(从1970年1月1日00:00:00 GMT开始计算的毫秒数)、待执行的PendingIntent
        // 注释掉的代码为另一种设置方式,不支持设备空闲时唤醒
        // alarmManager.set(AlarmManager.RTC_WAKEUP, 1000, pendingIntent);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, 1000, pendingIntent);
        }else{
            alarmManager.set(AlarmManager.RTC_WAKEUP, 1000, pendingIntent);
        }
    }

}

3.捕捉屏幕的变更事件

1.竖屏和横屏切换

为了避免横竖屏切换时重新加载界面的情况,Android设计了一种配置变更机制,在指定的环境配置发生变更之时,无需重启活动页面,只需执行特定的变更行为。该机制分为两步:

  1. 修改AndroidManifest.xml,给activity节点增加android:configChange属性
  2. 修改活动页面的Java代码,重写活动的onCinfigurationChanged方法,补充对应的代码处理逻辑。
onfigChanges属性的取值 说明
orientation 屏幕方向发生变化
screenLayout 屏幕的显示发生改变,例如在全屏和分屏之间切换
screenSize 屏幕大小发生改变,例如在竖屏和横屏之间切换
keyboard 键盘发生改变,例如使用了外部键盘
keyboardHidden 软键盘弹出或隐藏
navigation 导航方式发生改变,例如采用了轨迹球导航
fontSize 字体比例发生改变
locale 设备本地位置发生改变,例如切换了系统语言
uiMode 用户界面的模式发生改变,例如开启了夜间模式
xml 复制代码
<activity
            android:name=".ChangeDirectionActivity"
            android:exported="true"
            android:theme="@style/Theme.MyApplication"
            android:configChanges="orientation|screenLayout|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
java 复制代码
public class ChangeDirectionActivity extends AppCompatActivity {

    private static final String TAG = "ChangeDirectionActivity";

    private TextView tv_monitor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_change_direction);
        tv_monitor = findViewById(R.id.tv_monitor);
        Log.d(TAG, "onCreate");
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        switch (newConfig.orientation){
            case Configuration.ORIENTATION_PORTRAIT:
                tv_monitor.setText("竖屏");
                break;
            case Configuration.ORIENTATION_LANDSCAPE:
                tv_monitor.setText("横屏");
                break;
            default:
                break;
        }
    }
}

2.回到桌面与切换到任务列表

  • 按下主页键会回到桌面,按下任务键会打开任务列表,这两个操作并未提供相应的按键处理方法,而是通过广播发出事件信息。
  • 如果想知道是否回到桌面,以及是否打开任务列表,均需收听系统广播Intent.ACTION_CLOSE_SYSTEM_DIALOGS。
  • 从收到广播意图中获取原因reason字段,该字段值为homekey时表示回到桌面,值为recentapps时表示打开任务列表。
java 复制代码
public class ReturnDesktopActivity extends AppCompatActivity {

    private DesktopReceiver desktopReceiver;

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

        desktopReceiver = new DesktopReceiver();
        IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            registerReceiver(desktopReceiver,filter, Context.RECEIVER_EXPORTED);
        }

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(desktopReceiver);
    }

    @Override
    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
        super.onPictureInPictureModeChanged(isInPictureInPictureMode);
        if(isInPictureInPictureMode){
            Log.d("ReturnDesktopActivity", "进入小窗模式");
        }else{
            Log.d("ReturnDesktopActivity", "退出小窗模式");
        }
    }

    private class DesktopReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent != null && intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)){
                String reason = intent.getStringExtra("reason");
                if(!TextUtils.isEmpty(reason) && (reason.equals("home") || reason.equals("recentapps"))){
                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !isInPictureInPictureMode()){
                        PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
                        Rational aspectRatio = new Rational(10, 5);
                        builder.setAspectRatio(aspectRatio);
                        enterPictureInPictureMode(builder.build());

                    }
                }
            }
        }
    }
}
xml 复制代码
        <activity
            android:name=".ReturnDesktopActivity"
            android:configChanges="orientation|screenLayout|screenSize"
            android:exported="true"
            android:screenOrientation="portrait"
            android:supportsPictureInPicture="true"
            android:theme="@style/Theme.MyApplication">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
相关推荐
Ray Liang16 分钟前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计
Java水解32 分钟前
Java 中间件:Dubbo 服务降级(Mock 机制)
java·后端
砖厂小工2 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心3 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心3 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
SimonKing5 小时前
OpenCode AI辅助编程,不一样的编程思路,不写一行代码
java·后端·程序员
FastBean5 小时前
Jackson View Extension Spring Boot Starter
java·后端
Kapaseker5 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴6 小时前
Android17 为什么重写 MessageQueue
android
Seven976 小时前
剑指offer-79、最⻓不含重复字符的⼦字符串
java