乐趣区

关于android:全局大喇叭BroadcastReceiver

本篇文章记述了 Android 的四大组件之一 —— BroadcastReceiver(播送接收者)。播送的作用、播送注册的形式、自定义播送、播送的类型以及在较新的 Android 零碎中应用 BroadcastReceiver 须要留神的问题。Android 利用与 Android 零碎和其余 Android 利用之间能够互相收发播送音讯,这与公布 - 订阅设计模式类似,这些播送会在所关注的事件产生时发送进来。

<!– more –>

对于零碎播送

举例来说,Android 零碎会在产生各种零碎事件时发送播送,例如系统启动或设施开始充电时。利用能够发送自定义播送来告诉其余利用它们可能感兴趣的事件(例如,一些新数据已下载)。利用能够注册接管特定的播送。播送收回后,零碎会主动将播送传送给批准接管这种播送的利用。

零碎会在产生各种零碎事件时主动发送播送,例如当零碎进入和退出航行模式时,零碎播送会被发送给所有批准接管相干事件的利用,或者在手机低电量的时候,零碎也会收回一个手机电量低的播送:

常见的零碎播送:

<!-- 开机播送 -->
<action android:name="android.intent.action.BOOT_COMPLETED"/>

<!-- 开始充电播送 -->
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>

<!-- 低电量播送 -->
<action android:name="android.intent.action.BATTERY_LOW"/>

<!-- 利用卸载播送 -->
<action android:name="android.intent.action.PACKAGE_REMOVED"/>

<!-- 利用装置播送 -->
<action android:name="android.intent.action.PACKAGE_ADDED"/>

<!-- 申明这个数据类型才能够收到利用装置 / 卸载的播送 -->
<data android:scheme="package"/>

<... />

零碎播送的注册

应用程序能够通过两种形式接管播送:通过清单文件申明的播送接收者和上下文注册的播送接收者。

动态注册

首先作为四大组件之一必定是要在清单文件申明的,创立一个 AppReceiver 用来接管 App 装置和卸载的播送,此类要继承 BroadcastReceiver:

public class AppReceiver extends BroadcastReceiver {
    private static final String TAG = "AppReceiver";
    
    @Override
    public void onReceive(Context context, Intent intent) {
        // 接管播送
        if(intent != null){
            // 判断收到的是什么播送
            String action = intent.getAction();
            assert action != null;
            switch (action){
                case Intent.ACTION_PACKAGE_REMOVED:
                    Log.i(TAG, "onReceive: ACTION_PACKAGE_REMOVED" + "利用被卸载");
                    break;
                case Intent.ACTION_PACKAGE_ADDED:
                    Log.i(TAG, "onReceive: ACTION_PACKAGE_ADDED" + "利用被装置");
                    break;
            }
        }
    }
}

AndroidManifest.xml

<!-- 动态注册播送接收者 -->
<receiver android:name=".AppReceiver"
          android:enabled="true"
          android:exported="true">
    <intent-filter>
        <!-- 利用卸载播送 -->
        <action android:name="android.intent.action.PACKAGE_REMOVED"/>
        <!-- 利用装置播送 -->
        <action android:name="android.intent.action.PACKAGE_ADDED"/>
        <!-- 申明这个数据类型才能够收到利用装置 / 卸载的播送 -->
        <data android:scheme="package"/>
    </intent-filter>
</receiver>

在进行利用卸载和装置的时候,就能够收到对应的播送了:

动静注册

AppReceiver.java 还是和下面的一样,然而当初不在 AndroidManifest.xml 中申明,而是应用动静注册的形式,上面的例子演示了如何在 Activity 创立的动静的注册播送接收者:

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private BroadcastReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 新建一个播送接收器
        receiver = new AppReceiver();
        
        // 接管哪些播送
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addDataScheme("package");
        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);

        // 注册播送接收者
        registerReceiver(receiver, intentFilter);
    }

    @Override
    protected void onDestroy() {super.onDestroy();
        // 勾销注册播送接收器(不勾销会导致内存透露)if(receiver != null){unregisterReceiver(receiver);
        }
    }
}

不过动静注册播送接收者千万别遗记在 Activity 销毁的时候解除之前注册的播送接收者。

