Android:有线耳机拔出或蓝牙耳机断开连接,暂停播放

    当我们正在“专注学习”的过程中,如果不小心拔掉了耳机线或者断开蓝牙耳机的连接,突然之间我们APP里正在播放的声音就会打扰到周围的人,如果声音还挺大的话,是不是有些尴尬?所以呢,现在需要实现这么一个功能:在有线耳机拔掉或者蓝牙耳机断开连接时,立刻停止播放。

方式一、通过广播接收器监听有线耳机和蓝牙耳机的状态

步骤1:申请权限

    在工程的AndroidManifest.xml中,添加以下代码:

<manifast xxx>
    ...
    <!-- 获取蓝牙相关权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    ...
</manifast>

步骤2:注册广播接受器,监听耳机状态变化

    广播(Broadcast Receiver)注册有两种方式,一种方式是<静态注册>:即在项目的Manifest的文件里注册。

<receiver android:name=".HeadphoneReceiver">  
            <intent-filter>
                <action android:name="android.intent.action.HEADSET_PLUG" />
            </intent-filter>
</receiver>   

经测试,该方式无效:因为Android官方为了提升用户体验,对应用在后台运行时可以执行的操作施加了限制。从 Android O 版本(Android 8.0, API 26)开始,在 AndroidManifest.xml 注册的广播接收器,无法接收隐式广播。虽然,也有几种广播不受这些限制的约束,但是本文涉及到的两种隐式广播(Intent.ACTION_HEADSET_PLUGAudioManager.ACTION_AUDIO_BECOMING_NOISY)还是在限制范围之内,所有无法通过在Manifest清单文件注册接收器的方式来实现。

    所以可以选择另外一种<动态注册>的方式:在项目的启动页 LanchActivity 或其他合适地方注册广播接收器,笔者采用的方式是单独启动一个后台 Service, 在其 OnCreate() 方法里进行注册。

    不管是在Activity里进行注册,还是在 Service 里注册,经笔者测试(机型:huawei mate40 pro),在<用户完全退出用户界面或运行在后台>的情况下,只要不执行 unregisterReceiver 的操作,仍然可以接受到广播。但是笔者建议采用第二种方式,APP 有后台 Service 在运行的情况下被系统回收导致无法接收广播的概率会降低一些,相对来说更加稳定。当然如果该应用在后台运行,Service 还是有被系统回收的风险,建议启动该 Service 的同时开启一个前台服务:startForeground(id, notification),以尽可能降低该 Service 的被系统回收的几率。

public class HeadphoneService extends Service {

    private final HeadphoneReceiver receiver = new HeadphoneReceiver();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        IntentFilter intentFilter = new IntentFilter();
        //有线耳机
        intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
        //蓝牙耳机
        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
        registerReceiver(receiver, intentFilter);
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(receiver);
        super.onDestroy();
    }

}

步骤3:创建接收器,处理状态变化

public class HeadphoneReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();
        if (Intent.ACTION_HEADSET_PLUG.equals(action)) {
            int state = intent.getIntExtra("state", -1);
            if (state == 0) {
                // 耳机拔出
            } else if (state == 1) {
                // 耳机插入
            }
        } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {

            BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
            /* 申请权限
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            */
            //如果应用运行的Android系统版本大于等于12,获取蓝牙状态需要动态申请权限
            int state = adapter.getProfileConnectionState(BluetoothProfile.HEADSET);
            if (state == BluetoothAdapter.STATE_DISCONNECTED) {
                //Bluetooth headset is now disconnected
            }
        }
    }
}

步骤4: 启动Service

// 建议在LaunchActivity的OnCreate()里或其他合适的地方启动服务
Intent intent = new Intent(this, HeadphoneService.class);
startService(intent);

第一种方式虽然可以实现我们的需求,但有两个缺点:

缺点1:如果应用运行的Android系统版本大于等于12,获取蓝牙状态需要动态申请权限;

缺点2:拔出有线耳机或断开蓝牙耳机,会延迟大概一秒钟,才会收到Android的系统广播;

方式二:通过广播接收器监听噪音 - NOISY

    /**
     * Broadcast intent, a hint for applications that audio is about to become
     * 'noisy' due to a change in audio outputs. For example, this intent may
     * be sent when a wired headset is unplugged, or when an A2DP audio
     * sink is disconnected, and the audio system is about to automatically
     * switch audio route to the speaker. Applications that are controlling
     * audio streams may consider pausing, reducing volume or some other action
     * on receipt of this intent so as not to surprise the user with audio
     * from the speaker.
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";

     这段话的大概意思就是,如果耳机线被拔掉或是支持A2DP(蓝牙协议)的音频连接断开后,系统会认为该应用因为声音输出方式改变,导致产生噪音(NOISY),系统会广播该Intent Action:(ACTION_AUDIO_BECOMING_NOISY),让我们的广播接收器可以做出相应的处理,比如:暂停播放、降低音量等。

步骤1:注册广播接收器

public class HeadphoneService extends Service {

    private final HeadphoneReceiver receiver = new HeadphoneReceiver();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
        registerReceiver(receiver, intentFilter);
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(receiver);
        super.onDestroy();
    }

}

步骤2:创建接收器,处理状态变化

public class HeadphoneReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();
        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(action)) {
            //暂停播放
            pauseAudio();
        }
    }
}

步骤3: 启动Service

// 建议在LaunchActivity的OnCreate()里或其他合适的地方启动服务
Intent intent = new Intent(this, HeadphoneService.class);
startService(intent);

    综上,第二种方式是不是更加简洁呢?既不需要在 Manifest 文件里申明权限,也不需要在运行时动态申请蓝牙权限;而且相对第一种方式没有任何延迟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

思涛的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值