BLE扫描无法发现设备

时间:2018-06-27 13:53:30

标签: android bluetooth-lowenergy

我的代码如下->

清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.root.securityalert">

    <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">
        <activity android:name=".DeviceScanActivity" android:label="Activity2" />
        <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:label="@string/app_name" android:name=".BlueTooth"/>
    </application>
    <uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    </manifest>

deviceScanActivity

DeviceScanActivity类扩展了AppCompatActivity / ListActivity / {

    public BluetoothAdapter mBluetoothAdapter;
    public /*final*/ BluetoothManager mBluetoothManager;

    ArrayList<ViewHolder> arrayOfUsers2 = new ArrayList<ViewHolder>();
    private boolean mScanning;

    private Handler mHandler;

    ArrayList<String> mylist = new ArrayList<String>();
    private static final int REQUEST_ENABLE_BT = 1;
    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 50000;
    private static final int PERMISSION_REQUEST_COARSE_LOCATION = 456;

    UsersAdapter adapter;
    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        //getActionBar().setTitle("abc");

        mHandler = new Handler();

        requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
        // Use this check to determine whether BLE is supported on the device.  Then you can
        // selectively disable BLE-related features.
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
            finish();
        }
        // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
        // BluetoothAdapter through BluetoothManager.
        mBluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        // Checks if Bluetooth is supported on the device.
        if (mBluetoothAdapter == null ) {
            Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
            finish();
            return;
        }
        if( !mBluetoothAdapter.isEnabled())
        {
            Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBluetooth, 1);
        }
        if( !mBluetoothAdapter.isDiscovering()) {
            Intent discoverableIntent =
                    new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
            startActivity(discoverableIntent);
        }
        // Construct the data source

        ArrayList<ViewHolder> arrayOfUsers = new ArrayList<ViewHolder>();

       // Create the adapter to convert the array to views

         adapter = new UsersAdapter(this, arrayOfUsers);


        ListView listView = (ListView) findViewById(R.id.mobile_list);
        listView.setAdapter(adapter); 
        ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
         adapter.add(newUser2);

    }


    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
            adapter.add(newUser2);
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);


                     if(arrayOfUsers2.isEmpty())
                     {
                         ViewHolder currentX=new ViewHolder("No devices ","found");
                         adapter.add(currentX);
                     }
                     else {
                         Iterator<ViewHolder> it = arrayOfUsers2.iterator();
                         while (it.hasNext()) {
                             ViewHolder currentX = it.next();
                             adapter.add(currentX);
                         }
                     }
                        // Do something with the value

                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
        //invalidateOptionsMenu();
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
        switch (requestCode) {
            case PERMISSION_REQUEST_COARSE_LOCATION: {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // Permission granted, yay! Start the Bluetooth device scan.
                    scanLeDevice(true);
                } else {
                    // Alert the user that this application requires the location permission to perform the scan.
                }
            }
        }
    }


    // Device scan callback.
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {

                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //mLeDeviceListAdapter.addDevice(device);
                            //mLeDeviceListAdapter.notifyDataSetChanged();

                            //ViewHolder newUser = new ViewHolder("Nathan", "San Diego");
                            String deviceName=null, deviceAddress=null;
                            if(device!=null)
                               deviceName= device.getName();
                            if (!(deviceName != null && deviceName.length() > 0))
                                deviceName = "unknown device";
                            if(device!=null)
                                deviceAddress= device.getAddress();
                            ViewHolder newUser = new ViewHolder(deviceName, deviceAddress);
                            ViewHolder newUser2 = new ViewHolder("adtv","vvg");
                            //if(!arrayOfUsers2.contains(newUser))
                                arrayOfUsers2.add(newUser);
                            adapter.add(newUser);
                        }
                    });
                }
            };
    public class UsersAdapter extends ArrayAdapter<ViewHolder> {


        public UsersAdapter(Context context, ArrayList<ViewHolder> users) {

            super(context, 0, users);

        }



        @Override

        public View getView(int position, View convertView, ViewGroup parent) {

            // Get the data item for this position

            ViewHolder user = getItem(position);


            // Check if an existing view is being reused, otherwise inflate the view

            if (convertView == null) {

                convertView = LayoutInflater.from(getContext()).inflate(R.layout.bt_details, parent, false);

            }

            // Lookup view for data population

            TextView tvName = (TextView) convertView.findViewById(R.id.DeviceName);

            TextView tvHome = (TextView) convertView.findViewById(R.id.DeviceAddress);

            // Populate the data into the template view using the data object

            tvName.setText(user.deviceName);

            tvHome.setText(user.deviceAddress);

            // Return the completed view to render on screen

            return convertView;

        }

    }
    public class ViewHolder {
        String deviceName;
        String deviceAddress;

        public ViewHolder(String device, String __address) {
            this.deviceName =device;
            this.deviceAddress= __address;
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ViewHolder a = (ViewHolder) o;
            return Objects.equals(deviceAddress, a.deviceAddress);
        }
    }



}


            }});
        ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
         adapter.add(newUser2);

    }


    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
            adapter.add(newUser2);
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);


                     if(arrayOfUsers2.isEmpty())
                     {
                         ViewHolder currentX=new ViewHolder("No devices ","found");
                         adapter.add(currentX);
                     }
                     else {
                         Iterator<ViewHolder> it = arrayOfUsers2.iterator();
                         while (it.hasNext()) {
                             ViewHolder currentX = it.next();
                             adapter.add(currentX);
                         }
                     }
                        // Do something with the value

                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }

    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
        switch (requestCode) {
            case PERMISSION_REQUEST_COARSE_LOCATION: {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // Permission granted, yay! Start the Bluetooth device scan.
                    scanLeDevice(true);
                } else {
                    // Alert the user that this application requires the location permission to perform the scan.
                }
            }
        }
    }


    // Device scan callback.
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {

                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {

                            String deviceName=null, deviceAddress=null;
                            if(device!=null)
                               deviceName= device.getName();
                            if (!(deviceName != null && deviceName.length() > 0))
                                deviceName = "unknown device";
                            if(device!=null)
                                deviceAddress= device.getAddress();
                            ViewHolder newUser = new ViewHolder(deviceName, deviceAddress);
                            ViewHolder newUser2 = new ViewHolder("adtv","vvg");
                            //if(!arrayOfUsers2.contains(newUser))
                                arrayOfUsers2.add(newUser);
                            adapter.add(newUser);
                        }
                    });
                }
            };
    public class UsersAdapter extends ArrayAdapter<ViewHolder> {


        public UsersAdapter(Context context, ArrayList<ViewHolder> users) {

            super(context, 0, users);

        }



        @Override

        public View getView(int position, View convertView, ViewGroup parent) {

            // Get the data item for this position

            ViewHolder user = getItem(position);


            // Check if an existing view is being reused, otherwise inflate the view

            if (convertView == null) {

                convertView = LayoutInflater.from(getContext()).inflate(R.layout.bt_details, parent, false);

            }

            // Lookup view for data population

            TextView tvName = (TextView) convertView.findViewById(R.id.DeviceName);

            TextView tvHome = (TextView) convertView.findViewById(R.id.DeviceAddress);

            // Populate the data into the template view using the data object

            tvName.setText(user.deviceName);

            tvHome.setText(user.deviceAddress);

            // Return the completed view to render on screen

            return convertView;

        }

    }
    public class ViewHolder {
        String deviceName;
        String deviceAddress;

        public ViewHolder(String device, String __address) {
            this.deviceName =device;
            this.deviceAddress= __address;
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ViewHolder a = (ViewHolder) o;
            return Objects.equals(deviceAddress, a.deviceAddress);
        }
    }



}

