Android蓝牙开发前,首先要区分是经典蓝牙开发还是BLE(低功耗)蓝牙开发,它们的开发是有区别的,如果还分不清经典蓝牙和BLE(低功耗)蓝牙的小伙伴,可以先看Android蓝牙开发—经典蓝牙和BLE(低功耗)蓝牙的区别

本文是针对经典蓝牙开发的,如果是BLE(低功耗)蓝牙开发,可以看Android蓝牙开发—BLE(低功耗)蓝牙详细开发流程

开发流程

  • 开启蓝牙
  • 扫描蓝牙
  • 配对蓝牙
  • 连接蓝牙
  • 通信

开启蓝牙

1.获取BluetoothAdapter对象

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

2.判断设备是否支持蓝牙

/** * 设备是否支持蓝牙  true为支持 * @return */public boolean isSupportBlue(){    return mBluetoothAdapter != null;}

3.判断蓝牙是否开启

/** * 蓝牙是否打开   true为打开 * @return */public boolean isBlueEnable(){    return isSupportBlue() && mBluetoothAdapter.isEnabled();}

4.开启蓝牙

  • 异步自动开启蓝牙
/** * 自动打开蓝牙(异步:蓝牙不会立刻就处于开启状态) * 这个方法打开蓝牙不会弹出提示 */public void openBlueAsyn(){    if (isSupportBlue()) {        mBluetoothAdapter.enable();    }}
  • 同步提示开启蓝牙
/** * 自动打开蓝牙(同步) * 这个方法打开蓝牙会弹出提示 * 需要在onActivityResult 方法中判断resultCode == RESULT_OK  true为成功 */public void openBlueSync(Activity activity, int requestCode){    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);    activity.startActivityForResult(intent, requestCode);}

5.权限处理

  • 处理6.0以下版本的权限

    在AndroidManifest里面添加权限

  • 处理6.0以上版本的权限

    (1)在AndroidManifest里面添加权限

    (2)动态检查权限

/** * 检查权限 */private void checkPermissions() {    String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};    List permissionDeniedList = new ArrayList<>();    for (String permission : permissions) {        int permissionCheck = ContextCompat.checkSelfPermission(this, permission);        if (permissionCheck == PackageManager.PERMISSION_GRANTED) {            onPermissionGranted(permission);        } else {            permissionDeniedList.add(permission);        }    }    if (!permissionDeniedList.isEmpty()) {        String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]);        ActivityCompat.requestPermissions(this, deniedPermissions, REQUEST_CODE_PERMISSION_LOCATION);    }}/** * 权限回调 * @param requestCode * @param permissions * @param grantResults */@Overridepublic final void onRequestPermissionsResult(int requestCode,                                             @NonNull String[] permissions,                                             @NonNull int[] grantResults) {    super.onRequestPermissionsResult(requestCode, permissions, grantResults);    switch (requestCode) {        case REQUEST_CODE_PERMISSION_LOCATION:            if (grantResults.length > 0) {                for (int i = 0; i < grantResults.length; i++) {                    if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {                        onPermissionGranted(permissions[i]);                    }                }            }            break;    }}

    (3)开启GPS

/** * 开启GPS * @param permission */private void onPermissionGranted(String permission) {    switch (permission) {        case Manifest.permission.ACCESS_FINE_LOCATION:            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !checkGPSIsOpen()) {                new AlertDialog.Builder(this)                        .setTitle("提示")                        .setMessage("当前手机扫描蓝牙需要打开定位功能。")                        .setNegativeButton("取消",                                new DialogInterface.OnClickListener() {                                    @Override                                    public void onClick(DialogInterface dialog, int which) {                                        finish();                                    }                                })                        .setPositiveButton("前往设置",                                new DialogInterface.OnClickListener() {                                    @Override                                    public void onClick(DialogInterface dialog, int which) {                                        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);                                        startActivityForResult(intent, REQUEST_CODE_OPEN_GPS);                                    }                                })                        .setCancelable(false)                        .show();            } else {                //GPS已经开启了            }            break;    }}

    (4)检查GPS是否开启

/** * 检查GPS是否打开 * @return */private boolean checkGPSIsOpen() {    LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);    if (locationManager == null)        return false;    return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);}

扫描蓝牙

