編輯:關於Android編程
基本概念:
安卓平台提供對藍牙的通訊棧的支持,允許設別和其他的設備進行無線傳輸數據。應用程序層通過安卓API來調用藍牙的相關功
能,這些API使程序無線連接到藍牙設備,並擁有P2P或者多端無線連接的特性。
藍牙的功能:
1、掃描其他藍牙設備
2、為可配對的藍牙設備查詢藍牙適配器
3、建立RFCOMM通道
4、通過服務搜索來鏈接其他的設備
5、與其他的設備進行數據傳輸
6、管理多個連接
藍牙建立連接必須要求:
1、打開藍牙
2、查找附近已配對或可用設備
3、連接設備
4、設備間數據交換
常用的藍牙API如下:
BluetoothAdapter
代表本地藍牙適配器(藍牙無線電)。BluetoothAdapter是所有藍牙交互的入口。使用這個你可以發現其他藍牙設備,查詢已配對的設備列表,使用一個已知的MAC地址來實例化一個BluetoothDevice,以及創建一個BluetoothServerSocket來為監聽與其他設備的通信。
BlueDevice 代表一個遠程藍牙設備,使用這個來請求一個與遠程設備的BluetoothSocket連接,或者查詢關於設備名稱、地址、類和連接狀態等設備信息。 BluetoothSocket 代表一個藍牙socket的接口(和TCP Socket類似)。這是一個連接點,它允許一個應用與其他藍牙設備通過InputStream和OutputStream交換數據。 BluetoothServerSocket 代表一個開放的服務器socket,它監聽接受的請求(與TCP ServerSocket類似)。為了連接兩台Android設備,一個設備必須使用這個類開啟一個服務器socket。當一個遠程藍牙設備開始一個和該設備的連接請求,BluetoothServerSocket將會返回一個已連接的BluetoothSocket,接受該連接。
BluetoothAdapter 中常用方法如下所示:
Alphabetic characters must be uppercase to be valid.
BluetoothDeviceobjects that are bonded (paired) to the local adapter.
BluetoothDeviceobject for the given Bluetooth hardware address.
BluetoothDeviceobject for the given Bluetooth hardware address.
BluetoothDevice 中常用方法如下所示:
BluetoothSocketsocket ready to start an insecure outgoing connection to this remote device using SDP lookup of uuid.
BluetoothSocketready to start a secure outgoing connection to this remote device using SDP lookup of uuid.
PAIRING_VARIANT_PASSKEY_CONFIRMATIONpairing.
PAIRING_VARIANT_PIN
Requires
BLUETOOTH_ADMIN.
BluetoothSocket 中常用方法如下所示:
BluetoothServerSocket 中常用方法如下所示:
以上四個類貫穿於我們藍牙通信的全過程,包括藍牙搜索、配對、連接以及通信。
使用藍牙需要在配置文件Androidmanifest.xml 中注冊兩種權限:
其中,權限1在得到默認藍牙適配器時需要,即BluetoothAdapter mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter( )
權限2在mBluetoothAdapter.enable( )或者mBluetoothAdapter.disable( ) 時需要使用到。
一、藍牙搜索功能的實現:
1、得到藍牙適配器:
BluetoothAdapter mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();
若mBluetoothAdapter為 null,則說明當前手機不支持藍牙功能(現在幾乎所有手機都支持了吧。。。)
2、判斷藍牙是否打開:
if (!mBluetoothAdapter.isEnabled()) { //若沒打開則打開藍牙 mBluetoothAdapter.enable(); }
值得注意的是,強制打開藍牙設備的情況有三種:
(1)沒有任何提示,直接打開了藍牙。如Nexus 5 Android 4.4.4 手機。
(2)會彈出提示框,提示安全警告 “ ***應用嘗試開啟藍牙”,可以選擇“拒絕”或“允許”。大多數手機都是這樣的。
(3)強制打開藍牙失敗,並且沒有任何提示。
3、注冊藍牙搜索廣播接收者:
(1)Android 的廣播機制:
Adnroid的廣播機制(以intent對象的形式廣播出去),Android系統廣播的時候不會關心你是否收得到消息、只負責廣播出去,而
且廣播的對象只是在應用程序中注冊了的廣播接收器。我們要做的就是自定義廣播接收器並將其注冊給應用程序,在廣播接收器中
將接收到廣播事件作出相應的處理。如果廣播的事件並不是我們定義的廣播接收器需要的事件類型,一般是會過濾掉不被接收。只
有當廣播事件和我們寫的接收器定義的接收的事件類型一致的時候才會觸發廣播接收器。並且觸發廣播接收器的onReceive方法。當
然我們自定義的廣播接收器需要接受事件的類型是在XML清單文件的
個IntentFilter對象然後通過對象的addAction()方法來自定義接收事件類型。然後我們需要將接收到的事件的處理代碼寫在onReceive
方法中。
(2)注冊分為兩種:靜態注冊和動態注冊。
靜態注冊就是在AndroidManifest.xml文件中定義,注冊的廣播接收器必須繼承BroadReceiver動態注冊就是在程序中使用Context.registerReceiver注冊。
我們先演示動態注冊:
//注冊設備被發現時的廣播 IntentFilter filter=new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver,filter); //注冊一個搜索結束時的廣播 IntentFilter filter2=new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); registerReceiver(mReceiver,filter2);
對應的靜態注冊如下:
我們如何知道BluetoothAdapter.ACTION_DISCOVERY_FINISHED對應著android.bluetooth.adapter.action.DISCOVERY_FINISHED呢?
這就要看強大的API了。如圖就是一種對應關系:
此處推薦別人上傳的中文API:
點我打開Android中文API
4、定義廣播接收:
自定義的廣播接收器對象必須要繼承BroadcastReceiver,然後重寫onReceive方法,處理接收的數據的代碼就寫在這個方法裡面。
兩種方法:
自定義一個類實現BroadcastReceiver抽象類,並且實現其onReceiver(Context context, Intent intent )方法。直接new BroadcastReceiver()來搞定。方法1如下:
public class BluetoothReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { ................... } }
方法2如下:
//定義廣播接收 private BroadcastReceiver mReceiver=new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { ....................... } };
5、開始廣播:
通過 mBluetoothAdapter.startDiscovery( ); 來開始廣播。當廣播的事件是我們剛剛注冊的事件時就會觸發廣播接收器,並且觸
發廣播接收器中的onReceiver()方法。
6、解除注冊:
通過 unregisterReceiver(mReceiver); 來解除剛剛的注冊。
至此我們完成了藍牙通信的第一步:藍牙搜索。
下邊給出一個完整Demo實例。
功能為:點擊按鈕將搜索附近的藍牙設備,並且判斷是否與本設備已經配對,分類顯示。
代碼如下:
mainActivity.java
package com.example.administrator.myapplication; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { //定義 private BluetoothAdapter mBluetoothAdapter; private TextView text,text2,text3; private Button botton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text=(TextView) this.findViewById(R.id.textView); //已配對 text2= (TextView) this.findViewById(R.id.textView2); //狀態信息 text3= (TextView) this.findViewById(R.id.textView3); //未配對 botton=(Button) this.findViewById(R.id.button); mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter(); IntentFilter filter=new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver,filter); IntentFilter filter2=new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); registerReceiver(mReceiver,filter2); botton.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View arg0) { if(!mBluetoothAdapter.isEnabled()) { mBluetoothAdapter.enable(); } mBluetoothAdapter.startDiscovery(); text2.setText("正在搜索..."); } }); } public void onDestroy() { super.onDestroy(); //解除注冊 unregisterReceiver(mReceiver); Log.e("destory","解除注冊"); } //定義廣播接收 private BroadcastReceiver mReceiver=new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { String action=intent.getAction(); Log.e("ywq", action); if(action.equals(BluetoothDevice.ACTION_FOUND)) { BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(device.getBondState()==BluetoothDevice.BOND_BONDED) { //顯示已配對設備 text.append("\n"+device.getName()+"==>"+device.getAddress()+"\n"); }else if(device.getBondState()!=BluetoothDevice.BOND_BONDED) { text3.append("\n"+device.getName()+"==>"+device.getAddress()+"\n"); } }else if(action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)){ text2.setText("搜索完成..."); } } }; }
AndroidManifest.xml代碼如下:
布局文件activity_main.xml代碼如下:
程序運行結果如下:
二、藍牙自動配對功能實現:
藍牙配對是建立連接的基礎和前提。為什麼不配對便無法建立連接?
任何無線通信技術都存在被監聽和破解的可能,藍牙SIG為了保證藍牙通信的安全性,采用認證的方式進行數據交互。同時為
了保證使用的方便性,以配對的形式完成兩個藍牙設備之間的首次通訊認證,經配對之後,隨後的通訊連接就不必每次都要做確
認。所以認證碼的產生是從配對開始的,經過配對,設備之間以PIN碼建立約定的link key用於產生初始認證碼,以用於以後建立的
連接。
所以如果不配對,兩個設備之間便無法建立認證關系,無法進行連接及其之後的操作,所以配對在一定程度上保證了藍牙通信
的安全,當然這個安全保證機制是比較容易被破解的,因為現在很多個人設備沒有人機接口,所以PIN碼都是固定的而且大都設置為
通用的0000或者1234之類的,所以很容易被猜到並進而建立配對和連接。
關於藍牙的自動配對,大家可以參考我的這篇博客:Android藍牙自動配對Demo,親測好使!!!
這裡自誇一下,這篇博客還是受到了大家的一些好評。該自動配對方法,博主在魅藍、華為、聯想、紅米以及Nexus手機上都有測
試過,使用的Android系統包括4.0+和5.0+,所以各位可以仔細閱讀該博客。
三、藍牙通信的實現:
藍牙通信分為Android 端藍牙設備之間的通信以及Android 端藍牙設備與其他藍牙設備之間的通信。
1、通信方式1:
Android手機端藍牙與Arduino外接HC-05藍牙模塊之間進行通信。
(待續......................)
1.准備工作每一個view,只是img不一樣,其他都是一樣的<framelayout android:layout_height=match_parent andr
在android應用的開發過程中,經常會出現啟動一個界面後填寫部分內容後帶著數據返回啟動前的界面,最典型的應用就是登錄過程。在很多應用程序的模塊中,都有“我的”這個模塊,
在我們玩手機游戲時能看到,很多游戲的登錄界面兩側往往會有一個小小的懸浮窗,可以提供相應功能菜單項,簡潔實用且不影響游戲體驗。具體效果如下圖所示。這篇博客將帶大家開發一個可
進度條可以方便的告訴用戶現在執行任務的進度,特別是一個執行程序比較長的時候,沒有進度條,用戶不知道程序在執行,會以為程序假死強制關閉。 進度條分為:1