Android-Broadcast组件及demo

一、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匹配,匹配后利用booleangetBooleanExtra("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提醒即成功。

### 回答1: android_udpbroadcast_demo 是一个 Android 平台上的示例程序,用于展示如何使用UDP广播进行网络通信。 首先需要明确的是,UDP(User Datagram Protocol)是一种面向无连接的网络传输协议,它不保证数据的可靠性和顺序,但是具有速度快的特点。而广播则是一种将数据包发送给网络上所有设备的通信方式。 在 android_udpbroadcast_demo 中,主要包括以下几个部分的功能: 1. 发送广播:通过UDP协议,将数据包发送给本地局域网内的所有设备。示例程序中提供了发送和停止发送广播的功能。 2. 接收广播:监听网络上的UDP数据包,获取其他设备发送的数据。示例程序中展示了如何设置UDP监听器,并在接收到数据包时进行处理。 3. 显示接收的广播数据:将接收到的数据显示在界面上,让用户可以直观地查看广播内容。 通过 android_udpbroadcast_demo,开发者可以学习如何在 Android 平台上实现UDP广播的功能,可以用于实现局域网内的设备间通信、发现和数据传输等功能。例如,可以用于创建多人游戏、局域网聊天应用等。 总之,android_udpbroadcast_demo 是一个帮助开发者了解和学习如何使用UDP广播进行网络通信的示例程序,通过该示例程序,开发者可以掌握如何在Android平台上实现UDP广播的功能。 ### 回答2: android_udpbroadcast_demo是一个用于在Android设备上进行UDP广播通信的示例程序。UDP (User Datagram Protocol) 是一种无连接的传输协议,它可以提供高效的数据传输。在该示例程序中,通过UDP广播,可以实现Android设备之间的快速通信。 Android设备通过使用DatagramSocket和DatagramPacket类来创建一个UDP套接字并发送和接收数据包。在示例程序中,首先创建一个用于接收数据的DatagramSocket,并设置一个固定的广播端口。然后使用一个线程来循环接收来自其他设备的广播消息,并对接收到的消息进行处理。 接下来,在发送广播消息之前,需要创建一个新的DatagramSocket和一个DatagramPacket。在发送消息时,将相应的数据封装到DatagramPacket中,并通过UDP广播发送给其他设备。 示例程序还包括了需要添加的权限,例如INTERNET权限和ACCESS_NETWORK_STATE权限,以便可以进行网络通信,并检查设备的网络状态。 通过运行该示例程序,可以在多个Android设备上实现UDP广播通信。例如,可以将一台设备设置为发送广播消息的服务器,其他设备可以接收并处理这些广播消息。这个示例程序可以用于各种场景,例如实时在线聊天、设备之间的实时数据同步等。 总之,android_udpbroadcast_demo是一个完整的示例程序,展示了如何在Android设备上使用UDP广播进行快速通信。使用该示例程序,可以轻松实现Android设备之间的通信,为各种应用场景提供了很大的灵活性和可扩展性。 ### 回答3: android_udpbroadcast_demo是一个使用UDP协议进行广播的Android应用程序示例。 UDP(User Datagram Protocol)是一种无连接的传输协议,它提供了面向数据报的简单传输服务。广播是一种网络通信方式,可以将数据包同时发送给同一局域网中的多个接收者。 在android_udpbroadcast_demo中,首先需要创建一个UDP广播发送器和接收器。发送器负责将数据打包成数据包,并通过UDP广播发送给局域网中的接收器。接收器则负责接收传输过来的数据包,并进行解析处理。 在示例中,可以通过点击按钮或在文本框中输入信息来触发发送广播。发送器将信息封装成数据包,并通过UDP广播发送。接收器侦听局域网中的UDP广播,一旦接收到数据包,就会解析其中的信息,并在界面上显示出来。这样,就实现了同一局域网中多台设备之间的信息交流。 android_udpbroadcast_demo可以用于开发各种需要广播通信功能的Android应用程序。例如,可以用于多人游戏中的消息广播、局域网内设备的发现与连接、实时通信应用的消息推送等场景。 总之,android_udpbroadcast_demo是一个使用UDP协议进行广播的Android应用程序示例,能够实现同一局域网中多台设备之间的消息传递。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值