Android 详解 BroadcastReceiver

1. Android 中 Broadcast 的类型?

答:

  • 广播性质的角度:

    • Normal broadcast:标准广播,完全异步执行的广播。在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息。因此它们之间没有任何先后顺序可言,标准广播的效率比较高,但同时也意味着它是无法被截断的。
    • Ordered broadcast:有序广播同步执行的广播。在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息。当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。
  • 广播来源的角度:

    • 系统广播registerReceiver()/unregisterReceiver())。
    • 自定义广播onReceive()/sendBroadcast()/sendOrderedBroadcast()/abortBroadcast())。
    • 本地广播LocalBroadcastManager)。

2. 注册广播的方式有?

答:

  • 静态注册:在 AndroidManifest.xml 文件中注册。
  • 动态注册:在代码中注册,同时动态注册的广播接收器一定都要取消注册才行缺点是必须要在程序启动之后才能接收到广播(因为注册的逻辑一般是写在 onCreate() 方法中的)。

3. Android 系统内置的系统级别的广播有?

答:手机开机广播、电池电量变化广播、时间或时区变化广播、网络连接情况变化广播等。

4. 【笔试题】手写一个通过动态注册的方式监听网络变化系统广播的 Demo?

答:APIregisterReceiver()/unregisterReceiver().

// 需要在 AndroidManifest.xml 文件中声明网络访问权限:<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
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 = 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) {
            ConnectivityManager connectionManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
            if(networkInfo != null && networkInfo.isAvailable()) {
                Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

5. 【笔试题】手写一个通过静态注册的方式监听开机启动系统广播的 Demo?

答:API<intent-filter>/<action>.

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

// 在 AndroidManifest.xml 文件中进行注册
<manifest xmlns:android="http://schema.android.com/apk/res/android"
    package="com.example.broadcasttest">

    <use-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <use-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> // 声明监听系统开机权限

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        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>
    </application>

</manifest>

6. 处理广播接收时的注意事项?

答:

  • 不要在 onReceive() 方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当 onReceive() 方法运行了较长时间而没有结束时,程序就会报错(Activity5 秒;Broadcast10 秒;Service20 秒)
  • 广播接收器更多的是扮演打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等。

7. 怎样发送一个自定义的标准广播

答:

  • 新建一个 Receiver 类继承 BroadcastReceiver重写 onReceive() 方法
  • AndroidManifest.xml 文件中 <receiver> 标签内添加一个子标签 <intent-filter>,在子标签内增加一个自定义的触发广播接收器的 action
  • 在页面适当位置增加发送广播逻辑:sendBroadcast(new Intent("和 AndridManifest.xml 中一致的自定义的 action"));(还可以在 Intent 中携带一些数据传递给广播接收器)。

8. 怎样发送一个自定义的有序广播

答:

  • 新建一个 Receiver 类继承 BroadcastReceiver重写 onReceive() 方法
  • AndroidManifest.xml 文件中 <receiver> 标签内添加一个子标签 <intent-filter>,在子标签内增加一个自定义的触发广播接收器的 action
  • 可以在子标签 <intent-filter> 内增加一个 priority 属性设置优先级,优先级高的广播接收器就可以先收到广播。
  • 在页面适当位置增加发送广播逻辑:sendOrderedBroadcast(new Intent("和 AndridManifest.xml 中一致的自定义的 action", null));(第二个参数是一个与权限相关的字符串,这里传入 null 就可以了)。
  • 如需中断广播,在 onReceive() 方法的最后调用 abortBroadcast() 方法。

9. 怎样理解本地广播?

答:

  • 系统全局广播可以被其他任何应用程序接收到,我们也可以接收到来自其他任何应用程序的广播。这样容易引起安全性问题垃圾广播的问题
  • 本地广播只能在本地应用程序的内部进行传递,并且广播接收器也只能接受来自本应用程序发出的广播,这样就可以解决安全性问题(自己的出不去解决安全性问题,别人的进不来解决垃圾广播问题)
  • 本地广播是无法通过静态注册的方式来接收的,因为静态注册主要就是为了让程序在未启动的情况下也能收到广播。而发送本地广播时,我们的程序肯定是已经启动了的,因此也完全不需要静态注册的功能。

10. 本地广播的优点?

答:

  • 可以明确地知道正在发送的广播不会离开我们的程序,因此不必担心机密数据泄露。
  • 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。
  • 发送本地广播比发送系统全局广播更加高效

11. 【笔试题】手写一个使用本地广播的 Demo?

答:

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(); // 获取实例
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("自定义的 action 字符串");
                localBroadcastManager.sentBroadcast(intent); // 发送本地广播
            }
        });
        intentFileter = new IntentFilter();
        intentFilter.addAction("自定义的 action 字符串");
        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();
        }
    }
}
-------------本文结束感谢您的阅读-------------