編輯:關於Android編程
之前兩篇都是在說與手機的連接,連接方法,和主動配對連接,都是手機與手機的操作,做起來還是沒問題的,但是最終的目的是與單片機的藍牙模塊的通信。
下面是到目前為止嘗試的與單片機的通信方法,沒有成功,但是從思路上來說沒有問題,最大的問題是與單片機配對的時候,單片機的藍牙模塊的PIN配對碼是寫死的,固定為1234,
而手機這邊連接配對都是自動生成的PIN配對碼,這種方式在手機與手機配對的時候是極為方便的,但是在這裡與單片機連接卻成了最大的問題,因為手機自動生成而且每次都不一樣,所以沒法與單片機藍牙模塊的1234相同也就沒法陪對了。下面只是介紹的到目前為止我們的大題思路,具體代碼很多,而且涉及到項目也就沒有貼。
如果關於上面的問題哪位同學有思路或者做過類似的項目還請指點。
首先,如何開啟藍牙設備和設置可見時間:
private void search() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (!adapter.isEnabled()) {
adapter.enable();
}
Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
enable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3600); //3600為藍牙設備可見時間
startActivity(enable);
Intent searchIntent = new Intent(this, ComminuteActivity.class);
startActivity(searchIntent);
}
正式開始與藍牙模塊進行通信
public class ComminuteActivity extends Activity {
private BluetoothReceiver receiver;
private BluetoothAdapter bluetoothAdapter;
private List devices;
private List deviceList;
private Bluetooth client;
private final String lockName = "YESYOU";
private String message = "000001";
private ListView listView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_layout);
listView = (ListView) this.findViewById(R.id.list);
deviceList = new ArrayList();
devices = new ArrayList();
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
bluetoothAdapter.startDiscovery();
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
receiver = new BluetoothReceiver();
registerReceiver(receiver, filter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
setContentView(R.layout.connect_layout);
BluetoothDevice device = deviceList.get(position);
client = new Bluetooth(device, handler);
try {
client.connect(message);
} catch (Exception e) {
Log.e("TAG", e.toString());
}
}
});
}
@Override
protected void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
private final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Bluetooth.CONNECT_FAILED:
Toast.makeText(ComminuteActivity.this, "連接失敗", Toast.LENGTH_LONG).show();
try {
client.connect(message);
} catch (Exception e) {
Log.e("TAG", e.toString());
}
break;
case Bluetooth.CONNECT_SUCCESS:
Toast.makeText(ComminuteActivity.this, "連接成功", Toast.LENGTH_LONG).show();
break;
case Bluetooth.READ_FAILED:
Toast.makeText(ComminuteActivity.this, "讀取失敗", Toast.LENGTH_LONG).show();
break;
case Bluetooth.WRITE_FAILED:
Toast.makeText(ComminuteActivity.this, "寫入失敗", Toast.LENGTH_LONG).show();
break;
case Bluetooth.DATA:
Toast.makeText(ComminuteActivity.this, msg.arg1 + "", Toast.LENGTH_LONG).show();
break;
}
}
};
private class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (isLock(device)) {
devices.add(device.getName());
}
deviceList.add(device);
}
showDevices();
}
}
private boolean isLock(BluetoothDevice device) {
boolean isLockName = (device.getName()).equals(lockName);
boolean isSingleDevice = devices.indexOf(device.getName()) == -1;
return isLockName && isSingleDevice;
}
private void showDevices() {
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,
devices);
listView.setAdapter(adapter);
}
}
這裡需要提一下的是,startDiscovery()這個方法和它的返回值,它是一個異步方法,會對其他藍牙設備進行搜索,持續時間為12秒。
搜索過程其實是在System Service中進行,我們可以通過cancelDiscovery()方法來停止這個搜索。在系統搜索藍牙設備的過程中,系統可能會發送以下三個廣播:ACTION_DISCOVERY_START(開始搜索),
ACTION_DISCOVERY_FINISHED(搜索結束)
和ACTION_FOUND(找到設備)。
ACTION_FOUND這個才是我們想要的,這個Intent中包含兩個extra fields: EXTRA_DEVICE和EXTRA_CLASS,
包含的分別是BluetoothDevice和BluetoothClass,
EXTRA_DEVICE中的BluetoothDevice就是我們搜索到的設備對象,從中獲得設備的名稱和地址。
而EXTRA_CLASS中的BluetoothClass是搜索到的設備的類型,比如搜索到的是手機還是耳機或者其他,之後我會寫一篇關於它的介紹。
在這個上面我現在在想,是否通過判斷搜索到的設備類型來識別單片機藍牙模塊與手機藍牙的不同,采取不一樣的配對方式,從而不自動生成配對碼。不知是否可行,一會嘗試。
搜索到該設備後,我們就要對該設備進行連接和通信。
public void connect(final String message) {
Thread thread = new Thread(new Runnable() {
public void run() {
BluetoothSocket tmp = null;
Method method;
try {
method = device.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
tmp = (BluetoothSocket) method.invoke(device, 1);
} catch (Exception e) {
setState(CONNECT_FAILED);
Log.e("TAG", e.toString());
}
socket = tmp;
try {
socket.connect();
isConnect = true;
} catch (Exception e) {
setState(CONNECT_FAILED);
Log.e("TAG", e.toString());
}
if (isConnect) {
try {
OutputStream outStream = socket.getOutputStream();
outStream.write(getHexBytes(message));
} catch (IOException e) {
setState(WRITE_FAILED);
Log.e("TAG", e.toString());
}
try {
InputStream inputStream = socket.getInputStream();
int data;
while (true) {
try {
data = inputStream.read();
Message msg = handler.obtainMessage();
msg.what = DATA;
msg.arg1 = data;
handler.sendMessage(msg);
} catch (IOException e) {
setState(READ_FAILED);
Log.e("TAG", e.toString());
break;
}
}
} catch (IOException e) {
setState(WRITE_FAILED);
Log.e("TAG", e.toString());
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
Log.e("TAG", e.toString());
}
}
}
}
這裡包括寫入和讀取,用法和基本的Socket是一樣的,但是寫入的時候,需要將字符串轉化為16進制:
private byte[] getHexBytes(String message) {
int len = message.length() / 2;
char[] chars = message.toCharArray();
String[] hexStr = new String[len];
byte[] bytes = new byte[len];
for (int i = 0, j = 0; j < len; i += 2, j++) {
hexStr[j] = "" + chars[i] + chars[i + 1];
bytes[j] = (byte) Integer.parseInt(hexStr[j], 16);
}
return bytes;
}
連接設備之前需要UUID,所謂的UUID,就是用來進行配對的,全稱是Universally Unique Identifier,是一個128位的字符串ID,用於進行唯一標識。網上的例子,包括谷歌的例子提供的uuid,通用的"00001101-0000-1000-8000-00805F9B34FB"也試過了,在配對的時候都是自動生成了配對碼,也無法正常與單片機的藍牙模塊連接,所以,我就利用反射的原理,讓設備自己提供UUID嘗試。到這裡其實我有點懷疑自己對於UUID的理解是否正確了。
在谷歌提供的例子中,我們可以看到谷歌的程序員的程序水平很高,一些好的編碼習慣我們可以學習一下,像是在try..catch中才定義的變量,我們應該在try...catch之前聲明一個臨時變量,然後再在try...catch後賦值給我們真正要使用的變量。這種做法的好處就是:如果我們直接就是使用真正的變量,當出現異常的時候,該變量的使用就會出現問題,而且很難進行排查,如果是臨時變量,我麼可以通過檢查變量的值來確定是否是賦值時出錯。
關於Android MVP模式的思考
最近經常看到各種介紹MVP模式的博客的,之前寫過不少的Android應用,在做那些應用的時候,都是要求快速完成,所以從開始設計到寫代碼就一直考慮著重用。以前寫的項目基本都
全面總結Android中線程的異步處理方式
一、概述Handler 、 Looper 、Message 這三者都與Android異步消息處理線程相關的概念。那麼什麼叫異步消息處理線程呢?異步消息處理線程啟動後會進入
微信第三方登錄Android實現代碼
記錄一下微信第三方實現登錄的方法。還是比較簡單。一、必要的准備工作1.首先需要注冊並被審核通過的微信開放平台帳號,然後創建一個移動應用,也需要被審核;2.然後到資源中心下
android 三種定位方式
android 三種定位方式 最近在看android關於定位的方式,查了很多資料,也做了相關實驗,在手機上做了測試,下面總結: 一共有三種定位方式,一種是GPS,一種