动态注册须要在 AndroidManifest.xml 中申明,只有 APP 启动过一次,所动态注册的播送就会失效,无论以后的 APP 处于停止使用还是正在应用状态。只有相应的播送事件产生,零碎就会遍历所有的清单文件,告诉相应的播送接收者接管播送,而后调用播送接收者的 onReceiver 办法。

动静注册动静注册形式依赖于所注册的组件,当 APP 敞开后,组件对象都不在了动静注册的代码都不存在了,所动静注册监听的 Action 天然就不再失效了。动态注册的播送传播速度要远远慢于动静注册的播送。

如果即应用了动静注册,又应用了动态注册,那么动静动静注册的播送的优先级大于动态注册的播送。

播送的生命周期

1、BroadCastReceiver 的生命周期很短暂,当接管到播送的时候创立,当 onReceive() 办法完结后销毁

2、正因为 BroadCastReceiver 的申明周期很短暂,所以不要在播送接收器中去创立子线程做耗时的操作,因为播送接受者被销毁后,这个子过程就会成为空过程,很容易被杀死

3、因为 BroadCastReceiver 是运行在主线程的,所以不能间接在 BroadCastReceiver 中去做耗时的操作,否则就会呈现 ANR 异样,耗时的较长的工作最好放到 Service 中去实现。这里不能应用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短,子线程可能还没有完结 BroadcastReceiver 就先完结了,BroadcastReceiver 一旦完结,此时 BroadcastReceiver 的所在过程很容易在零碎须要内存时被优先杀死,因为它属于空过程 (没有任何流动组件的过程),如果它的宿主过程被杀死,那么正在工作的子线程也会被杀死,所以采纳子线程来解决是不牢靠的。

自定义 Broadcast

MainActivity.java

public class MainActivity extends AppCompatActivity {

    public static final String MY_ACTION = "cn.tim.action.MY_ACTION";
    public static final String MY_ACTION_EXTRA_KEY = "input_content";
    private EditText etContent;
    private CustomReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        etContent = findViewById(R.id.et_content);
        TextView tvShow = findViewById(R.id.tv_show);

        receiver = new CustomReceiver();
        receiver.tvShow = tvShow;

        IntentFilter filter = new IntentFilter();
        filter.addAction(MY_ACTION);
        registerReceiver(receiver, filter);
    }

    public void send(View view) {
        // 新建播送
        Intent intent = new Intent(MY_ACTION);
        // 放入播送要携带的数据
        intent.putExtra(MY_ACTION_EXTRA_KEY, etContent.getText().toString());
        sendBroadcast(intent);
    }

    @Override
    protected void onDestroy() {super.onDestroy();
        // 解除注册
        if(receiver != null){unregisterReceiver(receiver);
        }
    }
}

CustomReceiver.java

public class CustomReceiver extends BroadcastReceiver {
    TextView tvShow;

    @Override
    public void onReceive(Context context, Intent intent) {if(intent != null){String action = intent.getAction();
            if(MainActivity.MY_ACTION.equals(action)){String inputContent = intent.getStringExtra(MainActivity.MY_ACTION_EXTRA_KEY);
                tvShow.setText(inputContent);
            }
        }
    }
}

不同的利用间通信,也是下面同样的代码,只有写雷同的 ACTION,那么就 OK 了:

Broadcast 分类

播送的发送,能够分为有序播送、无序播送、本地播送以及粘性播送。

有序播送

有序播送是一种分先后播送接收器的播送,播送接收者的优先级越高,越先接管播送。优先级高的播送先收到播送,收到播送后能够批改播送的内容,也能够拦挡播送不让播送向下传递。

如果在 CReceiver 中终止播送,那么优先级比拟低的 A 与 B 都收不到播送了。

MainActivity.java

public class MainActivity extends AppCompatActivity {
    public static final String MY_ACTION = "cn.tim.action.MY_ACTION";
    public static final String KEY = "cn.tim.action.MY_ACTION";

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

        // 新建播送
        Intent intent = new Intent(MY_ACTION);
        // 放入播送要携带的数据
        Bundle bundle = new Bundle();
        bundle.putInt(KEY, 100);
        intent.putExtras(bundle);
        //sendBroadcast(intent);

        // 发送程序播送,参数二:权限
        sendOrderedBroadcast(intent, null);
    }
}

留神:如果要批改数据的话前提得是 Intent 发送数据得格局必须是 Bundle。

