蓝牙开发
* 前几天售前拿来个电子秤,扔地上说,明天要拿这个取招商,功能就是称完之后在app显示重量...
* 内心一万个cnm,没有需求,没有原型,没有评估。还明天就要!大写的服。
* 抱怨太多也没用。问了下是什么通信协议,--->蓝牙发送数据。
*
* 不多说了,撸起袖子干吧。
* 问了生产商,没有sdk,没有文档。只有说明书,靠。等等...说明书上有解析协议。
* 然后开始。。。翻了下git.不太理想。自己撸吧。
都有注释。。
package com.basexhgx.xhgx.bthutils;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
/**
* Create: 2018/9/20 14:14
*
* @author: Winstronzeng [email protected]
* Description: 扫描蓝牙工具类,扫描设置
* Version: 1.0
**/
public class SearchBluetooth {
BluetoothAdapter mBluetootadapter;
//TODO 静态单例内部类
private SearchBluetooth() {
mBluetootadapter = BluetoothAdapter.getDefaultAdapter();
}
public static SearchBluetooth getInstance() {
return SearchBluetooth2.t;
}
private static class SearchBluetooth2 {
private static SearchBluetooth t = new SearchBluetooth();
}
/**
* 扫描蓝牙设备
* <p>
* mBluetootadapter.cancelDiscovery();//停止扫描
* mBluetootadapter.startDiscovery();//开始扫描附件蓝牙
* 通过广播接收扫描到的蓝牙设备。并通过配对后进行 socket连接传输数据
*/
public void doSearchBluetooth() {
if (null == mBluetootadapter) {
mBluetootadapter = BluetoothAdapter.getDefaultAdapter();
}
if (!mBluetootadapter.isEnabled()) {
//TODO 校验蓝牙是否打开
//不做提示,强行打开,此方法需要权限<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
boolean enable = mBluetootadapter.enable();
if (enable) {
//TODO 扫描蓝牙设备
mBluetootadapter.cancelDiscovery();
mBluetootadapter.startDiscovery();
}
} else {
//TODO 扫描蓝牙设备
mBluetootadapter.cancelDiscovery();//停止扫描
mBluetootadapter.startDiscovery();//开始扫描附件蓝牙
}
}
public void stopSearchBluetooth() {
//TODO 停止扫描蓝牙
if (null != mBluetootadapter) {
mBluetootadapter.cancelDiscovery();
}
}
public void doConnetBluetooth(int states, BluetoothDevice btDevice, BluetoothAsyncTask bluetoothAsyncTask) {
if (states == BluetoothDevice.BOND_NONE || states == BluetoothDevice.BOND_BONDED) {
//TODO 没有配对
try {
//通过工具类ClsUtils,调用createBond方法
boolean bond = BluetoothClsUtils.createBond(btDevice.getClass(), btDevice);
if (bond) {
//创建连接
mBluetootadapter.cancelDiscovery();//创建连接前,最好关闭扫描。否则会导致连接上的蓝牙断开。
bluetoothAsyncTask.execute("98:D3:32:30:95:B4");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (states == BluetoothDevice.BOND_BONDED) {
//TODO 已经配对直接链接
mBluetootadapter.cancelDiscovery();//创建连接前,最好关闭扫描。否则会导致连接上的蓝牙断开。
new BluetoothAsyncTask().execute("98:D3:32:30:95:B4");
}
}
}
}
[点击并拖拽以移动]
有个特别重要的事:
记得添加权限,你不添加as也会提示你的
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
扫描完,就到接收扫描到的设备了,系统通过广播接收
我们自己写个广播类接收
package com.basexhgx.xhgx.bthutils;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.basexhgx.xhgx.bthutils.Obsever.LeaderCenter;
/**
* Create: 2018/9/20 14:16
*
* @author: Winstronzeng [email protected]
* Description: 接受蓝牙广播
* Version: 1.0
**/
public class AcceptBluetoothReceiver extends BroadcastReceiver {
BluetoothDevice device = null;
String pin = "1234";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// 发现设备
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
//TODO 从Intent中获取设备对象
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//TODO 发现蓝牙设备
getBluetoothDevice(device);
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
//TODO 扫描结束
} else if (action.equals("android.bluetooth.device.action.PAIRING_REQUEST")) {
//再次得到的action,会等于PAIRING_REQUEST
if (device == null) {
return;
}
try {
//1.确认配对
BluetoothClsUtils.setPairingConfirmation(device.getClass(), device, true);
//2.终止有序广播
Log.i("info","提示信息" + "isOrderedBroadcast:" + isOrderedBroadcast() + ",isInitialStickyBroadcast:" + isInitialStickyBroadcast());
abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。
//3.调用setPin方法进行配对...
boolean ret = BluetoothClsUtils.setPin(device.getClass(), device, pin);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF) {
Log.i("info","--------------连接断开了-----------");
}
// Bluetooth is disconnected, do handling here
} else if (BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED.equals(action)) {
Log.i("info","正在断开蓝牙连接。。。");
} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
Log.i("info","已断开蓝牙连接。。。");
} else if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
Log.i("info","---------连接了蓝牙成功-----");
}
}
/**
* 发现蓝牙设备
*
* @param device
*/
public void getBluetoothDevice(BluetoothDevice device) {
Log.i("info","------->发现目标");
// 将设备名称和地址放入array adapter,以便在ListView中显示
LeaderCenter.getInstance().notifyObserver("bluetooth", device);
}
}
接收到广播数据通过观察者向外传递。也可以通过接口回调或者EventBus传递
通过Activity展示扫描到的蓝牙设备
package com.basexhgx.xhgx.bluetooth;
import android.bluetooth.BluetoothDevice;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import com.basexhgx.xhgx.bthutils.BluetoothAsyncTask;
import com.basexhgx.xhgx.bthutils.Obsever.BluetoothObserver;
import com.basexhgx.xhgx.bthutils.Obsever.LeaderCenter;
import com.basexhgx.xhgx.bthutils.SearchBluetooth;
import com.basexhgx.xhgx.modle.BluetoothEntity;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class SearchActivity extends AppCompatActivity implements BluetoothAsyncTask.BluetoothData, BluetoothObserver, AdapterView.OnItemClickListener {
List<BluetoothDevice> list = new ArrayList<>();
private Button mSearch;
private Button sendmsg;
private ListView mRecyclerview;
private TextView mPaccept;
private LinearLayout mLl;
private EditText editText;
public static String text;
private MyBluetoothAdapter mybluetoothAdapter;
private BluetoothAsyncTask bluetoothAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
mSearch = (Button) findViewById(R.id.search);
sendmsg = (Button) findViewById(R.id.sendmsg);
mRecyclerview = (ListView) findViewById(R.id.recyclerview);
mPaccept = (TextView) findViewById(R.id.paccept);
mLl = (LinearLayout) findViewById(R.id.ll);
editText = findViewById(R.id.edt);
LeaderCenter.getInstance().registerObserver(this);
mSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SearchBluetooth.getInstance().doSearchBluetooth();
}
});
sendmsg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
text = editText.getText().toString();
editText.setText("");
}
});
}
@Override
public void bluetoothData(String s, byte[] data) {
mPaccept.setText("收到=" + s);
}
@Override
public void connetState(String result) {
mPaccept.setText("收到=" + result);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void acceptSubjectState(Object obj) {
//TODO 扫描到的蓝牙设备
BluetoothDevice btDevice = (BluetoothDevice) obj;
list.add(btDevice);
if (mybluetoothAdapter == null) {
mybluetoothAdapter = new MyBluetoothAdapter(SearchActivity.this, list);
}
//列表展示
mRecyclerview.setAdapter(mybluetoothAdapter);
mRecyclerview.setOnItemClickListener(this);
mybluetoothAdapter.notifyDataSetChanged();
}
@Override
public String getTag() {
return "bluetooth";
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
SearchBluetooth.getInstance().stopSearchBluetooth();
bluetoothAsyncTask = new BluetoothAsyncTask();
bluetoothAsyncTask.setBluetoothData(this);
//TODO 选择蓝牙进行配对链接。如果没有配对过则先进行配对。配对过了直接进行socket链接
SearchBluetooth.getInstance().doConnetBluetooth(list.get(position).getBondState(), list.get(position), bluetoothAsyncTask);
}
}
接着开始读设备 穿过来的数据
这里使用异步线程防止主线程阻塞也必须这么干
代码如下:
package com.basexhgx.xhgx.bthutils;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.AsyncTask;
import android.util.Log;
import com.basexhgx.xhgx.bluetooth.SearchActivity;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
/**
* @author: ZengWQ(zwq_em @ 163.com)
* Date: 2017-10-12
* Time: 15:18 数据传输 获取,发送数据
* Verssion: 1.0
* Describe:
*/
public class BluetoothAsyncTask extends AsyncTask<String, String, String> {
public BluetoothAsyncTask() {
}
BluetoothData bluetoothData;
public BluetoothData getBluetoothData() {
return bluetoothData;
}
public void setBluetoothData(BluetoothData bluetoothData) {
this.bluetoothData = bluetoothData;
}
private BluetoothSocket btSocket;
private OutputStream outStream;
//这条是蓝牙串口通用的UUID,不要更改
private static final UUID MY_UUID =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
@Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(params[0]);
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
try {
btSocket.connect();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("ON RESUME: BT connection established, data transfer link open.");
} catch (IOException e) {
try {
btSocket.close();
return "3";//Socket 创建失败
} catch (IOException e2) {
System.out.println("ON RESUME: Unable to close socket during connection failure");
return "2";//Socket 关闭失败
}
} finally {
BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
readData();
writeData(SearchActivity.text);
return "0";
}
}
@Override //这个方法是在主线程中运行的,所以可以更新界面
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
//连接成功则启动监听
Log.i("info", "bluetooth_connet_state" + result);
super.onPostExecute(result);
}
/**
* 读取外来蓝牙设备的数据
*/
public void readData() {
byte b[] = new byte[30];// 所有的内容都读到此数组之中
StringBuilder stringBuilder = new StringBuilder();
try {
InputStream inputStream = btSocket.getInputStream();
while (true) {
if (inputStream.read(b) != -1) {
stringBuilder.append(b);
}
if (null != bluetoothData)
bluetoothData.connetState(new String(b));
System.out.println("--------size=" + new String(b));
}
} catch (IOException e) {
e.printStackTrace();
}
}
public interface BluetoothData {
void bluetoothData(String s, byte[] data);
void connetState(String result);
}
/**
* send 要发送的数据
*
* @return 0 soket 创建成功 1失败
*/
public String writeData(String send) {
try {
outStream = btSocket.getOutputStream();
while (true) {
byte[] bytes = SearchActivity.text.getBytes();
if (bytes.length > 0) {
outStream.write(bytes);
outStream.flush();
}
}
} catch (IOException e) {
Log.e("error", "ON RESUME: Output stream creation failed.", e);
return "1";//Socket 流创建失败
} finally {
Log.i("info", "----------蓝牙连接正常,Socket 创建成功---");
return "0";//蓝牙连接正常,Socket 创建成功
}
}
}
这段代码的接收方法亲测没问题。向蓝牙设备发送的代码没亲测过。不知道有问题没。
我目前做的是招商投标用的demo,没有仔细优化,读到数据之后的操作代码就不贴了。‘
整个过程大致就是以上的代码,在这里记录一下。比较low,勿喷。