Android入门系列(十一):标准与有序广播、广播的动态与静态注册、系统与自定义广播、本地与全局广播

一、广播机制简介

作为Android四大组件之一的Broadcast,同样被intent传递,通常情况下,自定义和Android预定义的广播加载到intent中被广播出去,注册的BroadcastReceiver就可以监听到这些Intent,并获得其中的数据

例如连接网络,电池充上电,来短信,这些预定义的广播,会被Android的intent自动的广播

也就是说,发送广播通过intent,接受广播通过BroadcastReceiver

1.广播分类

  1. 标准广播:异步执行的广播,广播发出后,所有的接收器会在同一时间接收到广播的内容,没有先后顺序,效率较高,缺点也显而易见,无返回值,不能截断,通过sendBroadcast()函数发送

  2. 有序广播:同步执行的广播,广播发出后,同一时刻只有一个接收器能接受到,该接收器接收到之后才能传递给下一个,广播可截断,且广播接收器有优先级,优先级高的先接收到广播,如果优先度高的广播截断,则优先度低的无法收到广播,通过sendOrderedBroadcast()发送

    优先级取值范围为-1000到1000,通过abortBroadcast的方法终止广播的传递,先接收到广播的接收器可以通过setResultExtras(Bundle)方法传递值给下一个接收者,下一个接收者通过getResultExtras(true).var获得bundle

2.广播注册

和Activity以及service一样,广播需要通过注册,注册分为动态注册和静态注册

  1. 动态注册:动态注册广播为java代码内部实现,我们可以*的控制该接收器的注册与取消。但是动态注册的广播接收器和Activity的生命周期相同
  2. 静态注册:在清单文件中实现,不受程序的约束,应用程序关闭后仍然可以接收到广播

二、系统广播

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.实现效果

Android入门系列(十一):标准与有序广播、广播的动态与静态注册、系统与自定义广播、本地与全局广播

上一篇:探索 App 秒开的秘密 —— 启动性能优化,想提高开发效率的必看


下一篇:微信公众号菜单openid 点击菜单即可打开并登录微站