我正在尝试使用BLE API扫描附近的蓝牙设备。我已经使用此代码进行了测试,直到今天早上仍在工作。但是我有2个新设备,并且正在将此代码同时应用于它们和测试中。他们没有发现对方,也没有发现我的笔记本电脑。较早之前,我可以在附近发现其他设备。希望他们现在不在附近,他们两个都发现同一台设备但彼此之间却没有发现。 build.gradle(ModuleApp)

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.example.root.securityalert"
        minSdkVersion 23
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.0'
compile 'io.reactivex.rxjava2:rxjava:2.0.0'
implementation 'com.android.support:design:27.0.2'-this line is in red(error)
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
    mavenCentral()
}

Build.gradle(Project:xyz)

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.2.50'

    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        compile 'io.reactivex.rxjava2:rxandroid:2.0.0'
compile 'io.reactivex.rxjava2:rxjava:2.0.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

logcat错误

build failed    213ms
Run build   90ms
Load build  3ms
Configure build 78ms
Load projects   6ms
rootProject 
Configure project : 61ms
Apply plugin org.gradle.help-tasks  1ms
Apply script build.gradle   53ms--- this line is in red(error)

我也收到此错误

Could not find method compile() for arguments [io.reactivex.rxjava2:rxjava:2.0.0] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.

1 个答案:

答案 0 :(得分:0)

确保已淘汰API级别,因为大多数Android BLE构造都是> API21。其中adapter.startLeScan{}是针对较低API设备的,这也许是新设备无法执行此操作的原因检测其他设备。我使用Kotlin用Rx创建了一个简单的BLE扫描器。这使处理线程和安排线程变得更加容易。

您可以使用Builder对其进行配置,并为stopCallback设置一个条件将在达到该条件时停止扫描。

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.os.Build
import android.support.annotation.RequiresApi
import io.reactivex.*
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.jetbrains.anko.bluetoothManager

