0.權限 AndroidManifest.xml
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<activity
android:name=".DeviceListActivity"
android:configChanges="screenSize|keyboardHidden|orientation"
android:launchMode="singleInstance"
android:screenOrientation="portrait"/>
1.設備列表布局 activity_device_list.xml (主要就一個listview了)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tvTitle" android:layout_width="match_parent" android:layout_height="48dp" android:gravity="center" android:text="掃描到的藍牙"/> <ListView android:id="@+id/listViewMessage" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:fastScrollEnabled="true"/> </LinearLayout>
2.設備列表java代碼 DeviceListActivity.java
package de.bvb.bluetoothchat; import android.app.Activity; import android.app.Dialog; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import java.io.IOException; import java.util.ArrayList; import java.util.List; import de.bvb.bluetoothchat.utils.BlueToothConnectCallback; import de.bvb.bluetoothchat.utils.BluetoothDeviceInfo; import de.bvb.bluetoothchat.utils.ClientUtil; /** * Created by Administrator on 2017/06/01. */ public class DeviceListActivity extends Activity implements AdapterView.OnItemClickListener { List<String> list; ArrayAdapter<String> adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_device_list); ListView listView = (ListView) findViewById(R.id.listViewMessage); list = new ArrayList<>(); adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list); listView.setAdapter(adapter); listView.setOnItemClickListener(this); ClientUtil.getInstance().onCreate(this); ClientUtil.getInstance().setOnFoundUnBondDeviceListener(new ClientUtil.OnFoundUnBondDeviceListener() { @Override public void foundUnBondDevice(BluetoothDevice unBondDevice) { list.add(unBondDevice.getName() + "|" + unBondDevice.getAddress()); adapter.notifyDataSetChanged(); } }); } @Override protected void onResume() { super.onResume(); List<BluetoothDeviceInfo> bluetoothDeviceInfoList = ClientUtil.getInstance().scanDevice(); list.clear(); for (BluetoothDeviceInfo bluetoothDeviceInfo : bluetoothDeviceInfoList) { list.add(bluetoothDeviceInfo.toString()); adapter.notifyDataSetChanged(); } } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { final Dialog dialog = new Dialog(this); dialog.setTitle("正在連接.."); dialog.show(); String macAddress = list.get(position).split("\\|")[1];// ClientUtil.getInstance().connectRemoteDevice(macAddress, new BlueToothConnectCallback() { @Override public void connecting(String serverBlueToothAddress) { } @Override public void connectSuccess(String serverBlueToothAddress) { dialog.dismiss(); Toast.makeText(DeviceListActivity.this, "連接成功", Toast.LENGTH_SHORT).show(); startActivity(new Intent(DeviceListActivity.this, ClientActivity.class)); } @Override public void connectFailure(IOException e) { dialog.dismiss(); Toast.makeText(DeviceListActivity.this, "連接失敗..", Toast.LENGTH_SHORT).show(); } }); } @Override protected void onDestroy() { super.onDestroy(); ClientUtil.getInstance().unregisterReceiver(this); } }
3.通信頁面調用代碼(收消息,發消息)
// 注冊收到消息以后的事件 ClientUtil.getInstance().setOnReceivedMessageListener(new ReceivedMessageListener() { @Override public void onReceiveMessage(final String messageContent) { list.add(new MessageEntity(messageContent, true)); listViewAdapterMessage.setData(list); } @Override public void onConnectionInterrupt(IOException e) { btnSend.setEnabled(false); etMessage.setEnabled(false); Toast.makeText(ClientActivity.this, "連接中斷", Toast.LENGTH_SHORT).show(); startActivity(new Intent(ClientActivity.this, DeviceListActivity.class)); } });
// 發送消息 ClientUtil.getInstance().sendMessage(message); list.add(new MessageEntity(message, false)); listViewAdapterMessage.setData(list);
4.工具類
package de.bvb.bluetoothchat.utils; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.text.TextUtils; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.Closeable; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; /** * 客戶端(連接端)工具類 */ public class ClientUtil { public static final String TAG = "BluetoothManagerUtil"; /////////////////////////////////////////////////////////////////////////// // 單例模式 private ClientUtil() { } public static synchronized ClientUtil getInstance() { return SingletonHolder.instance; } private static final class SingletonHolder { private static ClientUtil instance = new ClientUtil(); } /////////////////////////////////////////////////////////////////////////// private String serverBlueToothAddress; //連接藍牙地址 private BluetoothSocket socket = null; // 客戶端socket private BluetoothAdapter bluetoothAdapter; /** 打開藍牙,注冊掃描藍牙的廣播 onCreate()中執行.連接頁面調用 */ public void onCreate(Activity activity) { registerBluetoothScanReceiver(activity); bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (null != bluetoothAdapter) { //本地藍牙存在... if (!bluetoothAdapter.isEnabled()) { //判斷藍牙是否被打開... // 發送打開藍牙的意圖,系統會彈出一個提示對話框,打開藍牙是需要傳遞intent的... Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); //打開本機的藍牙功能...使用startActivityForResult()方法...這里我們開啟的這個Activity是需要它返回執行結果給主Activity的... activity.startActivityForResult(enableIntent, Activity.RESULT_FIRST_USER); Intent displayIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); // 設置藍牙的可見性,最大值3600秒,默認120秒,0表示永遠可見(作為客戶端,可見性可以不設置,服務端必須要設置) displayIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0); //這里只需要開啟另一個activity,讓其一直顯示藍牙...沒必要把信息返回..因此調用startActivity() activity.startActivity(displayIntent); // 直接打開藍牙 bluetoothAdapter.enable();//這步才是真正打開藍牙的部分.... LogUtil.d(TAG, "打開藍牙成功"); } else { LogUtil.d(TAG, "藍牙已經打開了..."); } } else { LogUtil.d(TAG, "當前設備沒有藍牙模塊"); } } /** 掃描設備 onResume()中執行.連接頁面調用 */ public List<BluetoothDeviceInfo> scanDevice() { if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) { LogUtil.e(TAG, "藍牙狀態異常"); return null; } List<BluetoothDeviceInfo> bluetoothDeviceInfoList = new ArrayList<>(); if (bluetoothAdapter.isDiscovering()) { // 如果正在處於掃描過程... /** 停止掃描 */ bluetoothAdapter.cancelDiscovery(); // 取消掃描... } else { // 每次掃描前都先判斷一下是否存在已經配對過的設備 Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices(); if (pairedDevices.size() > 0) { BluetoothDeviceInfo bluetoothDeviceInfo; for (BluetoothDevice device : pairedDevices) { bluetoothDeviceInfo = new BluetoothDeviceInfo(device.getName() + "", device.getAddress() + ""); bluetoothDeviceInfoList.add(bluetoothDeviceInfo); LogUtil.d(TAG, "已經匹配過的設備:" + bluetoothDeviceInfo.toString()); } } else { LogUtil.d(TAG, "沒有已經配對過的設備"); } /* 開始搜索 */ bluetoothAdapter.startDiscovery(); } return bluetoothDeviceInfoList; } /** 通過Mac地址去嘗試連接一個設備.連接頁面調用 */ public void connectRemoteDevice(final String serverBlueToothAddress, BlueToothConnectCallback connectInterface) { this.serverBlueToothAddress = serverBlueToothAddress; final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(serverBlueToothAddress); ThreadPoolUtil.execute(new ConnectRunnable(device, connectInterface)); } /** 廣播反注冊.連接頁面調用 */ public void unregisterReceiver(Activity activity) { if (receiver != null && receiver.getAbortBroadcast()) { activity.unregisterReceiver(receiver); } } /** 發送消息,在通信頁面使用 */ public void sendMessage(String message) { try { writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); writer.write(message + "\n"); writer.flush(); } catch (IOException e) { e.printStackTrace(); } } /** 收到消息的監聽事件,在通信頁面注冊這個事件 */ public void setOnReceivedMessageListener(ReceivedMessageListener listener) { if (listener != null) { // 可以開啟讀數據線程 // MainHandler.getInstance().post(new ReadRunnable(listener)); ThreadPoolUtil.execute(new ReadRunnable(listener)); } } /** 關閉藍牙,在app退出時調用 */ public void onExit() { if (bluetoothAdapter != null) { bluetoothAdapter.cancelDiscovery(); // 關閉藍牙 bluetoothAdapter.disable(); } closeCloseable(writer, socket); } /** 連接線程 */ class ConnectRunnable implements Runnable { private BluetoothDevice device; // 藍牙設備 private BlueToothConnectCallback connectInterface; public ConnectRunnable(BluetoothDevice device, BlueToothConnectCallback connectInterface) { this.device = device; this.connectInterface = connectInterface; } @Override public void run() { if (null != device) { try { if (socket != null) { closeCloseable(socket); } socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); // 連接 LogUtil.d(TAG, "正在連接 " + serverBlueToothAddress); connectInterface.connecting(serverBlueToothAddress); // Message.obtain(handler, MESSAGE_TYPE_SEND, "請稍候,正在連接服務器: " + serverBlueToothAddress).sendToTarget(); socket.connect(); MainHandler.getInstance().post(new Runnable() { @Override public void run() { connectInterface.connectSuccess(serverBlueToothAddress); LogUtil.d(TAG, "連接 " + serverBlueToothAddress + " 成功 "); } }); // 如果實現了連接,那么服務端和客戶端就共享一個RFFCOMM信道... // Message.obtain(handler, MESSAGE_TYPE_SEND, "已經連接上服務端!可以發送信息").sendToTarget(); // 如果連接成功了...這步就會執行...更新UI界面...否則走catch(IOException e) // Message.obtain(handler, MESSAGE_ID_REFRESH_UI).sendToTarget(); // 屏蔽點擊事件 // listViewMessage.setOnItemClickListener(null); } catch (final IOException e) { MainHandler.getInstance().post(new Runnable() { @Override public void run() { connectInterface.connectFailure(e); LogUtil.d(TAG, "連接" + serverBlueToothAddress + "失敗 " + e.getMessage()); } }); // e.printStackTrace(); } } } } private BufferedWriter writer = null; class ReadRunnable implements Runnable { private ReceivedMessageListener listener; public ReadRunnable(ReceivedMessageListener listener) { this.listener = listener; } public void run() { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String content; while (!TextUtils.isEmpty(content = reader.readLine())) { final String finalContent = content; MainHandler.getInstance().post(new Runnable() { @Override public void run() { listener.onReceiveMessage(finalContent); } }); // Message.obtain(handler, MESSAGE_TYPE_RECEIVED, content).sendToTarget(); } } catch (final IOException e) { MainHandler.getInstance().post(new Runnable() { @Override public void run() { LogUtil.d(TAG, "連接中斷 " + e.getMessage()); listener.onConnectionInterrupt(e); } }); // 連接斷開 // Message.obtain(handler, MESSAGE_ID_DISCONNECT).sendToTarget(); } closeCloseable(reader); } } private BroadcastReceiver registerBluetoothScanReceiver(Activity activity) { IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); activity.registerReceiver(receiver, filter); return receiver; } public void setOnFoundUnBondDeviceListener(OnFoundUnBondDeviceListener onFoundUnBondDeviceListener) { this.onFoundUnBondDeviceListener = onFoundUnBondDeviceListener; } private OnFoundUnBondDeviceListener onFoundUnBondDeviceListener; public interface OnFoundUnBondDeviceListener { void foundUnBondDevice(BluetoothDevice unBondDevice); } private void closeCloseable(Closeable... closeable) { if (null != closeable && closeable.length > 0) { for (int i = 0; i < closeable.length; i++) { if (closeable[i] != null) { try { closeable[i].close(); } catch (IOException e) { e.printStackTrace(); } finally { closeable[i] = null; } } } } } /** * 下面是注冊receiver監聽,注冊廣播...說一下為什么要注冊廣播... * 因為藍牙的通信,需要進行設備的搜索,搜索到設備后我們才能夠實現連接..如果沒有搜索,那還談什么連接... * 因此我們需要搜索,搜索的過程中系統會自動發出三個廣播...這三個廣播為: * ACTION_DISCOVERY_START:開始搜索... * ACTION_DISCOVERY_FINISH:搜索結束... * ACTION_FOUND:正在搜索...一共三個過程...因為我們需要對這三個響應過程進行接收,然后實現一些功能,因此 * 我們需要對廣播進行注冊...知道廣播的人應該都知道,想要對廣播進行接收,必須進行注冊,否則是接收不到的... */ private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) {//正在搜索過程... // 通過EXTRA_DEVICE附加域來得到一個BluetoothDevice設備 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 如果這個設備是不曾配對過的,添加到list列表 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { if (null != onFoundUnBondDeviceListener) { LogUtil.d(TAG, "發現沒有配對過的設備:" + parseDevice2BluetoothDeviceInfo(device)); onFoundUnBondDeviceListener.foundUnBondDevice(device); } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//搜索結束后的過程... LogUtil.d(TAG, "沒有發現設備"); } } } }; private String parseDevice2BluetoothDeviceInfo(BluetoothDevice device) { if (device == null) { return "device == null"; } return new BluetoothDeviceInfo(device.getName(), device.getAddress()).toString(); } }
