繁体   English   中英

当蓝牙连接状态改变时防止 onDestroy

[英]Prevent onDestroy when Bluetooth connection state changes

目标

  • 如果已经连接的蓝牙设备断开连接,并且 Activity 已经在运行,请关闭 Activity

问题

  • 当蓝牙设备连接状态通过BluetoothAdapterProperties: CONNECTION_STATE_CHANGE改变时,似乎创建了一个新的 Activity 或当前的 Activity 重新启动。

代码中没有任何内容可以侦听和/或应对蓝牙连接状态的变化做出反应。

问题表现在 BroadcastReceiver 的使用中,而 BroadcastReceiver 又使用意图启动 Activity。 出于某种原因,活动在其生命周期中继续运行,产生新的窗口,即使蓝牙连接的唯一变化是BluetoothAdapterProperties: CONNECTION_STATE_CHANGE

我仅在带有 Android N 的 Nexus 6P 上对此进行了测试。我还不知道这种实现对任何其他设备意味着什么样的影响。 但我至少需要在一台设备上完成这项工作。

更新

我做了一些实验,发现如果我不在 AndroidManifest 中注册 BroadcastReceiver,调用 onDestroy 的问题就会消失。 但是,我希望能够对蓝牙连接设备做出反应,以便我可以启动我的活动,然后处理输入。 如果每次新设备连接/断开时活动都被破坏,这将根本不起作用。 如果 BroadcastReceiver 已经在运行,那么让 BroadcastReceiver 完成活动的原因是什么,我可以控制这种行为吗?

更新 2

我还可以得出结论,使用此方法https://stackoverflow.com/a/6529365/975641禁用静态声明的 BroadcastReceiver 不会改善情况。 一旦 Manifest-BroadcastReceiver 从 Android 捕获 ACL_CONNECTED 意图并启动我的自定义活动,它就会在连接状态更改时(通常在 ACL_DISCONNECTED 之前)无情地调用 onDestroy。 我是否在 Manifest 中声明了 ACL_DISCONNECTED 并不重要。 只要我让我的接收器监听 ACL_CONNECTED 意图并基于此启动我的 Activity,当连接状态发生变化时就会调用 onDestroy。 太令人沮丧了。

舱单

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.VIBRATE" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".BtActivity"
        android:launchMode="singleTop" />
    <receiver android:name=".BtConnectionBroadcastReceiver" android:priority="100000">
        <intent-filter>
            <action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
            <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
            <action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
            <action android:name="android.intent.action.MEDIA_BUTTON" />
            <action android:name="android.media.VOLUME_CHANGED_ACTION" />
        </intent-filter>
    </receiver>
</application>

BtConnectionBroadcastReceiver

public class BtConnectionBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "BT";
    public static final String BROADCAST_ACTION_CONNECTED = "CONNECTED";
    public static final String BROADCAST_ACTION_DISCONNECTED = "DISCONNECTED";
    SharedPreferences mSharedPreferences;

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        // When discovery finds a device
        if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            Log.d(TAG, "DEVICE CONNECTED");
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            Log.d("DEVICE NAME", device.getName());
            Log.d("DEVICE ADDRESS", device.getAddress());
            Intent i = new Intent(context, BtActivity.class);
            i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
            context.startActivity(i);
        } else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
            Log.d(TAG, "DEVICE DISCONNECTED");
            intent = new Intent();
            intent.setAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
            context.sendBroadcast(intent);
        }
    }

活动

public class BtActivity extends AppCompatActivity {
private static final String TAG = "BT";
Window mWindow;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_bt);

    Log.d(TAG, "onCreate");
    IntentFilter filter = new IntentFilter(BtConnectionBroadcastReceiver.INTENT_FILTER);
    filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_CONNECTED);
    filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
    //registerReceiver(mReceiver, filter);

    mWindow = getWindow();
    WindowManager.LayoutParams params = new WindowManager.LayoutParams();
    //params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF;
    params.screenBrightness = 0.2f;
    mWindow.setAttributes(params);
    mWindow.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
    mWindow.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
    mWindow.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
    mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
    mWindow.getDecorView().setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                    View.SYSTEM_UI_FLAG_FULLSCREEN |
                    View.SYSTEM_UI_FLAG_IMMERSIVE);
    }

