1 什麼是智能藍牙防丟器java
所謂智能藍牙(Smart Bluetooth)防丟器,是採用藍牙技術專門爲智能手機設計的防丟器。其工做原理主要是經過距離變化來判斷物品是否還控制在你的安全範圍。主要適用於手機、錢包、鑰匙、行李等貴重物品的防丟,也可用於防止兒童或寵物的走失 。[請看正版請百度:beautifulzzzz(看樓主博客園官方博客,享高質量生活)嘻嘻!!!]android
圖 1-1 藍牙防丟器應用領域算法
2 藍牙防丟器的主要構造編程
目前比較成熟的產品通常是採用藍牙4.0技術,具備低功耗、雙向防丟、自動報警等優勢。雖然市場上該類產品種類繁多、層出不窮,但其核心構成通常包括:藍牙4.0芯片、藍牙芯片輔助電路、藍牙天線、蜂鳴器、開關、電源等。canvas
圖 2-1 藍牙防丟器構成數組
3 藍牙模塊的選擇安全
因爲這是第一個智能硬件DIY篇,樓主可不想一會兒把你們給嚇倒了。因此這裏咱們先用一個相對簡單但經常使用的藍牙模塊HC-05/HC-06進行DIY 。以下圖該模塊把天線、濾波電路、Soc、晶振都集成在了一塊兒,當咱們用的時候只要關注一、二、十二、1三、2四、26這幾個引腳就能實現比較完整的藍牙通訊功能,這樣就爲咱們製做藍牙防丟器節省了不少挑選元件、設計電路、焊接制板的功夫,是否是超讚呀?網絡
圖 3-1 藍牙模塊1app
其實還有更讚的呢!因爲考慮到不少讀者在硬件方面還都是新手,初次拿到郵票邊緣式引腳的模塊會焊接很差,因而樓主又找到了一款封裝更好的藍牙模塊(其實就是把上面的模塊加一個託,而後將VCC\GND\TXD\RXD四個關鍵的引腳引出)。當咱們只是想把藍牙模塊做爲標籤時,只要在VCC和GND之間給它加上相應的電壓就好了;當想用它進行無線數據傳輸時,這時TXD和RXD兩個引腳就起做用了。異步
圖 3-2 藍牙模塊2
4 開始製做一個簡易的藍牙防丟器
上面說了這麼多了,那麼咱們的藍牙防丟器的設計方案究竟是什麼樣的呢?簡單起見,咱麼僅僅實現經過距離變化來判斷物品是否還控制在你的安全範圍內的具備核心功能的防丟器,對於節能功能、雙向報警功能甚至是自拍功能我們就先不考慮了。哈哈,可能說到這裏你們仍是對我們要作的防丟器一點想法都沒有,其實經過上面的鋪墊樓主有信心你們能夠在一分鐘以內知道怎麼完成它!
圖 4-1 簡易藍牙防丟器
相信不少看完上面圖片同窗會恍然大悟——不是嘛,只要將藍牙模塊接上相應的電源模塊就可以作成一個簡單的能夠發出藍牙信號的防丟器了!對的,因爲咱們沒有加入複雜的通訊功能,因此咱們僅僅把藍牙模塊通上電作成一個藍牙標籤就能夠了。可是你們不要高興太早,雖然是第一個DIY,樓主也不會這麼水吧~(沒發現上面圖片中手機屏幕裏的應用仍是一片空白嗎?哈哈)。
5 如何找到並學習要用到的API
上面製做藍牙防丟器的硬件部分讓你們以爲沒什麼挑戰性,那麼接下來的東西可就有必定難度了!記得樓主當時學安卓藍牙開發的時候費了好大力氣的。這裏樓主強烈建議你們着手瞭解安卓某個功能的應用時最好去安卓開發者社區,可是隨着Google被屏Android Developer也不能被訪問了。雖然樓主在MIT網站上又找到了一個相似的網頁,可是訪問速度不是那麼流暢,爲了方便,LZ挑出了和本節相關的知識幫助你們理解和運用。
圖 5-1 Android Developer頁面
6 安卓藍牙編程小知識
安卓平臺支持藍牙協議棧,容許一臺設備和其餘設備進行無線數據交換,當你們想使用藍牙時能夠經過調用相應的API實現功能。這些API提供的主要功能有:
△ 掃描搜索其餘藍牙設備(Scan for other Bluetooth devices)
△ 查詢本地藍牙適配器配對的藍牙設備(Query the local Bluetooth adapter for paired Bluetooth devices)
△ 創建RFCOMM通道(Establish RFCOMM channels)
△ 鏈接其餘設備(Connect to other devices through service discovery)
△ 與其餘設備進行數據交換(Transfer data to and from other devices)
△ 管理多組鏈接(Manage multiple connections)
當準備在應用程序中使用藍牙時,首先須要在manifest文件中包含BLUETOOTH和BLUETOOTH_ADMIN權限:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
接着涉及藍牙的操做主要有:一、開啓藍牙 二、關閉藍牙 三、能被搜到 四、獲取配對設備 五、傳輸數據。因爲本節只涉及到搜索周邊設備,因此配對並傳輸數據暫時不介紹。
7 安卓藍牙編程主要操做
要想經過編程操做藍牙設備,首先咱們得了解一下有可供咱們使用的相關類及其成員函數有哪些,以下圖:藍牙設備包括本地設備和遠程設備,本地設備對應的類爲BluetoothAdapter,遠程設備對應的類爲BluetoothDevice,二者的成員函數基本相同,樓主將在接下來詳細講解。
圖 7-1 藍牙設備相關函數
7_1 打開本地藍牙設備
咱們要作帶有藍牙的應用,首先要保證用戶的本地藍牙設備是打開的,不然什麼都無論直接使用搜索、鏈接、通訊等函數確定會獲得和預期不同的效果。可是有時候用戶爲了節省電池電量會把藍牙關閉,那咱們該怎麼辦呢?
其實,遇到這種狀況你們也不用擔憂!請看下面的解決方案:其中第1行調用BluetoothAdapter的getDefaultAdapter()方法得到本地藍牙設備,接着調用isEnabled()判斷本地藍牙設備是否被打開,若是被打開了就執行接下來的操做,不然能夠發送Intent消息,請求打開本地藍牙設備,接着待用戶打開藍牙後再進入接下來的操做。
1 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 2 if (!mBluetoothAdapter.isEnabled()) { 3 Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 4 startActivityForResult(enableIntent, REQUEST_ENABLE_BT); 5 } else { 6 nextOperation(); 7 }
這時候有些同窗可能要吐槽樓主了「你上面發送、請求、接着、而後說的挺輕巧,我怎麼知道我發送完Intent消息後系統到底幹了什麼?用戶又如何打開藍牙的?應用程序又在哪裏等待用戶完成打開藍牙事件,而後在哪裏執行nextOperation()函數的?」哈哈,樓主知道你們動手心切啦!下面將給你們詳細介紹這一過程。
想解答你們的這些問題還得看上面代碼:其中第4行startActIvityForResult會啓動一個系統Preference Activity並將ACTION_REQUEST_ENABLE靜態常量做爲其動做字符串,獲得的Preference Activity以下圖:
該Activity提醒用戶是否授予權限打開本地藍牙設備,當用戶點擊「是」或者「否」的時候,該Activity將會關閉。咱們可使用onActivityResult處理程序中返回的結果代碼參數來肯定是否操做成功。
正如上面介紹,當用戶點擊「是」授予藍牙權限請求後,確認請求的Activity會關閉,在下面的函數中將會收到此操做的消息,這樣咱們就能很瀟灑的在第4行安全的進入接下來的操做了。
1 protected void onActivityResult(int requestCode,int resultCode,Intent data){ 2 if(requestCode==ENABLE_BLUETOOTH){ 3 if(resultCode==RESULT_OK){ 4 nextOperation(); 5 } 6 } 7 }
7_2 搜索周邊藍牙設備
上面解決了藍牙設備打開問題,接下來咱們就要嘗試調用相關函數搜索周邊的藍牙設備,這樣咱們就能知道咱們製做的藍牙防丟器是否在咱們周邊了(是否是很興奮呢?)。
這裏咱們要用到BluetoothAdapter的成員函數startDiscovery,該方法能夠執行一個異步方式得到周邊藍牙設備,由於是一個異步的方法因此咱們不須要考慮線程被阻塞問題,整個過程大約須要12秒時間。這時咱們能夠註冊一個BroadcastReceiver 對象來接收查找到的藍牙設備信息,咱們經過Filter來過濾ACTION_FOUND這個Intent動做以獲取每一個遠程設備的詳細信息,過濾ACTION_DISCOVERY_FINISHED這個Intent動做以獲取整個藍牙搜索過程結束的信息。
1 // Register for broadcasts when a device is discovered 2 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 3 this.registerReceiver(mReceiver, filter); 4 // Register for broadcasts when discovery has finished 5 filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 6 this.registerReceiver(mReceiver, filter);
上面講的可能有點專業,下面俺用一種簡單非專業的方式向你們介紹一下:每次咱們想編程搜索周邊的藍牙設備時,只要簡單調用BluetoothAdapter的成員函數startDiscovery就能夠了。所謂的異步方法你們能夠這樣理解——start方法就好像一個總司令告訴情報搜查員去搜查情報,而後本身繼續作本身的事,情報員去收集各類情報;這裏的filter就好像總司令告訴情報搜查員,我只要ACTION_FOUND和ACTION_DISCOVERY_FINISHED信息;那麼這裏的mReceiver就是總司令指定的情報搜查員了。
接下來就讓我們神奇的情報搜查員登場!以下,相信你們一看就明白了,我們的情報搜查員會一絲不苟地將總司令下達的任務執行。這樣當他收到FOUND動做信息時就用第6~8行的方法將每一個發現的藍牙設備的名字和地址存儲進一個Vector中,這樣等本身完成任務時,就能告訴總司令任務完成,全部周邊藍牙情報都在xxx向量中(嘻嘻,多麼讓領導喜歡的員工呀!)。
1 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 2 @Override 3 public void onReceive(Context context, Intent intent) { 4 String action = intent.getAction(); 5 if (BluetoothDevice.ACTION_FOUND.equals(action)) { 6 BluetoothDevice device = 7 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 8 mDevicesVector.add(device.getName() + "\n" + device.getAddress()); 9 } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { 10 //... 11 } 12 } 13 };
7_3 獲取藍牙設備信號強度
到目前想必不少人已經可以一鼓作氣本身的簡單藍牙防丟器了,由於你們已經掌握了硬件部分的設計方法、擁有軟件藍牙開發打開和搜索周邊藍牙的編程技巧。可是若是心急的同窗只用前面介紹的一點小知識的話,確定設計的小東西很不盡人意(本身都很差意思給本身及格)。是否是出現了藍牙防丟器放到很遠很遠時應用程序纔會報告東西已丟?若是是這樣請耐心地看完下面的介紹(也許能給你更多的啓發(⊙o⊙)哦)。
想必你們也想到了問題所在——距離沒控制好!若是咱們可以知道藍牙防丟器距離咱們手機的距離那就行了,這樣咱們就能設定一個範圍值,當它超出這個範圍時手機就發出丟失警告,這樣就不會出現防丟器要放很遠才能被手機察覺已丟失的問題了。要解決這個問題就要向你們介紹一下無線傳感器網絡中的RSSI了!
RSSI全名Received Signal Strength Indication,翻譯過來是接收的信號強度指示。在咱們的經驗中通常離得越近信號越強,因此經過這個RSSI可以大體估計接收點與發射點之間的距離。而在無線傳感器網絡中常常會設置多個固定的發射源(也是所謂的標籤),而後根據必定的算法來肯定移動目標的空間位置信息。例以下圖左分別在A、B、C三點放置三個發射功率相同的信號源(標籤),這樣移動點D經過收集A、B、C三點的RSSI並估計距離rA、rB、rC,因爲這個距離並非徹底精準,因此三個圓並不必定交於一點,大多數狀況是下面交於一個公共區域的狀況,而移動點D的當前位置頗有可能在該區域。
圖 7-2 藍牙標籤訂位
咱們這裏只要簡單地用到經過RSSI估計手機和防丟器之間的距離就好了,上面的定位技術相信你們也能觸類旁通輕鬆搞定(\(^o^)/~其實沒那麼簡單,哈哈)。那麼在安卓編程時如何得到RSSI呢?其實並不難,咱們能夠利用和獲取周邊藍牙設備的名稱和地址相似的方法獲取RSSI:
1 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2 mDevicesVector.add(device.getName() + "\n" + device.getAddress()); 3 short rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI); 4 mRSSIVector.add(rssi);
8 着手開發咱們的APP
1) 使用Eclipse建立一個安卓項目,命名爲first_test,並將Activity Name命名爲UI_Main,Layout Name命名爲ui_main(如圖8-1所示)。
圖8-1 新建安卓項目
2) 展開res文件夾下的layout文件夾,雙擊ui_main.xml文件,選擇xml編輯模式,並將其代碼改成:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <SurfaceView 8 android:id="@+id/surfaceView" 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:layout_weight="0.75" /> 12 13 <Button 14 android:id="@+id/button_add" 15 android:layout_width="match_parent" 16 android:layout_height="wrap_content" 17 android:text="添加藍牙防丟器" /> 18 19 <Button 20 android:id="@+id/button_start" 21 android:layout_width="match_parent" 22 android:layout_height="wrap_content" 23 android:text="開始防丟" /> 24 25 </LinearLayout>
操做說明:這裏修改的ui_main.xml爲應用的主界面(如圖8-2所示)。該頁面採用LinearLayout佈局模式,從上到下依次爲用於動態顯示藍牙信號強弱的SurfaceView控件、用於添加防丟設備的按鈕和用於開始防丟控制的按鈕。
圖 8-2 ui_main.xml效果
3) 右擊layout,依次選擇New|Android XML File新建安卓XML文件。
4) 將新建的Android XML File命名爲ui_list,點擊Finish按鈕。接着同第二步將新建的ui_list.xml修改成:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <ListView 8 android:id="@+id/listView1" 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:layout_weight="1" > 12 </ListView> 13 14 <Button 15 android:id="@+id/button_search" 16 android:layout_width="match_parent" 17 android:layout_height="wrap_content" 18 android:text="search" /> 19 20 <Button 21 android:id="@+id/button_ok" 22 android:layout_width="match_parent" 23 android:layout_height="wrap_content" 24 android:text="OK" /> 25 26 </LinearLayout>
操做說明:這裏新建的ui_list.xml爲搜索並添加藍牙防丟器界面。該界面一樣採用LinearLayout佈局模式,包含一個用於開始搜索的按鈕、一個用於列出搜索結果的list和一個用於確認返回的OK按鈕。
5) 右擊src文件夾下的包名,依次選擇New|Class命令(如圖8-3所示)
圖 8-3 新建類
6) 新建類文件命名爲My_BTS,點擊Finish按鈕。
7) 將My_BTS.java文件修改成:
1 package com.example.first_test; 2 3 import java.util.Vector; 4 5 public class My_BTS { 6 public String mName; 7 public String mAddr; 8 public Vector<Short> mRSSIVector; 9 10 public My_BTS() { 11 mName = new String(); 12 mAddr = new String(); 13 mRSSIVector = new Vector<Short>(); 14 } 15 16 public My_BTS(String name, String addr) { 17 mName = name; 18 mAddr = addr; 19 mRSSIVector = new Vector<Short>(); 20 } 21 }
操做說明:該類表示藍牙防丟器。其中mName和mAddr分別表示藍牙防丟器的名字和地址;mSSIVector用來存放一段時間檢測到該藍牙防丟器的RSSI值(之因此保留多組數據,是方便從此你們擴展)。
8) 採用一樣的方法新建一個Func_Draw.java文件,並將文件修改成:
1 package com.example.first_test; 2 3 import java.util.Vector; 4 5 import android.graphics.Canvas; 6 import android.graphics.Color; 7 import android.graphics.Paint; 8 import android.graphics.Paint.Style; 9 import android.view.SurfaceHolder; 10 11 public class Func_Draw { 12 private static Vector<Paint> mPaint = new Vector<Paint>(); 13 public static Integer times = 0;// 防丟搜索次數 14 public static float Bei = 200;// 繪製圖形時放大倍數 15 16 public static void initPaint() { 17 Paint paint0 = new Paint(); 18 paint0.setAntiAlias(true); 19 paint0.setStyle(Style.STROKE); 20 paint0.setColor(Color.RED); 21 mPaint.add(paint0); 22 Paint paint1 = new Paint(); 23 paint1.setAntiAlias(true); 24 paint1.setStyle(Style.STROKE); 25 paint1.setColor(Color.GREEN); 26 mPaint.add(paint1); 27 Paint paint2 = new Paint(); 28 paint2.setAntiAlias(true); 29 paint2.setStyle(Style.STROKE); 30 paint2.setColor(Color.BLUE); 31 mPaint.add(paint2); 32 Paint paint3 = new Paint(); 33 paint3.setAntiAlias(true); 34 paint3.setStyle(Style.STROKE); 35 paint3.setColor(Color.YELLOW); 36 mPaint.add(paint3); 37 Paint paint4 = new Paint(); 38 paint4.setAntiAlias(true); 39 paint4.setStyle(Style.STROKE); 40 paint4.setColor(Color.WHITE); 41 mPaint.add(paint4); 42 Paint paint5 = new Paint(); 43 paint5.setAntiAlias(true); 44 paint5.setStyle(Style.STROKE); 45 paint5.setColor(Color.LTGRAY); 46 mPaint.add(paint5); 47 Paint paint6 = new Paint(); 48 paint6.setAntiAlias(true); 49 paint6.setStyle(Style.STROKE); 50 paint6.setColor(Color.CYAN); 51 mPaint.add(paint6); 52 } 53 54 public static void draw(SurfaceHolder mHolder) { 55 Canvas canvas = mHolder.lockCanvas(); 56 canvas.drawRGB(0, 0, 0); 57 for (int i = 0; i < UI_Main.mBTSArrayList.size(); i++) { 58 boolean find = false; 59 short rssi = 0; 60 for (int j = 0; j < UI_Main.mFuncBT.mAddrVector.size(); j++) { 61 if (UI_Main.mBTSArrayList.get(i).mAddr 62 .equals(UI_Main.mFuncBT.mAddrVector.get(j))) { 63 find = true; 64 rssi = UI_Main.mFuncBT.mRSSIVector.get(j); 65 } 66 } 67 if (find == false) { 68 canvas.drawText( 69 times + ": NOT_FIND " 70 + UI_Main.mBTSArrayList.get(i).mName, 5, 71 i * 10 + 12, mPaint.get(i)); 72 } else { 73 float power = (float) ((Math.abs(rssi) - 59) / (10 * 2.0)); 74 float dis = (float) Math.pow(10, power); 75 76 canvas.drawText( 77 times + ": FIND " + UI_Main.mBTSArrayList.get(i).mName 78 + " dis: " + new Float(dis).toString() 79 + " rssi: " + rssi, 5, i * 10 + 12, 80 mPaint.get(i)); 81 canvas.drawCircle(canvas.getWidth() / 2, 82 canvas.getHeight() / 2, Bei * dis, mPaint.get(i));//畫圓圈 83 } 84 } 85 times++; 86 mHolder.unlockCanvasAndPost(canvas);// 更新屏幕顯示內容 87 UI_Main.mFuncBT.mRSSIVector.clear(); 88 UI_Main.mFuncBT.mNameVector.clear(); 89 UI_Main.mFuncBT.mAddrVector.clear(); 90 } 91 }
操做說明:該類提供在SurfaceView上繪製功能。其中靜態方法initPaint對畫筆進行初始化,draw函數負責繪製。
draw函數的核心在於canvas繪圖,canvas繪圖的過程和咱們在白紙上繪繪圖的過程很像,如:
l 第55行鎖定canvas至關於獲得一張紙;
l 第56行用RGB爲0的顏色來刷新canvas至關於用橡皮擦把紙上原來的東西擦掉;
l 第68和76行drawText至關於在紙的相應位置寫文字;
l 第81行drawCircle至關於在紙的相應位置繪製一個指定的圓;
l 第86行的nlockCanvasAndPost至關於你把繪製好的做品拿出來展現給別人看;
正是由於canvas的加鎖和解鎖這一機制,才保證了繪製過程當中屏幕正確地顯示。
接着再來理解這裏draw函數的功能:mBTSArrayList是一個My_BTS類型的數組,保存咱們想要防丟的藍牙防丟器設備的名稱、地址等信息;mFuncBT是一個能夠實時搜索周邊藍牙設備的一個對象,其靜態變量mNameVector、mAddrVector、mRSSIVector保存着實時搜索結果;這樣核心部分的功能即是經過兩層循環遍歷待防丟設備是否在本次搜索中,若是不在則顯示「NOT_FIND」,若是在則由RSSI計算距離。(效果如圖8-4)
圖 8-4 找到藍牙設備圖
9) 新建一個Func_BT.java文件,並修改成:
1 package com.example.first_test; 2 3 import java.util.Vector; 4 5 import android.app.Activity; 6 import android.bluetooth.BluetoothAdapter; 7 import android.bluetooth.BluetoothDevice; 8 import android.content.BroadcastReceiver; 9 import android.content.Context; 10 import android.content.Intent; 11 import android.content.IntentFilter; 12 import android.os.Bundle; 13 import android.os.Handler; 14 import android.os.Message; 15 16 public class Func_BT { 17 private BluetoothAdapter mBtAdapter;// 藍牙適配器 18 private static final int ENABLE_BLUETOOTH = 1; 19 // 分別用於存儲設備名地址名稱和RSSI的向量 20 public Vector<String> mNameVector; 21 public Vector<String> mAddrVector; 22 public Vector<Short> mRSSIVector; 23 24 private Handler myHandler; 25 private Activity activity; 26 27 public Func_BT(Activity activity, Handler myHandler) { 28 this.myHandler = myHandler; 29 this.activity = activity; 30 31 mNameVector = new Vector<String>();// 向量 32 mAddrVector = new Vector<String>(); 33 mRSSIVector = new Vector<Short>(); 34 35 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 36 activity.registerReceiver(mReceiver, filter); 37 filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 38 activity.registerReceiver(mReceiver, filter); 39 activity.registerReceiver(mReceiver, filter); 40 41 mBtAdapter = BluetoothAdapter.getDefaultAdapter(); 42 } 43 44 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 45 @Override 46 public void onReceive(Context context, Intent intent) { 47 String action = intent.getAction(); 48 if (BluetoothDevice.ACTION_FOUND.equals(action)) { 49 BluetoothDevice device = intent 50 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 51 short rssi = intent.getExtras().getShort( 52 BluetoothDevice.EXTRA_RSSI); 53 mNameVector.add(device.getName()); 54 mAddrVector.add(device.getAddress()); 55 mRSSIVector.add(rssi); 56 } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED 57 .equals(action)) { 58 /*if (mNameVector.size() != 0) { 59 Message msg = new Message();// 消息 60 msg.what = 0x01;// 消息類別 61 myHandler.sendMessage(msg); 62 }*/ 63 } 64 } 65 }; 66 67 public void doDiscovery() { 68 if (mBtAdapter.isDiscovering()) { 69 mBtAdapter.cancelDiscovery(); 70 } 71 mBtAdapter.startDiscovery(); 72 new TimeLimitThread().start(); 73 } 74 75 public void openBT() { 76 // 若是沒有打開則打開 77 if (!mBtAdapter.isEnabled()) { 78 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 79 activity.startActivityForResult(intent, ENABLE_BLUETOOTH); 80 } else { 81 doDiscovery(); 82 } 83 } 84 85 protected void onActivityResult(int requestCode, int resultCode, Intent data){ 86 if (requestCode == ENABLE_BLUETOOTH) { 87 if (resultCode == Activity.RESULT_OK) { 88 doDiscovery(); 89 } 90 } 91 } 92 93 public void setHandler(Handler myHandler) { 94 this.myHandler = myHandler; 95 } 96 97 public void setFunc_BT(Activity activity, Handler myHandler) { 98 this.myHandler = myHandler; 99 this.activity = activity; 100 } 101 102 class TimeLimitThread extends Thread{ 103 public void run() { 104 try { 105 sleep(3000); 106 if (mBtAdapter.isDiscovering()) { 107 mBtAdapter.cancelDiscovery(); 108 } 109 Message msg = new Message();// 消息 110 msg.what = 0x01;// 消息類別 111 myHandler.sendMessage(msg); 112 } catch (InterruptedException e) { 113 e.printStackTrace(); 114 } 115 } 116 } 117 }
操做說明:該類用來實時搜索周邊藍牙設備,並把搜索結果保存在其靜態成員變量mNameVector、mAddrVector、mRSSIVector中。此外這裏還使用Handler用於向調用其搜索方法的Activity發送本次搜索完畢的消息。具體介紹以下:
l 第27到42行爲構造函數,主要負責成員變量初始化、註冊filter、獲取本地藍牙設備;
l 第44到65行爲BroadcastReceiver,主要負責異步搜索周邊藍牙設備的廣播的接收;
l 第67到73行爲開始搜索周邊藍牙設備函數;
l 第75到83行爲帶有檢查本地藍牙是否開啓並可以發出開啓請求的搜索周邊藍牙設備函數;
l 第85到91行爲接收用戶是否受權打開藍牙設備的Activity的結果函數;
l 第102到116行是一個線程類主要用於限定搜索周邊藍牙設備的最長時間(默認爲12s左右);
正是由於加了獨立用於限定搜索時長的線程,才讓搜索過程的時間長短便於咱們控制,可是sleep的時間也不要設置的太小,不然會出現一個藍牙設備也搜索不到的狀況。(建議最好參照第七部分——「安卓藍牙編程主要操做」來理解本部分的代碼)
10) 修改UI_Main.java爲:
1 package com.example.first_test; 2 3 import java.util.ArrayList; 4 import android.app.Activity; 5 import android.content.Intent; 6 import android.os.Bundle; 7 import android.os.Handler; 8 import android.os.Message; 9 import android.view.SurfaceHolder; 10 import android.view.SurfaceHolder.Callback; 11 import android.view.SurfaceView; 12 import android.view.View; 13 import android.view.View.OnClickListener; 14 import android.widget.Button; 15 16 public class UI_Main extends Activity implements Callback { 17 18 public Func_Draw mFuncDraw; 19 public SurfaceHolder mHolder; 20 public static Func_BT mFuncBT; 21 // 防丟設備 22 public static ArrayList<My_BTS> mBTSArrayList = new ArrayList<My_BTS>(); 23 24 // 消息句柄(線程裏沒法進行界面更新, 25 // 因此要把消息從線程裏發送出來在消息句柄裏進行處理) 26 public Handler myHandler = new Handler() { 27 @Override 28 public void handleMessage(Message msg) { 29 if (msg.what == 0x01) { 30 Func_Draw.draw(mHolder); 31 } 32 mFuncBT.doDiscovery(); 33 } 34 }; 35 36 @Override 37 protected void onCreate(Bundle savedInstanceState) { 38 super.onCreate(savedInstanceState); 39 setContentView(R.layout.ui_main); 40 41 Func_Draw.initPaint(); 42 43 SurfaceView mSurface = (SurfaceView) findViewById(R.id.surfaceView); 44 mHolder = mSurface.getHolder(); 45 mHolder.addCallback(this); 46 47 mFuncBT = new Func_BT(this, myHandler); 48 49 Button mButton1 = (Button) findViewById(R.id.button_start); 50 mButton1.setOnClickListener(new OnClickListener() { 51 @Override 52 public void onClick(View v) { 53 Func_Draw.times = 0; 54 mFuncBT.openBT(); 55 } 56 }); 57 58 Button mButton2 = (Button) findViewById(R.id.button_add); 59 mButton2.setOnClickListener(new OnClickListener() { 60 @Override 61 public void onClick(View v) { 62 startActivity(new Intent(UI_Main.this, UI_List.class)); 63 } 64 }); 65 } 66 67 @Override 68 public void surfaceCreated(SurfaceHolder holder) { 69 // TODO Auto-generated method stub 70 71 } 72 73 @Override 74 public void surfaceChanged(SurfaceHolder holder, int format, int width, 75 int height) { 76 // TODO Auto-generated method stub 77 78 } 79 80 @Override 81 public void surfaceDestroyed(SurfaceHolder holder) { 82 // TODO Auto-generated method stub 83 84 } 85 }
操做說明:上面第2步時咱們修改了ui_main.xml並作出應用程序主頁面的效果,而該類則是其對應的邏輯實現。
nn 首先看第37到65行的onCreate函數:
l 第39行設置要顯示的頁面爲ui_main.xml所展現的效果;
l 第41行調用Func_Draw的靜態initPaint()方法對畫筆進行初始化;
l 第43到45行負責獲取並設置SurfaceView;
l 第47行實例化一個Func_BT;
l 第49到56行是給開始防丟按鈕綁定一個監聽器,一旦點擊該按鈕則執行onClick內代碼;
l 第58到65行是給添加藍牙防丟器綁定一個監聽器,一旦點擊則啓動另外一個Activity;
nn 第24到34行實例化一個Handler用於接收Func_BT一次搜索結束時發回結束的消息。在這裏一旦收到本次周邊藍牙設備搜索結束的消息就調用Func_Draw.draw(mHolder)進行繪製,而後繼續調用mFuncBT.doDiscovery()實現週期性搜索/防丟。
nn 第67到84行是自動生成的,暫且不用管(也不要刪除!)。
11) 新建一個UI_List.java文件,並修改代碼爲:
1 package com.example.first_test; 2 3 import java.util.ArrayList; 4 import android.annotation.SuppressLint; 5 import android.app.Activity; 6 import android.app.ProgressDialog; 7 import android.content.Intent; 8 import android.os.Bundle; 9 import android.os.Handler; 10 import android.os.Message; 11 import android.util.Log; 12 import android.view.View; 13 import android.view.View.OnClickListener; 14 import android.widget.ArrayAdapter; 15 import android.widget.Button; 16 import android.widget.ListView; 17 18 public class UI_List extends Activity { 19 20 private ArrayList<String> Items; 21 private ListView myListView; 22 private ArrayAdapter<String> aa; 23 private boolean getDIV; 24 25 @SuppressLint("InlinedApi") 26 protected void onCreate(Bundle savedInstanceState) { 27 super.onCreate(savedInstanceState); 28 setContentView(R.layout.ui_list); 29 30 UI_Main.mFuncBT.setFunc_BT(this, myHandler); 31 32 // 獲取listview並設置爲多選 33 myListView = (ListView) findViewById(R.id.listView1); 34 myListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); 35 myListView.setTextFilterEnabled(true); 36 // 設置listview數組並綁定 37 Items = new ArrayList<String>(); 38 aa = new ArrayAdapter<String>(this, 39 android.R.layout.simple_list_item_checked, Items); 40 myListView.setAdapter(aa); 41 42 // 獲取OK按鈕,並遍歷選擇的設備,返回主Activity 43 Button myButton1 = (Button) findViewById(R.id.button_ok); 44 myButton1.setOnClickListener(new OnClickListener() { 45 @Override 46 public void onClick(View v) { 47 int num = 0; 48 for (int i = 0; i < myListView.getCount(); i++) { 49 if (myListView.isItemChecked(i)) { 50 String item = myListView.getItemAtPosition(i) 51 .toString(); 52 String name = item.substring(0, item.indexOf("\n")); 53 String addr = item.substring(item.indexOf("\n") + 1); 54 Log.i("UI_LIST", name + " " + addr); 55 UI_Main.mBTSArrayList 56 .add(num++, new My_BTS(name, addr)); 57 } 58 } 59 Intent mIntent = new Intent(UI_List.this, UI_Main.class); 60 mIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 61 startActivity(mIntent); 62 } 63 }); 64 // 獲取Search按鈕,設置監聽事件 65 Button myButton2 = (Button) findViewById(R.id.button_search); 66 myButton2.setOnClickListener(new OnClickListener() { 67 @Override 68 public void onClick(View v) { 69 getDIV = false; 70 UI_Main.mFuncBT.openBT(); 71 final ProgressDialog dialog = ProgressDialog.show(UI_List.this, 72 "搜索藍牙設備", "稍等一下~", true); 73 new Thread(new Runnable() { 74 public void run() { 75 while (getDIV == false); 76 dialog.dismiss(); 77 } 78 }).start(); 79 } 80 }); 81 } 82 83 // 消息句柄(線程裏沒法進行界面更新, 84 // 因此要把消息從線程裏發送出來在消息句柄裏進行處理) 85 public Handler myHandler = new Handler() { 86 @Override 87 public void handleMessage(Message msg) { 88 if (msg.what == 0x01) { 89 Items.clear(); 90 for (int i = 0; i < UI_Main.mFuncBT.mNameVector.size(); i++) { 91 Items.add(i, UI_Main.mFuncBT.mNameVector.get(i) + '\n' 92 + UI_Main.mFuncBT.mAddrVector.get(i)); 93 aa.notifyDataSetChanged();// 通知數據變化 94 } 95 getDIV = true; 96 UI_Main.mFuncBT.mRSSIVector.clear(); 97 UI_Main.mFuncBT.mNameVector.clear(); 98 UI_Main.mFuncBT.mAddrVector.clear(); 99 } 100 } 101 }; 102 }
操做說明:這個類和ui_list.xml也是配套的。所以:
nn 在onCreate中:
l 第28行設置要顯示的頁面爲ui_list.xml所展現的效果;
l 第30行負責將周邊藍牙搜索對象的Handler設置爲本Activity內的Handler;
l 第32到40行是list相關;
l 第42到80行是兩個按鈕點擊事件監聽相關;
nn 第85到101行的Handler則同UI_Main.java中的Handler相似負責接收週期性藍牙搜索結束消息。在這裏當接到藍牙搜索結束的消息後是將搜索到的設備信息放入list中待用戶選擇。(沒有再次調用藍牙搜索)
nn 此外兩個按鈕監聽中執行部分要特別說明下:
l 當點擊OK按鈕時程序將用戶在list中選擇的設備的信息存放在UI_Main.mBTSArrayList中,而後結束當前Activity並啓動UI_Main所對應的Activity。
l 當點擊search按鈕時會啓動周邊藍牙設備搜索並打開一個等待對話框,其中的Thread負責等待直到搜索完畢。
12) 最後(如圖8-5)找到AndroidMainFest.xml文件修改成:
圖 8-5 AnDroidManifest.xml文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.first_test" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="8" 9 android:targetSdkVersion="19" /> 10 11 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> 12 <uses-permission android:name="android.permission.BLUETOOTH" /> 13 14 <application 15 android:allowBackup="true" 16 android:icon="@drawable/ic_launcher" 17 android:label="@string/app_name" 18 android:theme="@style/AppTheme" > 19 <activity 20 android:name=".UI_Main" 21 android:label="@string/app_name" > 22 <intent-filter> 23 <action android:name="android.intent.action.MAIN" /> 24 <category android:name="android.intent.category.LAUNCHER" /> 25 </intent-filter> 26 </activity> 27 28 <activity 29 android:name=".UI_List" 30 android:label="@string/app_name" > 31 <intent-filter> 32 <action android:name="android.intent.action.UI_List" /> 33 <category android:name="android.intent.category.DEFAULT" /> 34 </intent-filter> 35 </activity> 36 37 </application> 38 39 40 </manifest>
操做說明:主要是在十一、12行添加藍牙權限以及在28到35行添加一個activity。
9 最終成果檢查
到目前爲止咱們已經所有完工,下面是成果自評的時候啦(請參照下列條目給本身打分):
及格分70分,不夠的要注意打好基礎哦!
[請看正版請百度:beautifulzzzz(看樓主博客園官方博客,享高質量生活)嘻嘻!!!]
[若是有須要製做藍牙防丟器或藍牙室內定位的能夠聯繫我哦~]
若是您以爲不錯,別忘點個贊讓更多的小夥伴看到\(^o^)/~