Android9.0蓝牙扫描失败,ScanCallback.onScanFailed(int errorCode),errorCode=2

      最近蓝牙手环在Android9.0的手机上回连总是连接不上,老在扫描的地方onScanFailed里回调errorCode=2,errorCode共有下面4种情况:

errorCode=1;Fails to start scan as BLE scan with the same settings is already started by the app.
errorCode=2;Fails to start scan as app cannot be registered.
errorCode=3;Fails to start scan due an internal error
errorCode=4;Fails to start power optimized scan as this feature is not supported

       发现BluetoothGatt.close()不管用,关闭重启手机蓝牙或者重启App是可以重新连接蓝牙,但是咱不能一碰到这个问题就让用户去重启App或者重启手机蓝牙。经过查阅各种资料,说问题的原因是:

       手机在startScan扫描的过程中还没来得及stopScan ,就被系统强制杀掉了, 导致mClientIf未被正常释放,实例和相关蓝牙对象已被残留到系统蓝牙服务中, 打开app后又重新初始化ScanCallback多次被注册,导致每次的扫描mClientIf的值都在递增, 于是mClientIf的值在增加到一定程度时(最大mClientIf数量视国产系统而定 不做深究),onScanFailed 返回了 errorCode =2  。

     解决办法是:重新刷新手机的蓝牙状态

    public static boolean releaseAllScanClient() {
        try {
            Object mIBluetoothManager = getIBluetoothManager(BluetoothAdapter.getDefaultAdapter());
            if (mIBluetoothManager == null) return false;
            Object iGatt = getIBluetoothGatt(mIBluetoothManager);
            if (iGatt == null) return false;

            Method unregisterClient = getDeclaredMethod(iGatt, "unregisterClient", int.class);
            Method stopScan;
            int type;
            try {
                type = 0;
                stopScan = getDeclaredMethod(iGatt, "stopScan", int.class, boolean.class);
            } catch (Exception e) {
                type = 1;
                stopScan = getDeclaredMethod(iGatt, "stopScan", int.class);
            }

            for (int mClientIf = 0; mClientIf <= 40; mClientIf++) {
                if (type == 0) {
                    try {
                        stopScan.invoke(iGatt, mClientIf, false);
                    } catch (Exception ignored) {
                    }
                }
                if (type == 1) {
                    try {
                        stopScan.invoke(iGatt, mClientIf);
                    } catch (Exception ignored) {
                    }
                }
                try {
                    unregisterClient.invoke(iGatt, mClientIf);
                } catch (Exception ignored) {
                }
            }
            stopScan.setAccessible(false);
            unregisterClient.setAccessible(false);
//            BLESupport.getDeclaredMethod(iGatt, "unregAll").invoke(iGatt);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
@SuppressLint("PrivateApi")
    public static Object getIBluetoothGatt(Object mIBluetoothManager) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Method getBluetoothGatt = getDeclaredMethod(mIBluetoothManager, "getBluetoothGatt");
        Object object=new Object();
        try {
            object=getBluetoothGatt.invoke(mIBluetoothManager);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return object;
    }


    @SuppressLint("PrivateApi")
    public static Object getIBluetoothManager(BluetoothAdapter adapter) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Method getBluetoothManager = getDeclaredMethod(BluetoothAdapter.class, "getBluetoothManager");
        return getBluetoothManager.invoke(adapter);
    }


    public static Field getDeclaredField(Class clazz, String name) throws NoSuchFieldException {
        Field declaredField = clazz.getDeclaredField(name);
        declaredField.setAccessible(true);
        return declaredField;
    }


    public static Method getDeclaredMethod(Class clazz, String name, Class... parameterTypes) throws NoSuchMethodException {
        Method declaredMethod = clazz.getDeclaredMethod(name, parameterTypes);
        declaredMethod.setAccessible(true);
        return declaredMethod;
    }


    public static Field getDeclaredField(Object obj, String name) throws NoSuchFieldException {
        Field declaredField = obj.getClass().getDeclaredField(name);
        declaredField.setAccessible(true);
        return declaredField;
    }


    public static Method getDeclaredMethod(Object obj, String name, Class... parameterTypes) throws NoSuchMethodException {
        Method declaredMethod = obj.getClass().getDeclaredMethod(name, parameterTypes);
        declaredMethod.setAccessible(true);
        return declaredMethod;
    }

 

调用上面的反射还不行,因为Android9.0对SDK的反射会抛出异常,弹出下面的警告窗口:

Android9.0蓝牙扫描失败,ScanCallback.onScanFailed(int errorCode),errorCode=2_第1张图片

所以需要在App初始化的时候调用下面的反射来关闭Android9.0的弹窗:

@SuppressLint("PrivateApi")
private void closeAndroidPDialog(){
    try {
        Class aClass = Class.forName("android.content.pm.PackageParser$Package");
        Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
    } catch (Exception e) {
        e.printStackTrace();
    }
    try {
        Class cls = Class.forName("android.app.ActivityThread");
        Method declaredMethod = cls.getDeclaredMethod("currentActivityThread");
        declaredMethod.setAccessible(true);
        Object activityThread = declaredMethod.invoke(null);
        Field mHiddenApiWarningShown = cls.getDeclaredField("mHiddenApiWarningShown");
        mHiddenApiWarningShown.setAccessible(true);
        mHiddenApiWarningShown.setBoolean(activityThread, true);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

你可能感兴趣的:(Android)