先做一下声明,我这里使用的是经典蓝牙的开发,设备端选择的时候蓝牙打印机 这里就是简单信息发送,文末会给出接收消息的代码。好了 下面步入正题,请跟着我的步伐 开始摩擦!!
开发背景:手机通过蓝牙向蓝牙打印机发送指令,将蓝牙打印机的相关信息进行打印 功能很简单,就是连接蓝牙发个数据打印即可
一、首先 AndroidManifest.xml添加必要的权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
可能有些童靴在开发中会出现一种情况,代码没问题,为什么搜索不到附近的蓝牙设备,这是因为没有权限申请的不够,没有申请
ACCESS_FINE_LOCATION 的原因
二、创建 BluetoothMsg 用于存储蓝牙设备的相关信息
public class BluetoothMsg {
/**
* 蓝牙连接类型
*/
public enum ServerOrClient{
NONE,
SERVICE,
CLIENT
}
/**
* 蓝牙连接方式
*/
public static ServerOrClient serverOrClient = ServerOrClient.NONE;
// 连接蓝牙地址 最后连接地址 蓝牙名称 密码
public static String blueToothAddress = null,lastBlueToothAddress = null,name = null,pin=null;
//通信线程是否开启
public static boolean isOpen = false;
}
三、上布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@mipmap/timg"
android:drawableLeft="@mipmap/lanya"
android:gravity="center"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="获取蓝牙设备列表"
android:textColor="@color/colorPrimaryDark" />
<LinearLayout
android:id="@+id/state"
android:layout_below="@id/tv_title"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@android:color/holo_blue_bright"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textColor="@android:color/black"
android:text="当前连接设备:"/>
<TextView
android:id="@+id/tv_device_name"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textColor="@android:color/black"
android:paddingLeft="20dp"
android:text="xxxx"/>
<Button
android:id="@+id/btn_disconnect"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="断开"/>
</LinearLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swip"
android:layout_below="@id/state"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lv_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@color/colorPrimaryDark"
android:dividerHeight="1dp"
android:scrollbars="none">
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical"
android:background="@mipmap/timg"
android:layout_alignParentBottom="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:background="@android:color/transparent"
android:clickable="true"
android:gravity="center"
>
<ImageButton
android:id="@+id/btn_jian"
android:layout_width="50dp"
android:layout_height="match_parent"
android:src="@mipmap/delete1"/>
<Button
android:id="@+id/tv_currentCode"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text="编号:190800001"/>
<ImageButton
android:id="@+id/btn_jia"
android:layout_width="50dp"
android:layout_height="match_parent"
android:src="@mipmap/plus1"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_bottom"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:clickable="true"
android:layout_marginRight="30dp"
android:layout_marginLeft="30dp"
android:background="@android:color/white"
>
<Button
android:id="@+id/btn_jiaoyan"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:text="校验"/>
</LinearLayout>
</LinearLayout>
<ProgressBar
android:id="@+id/wait"
android:layout_width="80dp"
android:layout_height="80dp"
android:indeterminateDrawable="@drawable/progressbar"
android:layout_centerInParent="true"
android:visibility="gone"/>
</RelativeLayout>
四、程序代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
private ListView listView;
private SwipeRefreshLayout swip;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothServerSocket mServerSocket = null;
private BluetoothSocket socket = null;
private BluetoothDevice device = null;
private static String TAG = "Tag";
private List<String> mList = new ArrayList<>(); // 设备集合
private ArrayAdapter<String> adapter; // 蓝牙列表集合
private String mMsg;
private ProgressBar progressBar;
private ServerThread startServerThread = null;
private clientThread clientConnectThread = null;
public static final String PROTOCOL_SCHEME_RFCOMM = "btspp";
private MyReceiver mReceiver = null;
private boolean hasregister = false;
private Button btn_jiaoyan, btn_disconnect, tv_currentCode;
private TextView tv_device_name;
private String code;
private ImageButton btn_jia, btn_jian;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Android M Permission check
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE},
PERMISSION_REQUEST_COARSE_LOCATION);
}
}
getSupportActionBar().hide();
initView();
initBluetooth();
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
} else {
findAvalibleDevice();
mBluetoothAdapter.startDiscovery();
}
mReceiver = new MyReceiver();
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
filter.setPriority(50);
registerReceiver(mReceiver, filter);
IntentFilter stateFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
stateFilter.setPriority(100);
registerReceiver(mReceiver, stateFilter);
}
/**
* 蓝牙的初始化操作
*/
private void initBluetooth() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
// 确认开启蓝牙
if (!mBluetoothAdapter.isEnabled()) {
// 请求用户开启
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, RESULT_FIRST_USER);
// 使蓝牙设备可见,方便配对
Intent in = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
in.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 20);
startActivity(in);
// 直接开启 不经过提示
mBluetoothAdapter.enable();
}
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("没有蓝牙设备");
builder.setMessage("当前设备不具备蓝牙功能,请更换设备");
builder.setNegativeButton("知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setCancelable(false);
builder.show();
}
}
private void initView() {
listView = findViewById(R.id.lv_list);
swip = findViewById(R.id.swip);
progressBar = findViewById(R.id.wait);
btn_disconnect = findViewById(R.id.btn_disconnect);
btn_jiaoyan = findViewById(R.id.btn_jiaoyan);
tv_device_name = findViewById(R.id.tv_device_name);
btn_jia = findViewById(R.id.btn_jia);
btn_jian = findViewById(R.id.btn_jian);
tv_currentCode = findViewById(R.id.tv_currentCode);
code = SharedPreference.getString(this, "code", "190800001");
tv_currentCode.setText("编号:" + code);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mList);
listView.setAdapter(adapter);
btn_jiaoyan.setOnClickListener(this);
btn_disconnect.setOnClickListener(this);
btn_jia.setOnClickListener(this);
tv_currentCode.setOnClickListener(this);
btn_jian.setOnClickListener(this);
swip.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mList.clear();
adapter.notifyDataSetChanged();
mBluetoothAdapter.cancelDiscovery();
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
} else {
findAvalibleDevice();
mBluetoothAdapter.startDiscovery();
}
swip.setRefreshing(false);
}
});
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mMsg = mList.get(position);
if (BluetoothMsg.lastBlueToothAddress != BluetoothMsg.blueToothAddress) {
BluetoothMsg.lastBlueToothAddress = BluetoothMsg.blueToothAddress;
BluetoothMsg.name = mMsg.substring(0, mMsg.indexOf("\n"));
}
if (mBluetoothAdapter != null) {
mBluetoothAdapter.cancelDiscovery();
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);// 定义一个弹出框对象
dialog.setTitle("确认连接设备");
dialog.setIcon(R.mipmap.lanya);
dialog.setMessage(mMsg);
dialog.setPositiveButton("连接",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
BluetoothMsg.serverOrClient = BluetoothMsg.ServerOrClient.CLIENT;
BluetoothMsg.name = mMsg.substring(0, mMsg.indexOf("\n"));
BluetoothMsg.blueToothAddress = mMsg.substring(mMsg.length() - 17);
BluetoothMsg.pin = "配对码:".concat(BluetoothMsg.name.substring(BluetoothMsg.name.indexOf("-") + 1, BluetoothMsg.name.length())); // 蓝牙配对码
if (BluetoothMsg.name.contains("-")) {
connectBL();
progressBar.setVisibility(View.VISIBLE);
} else {
Toast.makeText(MainActivity.this, "当前连接对象非本产品倾向设备,无法完成连接", Toast.LENGTH_SHORT).show();
}
}
});
dialog.setNeutralButton("取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
BluetoothMsg.isOpen = false;
progressBar.setVisibility(View.GONE);
}
});
dialog.setCancelable(false);
dialog.show();
} else {
Toast.makeText(MainActivity.this, "该设备不可用,请断开所有连接重试", Toast.LENGTH_SHORT).show();
}
}
});
}
/**
* 连接蓝牙设备
*/
private void connectBL() {
try {
if (BluetoothMsg.serverOrClient == BluetoothMsg.ServerOrClient.CLIENT) {
String address = BluetoothMsg.blueToothAddress;
if (!address.equals("null")) {
// 获取远程设备
device = mBluetoothAdapter.getRemoteDevice(address);
clientConnectThread = new clientThread();
clientConnectThread.start();
}
} else if (BluetoothMsg.serverOrClient == BluetoothMsg.ServerOrClient.SERVICE) {
startServerThread = new ServerThread();
startServerThread.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 添加已经配对的蓝牙设备
*/
private void findAvalibleDevice() {
try {
// 获取可配对蓝牙设备
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
if (mBluetoothAdapter != null && mBluetoothAdapter.isDiscovering()) {
mList.clear();
adapter.notifyDataSetChanged();
}
if (devices.size() > 0) { // 存在已经配对过的蓝牙设备
for (Iterator<BluetoothDevice> it = devices.iterator(); it.hasNext(); ) {
BluetoothDevice btd = it.next();
// 如果长度大于零的话,需要判断加入的数据是不是重复
if (!mList.contains(btd.getName() + '\n' + btd.getAddress())) {
if (!TextUtils.isEmpty(btd.getName()) && btd.getAddress() != null) {
mList.add(btd.getName() + '\n' + btd.getAddress());
}
}
adapter.notifyDataSetChanged();
}
}
} catch (Exception e) {
}
}
@Override
public void onClick(View v) {
String s = SharedPreference.getString(MainActivity.this, "code", "190800001");
int i = Integer.parseInt(s);
switch (v.getId()) {
case R.id.btn_disconnect:
progressBar.setVisibility(View.VISIBLE);
disConnectBlueTooth();
progressBar.setVisibility(View.GONE);
break;
case R.id.btn_jiaoyan:
if (BluetoothMsg.isOpen) {
String name = BluetoothMsg.name; // 蓝牙名称
if (!TextUtils.isEmpty(name)) {
String lyName = null;
try {
lyName = "蓝牙名称:".concat(name + "\n");
String password = "配 对 码:".concat(name.substring(name.indexOf("-") + 1, name.length()) + "\n"); // 蓝牙配对码
String order = "测试数据:验证通过 \n时 间:" + NumUtil.getDateFormat(System.currentTimeMillis()) + "\n";
String code = "编 号:" + i;
SharedPreference.saveString(MainActivity.this, "code", "" + i);
sendMessage(lyName.concat(password).concat(order).concat(code).concat("\n\n\n").getBytes("gbk"));
tv_currentCode.setText("编号:" + i);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
} else {
Toast.makeText(MainActivity.this, "请连接蓝牙设备", Toast.LENGTH_SHORT).show();
}
break;
case R.id.tv_currentCode:
NumUtil.showDialog(MainActivity.this, tv_currentCode);
break;
case R.id.btn_jian:
i--;
SharedPreference.saveString(MainActivity.this, "code", "" + i);
tv_currentCode.setText("编号:" + i);
break;
case R.id.btn_jia:
i++;
SharedPreference.saveString(MainActivity.this, "code", "" + i);
tv_currentCode.setText("编号:" + i);
break;
}
}
//开启服务器
private class ServerThread extends Thread {
@Override
public void run() {
try {
/* 创建一个蓝牙服务器
* 参数分别:服务器名称、UUID */
mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(PROTOCOL_SCHEME_RFCOMM,
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
/* 接受客户端的连接请求 */
socket = mServerSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//开启客户端
private class clientThread extends Thread {
@Override
public void run() {
try {
//创建一个Socket连接:只需要服务器在注册时的UUID号
// socket = device.createRfcommSocketToServiceRecord(BluetoothProtocols.OBEX_OBJECT_PUSH_PROTOCOL_UUID);
socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
//连接
socket.connect();
boolean connected = socket.isConnected();
//启动接受数据
if (connected) {
BluetoothMsg.isOpen = true;
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_device_name.setText(BluetoothMsg.name);
progressBar.setVisibility(View.GONE);
}
});
}
} catch (IOException e) {
// Log.i(TAG, "run: "+e.getMessage());
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setIcon(R.mipmap.lanya);
builder.setTitle("连接失败,是否重连 ?");
builder.setMessage(BluetoothMsg.name + "\n" + BluetoothMsg.blueToothAddress);
builder.setPositiveButton("是", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// 设备重连
BluetoothMsg.serverOrClient = BluetoothMsg.ServerOrClient.CLIENT;
connectBL();
progressBar.setVisibility(View.VISIBLE);
}
});
builder.setNeutralButton("否", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
BluetoothMsg.isOpen = false;
progressBar.setVisibility(View.GONE);
}
});
builder.setCancelable(false);
builder.show();
} catch (Exception ex) {
}
}
});
}
}
}
/* 停止服务器 */
private void shutdownServer() {
new Thread() {
@Override
public void run() {
if (startServerThread != null) {
startServerThread.interrupt();
startServerThread = null;
}
try {
if (socket != null) {
socket.close();
socket = null;
}
if (mServerSocket != null) {
mServerSocket.close();/* 关闭服务器 */
mServerSocket = null;
}
} catch (IOException e) {
}
}
;
}.start();
}
/* 停止客户端连接 */
private void shutdownClient() {
new Thread() {
@Override
public void run() {
if (clientConnectThread != null) {
clientConnectThread.interrupt();
clientConnectThread = null;
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
}
}.start();
}
//发送数据
private void sendMessage(byte[] by) {
try {
if (socket == null) {
Toast.makeText(MainActivity.this, "设备没有连接", Toast.LENGTH_SHORT).show();
return;
} else {
OutputStream os = socket.getOutputStream();
os.write(by);
os.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onStart() {
if (!hasregister) {
hasregister = true;
IntentFilter filterStart = new IntentFilter(BluetoothDevice.ACTION_FOUND);
IntentFilter filterEnd = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filterStart);
registerReceiver(mReceiver, filterEnd);
}
super.onStart();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (resultCode) {
case RESULT_OK: // RESULT_OK == -1
findAvalibleDevice();
break;
case RESULT_CANCELED: // RESULT_CANCELED == 0;
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice btd = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//搜索没有配过对的蓝牙设备
if (btd.getBondState() != BluetoothDevice.BOND_BONDED) {
if (!mList.contains(btd.getName() + '\n' + btd.getAddress())) {
mList.add(btd.getName() + '\n' + btd.getAddress());
}
adapter.notifyDataSetChanged();
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { //搜索结束
if (listView.getCount() == 0) {
mList.add("没有可用的蓝牙设备");
adapter.notifyDataSetChanged();
}
} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
if (BluetoothMsg.serverOrClient == BluetoothMsg.ServerOrClient.CLIENT) {
shutdownClient();
} else if (BluetoothMsg.serverOrClient == BluetoothMsg.ServerOrClient.SERVICE) {
shutdownServer();
}
BluetoothMsg.isOpen = false;
BluetoothMsg.serverOrClient = BluetoothMsg.ServerOrClient.NONE;
} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF) {
// BlueTooth is disconnected do handling here
disConnectBlueTooth();
}
}
}
}
private void disConnectBlueTooth() {
if (BluetoothMsg.serverOrClient == BluetoothMsg.ServerOrClient.CLIENT) {
shutdownClient();
} else if (BluetoothMsg.serverOrClient == BluetoothMsg.ServerOrClient.SERVICE) {
shutdownServer();
}
BluetoothMsg.isOpen = false;
BluetoothMsg.pin = null;
BluetoothMsg.serverOrClient = BluetoothMsg.ServerOrClient.NONE;
tv_device_name.setText("xxxx");
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
if (mBluetoothAdapter != null && mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
if (hasregister) {
hasregister = false;
unregisterReceiver(mReceiver);
}
if (BluetoothMsg.serverOrClient == BluetoothMsg.ServerOrClient.CLIENT) {
shutdownClient();
} else if (BluetoothMsg.serverOrClient == BluetoothMsg.ServerOrClient.SERVICE) {
shutdownServer();
}
BluetoothMsg.isOpen = false;
BluetoothMsg.serverOrClient = BluetoothMsg.ServerOrClient.NONE;
BluetoothMsg.pin = null;
} catch (Exception e) {
}
}
文章篇幅可能有点长,因为我把项目中的内容都贴出来了,摘出来浪费时间(其实就是懒,好吧,我承认了)但是吧 你可以直接粘贴复制 复制到自己的工程里面增删改就行了,可以直接运行,如果想要学习的话,自己细心捋一下还是能够有所收获
五、这里讲一下接收蓝牙数据
开启一个新的线程专门用于数据接收,通过 BluetoothSocket 获得 输入流 InputStream
开始无限循环 开始取数 上代码了 代码里的注释也是很清晰了
//读取数据
private class readThread extends Thread {
@Override
public void run() {
int bytes = 0;
InputStream mmInStream = null;
try {
mmInStream = socket.getInputStream();
} catch (IOException e1) {
e1.printStackTrace();
}
while (true) {
/**
*接收数据 这里要加入一段睡眠时间 出现分段过来的时候mmInStream.available()可能是100 但是休眠0.5s
* 过后执行 bytes = mmInStream.read(buffer);时 输入流应该已经完整了 而且不像分成两段时候执行了两个缺失
* 输入流 这里数据虽然是分两段过来的 但是sleep后面的代码只会执行一次
*/
try {
// Read from the InputStream
if (mmInStream.available() > 0 == false) {
continue;
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int count = 0;
while (count == 0) {
count = mmInStream.available();
}
byte[] buffer = new byte[count];
// 这里就获取到字节数组了,然后转string 还是怎么着就看你的需求要怎么处理了
} catch (IOException e) {
try {
mmInStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
break;
}
}
}
有些开发可能会出现大小端的问题(我当时做另一款蓝牙软件的时候就碰到了)这时候就需要将端与端统一之后 再进行数据的解析,有不懂的可以私聊我吆!!!