// 优先级 2
public class CReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {if(intent != null){String action = intent.getAction();
            if(MainActivity.MY_ACTION.equals(action)){Bundle bundle = intent.getExtras();
                Toast.makeText(context, "C:" + bundle.getInt(MainActivity.KEY), Toast.LENGTH_SHORT).show();
                Bundle newBundle = new Bundle();
                newBundle.putInt(MainActivity.KEY, 90);
                setResultExtras(newBundle);
            }
        }
    }
}

// 优先级 1
public class AReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {if(intent != null){String action = intent.getAction();
            if(MainActivity.MY_ACTION.equals(action)){Bundle bundle = getResultExtras(true);
                Toast.makeText(context, "A:" + bundle.getInt(MainActivity.KEY), Toast.LENGTH_SHORT).show();
                Bundle newBundle = new Bundle();
                newBundle.putInt(MainActivity.KEY, 80);
                setResultExtras(newBundle);
            }
        }
    }
}

// 优先级 0
public class BReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {if(intent != null){String action = intent.getAction();
            if(MainActivity.MY_ACTION.equals(action)){Bundle bundle = getResultExtras(true);
                Toast.makeText(context, "B:" + bundle.getInt(MainActivity.KEY), Toast.LENGTH_SHORT).show();}
        }
    }
}

无序播送

无序播送指所有与之匹配的播送接收者都能收到播送,没有先后顺序,直到没有播送接收者接管播送为止才会进行播送的传递。

默认状况下有播送发送时,零碎会遍历全副 APP 的 Receiver。如果想使得本 APP 的 Receiver 不被外界的播送所烦扰,能够在 Receiver 节点增加 android:exported=”false” 属性,这样零碎遍历全副 APP 清单文件的播送接收者时不会对本 App 的 Receiver 进行判断及解决。

本地播送

本地播送仅仅在 APP 内流传,其余的程序无奈收到这个播送。这种播送保障安全性,不会流传到外界。同时因为 LocalBroadcastManager 不须要用到跨过程机制,因而绝对 BroadcastReceiver 而言要更为高效。LocalBroadcastManager 只在动静播送时应用,动态播送不能应用 LocalBroadcastManager。

implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

MainActivity.java

public class MainActivity extends AppCompatActivity {
    public static final String MY_NEW_ACTION = "cn.tim.action.MY_NEW_ACTION";
    
    private LocalReceiver localReceiver;
    private LocalBroadcastManager localBroadcastManager;

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

        // 取得 LocalBroadcastManager 对象
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        // 动静注册播送
        localReceiver = new LocalReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(MY_NEW_ACTION);
        localBroadcastManager.registerReceiver(localReceiver, filter);
    }

    public void sendLocalBroadcast(View view) {
        // 发送本地播送
        Intent intent = new Intent(MY_NEW_ACTION);
        localBroadcastManager.sendBroadcast(intent);
    }


    static class LocalReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent){Toast.makeText(context,"Received LocalBroadcast!",Toast.LENGTH_SHORT).show();}
    }

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

new SDK 中应用播送

随着 Android 平台的倒退,它会不定期地更改零碎播送的行为形式。如果要在 Android 7.0(API 级别 24)或更高版本的 SDK 应用播送,必须留神以下更改:

Android 7.0 API 24

Android 7.0(API 级别 24)及更高版本不发送以下零碎播送:

ACTION_NEW_PICTURE
ACTION_NEW_VIDEO

此外,以 Android 7.0 及更高版本为指标平台的利用必须应用动静注册的形式,无奈在清单中申明播送接收器,所以当前所有播送间接全副应用动静注册的形式吧,如果非要用动态注册的形式,能够发送显式播送,(即指定指定包名再发送,然而咱们有时不晓得有谁要接管播送,所以显式播送用的比拟少),这个时候能够在发送播送的时候携带 intent.addFlags(0x01000000); 就能让播送冲破隐式播送限度,然而仍旧不倡议动态注册,还是动静注册比拟好。

Android 9.0 API 28

从 Android 9(API 级别 28)开始,NETWORK_STATE_CHANGED_ACTION 播送不再接管无关用户地位或个人身份数据的信息。通过 WLAN 接管的零碎播送不蕴含 SSID、BSSID 连贯信息或扫描后果,如果要获取这些信息,能够调用 WifiManager.getConnectionInfo()。

对于播送的文档能够看这里:《Receiving broadcasts》

原文地址:https://zouchanglin.cn/2020/1…

退出移动版