1.扫描周围蓝牙设备(配对上的设备有可能扫描不出来)

/** * 扫描的方法 返回true 扫描成功 * 通过接收广播获取扫描到的设备 * @return */public boolean scanBlue(){    if (!isBlueEnable()){        Log.e(TAG, "Bluetooth not enable!");        return false;    }    //当前是否在扫描,如果是就取消当前的扫描,重新扫描    if (mBluetoothAdapter.isDiscovering()){        mBluetoothAdapter.cancelDiscovery();    }    //此方法是个异步操作,一般搜索12秒    return mBluetoothAdapter.startDiscovery();}

2.取消扫描蓝牙

/** * 取消扫描蓝牙 * @return  true 为取消成功 */public boolean cancelScanBule(){    if (isSupportBlue()){        return mBluetoothAdapter.cancelDiscovery();    }    return true;}

3.通过广播的方式接收扫描结果

    (1)注册广播

IntentFilter filter1 = new IntentFilter(android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_STARTED);IntentFilter filter2 = new IntentFilter(android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_FINISHED);IntentFilter filter3 = new IntentFilter(BluetoothDevice.ACTION_FOUND);registerReceiver(scanBlueReceiver,filter1);registerReceiver(scanBlueReceiver,filter2);registerReceiver(scanBlueReceiver,filter3);

    (2)接收广播

/** *扫描广播接收类 * Created by zqf on 2018/7/6. */public class ScanBlueReceiver extends BroadcastReceiver {    private static final String TAG = ScanBlueReceiver.class.getName();    private ScanBlueCallBack callBack;    public ScanBlueReceiver(ScanBlueCallBack callBack){        this.callBack = callBack;    }    //广播接收器,当远程蓝牙设备被发现时,回调函数onReceiver()会被执行    @Override    public void onReceive(Context context, Intent intent) {        String action = intent.getAction();        Log.d(TAG, "action:" + action);        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);        switch (action){            case BluetoothAdapter.ACTION_DISCOVERY_STARTED:                Log.d(TAG, "开始扫描...");                callBack.onScanStarted();                break;            case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:                Log.d(TAG, "结束扫描...");                callBack.onScanFinished();                break;            case BluetoothDevice.ACTION_FOUND:                Log.d(TAG, "发现设备...");                callBack.onScanning(device);                break;        }    }}

配对蓝牙

1.开始配对

/** * 配对(配对成功与失败通过广播返回) * @param device */public void pin(BluetoothDevice device){    if (device == null){        Log.e(TAG, "bond device null");        return;    }    if (!isBlueEnable()){        Log.e(TAG, "Bluetooth not enable!");        return;    }    //配对之前把扫描关闭    if (mBluetoothAdapter.isDiscovering()){        mBluetoothAdapter.cancelDiscovery();    }    //判断设备是否配对,没有配对在配,配对了就不需要配了    if (device.getBondState() == BluetoothDevice.BOND_NONE) {        Log.d(TAG, "attemp to bond:" + device.getName());        try {            Method createBondMethod = device.getClass().getMethod("createBond");            Boolean returnValue = (Boolean) createBondMethod.invoke(device);            returnValue.booleanValue();        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();            Log.e(TAG, "attemp to bond fail!");        }    }}

2.取消配对

/** * 取消配对(取消配对成功与失败通过广播返回 也就是配对失败) * @param device */public void cancelPinBule(BluetoothDevice device){    if (device == null){        Log.d(TAG, "cancel bond device null");        return;    }    if (!isBlueEnable()){        Log.e(TAG, "Bluetooth not enable!");        return;    }    //判断设备是否配对,没有配对就不用取消了    if (device.getBondState() != BluetoothDevice.BOND_NONE) {        Log.d(TAG, "attemp to cancel bond:" + device.getName());        try {            Method removeBondMethod = device.getClass().getMethod("removeBond");            Boolean returnValue = (Boolean) removeBondMethod.invoke(device);            returnValue.booleanValue();        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();            Log.e(TAG, "attemp to cancel bond fail!");        }    }}

3.通过广播的方式接收配对结果

    (1)注册广播

IntentFilter filter4 = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);IntentFilter filter5 = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);registerReceiver(pinBlueReceiver,filter4);registerReceiver(pinBlueReceiver,filter5);

    (2)接收广播

/**配对广播接收类 * Created by zqf on 2018/7/7. */public class PinBlueReceiver extends BroadcastReceiver {    private String pin = "0000";  //此处为你要连接的蓝牙设备的初始密钥,一般为1234或0000    private static final String TAG = PinBlueReceiver.class.getName();    private PinBlueCallBack callBack;    public PinBlueReceiver(PinBlueCallBack callBack){        this.callBack = callBack;    }    //广播接收器,当远程蓝牙设备被发现时,回调函数onReceiver()会被执行    @Override    public void onReceive(Context context, Intent intent) {        String action = intent.getAction();        Log.d(TAG, "action:" + action);        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);        if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)){            try {                callBack.onBondRequest();                //1.确认配对//                ClsUtils.setPairingConfirmation(device.getClass(), device, true);                Method setPairingConfirmation = device.getClass().getDeclaredMethod("setPairingConfirmation",boolean.class);                setPairingConfirmation.invoke(device,true);                //2.终止有序广播                Log.d("order...", "isOrderedBroadcast:"+isOrderedBroadcast()+",isInitialStickyBroadcast:"+isInitialStickyBroadcast());                abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。                //3.调用setPin方法进行配对...//                boolean ret = ClsUtils.setPin(device.getClass(), device, pin);                Method removeBondMethod = device.getClass().getDeclaredMethod("setPin", new Class[]{byte[].class});                Boolean returnValue = (Boolean) removeBondMethod.invoke(device, new Object[]{pin.getBytes()});            } catch (Exception e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){            switch (device.getBondState()) {                case BluetoothDevice.BOND_NONE:                    Log.d(TAG, "取消配对");                    callBack.onBondFail(device);                    break;                case BluetoothDevice.BOND_BONDING:                    Log.d(TAG, "配对中");                    callBack.onBonding(device);                    break;                case BluetoothDevice.BOND_BONDED:                    Log.d(TAG, "配对成功");                    callBack.onBondSuccess(device);                    break;            }        }    }}

连接蓝牙

经典蓝牙连接相当于socket连接,是个非常耗时的操作,所以应该放到子线程中去完成。

1.连接线程

/**连接线程 * Created by zqf on 2018/7/7. */public class ConnectBlueTask extends AsyncTask {    private static final String TAG = ConnectBlueTask.class.getName();    private BluetoothDevice bluetoothDevice;    private ConnectBlueCallBack callBack;    public ConnectBlueTask(ConnectBlueCallBack callBack){        this.callBack = callBack;    }    @Override    protected BluetoothSocket doInBackground(BluetoothDevice... bluetoothDevices) {        bluetoothDevice = bluetoothDevices[0];        BluetoothSocket socket = null;        try{            Log.d(TAG,"开始连接socket,uuid:" + ClassicsBluetooth.UUID);            socket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(ClassicsBluetooth.UUID));            if (socket != null && !socket.isConnected()){                socket.connect();            }        }catch (IOException e){            Log.e(TAG,"socket连接失败");            try {                socket.close();            } catch (IOException e1) {                e1.printStackTrace();                Log.e(TAG,"socket关闭失败");            }        }        return socket;    }    @Override    protected void onPreExecute() {        Log.d(TAG,"开始连接");        if (callBack != null) callBack.onStartConnect();    }    @Override    protected void onPostExecute(BluetoothSocket bluetoothSocket) {        if (bluetoothSocket != null && bluetoothSocket.isConnected()){            Log.d(TAG,"连接成功");            if (callBack != null) callBack.onConnectSuccess(bluetoothDevice, bluetoothSocket);        }else {            Log.d(TAG,"连接失败");            if (callBack != null) callBack.onConnectFail(bluetoothDevice, "连接失败");        }    }}

2.启动连接线程

/** * 连接 (在配对之后调用) * @param device */public void connect(BluetoothDevice device, ConnectBlueCallBack callBack){    if (device == null){        Log.d(TAG, "bond device null");        return;    }    if (!isBlueEnable()){        Log.e(TAG, "Bluetooth not enable!");        return;    }    //连接之前把扫描关闭    if (mBluetoothAdapter.isDiscovering()){        mBluetoothAdapter.cancelDiscovery();    }    new ConnectBlueTask(callBack).execute(device);}

3.判断是否连接成功

/** * 蓝牙是否连接 * @return */public boolean isConnectBlue(){    return mBluetoothSocket != null && mBluetoothSocket.isConnected();}

4.断开连接

/** * 断开连接 * @return */public boolean cancelConnect(){    if (mBluetoothSocket != null && mBluetoothSocket.isConnected()){        try {            mBluetoothSocket.close();        } catch (IOException e) {            e.printStackTrace();            return false;        }    }    mBluetoothSocket = null;    return true;}

5.MAC地址连接

/** * 输入mac地址进行自动配对 * 前提是系统保存了该地址的对象 * @param address * @param callBack */public void connectMAC(String address, ConnectBlueCallBack callBack) {    if (!isBlueEnable()){        return ;    }    BluetoothDevice btDev = mBluetoothAdapter.getRemoteDevice(address);    connect(btDev, callBack);}

通信

1.读取数据线程

/**读取线程 * Created by zqf on 2018/7/7. */public class ReadTask extends AsyncTask {    private static final String TAG = ReadTask.class.getName();    private ReadCallBack callBack;    private BluetoothSocket socket;    public ReadTask(ReadCallBack callBack, BluetoothSocket socket){        this.callBack = callBack;        this.socket = socket;    }    @Override    protected String doInBackground(String... strings) {        BufferedInputStream in = null;        try {            StringBuffer sb = new StringBuffer();            in = new BufferedInputStream(socket.getInputStream());            int length = 0;            byte[] buf = new byte[1024];            while ((length = in.read()) != -1) {                sb.append(new String(buf,0,length));            }            return sb.toString();        } catch (IOException e) {            e.printStackTrace();        }finally {            try {                in.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return "读取失败";    }    @Override    protected void onPreExecute() {        Log.d(TAG,"开始读取数据");        if (callBack != null) callBack.onStarted();    }    @Override    protected void onPostExecute(String s) {        Log.d(TAG,"完成读取数据");        if (callBack != null){            if ("读取失败".equals(s)){                callBack.onFinished(false, s);            }else {                callBack.onFinished(true, s);            }        }    }}

2.写入数据线程

/**写入线程 * Created by zqf on 2018/7/7. */public class WriteTask extends AsyncTask{    private static final String TAG = WriteTask.class.getName();    private WriteCallBack callBack;    private BluetoothSocket socket;    public WriteTask(WriteCallBack callBack, BluetoothSocket socket){        this.callBack = callBack;        this.socket = socket;    }    @Override    protected String doInBackground(String... strings) {        String string = strings[0];        OutputStream outputStream = null;        try{            outputStream = socket.getOutputStream();            outputStream.write(string.getBytes());        } catch (IOException e) {            Log.e("error", "ON RESUME: Exception during write.", e);            return "发送失败";        }finally {            try {                outputStream.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return "发送成功";    }    @Override    protected void onPreExecute() {        if (callBack != null) callBack.onStarted();    }    @Override    protected void onPostExecute(String s) {        if (callBack != null){            if ("发送成功".equals(s)){                callBack.onFinished(true, s);            }else {                callBack.onFinished(false, s);            }        }    }}

 

以上就是经典蓝牙的开发流程和部分代码,后期会提供demo下载。若有不当之处,请留言讨论,一起学习进步。

更多相关文章

  1. android IBeacon 开发(一)搜索IBeacon基站
  2. android 蓝牙开发
  3. Android音频系统之USB设备通路(Android(安卓)5.1)
  4. 条码扫描二维码扫描——ZXing android 源码简化
  5. android zbar二维码扫描(中文不乱码)
  6. Settings中蓝牙连接流程
  7. Android(安卓)8 蓝牙打开过程
  8. android蓝牙开发————实现服务端客户端通信
  9. Android获取本机蓝牙地址

随机推荐

  1. 收藏老罗的----------Android应用程序组
  2. Android(二) 基于 eclipse 的 Android配置
  3. Android应用如何支持屏幕多尺寸多分辨率
  4. Android(安卓)5.1 彩蛋游戏分析
  5. android 屏幕分辨率问题
  6. Android(安卓)音频开发之 MediaPlayer
  7. 关于 Chrome for Android(安卓)你必须知
  8. 一切都“Android化”
  9. Android简单登录系统
  10. Android 5.0最应该实现的8个期望