@Override
protected void onResume() {
    super.onResume();
    Log.d(TAG, "onResume");
}

@Override
protected void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "onDestroy");
}

BroadcastReceiver mReceiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "BROADCAST RECEIVED IN ACTIVITY");
        String mac;
        if(intent.getAction().equals(BtConnectionBroadcastReceiver.BROADCAST_DEVICE_CONNECTED)) {
            Log.d(TAG, "CONNECT BROADCAST RECEIVED");
            mac = intent.getStringExtra("mac");
            checkConnectedDevice(mac, true); // This adds a device to an internal list
            Log.d(TAG, "Activity nr of devices:" +mNrOfDevices);
        }
        if(intent.getAction().equals(BtConnectionBroadcastReceiver.BROADCAST_DEVICE_DISCONNECTED)) {
            Log.d(TAG, "DISCONNECT BROADCAST RECEIVED");
            mac = intent.getStringExtra("mac");
            checkConnectedDevice(mac, false); // This removes a device from an internal list
            Log.d(TAG, "Activity nr of devices:" +mNrOfDevices);
            if(mNrOfDevices < 1) {
                Log.d(TAG, "No more connected devices");
                finish();
            }
        }
        abortBroadcast();
    }
};
}

当我运行此代码时,我得到以下链:

  1. 启动 MainActivity(不包括,它只包含一个具有默认主布局的活动,以便注册应用程序接收器)
  2. 打开蓝牙设备(这个已经配对过了,所以android知道它)
  3. 等到它连接并得到这个:
    • 设备连接
    • 在创建
    • 在简历上
  4. 关闭蓝牙设备,然后我得到这个:
    • 设备断开
    • 销毁
    • 在创建
    • 在简历上

我无法理解为什么此时重新启动活动会被破坏。 活动已经在运行,BroadcastReceiver 只向已经运行的活动发送广播。 我无法弄清楚为什么 Activity 有理由杀死自己然后再次重新启动。 这让我处于 Activity 仍在运行的状态,但它不是启动的原始 Activity。

然而,我确实在 logcats 中看到一些似乎与此有关的东西,它在这个序列中;

06-02 15:45:09.156 26431 26431 D BT:设备断开连接

06-02 15:45:09.213 19547 19547 D 蓝牙适配器服务:handleMessage()-MESSAGE_PROFILE_CONNECTION_STATE_CHANGED

06-02 15:45:09.213 26431 26431 D BT:onDestroy

06-02 15:45:09.214 19547 19547 D BluetoothAdapterProperties:CONNECTION_STATE_CHANGE:FF:FF:20:00:C1:47:2->0

06-02 15:45:09.216 3502 3805 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 0

06-02 15:45:09.237 414 414 W SurfaceFlinger:无法记录到二进制事件日志:溢出。

06-02 15:45:09.239 26431 26431 D BT:onCreate

06-02 15:45:09.243 26431 26431 D BT:onResume

在 AndroidManifest.xml 中为 Activity 添加以下内容,它对我有用。

android:configChanges="keyboard|keyboardHidden"

阅读此https://developer.android.com/guide/components/broadcasts.html#effects_on_process_state 后,我可能可以安全地得出结论,调用 onDestroy 的原因是因为接收器影响了它运行的进程,实际上意味着当接收者运行它的 onReceive 方法时,它会销毁自己并带走 Activity。

我当然希望它以不同的方式工作,但我相信这就是有效发生的事情,需要采取另一种方法。

我知道这个答案很晚了,但是我的活动标签遇到了这个问题。 在清单文件中,我为 configChange 添加了以下行。

android:configChanges="keyboard|orientation|screenSize|keyboardHidden|navigation|screenLayout"

现在我的应用程序不会自我毁灭。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM