Android Oreo隐式和显式广播接收器

时间:2020-02-23 14:29:07  来源:igfitidea点击:

在本教程中,我们将讨论自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",以限制该应用程序接收来自其他应用程序的广播。