class BleScannerFlowable private constructor(
    private val adapter: BluetoothAdapter,
    private val stopCallback: ((BluetoothDevice) -> Boolean)? = null,
    private val scanFilter: Array<out ScanFilter>? = null,
    private val scanSettings: ScanSettings? = null
): FlowableOnSubscribe<BluetoothDevice> {

    override fun subscribe(e: FlowableEmitter<BluetoothDevice>) {
        if (isAboveLollipop()) {
            setupSubscriber21(e)
        } else {
            setupSubscriber(e)
        }
    }

    private fun setupSubscriber(emitter: FlowableEmitter<BluetoothDevice>) {
        adapter.startLeScan { device, rssi, scanRecord ->
            if (stopCallback?.invoke(device) == true) {
                adapter.stopLeScan { device, rssi, scanRecord ->
                    emitter.onComplete()    
                }
            }
            emitter.onNext(device)
        }
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun setupSubscriber21(emitter: FlowableEmitter<BluetoothDevice>) {
        val isFilterSet = scanFilter?.isNotEmpty() == true
        val filters = if (isFilterSet) scanFilter!!.toMutableList() else mutableListOf()
        adapter.bluetoothLeScanner?.startScan(filters, scanSettings, ScanCaller(emitter))
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private inner class ScanCaller(private val emitter: FlowableEmitter<BluetoothDevice>) : ScanCallback() {
    override fun onScanFailed(errorCode: Int) {
        emitter.onError(Throwable("Scan failed with code $errorCode"))
    }

        override fun onScanResult(callbackType: Int, result: ScanResult?) {
            if (result != null && result.device != null) {
                if (stopCallback?.invoke(result.device) == true) {
                    adapter.bluetoothLeScanner?.stopScan(this)
                    emitter.onComplete()
                } else {
                    emitter.onNext(result.device)
                }
            }
        }
    }

    class Builder(private val adapter: BluetoothAdapter) {
        private var scanFilter: Array<out ScanFilter>? = null
        private var stopCallback: ((BluetoothDevice) -> Boolean)? = null
        private var settingsBuilder: ScanSettings.Builder? = null
        private var scanSettings: ScanSettings? = null

        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        fun setFilterByName(name: String) : Builder {
            setScanFilters(ScanFilter.Builder()
                .setDeviceName(name)
                .build())
            return this
        }

        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        fun setScanFilters(vararg scanFilter: ScanFilter) : Builder{
            this.scanFilter = scanFilter 
            return this
        }

        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        fun setScanMode(scanMode: ScanModes) : Builder{
            getSettingsBuilder().setScanMode(scanMode.settingId)
            return this
        }

        fun setStopCallback(function: (BluetoothDevice) -> Boolean) : Builder {
            this.stopCallback = function
            return this
        }

        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        fun setScanSettings(scanSettings: ScanSettings) : Builder {
            this.scanSettings = scanSettings
            return this
        }

        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        fun build(): Flowable<BluetoothDevice> = 
            Flowable.create(BleScannerFlowable(adapter, 
                    stopCallback, 
                    scanFilter, 
                    scanSettings ?: settingsBuilder?.build()),
                    BackpressureStrategy.LATEST)

        fun buildLower(): Flowable<BluetoothDevice> = 
            Flowable.create(BleScannerFlowable(adapter, stopCallback), 
                    BackpressureStrategy.LATEST)

        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        private fun getSettingsBuilder() : ScanSettings.Builder { 
            if (settingsBuilder == null) {
                settingsBuilder = ScanSettings.Builder()
            }
            return settingsBuilder!!
        }
    }

}


@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
enum class ScanModes(val settingId: Int) {
    LOW_LATENCY(ScanSettings.SCAN_MODE_LOW_LATENCY),
    LOW_POWER(ScanSettings.SCAN_MODE_LOW_POWER),
    BALANCED(ScanSettings.SCAN_MODE_BALANCED),
    OPPORTUNISTIC(ScanSettings.SCAN_MODE_BALANCED)
}

fun startScanning() {
    val adapter = applicationContext.bluetoothManager.adapter ?: return
    val bleScanner = if (isAboveLollipop()) {
        BleScannerFlowable.Builder(adapter)
            .setScanMode(ScanModes.BALANCED)
            .setStopCallback { it.name == "MyBleDevice" }
            .build()
    } else {
        BleScannerFlowable.Builder(adapter)
            .setStopCallback { it.name == "MyBleDevice" }
            .buildLower()
    }

    bleScanner
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({
            // do something with the bluetooth device
        }, {
            // handle error 
            it.printStackTrace()
        }, {
            // do on finish scan
        })
}

fun isAboveLollipop() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP