Android Oreo隐式和显式广播接收器
在本教程中,我们将讨论自Android Oreo以来Broadcast Receiver中的更改。
我们将了解为什么对Android的后台操作设置了限制。
Android广播接收机
广播接收器就像天线。
就像可以调整天线以捕获某些频率一样,可以将广播接收器注册为某些意图动作。
当该动作事件触发时,将调用BroadcastReceiver。
隐式与显式广播接收器
在进行区分之前,请先了解隐式意图和显式意图之间的区别。
显式意图用于调用您知道的特定组件。
当您不知道要调用的确切组件时,将使用隐式意图。
以从相机或者画廊捕获照片为例。
这是一个隐式意图,因为调用该意图会显示一个对话框,供您选择不同的应用程序。
电子邮件/浏览器的意图也是如此(如果它们向您显示对话框选择器)。
隐式广播接收器并非仅适用于您的应用程序。
诸如" ACTION_BOOT_COMPLETED"或者" CONNECTIVITY_CHANGE"之类的动作在隐式广播接收器中分类。
这是因为发生这些事件时,向该事件注册的所有应用程序都将获取该信息。
显式广播接收器是您的应用程序专有的。
调用您定义的自定义意图操作时,只会触发您应用程序的广播接收器。
处理Android Oreo广播接收器
现在,广播接收器在后台运行。
因此,它会耗尽电池电量。
想象每个应用程序都在接收者中隐式注册了" android.net.conn.CONNECTIVITY_CHANGE"。
现在,每当wifi /移动互联网连接切换时,所有应用程序以及接收器都会被触发。
想象一下,当您的wifi路由器不稳定时会发生什么。
可能会导致电池问题。
从Android Oreo开始,在" AndroidManifest.xml"中注册后,隐式广播接收器将无法工作。
如何在Android Oreo中处理隐式接收器?
要在应用程序中使用隐式接收器,您需要使用" registerReceiver()"在代码中以编程方式定义它们。
使用registerReceiver()
,我们可以在活动的生命周期内以编程方式注册和unregisterReceiver()
。
这样,隐式接收器仅在我们的活动/应用程序处于活动状态时才被调用,而在其他时间则不会被调用。
除了以编程方式注册接收者之外,我们也可以使用JobSchedulers
。
某些隐式广播是免税的,可以在列表中声明:
注意:从Android Pie开始," NETWORK_STATE_CHANGED_ACTION"广播不会接收到有关用户位置或者个人身份数据的信息。
在下一部分中,我们将创建一个应用程序,该应用程序将实现广播接收器,并保持Android Oreo的限制。
项目结构
代码
AndroidManifest.xml文件如下所示:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" package="com.theitroad.androidoreobroadcastreceiver"> <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" tools:ignore="GoogleAppIndexingWarning"> <receiver android:name="com.theitroad.androidoreobroadcastreceiver.MyReceiver" android:enabled="true"> <intent-filter> <action android:name="com.theitroad.AN_INTENT" </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>
我们定义了一个意图过滤器,其作用是明确的。
隐式代码仅在代码中定义:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" tools:context=".MainActivity"> <Button android:id="@+id/btnExplicitBroadcast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Explicit Broadcast" </LinearLayout>
MainActivity.java类的代码如下:
package com.theitroad.androidoreobroadcastreceiver; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity implements View.OnClickListener { Button btnExplicitBroadcast; MyReceiver myReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnExplicitBroadcast = findViewById(R.id.btnExplicitBroadcast); btnExplicitBroadcast.setOnClickListener(this); myReceiver= new MyReceiver(); } @Override protected void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(); filter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); registerReceiver(myReceiver, filter); } public void broadcastIntent() { Intent intent = new Intent(); intent.setAction("com.theitroad.AN_INTENT"); intent.setComponent(new ComponentName(getPackageName(),"com.theitroad.androidoreobroadcastreceiver.MyReceiver")); getApplicationContext().sendBroadcast(intent); } @Override protected void onStop() { super.onStop(); unregisterReceiver(myReceiver); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btnExplicitBroadcast: broadcastIntent(); break; } } }
在按钮上单击" sendBroadcast()"以发送显式广播。
注意:sendStickyBroadcasts()
用于发送广播意图,这些意图留在其他接收者以后再访问。
该方法已在API 21中弃用。
下面给出了MyReceiver.java BroadcastReceiver类的代码:
package com.theitroad.androidoreobroadcastreceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals("com.theitroad.AN_INTENT")) { Toast.makeText(context, "Explicit Broadcast was triggered", Toast.LENGTH_SHORT).show(); } if (("android.net.conn.CONNECTIVITY_CHANGE").equals(action)) { Toast.makeText(context, "Implicit Broadcast was triggered using registerReceiver", Toast.LENGTH_SHORT).show(); } } }
上面应用程序的输出如下:
LocalBroadcastManager
LocalBroadcastManager
仅用于在当前应用程序本地发送或者接收事件。
LocalBroadcastManager不收听系统范围的广播,并且比BroadcastReceiver安全。
另外,它的开销较小,因此通信速度更快。
可以初始化为:
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context); localBroadcastManager.sendBroadcast(intent);
如果您进行系统范围的事件,LocalBroadcastManager将失败。
我们可以通过在广播接收器中设置权限来保护它们。
只有在列表中请求了权限的接收者才能接收广播。
<receiver android:name=".MyReciever" android:permission="android.permission.SEND_SMS"> <intent-filter> <action android:name="MY_ACTION" </intent-filter> </receiver>
sendBroadcast(new Intent("MY_ACTION"), Manifest.permission.SEND_SMS);
在列表的接收方标签中将android:exported属性设置为" false",以限制该应用程序接收来自其他应用程序的广播。