藍芽(簡單的通訊連線)
藍芽是什麼(Bluetooth):
- 一種短距離無線通訊技術
- 愛立信公司建立
- 如今由藍芽技術聯盟(Bluetooth Special Interest Group,簡稱SIG)管理。
- 現在用的都是低功耗藍芽 Android 4.3(API Level 18)開始引入Bluetooth Low Energy(BLE,低功耗藍芽)
- 在 5.0 以後才支援外設模式,
無線通訊方案:
- NFC 一個近距離接觸連線的通訊技術 如把手機當公交卡用 消耗最低
- 藍芽 一種短距離無線通訊技術
- WIFI 。。。
為什麼要用藍芽:
- 低功率 便於電池供電裝置工作
- 使用方便,點對點連線
- 短距離,低成本,以及高速
- 在智慧裝置的普及性高,應用廣。
理論沒有詳細瞭解 這裡貼出網址有興趣可以去看下
UUID:全域性唯一標識
- UUID是根據一定演算法,計算得到的一長串數字,這個數字的產生使用了多種元素,所以使得這串數字不會重複,每次生成都會產生不一樣的序列,所以可以用來作為唯一標識。
- 建立伺服器端和客戶端時都需要用UUID來建立 連線通訊時只有一樣的才可以成功連線上
- 可以程式碼生成 也可以用uuid生成器
UUID.randomUUID().toString().replace("-", "");
藍芽通訊的流程:
- 註冊介面卡開啟藍芽
- 註冊廣播監聽藍芽狀態
- 搜尋附近裝置和已配對裝置
- 選擇未配對裝置進行配對
- 選擇已配對裝置進行連線通訊
1、開啟藍芽:
首先呢 先加許可權 獲取位置的許可權屬於高危許可權 所以還需要動態呼叫:
1、AndroidManifest.xml <6.0定位許可權那別人都說只加一個就好了 我的貌似不行 所以我就都加上了>
-
<!-- 藍芽通訊許可權 -->
-
<uses-permission android:name="android.permission.BLUETOOTH" />//一些配置連線藍芽的許可權
-
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />//進行操作的許可權
-
<!-- 6.0以上需要的許可權 -->
-
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
-
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
2、activity
檢測位置許可權有沒有同意 沒有的話無法進行附近搜尋 所以直接退出應用
-
@RequiresApi(api = Build.VERSION_CODES.M)
-
@Override
-
protected void onResume() {
-
super.onResume();
-
//動態獲取許可權
-
checkBluetoothAndLocationPermission();
-
}
-
@RequiresApi(api = Build.VERSION_CODES.M)
-
private void checkBluetoothAndLocationPermission() {
-
//判斷是否有訪問位置的許可權,沒有許可權,直接申請位置許可權
-
if ((checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
-
|| (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
-
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
-
Manifest.permission.ACCESS_FINE_LOCATION}, 2);
-
}
-
}
-
@Override
-
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
-
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
-
switch (requestCode) {
-
case 2:
-
//再次判斷是否獲取到許可權 沒有就關閉當前介面
-
for (int i : grantResults) {
-
if (i != PackageManager.PERMISSION_GRANTED) {
-
Toast.makeText(this, "Permission error !!!", Toast.LENGTH_SHORT).show();
-
finish();
-
}
-
}
-
break;
-
}
-
}
在onCreate()方法裡,獲取藍芽介面卡
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothAdapter這個類常用的方法有:
- getDefaultAdapter: 獲取本地的藍芽介面卡
- enable(); 開啟藍芽(不帶提示框 但是手機有自帶的。。。)
- disable(); 關閉藍芽
- isEnabled(): 本地藍芽是否開啟
- getAddress(); 獲取自己的藍芽MAC地址
- cancelDiscovery() 停止掃描
- isDiscovering() 是否正在處於掃描過程中
- getState():獲取本地藍芽介面卡的狀態
- getScanMode(): 獲取本地藍芽介面卡的當前藍芽掃描模式。
開啟藍芽的兩種方式:
- 第一種開啟方法: 呼叫enable
- 第二種開啟方法 ,呼叫系統API去開啟藍芽
mBluetoothAdapter.enable();
//不會自動提示使用者預設開啟 有的手機還是會提示的
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_OPEN_BT_CODE); //CODE值只是標記可以更改
//會以Dialog樣式顯示一個Activity , 我們可以在onActivityResult()方法去處理返回值
接下來為了保險起見先判斷裝置是否支援藍芽:
-
if(mBluetoothAdapter == null){
-
Toast.makeText(this,"本地藍芽不可用",Toast.LENGTH_SHORT).show();
-
finish(); //退出應用
-
}
確認支援藍芽的話就可以呼叫藍芽介面卡的方法了:
-
String Address = bluetoothAdapter.getAddress(); //獲取本機藍芽MAC地址
-
String Name = bluetoothAdapter.getName(); //獲取本機藍芽名稱
-
// 若藍芽沒開啟
-
if(!bluetoothAdapter.isEnabled()){
-
bluetoothAdapter.enable(); //開啟藍芽
-
}
上邊這些除了 開啟藍芽其他的並沒有什麼用處 這裡只是舉個例子
設定可以被搜尋到
-
//設定可以被搜尋到
-
//判斷藍芽介面卡的當前藍芽掃描模式
-
if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)
-
{
-
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
-
// 設定被發現時間,最大值是3600秒,0表示裝置總是可以被發現的(小於0或者大於3600則會被自動設定為120秒)
-
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
-
startActivity(discoverableIntent);
-
}
接下來開始搜尋附近:
判斷是否正在搜尋 如果是就停止搜尋之類的
-
if (!mBluetoothAdapter.isDiscovering()) {//判斷是否正在搜尋
-
mBluetoothAdapter.startDiscovery();//開始搜尋附近裝置
-
textView2.setText("正在搜尋...");
-
} else {
-
mBluetoothAdapter.cancelDiscovery();//停止搜尋
-
}
搜尋附近需要先註冊個廣播來監聽查詢附近藍芽裝置:
藍芽掃描時,掃描到任一遠端藍芽裝置時,會發送此廣播。兩種廣播用一個就行
註冊分為兩種:靜態註冊和動態註冊。
- 靜態註冊就是在AndroidManifest.xml檔案中定義,
- 註冊的廣播接收器必須繼承BroadReceiver 動態註冊就是在程式中使用Context.registerReceiver註冊。
動態廣播
-
//註冊廣播
-
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);//搜尋到裝置
-
registerReceiver(receiver, filter);
-
IntentFilter filter2 = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜尋結束
-
registerReceiver(receiver, filter2);
靜態廣播
-
<!-- 廣播接收 -->
-
<receiver android:name="包名.類名" >
-
<intent-filter android:priority="1000">
-
<action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED"/>
-
<action android:name="android.bluetooth.device.action.FOUND" />
-
</intent-filter>
-
</receiver>
new個BroadcastReceiver來接收:
廣播一旦傳送 這邊就會呼叫 onReceive()方法
-
BroadcastReceiver receiver = new BroadcastReceiver() {
-
@Override
-
public void onReceive(Context context, Intent intent) {
-
String action = intent.getAction();
-
if (action.equals(BluetoothDevice.ACTION_FOUND)) {
-
//獲取已配對藍芽裝置
-
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
-
for (BluetoothDevice bonddevice : devices) {
-
mPeiDuiList.add(bonddevice);
-
peiDuiAdapter.notifyDataSetChanged();
-
}
-
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-
//判斷未配對的裝置
-
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
-
mFuJinList.add(device);
-
fuJinAdapter.notifyDataSetChanged();
-
}
-
} else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
-
textView2.setText("搜尋完成...");
-
}
-
}
-
};
我這邊是建立了兩個集合 分別把已配對的和附近的儲存進去
-
private ArrayList<BluetoothDevice> mPeiDuiList = new ArrayList<>();
-
private ArrayList<BluetoothDevice> mFuJinList = new ArrayList<>();
接下來把獲取到的資料放入介面卡顯示 這步我就不寫了 大家都會
這裡用到了藍芽裝置BluetoothDevice
BluetoothDevice用於指代某個藍芽裝置,通常表示對方裝置
- getName:獲得該裝置的名稱;
- getAddress:獲得該裝置的地址;
- createRfcommSocketToServiceRecord:根據UUID建立並返回一個BluetoothSocket。
1 配對
兩個裝置建立連線以後,就可以進行一個配對的過程。
配對進行的時候,會產生一個金鑰用來加密與鑑別連線。一個典型的情況是,從機裝置會要求主機裝置提供一個密碼來完成一個配對過程。
這個密碼可以是固定的例如“000000”,也可以是提供給上層的隨機產生的一個值。當主機裝置傳送一個正確的密碼是,這兩個裝置就會相互交換金鑰來加密並鑑別連線。
2 繫結
很多情況下,兩個裝置會需要經常性的斷開連線,連線這樣的過程,BLE有一個安全功能,允許兩臺裝置配對的時候給對方一個長期的一套安全金鑰。
這種機制稱作為繫結。允許兩個裝置再次連線時不需要再次進行配對就能從新加密與鑑別,只要他們儲存了這個安全金鑰。
配對:
主要寫配對和通訊 繫結先不多做介紹
-
//點選附近的開始配對
-
mBluetoothAdapter.cancelDiscovery();//停止搜尋
-
String address = mFuJinList.get(position).getAddress();
-
Toast.makeText(MainActivity.this, mFuJinList.get(position).getName() + "配對中。。。", Toast.LENGTH_SHORT).show();
-
try {
-
//呼叫工具類與裝置配對
-
//已知自己的地址 點選獲取對方的地址 配對
-
remoteDevice = mBluetoothAdapter.getRemoteDevice(address);//通過mac地址獲取藍芽裝置
-
ClsUtils.createBond(remoteDevice.getClass(), remoteDevice);
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
這裡用到了一個配對工具類ClsUtils
-
package com.example.lin.mylanya.utils;
-
import java.lang.reflect.Field;
-
import java.lang.reflect.Method;
-
import android.bluetooth.BluetoothDevice;
-
import android.util.Log;
-
public class ClsUtils {
-
/**
-
* 與裝置配對 參考原始碼:platform/packages/apps/Settings.git
-
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
-
*/
-
static public boolean createBond(Class btClass,BluetoothDevice btDevice) throws Exception {
-
Method createBondMethod = btClass.getMethod("createBond");
-
Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
-
return returnValue.booleanValue();
-
}
-
/**
-
* 與裝置解除配對 參考原始碼:platform/packages/apps/Settings.git
-
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
-
*/
-
static public boolean removeBond(Class btClass,BluetoothDevice btDevice) throws Exception {
-
Method removeBondMethod = btClass.getMethod("removeBond");
-
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
-
return returnValue.booleanValue();
-
}
-
}
通訊:
通訊需要建立客戶端和伺服器來建立連線
通訊是耗時操作所以都用另建了一個類繼承Thread來寫了
先建立伺服器:
伺服器一般是在開啟藍芽後建立 這裡的ChatController是通訊的工具類 這個方法裡面呼叫了伺服器執行緒(AccepThread)的啟動方法
-
if (!mBluetoothAdapter.isEnabled()) {//判斷藍芽是否開啟
-
mBluetoothAdapter.enable();//開啟藍芽
-
}
-
ChatController.getInstance().waitingForFriends(mBluetoothAdapter, handler);//等待客戶端來連線
- 伺服器端建立套接字,等待客戶端連線,
- 呼叫BluetoothAdapter的listenUsingRfcommWithServiceRecord方法,產生一個BluetoothServerSocket物件,
- 然後呼叫BluetoothServerSocket物件的accept方法,
- 注意accept方法會產生阻塞,直到一個客戶端連線建立,所以伺服器端的socket的建立需要在一個子執行緒中去做,
AccepThread
-
package com.example.lin.mylanya.utils;
-
import android.bluetooth.BluetoothAdapter;
-
import android.bluetooth.BluetoothServerSocket;
-
import android.bluetooth.BluetoothSocket;
-
import android.os.Handler;
-
import com.example.lin.mylanya.Constant;
-
import java.io.IOException;
-
import java.util.UUID;
-
public class AccepThread extends Thread {
-
/** 連線的名稱*/
-
private static final String NAME = "BluetoothClass";
-
/** UUID*/
-
private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
-
/** 服務端藍芽Sokcet*/
-
private final BluetoothServerSocket mmServerSocket;
-
private final BluetoothAdapter mBluetoothAdapter;
-
/** 執行緒中通訊的更新UI的Handler*/
-
private final Handler mHandler;
-
/** 監聽到有客戶端連線,新建一個執行緒單獨處理,不然在此執行緒中會堵塞*/
-
private ConnectedThread mConnectedThread;
-
public AccepThread(BluetoothAdapter adapter, Handler handler) throws IOException {
-
mBluetoothAdapter = adapter;
-
this.mHandler = handler;
-
// 獲取服務端藍芽socket
-
mmServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
-
}
-
@Override
-
public void run() {
-
super.run();
-
// 連線的客戶端soacket
-
BluetoothSocket socket = null;
-
// 服務端是不退出的,要一直監聽連線進來的客戶端,所以是死迴圈
-
while (true){
-
try {
-
// 獲取連線的客戶端socket
-
socket = mmServerSocket.accept();
-
} catch (IOException e) {
-
// 通知主執行緒更新UI, 獲取異常
-
mHandler.sendEmptyMessage(Constant.MSG_ERROR);
-
e.printStackTrace();
-
// 服務端退出一直監聽執行緒
-
break;
-
}
-
if(socket != null) {
-
// 管理連線的客戶端socket
-
manageConnectSocket(socket);
-
}
-
}
-
}
-
/**
-
* 管理連線的客戶端socket
-
* @param socket
-
*/
-
private void manageConnectSocket(BluetoothSocket socket) {
-
// 只支援同時處理一個連線
-
// mConnectedThread不為空,踢掉之前的客戶端
-
if(mConnectedThread != null) {
-
mConnectedThread.cancle();
-
}
-
// 新建一個執行緒,處理客戶端發來的資料
-
mConnectedThread = new ConnectedThread(socket, mHandler);
-
mConnectedThread.start();
-
}
-
/**
-
* 斷開服務端,結束監聽
-
*/
-
public void cancle() {
-
try {
-
mmServerSocket.close();
-
mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
/**
-
* 傳送資料
-
* @param data
-
*/
-
public void sendData(byte[] data){
-
if(mConnectedThread != null) {
-
mConnectedThread.write(data);
-
}
-
}
-
}
藍芽伺服器套接字BluetoothServiceSocket
- BluetoothServiceSocket是服務端的Socket,用來接收客戶端的Socket連線請求
- accept:監聽外部的藍芽連線請求;
- close:關閉服務端的藍芽監聽。
-
當遠端裝置連線到了的時候,Blueboothserversocket 類將會返回一個 bluetoothsocket。
客戶端:
客戶端是在點選連線時呼叫 同上 是不過是呼叫了客戶端執行緒的啟動方法
-
//點選連線
-
String address = mPeiDuiList.get(position).getAddress();
-
String name = mPeiDuiList.get(position).getName();
-
Toast.makeText(MainActivity.this, name + "開始連線 請稍後。。。", Toast.LENGTH_SHORT).show();
-
remoteDevice = mBluetoothAdapter.getRemoteDevice(address);
-
ChatController.getInstance().startChatWith(remoteDevice, mBluetoothAdapter, handler);//與伺服器連線進行聊天 也就是客戶端連線服務端
- 客戶端去連線伺服器端,需要先持有伺服器端的BluetoothDevice物件,
- 先呼叫BluetoothDevice的createRfcommSocketToServiceRecord方法,這個方法會產生一個客戶端的BluetoothSocket物件,
- 然後呼叫該物件的connect方法,該過程最好也是單獨起一個執行緒去做
- 建立客戶端需要一個UUID
-
package com.example.lin.mylanya.utils;
-
import android.bluetooth.BluetoothAdapter;
-
import android.bluetooth.BluetoothDevice;
-
import android.bluetooth.BluetoothSocket;
-
import android.os.Handler;
-
import com.example.lin.mylanya.Constant;
-
import com.example.lin.mylanya.utils.ConnectedThread;
-
import java.io.IOException;
-
import java.util.UUID;
-
public class ConnectThread extends Thread{
-
private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
-
/** 客戶端socket*/
-
private final BluetoothSocket mmSoket;
-
/** 要連線的裝置*/
-
private final BluetoothDevice mmDevice;
-
private BluetoothAdapter mBluetoothAdapter;
-
/** 主執行緒通訊的Handler*/
-
private final Handler mHandler;
-
/** 傳送和接收資料的處理類*/
-
private ConnectedThread mConnectedThread;
-
public ConnectThread(BluetoothDevice device, BluetoothAdapter bluetoothAdapter, Handler mUIhandler) {
-
mmDevice = device;
-
mBluetoothAdapter = bluetoothAdapter;
-
mHandler = mUIhandler;
-
BluetoothSocket tmp = null;
-
try {
-
// 建立客戶端Socket
-
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
mmSoket = tmp;
-
}
-
@Override
-
public void run() {
-
super.run();
-
// 關閉正在發現裝置.(如果此時又在查詢裝置,又在傳送資料,會有衝突,影響傳輸效率)
-
mBluetoothAdapter.cancelDiscovery();
-
try {
-
// 連線伺服器
-
mmSoket.connect();
-
} catch (IOException e) {
-
// 連線異常就關閉
-
try {
-
mmSoket.close();
-
} catch (IOException e1) {
-
}
-
return;
-
}
-
manageConnectedSocket(mmSoket);
-
}
-
private void manageConnectedSocket(BluetoothSocket mmSoket) {
-
// 新建一個執行緒進行通訊,不然會發現執行緒堵塞
-
mConnectedThread = new ConnectedThread(mmSoket,mHandler);
-
mConnectedThread.start();
-
}
-
/**
-
* 關閉當前客戶端
-
*/
-
public void cancle() {
-
try {
-
mmSoket.close();
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
/**
-
* 傳送資料
-
* @param data
-
*/
-
public void sendData(byte[] data) {
-
if(mConnectedThread != null) {
-
mConnectedThread.write(data);
-
}
-
}
-
}
藍芽客戶端套接字BluetoothSocket
- BluetoothSocket是客戶端的Socket,用於與對方裝置進行資料通訊。
- connect:建立藍芽的socket連線;
- close:關閉藍芽的socket連線;
- getInputStream:獲取socket連線的輸入流物件;
- getOutputStream:獲取socket連線的輸出流物件;
- getRemoteDevice:獲取遠端裝置資訊。
- 伺服器和客戶端連線上後都需要新建一個執行緒進行通訊 不然會執行緒阻塞
- 傳送資料需要呼叫BluetoothSocket的getOutputStream(),接收資料需要呼叫getInputStream()方法
String uuid = java.util.UUID.randomUUID().toString();
- 一般在建立Socket時需要UUID作為埠的唯一性,如果兩臺Android裝置互聯,則沒有什麼特殊的,
- 如果讓非Android的藍芽裝置連線Android藍芽裝置,則UUID必須使用某個固定保留的UUID
- Android中建立UUID:
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
-
package com.example.lin.mylanya.utils;
-
import android.bluetooth.BluetoothSocket;
-
import android.os.Handler;
-
import android.os.Message;
-
import android.util.Log;
-
import com.example.lin.mylanya.Constant;
-
import java.io.IOException;
-
import java.io.InputStream;
-
import java.io.OutputStream;
-
public class ConnectedThread extends Thread{
-
/** 當前連線的客戶端BluetoothSocket*/
-
private final BluetoothSocket mmSokcet;
-
/** 讀取資料流*/
-
private final InputStream mmInputStream;
-
/** 傳送資料流*/
-
private final OutputStream mmOutputStream;
-
/** 與主執行緒通訊Handler*/
-
private Handler mHandler;
-
private String TAG = "ConnectedThread";
-
public ConnectedThread(BluetoothSocket socket,Handler handler) {
-
mmSokcet = socket;
-
mHandler = handler;
-
InputStream tmpIn = null;
-
OutputStream tmpOut = null;
-
try {
-
tmpIn = socket.getInputStream();
-
tmpOut = socket.getOutputStream();
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
mmInputStream = tmpIn;
-
mmOutputStream = tmpOut;
-
}
-
@Override
-
public void run() {
-
super.run();
-
byte[] buffer = new byte[1024];
-
while (true) {
-
try {
-
// 讀取資料
-
int bytes = mmInputStream.read(buffer);
-
if(bytes > 0) {
-
String data = new String(buffer,0,bytes,"utf-8");
-
// 把資料傳送到主執行緒, 此處還可以用廣播
-
Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA,data);
-
mHandler.sendMessage(message);
-
}
-
Log.d(TAG, "messge size :" + bytes);
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
// 踢掉當前客戶端
-
public void cancle() {
-
try {
-
mmSokcet.close();
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
/**
-
* 服務端傳送資料
-
* @param data
-
*/
-
public void write(byte[] data) {
-
try {
-
mmOutputStream.write(data);
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
ChatController
-
package com.example.lin.mylanya.utils;
-
import android.bluetooth.BluetoothAdapter;
-
import android.bluetooth.BluetoothDevice;
-
import android.os.Handler;
-
import java.io.IOException;
-
import java.io.UnsupportedEncodingException;
-
import java.net.ProtocolFamily;
-
public class ChatController {
-
/**
-
* 客戶端的執行緒
-
*/
-
private ConnectThread mConnectThread;
-
/**
-
* 服務端的執行緒
-
*/
-
private AccepThread mAccepThread;
-
private ChatProtocol mProtocol = new ChatProtocol();
-
/**
-
* 網路協議的處理函式
-
*/
-
private class ChatProtocol {
-
private static final String CHARSET_NAME = "utf-8";
-
/**
-
* 封包(傳送資料)
-
* 把傳送的資料變成 陣列 2進位制流
-
*/
-
public byte[] encodePackge(String data) {
-
if (data == null) {
-
return new byte[0];
-
} else {
-
try {
-
return data.getBytes(CHARSET_NAME);
-
} catch (UnsupportedEncodingException e) {
-
e.printStackTrace();
-
return new byte[0];
-
}
-
}
-
}
-
/**
-
* 解包(接收處理資料)
-
* 把網路上資料變成自己想要的資料體
-
*/
-
public String decodePackage(byte[] netData) {
-
if (netData == null) {
-
return "";
-
} else {
-
try {
-
return new String(netData, CHARSET_NAME);
-
} catch (UnsupportedEncodingException e) {
-
e.printStackTrace();
-
return "";
-
}
-
}
-
}
-
}
-
/**
-
* 與伺服器連線進行聊天
-
*/
-
public void startChatWith(BluetoothDevice device, BluetoothAdapter adapter, Handler handler) {
-
mConnectThread = new ConnectThread(device, adapter, handler);
-
mConnectThread.start();
-
}
-
/**
-
* 等待客戶端來連線
-
* handler : 用來跟主執行緒通訊,更新UI用的
-
*/
-
public void waitingForFriends(BluetoothAdapter adapter, Handler handler) {
-
try {
-
mAccepThread = new AccepThread(adapter, handler);
-
mAccepThread.start();
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
/**
-
* 發出訊息
-
*/
-
public void sendMessage(String msg) {
-
// 封包
-
byte[] data = mProtocol.encodePackge(msg);
-
if (mConnectThread != null) {
-
mConnectThread.sendData(data);
-
} else if (mAccepThread != null) {
-
mAccepThread.sendData(data);
-
}
-
}
-
/**
-
* 網路資料解碼
-
*/
-
public String decodeMessage(byte[] data){
-
return mProtocol.decodePackage(data);
-
}
-
/**
-
* 停止聊天
-
*/
-
public void stopChart(){
-
if(mConnectThread != null) {
-
mConnectThread.cancle();
-
}
-
if(mAccepThread != null) {
-
mAccepThread.cancle();
-
}
-
}
-
/**
-
* 以下是單例寫法
-
*/
-
private static class ChatControlHolder{
-
private static ChatController mInstance = new ChatController();
-
}
-
public static ChatController getInstance(){
-
return ChatControlHolder.mInstance;
-
}
-
}
傳送資料
-
//客戶端向伺服器傳送資料
-
String s = mEdit.getText().toString();
-
ChatController.getInstance().sendMessage(s);//發出訊息
handler接收資料
-
private Handler handler = new Handler() {
-
@Override
-
public void handleMessage(Message msg) {
-
super.handleMessage(msg);
-
switch (msg.what) {
-
case MSG_GOT_DATA:
-
//接收訊息
-
String data = (String) msg.obj;
-
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
-
break;
-
}
-
}
-
};
最後銷燬:
-
@Override
-
protected void onDestroy() {
-
super.onDestroy();
-
unregisterReceiver(receiver);//關閉服務
-
mBluetoothAdapter.disable();//關閉藍芽
-
ChatController.getInstance().stopChart();//停止聊天
-
}