• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

Android中BroadcastReceiver广播的使用详解

互联网 diligentman 3天前 9次浏览

Broadcast(广播)

注:本章内容多数摘自郭霖大神的《第一行代码-Android》,因为书上这一章写的很完美了,我自己再另写就有些多余了,但由于第二版《第一行代码》书籍年代久远,书上这一章有一小部分细节对于新版安卓系统有不适用、需要改动的地方,本文会将这些会踩的坑指出来

一、接收系统广播

1、动态注册监听网络变化

接收系统广播必须先创建广播接收器,就是新建一个类,让他继承自BroadcastReceiver,并重写父类的onReceiver()就行了,这样当有广播到来时,onReceiver()方法就会执行

新建一个BroadcastTest项目

public class MainActivity extends AppCompatActivity {
    private IntentFilter intentFilter;
    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);
    }
    class NetworkChangeReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            //收到广播
            Toast.makeText(context,"network chages",Toast.LENGTH_LONG).show();
        }
    }
}

系统网络状态变化时,系统发出的就是android.net.conn.CONNECTIVITY_CHANGE这条广播,因此intentFilter需要添加这个值,因此每次网络状态变化时都会触发广播接收器的onReceive()方法

这样的话,只能得到网络状态改变的提示,但不知道具体是连接了网络还是断开了网络。我们对NetworkChangeReceiver类的代码改进一下

public class MainActivity extends AppCompatActivity {
    private IntentFilter intentFilter;
    private NetworkChangeReceiver networkChangeReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        IntentFilter intentFilter=new IntentFilter();
        //系统网络状态变化时,系统发出的就是android.net.conn.CONNECTIVITY_CHANGE这条广播
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        networkChangeReceiver=new NetworkChangeReceiver();
        //注册广播
        registerReceiver(networkChangeReceiver,intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(networkChangeReceiver);
    }
    class NetworkChangeReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            //收到广播
            ConnectivityManager connectivityManager=(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo();
            if(networkInfo!=null && networkInfo.isAvailable()){
                Toast.makeText(context,"network is available",Toast.LENGTH_LONG).show();
            }else {
                Toast.makeText(context,"network is unavailabe",Toast.LENGTH_LONG).show();
            }
        }
    }
}

ConnectivityManager是一个系统服务类,专门管理网络连接的,然后调用getActivityNetworkInfo()方法就可以得到NetworkInfo的实例,接着调用NetworkInfo的isAvailable()方法,就可以判断出是否有网络了

这里的访问网络状态是需要声明权限的

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

这样动态注册就结束了,具体的效果可以自己测试

2、静态注册实现开机注册

先创建一个广播接收器(package->New->Other->Broadcast Receiver),命名为BootCompleteReceiver,并修改BootCompleteReceiver中的代码

public class BootCompleteReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"Boot Complete",Toast.LENGTH_LONG).show();
    }
}

静态注册的广播接收器一定要在AndroidManifest.xml文件中注册才可以使用,不过由于我们是使用AndroidStudio的快捷方式创建的广播接收器,因此注册这一步已经被自动完成了。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.broadcasttest">
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <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/AppTheme">
        <receiver
            android:name=".BootCompleteReceiver"
            android:enabled="true"
            android:exported="true"></receiver>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

可以看到,标签内出现了一个新的标签,所有静态的广播接收器都是在这里进行注册的,它的用法其实和标签非常相似,通过android:name来指定具体注册哪一个广播接收器

有了BootCompleteReceiver这个广播接收器还是不能接收到开机广播的,需要声明监听开机广播权限,并且修改标签

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.broadcasttest">
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-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/AppTheme">
        <receiver
            android:name=".BootCompleteReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"></action>
            </intent-filter>
        </receiver>

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

这样就实现了接收开机广播了,将模拟器关闭并重新启动,在启动完成后就会接收到开机广播

总结:在静态注册广播中,需要将想要接收的广播填写到标签下!相应的触发事件写在广播接收器的onReceive()里面

提示:以上代码在模拟器上测试是有效的,但可能到了真机会失效,具体原因见链接开机广播接收不到的原因 解决不了也不要紧,因为重点不在开机广播,而在于静态广播的原理的学习

3、自定义广播
(1)发送标准广播
我们先新建一个广播接收器来接收广播

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"received in MyBroadcastReceiver",Toast.LENGTH_LONG).show();
    }
}

然后在AndroidManifest.xml中对这个广播接收器的标签进行修改

		<receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.broadcasttest.MY_BROADCAST"></action>
            </intent-filter>
        </receiver>

我们定义一个按钮来发送广播

<LinearLayout 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:text="Send Broadcast"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </Button>
</LinearLayout>

接着编写按钮事件

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST");
                //重点!必须设置包名!不然发送不出去
                intent.setPackage(getPackageName());
                sendBroadcast(intent);
            }
        });

    }
}

发送广播的时候一定要给intent设置包名,这是新版安卓的特性,不加不给用,老版本安卓可以不加

现在点击按钮后,就会触发广播了
Android中BroadcastReceiver广播的使用详解
(2)发送广播给别的程序
步骤同(1),但要注意将setPackage()的参数填成要发送的程序的包名!!

4、使用本地广播
本地广播顾名思义,就是防止全局广播引起的安全性问题,这种广播只有自己的程序可以接收
这里直接修改JAVA源码,和普通的动态注册差不多

public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取实例
        localBroadcastManager=LocalBroadcastManager.getInstance(this);
        Button button=findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent("com.example.broadcasttest.LOCAL_BROADCAST");
                //本地广播可以不设置包名
                //intent.setPackage(getPackageName());
                localBroadcastManager.sendBroadcast(intent);
            }
        });
        intentFilter=new IntentFilter();
        intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
        localReceiver=new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver,intentFilter);
    }

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

    class LocalReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context,"received local broadcast",Toast.LENGTH_SHORT).show();
        }
    }
}

到这里文章就结束了,写了足足3个小时,期间吃了一桶泡面,2包干脆面,看了20分钟电影,又聊了会QQ,在快乐的氛围中我已经把广播的内容从接近遗忘到烂熟于心,写博客的用处真的非同凡响!一日一更,加油加油加油


喜欢 (0)