在我们正常的Android蓝牙功能开发步骤中,一般要经过系统权限和蓝牙开关状态监测、设备扫描、设备连接、蓝牙数据通信这几个过程。
在Android 4.3系统之后,我们可以使用蓝牙4.0(低功耗蓝牙),它最主要的特点是低功耗,普及率高。现在所说的蓝牙设备,大部分都是在说4.0设备,ble也特指4.0设备。 在4.0之前重要的版本有2.1版本-基本速率/增强数据率(BR/EDR)和3.0 高速蓝牙版本,这些统称为经典蓝牙。
如果想让支持低功耗蓝牙的设备使用蓝牙4.0,我们可以通过如下代码去监测
// AndroidManifest.xml
"android.hardware.bluetooth_le" android:required="false"/>
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
首先我们必须要给予应用相应的蓝牙权限。
//需要此权限来执行任何蓝牙通信,如请求一个连接、接受一个连接和传输数据。
<uses-permission android:name="android.permission.BLUETOOTH"/>
//如果你想让你的应用启动设备发现或操纵蓝牙设置,必须申报bluetooth_admin许可
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
然后在程序启动时监测当前设备是否已经打开蓝牙,若没打开则跳转到系统蓝牙功能开关界面选择开启蓝牙
// If BT is not on, request that it be enabled.
// setupChat() will then be called during onActivityResult
if (!mBluetoothAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
// Otherwise, setup the chat session
} else if (mChatService == null) {
setupChat();
}
在onActivityResult中捕获
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_ENABLE_BT:
// When the request to enable Bluetooth returns
if (resultCode == Activity.RESULT_OK) {
// Bluetooth is now enabled, so set up a chat session
setupChat();
} else {
// User did not enable Bluetooth or an error occurred
Log.d(TAG, "BT not enabled");
Toast.makeText(getActivity(), R.string.bt_not_enabled_leaving,
Toast.LENGTH_SHORT).show();
getActivity().finish();
}
}
}
我们只需要调用BluetoothAdapter的startDiscovery()方法,便开始搜索附近的其他蓝牙设备,
/**
* Start device discover with the BluetoothAdapter
*/
private void doDiscovery() {
// If we're already discovering, stop it
if (mBtAdapter.isDiscovering()) {
mBtAdapter.cancelDiscovery();
}
// Request discover from BluetoothAdapter
mBtAdapter.startDiscovery();
}
之后,如果搜索到一个蓝牙设备,系统就是发出一个广播,我们可以对它进行接收并且进行相应的处理:
// Register for broadcasts when a device is discovered
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, filter);
// Register for broadcasts when discovery has finished
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver, filter);
/**
* The BroadcastReceiver that listens for discovered devices and changes the title when
* discovery is finished
*/
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// If it's already paired, skip it, because it's been listed already
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
// When discovery is finished, change the Activity title
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
setTitle(R.string.select_device);
if (mNewDevicesArrayAdapter.getCount() == 0) {
//do something here after finished discovery
}
}
}
};
在搜索到的其它设备里选择一个需要连接通信的设备,传入设备的地址,调用getRemoteDevice方法去获得一个BluetoothDevice 实例,然后开辟一个子线程用于建立连接,
private void connectDevice(Intent data, boolean secure) {
// Get the device MAC address
String address = data.getExtras()
.getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
// Get the BluetoothDevice object
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
// Attempt to connect to the device
mChatService.connect(device, secure);
}
/**
* Start the ConnectThread to initiate a connection to a remote device.
*
* @param device The BluetoothDevice to connect
* @param secure Socket Security type - Secure (true) , Insecure (false)
*/
public synchronized void connect(BluetoothDevice device, boolean secure) {
Log.d(TAG, "connect to: " + device);
// Cancel any thread attempting to make a connection
if (mState == STATE_CONNECTING) {
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Start the thread to connect with the given device
mConnectThread = new ConnectThread(device, secure);
mConnectThread.start();
// Update UI
}
连接线程:此处调用了device.createRfcommSocketToServiceRecord方法去创建一个用于通信的socket,参数是UUID,是一个通用标识符。
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private String mSocketType;
public ConnectThread(BluetoothDevice device, boolean secure) {
mmDevice = device;
BluetoothSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
MY_UUID_SECURE);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
MY_UUID_INSECURE);
}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
}
mmSocket = tmp;
mState = STATE_CONNECTING;
}
public void run() {
Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
setName("ConnectThread" + mSocketType);
// Always cancel discovery because it will slow down a connection
mAdapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, "unable to close() " + mSocketType +
" socket during connection failure", e2);
}
connectionFailed();
return;
}
// Reset the ConnectThread because we're done
synchronized (BluetoothChatService.this) {
mConnectThread = null;
}
// Start the connected thread
connected(mmSocket, mmDevice, mSocketType);
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
}
}
}
设备连接完成后,要取消连接线程,防止造成资源的浪费,然后创建通信线程,维持输入输出流的接收和发送。
public synchronized void connected(BluetoothSocket socket, BluetoothDevice
device, final String socketType) {
Log.d(TAG, "connected, Socket Type:" + socketType);
// Cancel the thread that completed the connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
// Start the thread to manage the connection and perform transmissions
mConnectedThread = new ConnectedThread(socket, socketType);
mConnectedThread.start();
// Send the name of the connected device back to the UI Activity
Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(Constants.DEVICE_NAME, device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);
// Update UI title
updateUserInterfaceTitle();
}
消息传输线程:run方法是一个while循环,当处于连接状态时,会一直从输入流中获取数据,并将获取到的字节数据通过handle机制传输到主线程并显示。在需要发送数据时,只要调用write方法即可。
/**
* This thread runs during a connection with a remote device.
* It handles all incoming and outgoing transmissions.
*/
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket, String socketType) {
Log.d(TAG, "create ConnectedThread: " + socketType);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
mState = STATE_CONNECTED;
}
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (mState == STATE_CONNECTED) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
/**
* Write to the connected OutStream.
*
* @param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
至此,通过蓝牙实现数据传输的过程基本建立完毕。此处是针对普通蓝牙的通信方式,若使用低功耗蓝牙,可详见Google的官方sample:
android-BuletoothLeGatt
Android WiFi开发需掌握基本的操作,包括扫描附近WiFi、控制WiFi的开闭、发射WiFi热点等。
在Android的sdk中, WiFi相关的操作类都在Android.net.wifi包里面。接下来就跟随官方Guide来学习Android中WiFi的基本操作。
Google开发者中国网站api
其中主要的类有ScanResult ,wifiConfiguration, WifiInfo ,WifiManager。
主要用来描述已经检测出的接入点,包括接入点的地址,接入点的名称,身份认证,频率,信号强度等信息。
打开这个类,我们可以看到以下几个信息
包括以下六个子类:
在我们的wifi 已经连通了以后,可以通过这个类获得一些已经连通的wifi 连接的信息获取当前链接的信息.
这个类提供了WiFi连接的管理功能,我们可以调用Context.getSystemService(Context.WIFI_SERVICE)来获取,常用方法如下:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
这是从网上整理的一个WiFi相关的工具类,WiFi开发的很多场景需要用到的方法都在里面实现了。
/**
* WIFI管理类
* @author ZHF
*
*/
public class WifiAdmin {
private static WifiAdmin wifiAdmin = null;
private List mWifiConfiguration; //无线网络配置信息类集合(网络连接列表)
private List mWifiList; //检测到接入点信息类 集合
//描述任何Wifi连接状态
private WifiInfo mWifiInfo;
WifiManager.WifiLock mWifilock; //能够阻止wifi进入睡眠状态,使wifi一直处于活跃状态
public WifiManager mWifiManager;
/**
* 获取该类的实例(懒汉)
* @param context
* @return
*/
public static WifiAdmin getInstance(Context context) {
if(wifiAdmin == null) {
wifiAdmin = new WifiAdmin(context);
return wifiAdmin;
}
return null;
}
private WifiAdmin(Context context) {
//获取系统Wifi服务 WIFI_SERVICE
this.mWifiManager = (WifiManager) context.getSystemService("wifi");
//获取连接信息
this.mWifiInfo = this.mWifiManager.getConnectionInfo();
}
/**
* 是否存在网络信息
* @param str 热点名称
* @return
*/
private WifiConfiguration isExsits(String str) {
Iterator localIterator = this.mWifiManager.getConfiguredNetworks().iterator();
WifiConfiguration localWifiConfiguration;
do {
if(!localIterator.hasNext()) return null;
localWifiConfiguration = (WifiConfiguration) localIterator.next();
}while(!localWifiConfiguration.SSID.equals("\"" + str + "\""));
return localWifiConfiguration;
}
/**锁定WifiLock,当下载大文件时需要锁定 **/
public void AcquireWifiLock() {
this.mWifilock.acquire();
}
/**创建一个WifiLock**/
public void CreateWifiLock() {
this.mWifilock = this.mWifiManager.createWifiLock("Test");
}
/**解锁WifiLock**/
public void ReleaseWifilock() {
if(mWifilock.isHeld()) { //判断时候锁定
mWifilock.acquire();
}
}
/**打开Wifi**/
public void OpenWifi() {
if(!this.mWifiManager.isWifiEnabled()){ //当前wifi不可用
this.mWifiManager.setWifiEnabled(true);
}
}
/**关闭Wifi**/
public void closeWifi() {
if(mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(false);
}
}
/**端口指定id的wifi**/
public void disconnectWifi(int paramInt) {
this.mWifiManager.disableNetwork(paramInt);
}
/**添加指定网络**/
public void addNetwork(WifiConfiguration paramWifiConfiguration) {
int i = mWifiManager.addNetwork(paramWifiConfiguration);
mWifiManager.enableNetwork(i, true);
}
/**
* 连接指定配置好的网络
* @param index 配置好网络的ID
*/
public void connectConfiguration(int index) {
// 索引大于配置好的网络索引返回
if (index > mWifiConfiguration.size()) {
return;
}
//连接配置好的指定ID的网络
mWifiManager.enableNetwork(mWifiConfiguration.get(index).networkId, true);
}
/**
* 根据wifi信息创建或关闭一个热点
* @param paramWifiConfiguration
* @param paramBoolean 关闭标志
*/
public void createWifiAP(WifiConfiguration paramWifiConfiguration,boolean paramBoolean) {
try {
Class localClass = this.mWifiManager.getClass();
Class[] arrayOfClass = new Class[2];
arrayOfClass[0] = WifiConfiguration.class;
arrayOfClass[1] = Boolean.TYPE;
Method localMethod = localClass.getMethod("setWifiApEnabled",arrayOfClass);
WifiManager localWifiManager = this.mWifiManager;
Object[] arrayOfObject = new Object[2];
arrayOfObject[0] = paramWifiConfiguration;
arrayOfObject[1] = Boolean.valueOf(paramBoolean);
localMethod.invoke(localWifiManager, arrayOfObject);
return;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建一个wifi信息
* @param ssid 名称
* @param passawrd 密码
* @param paramInt 有3个参数,1是无密码,2是简单密码,3是wap加密
* @param type 是"ap"还是"wifi"
* @return
*/
public WifiConfiguration createWifiInfo(String ssid, String passawrd,int paramInt, String type) {
//配置网络信息类
WifiConfiguration localWifiConfiguration1 = new WifiConfiguration();
//设置配置网络属性
localWifiConfiguration1.allowedAuthAlgorithms.clear();
localWifiConfiguration1.allowedGroupCiphers.clear();
localWifiConfiguration1.allowedKeyManagement.clear();
localWifiConfiguration1.allowedPairwiseCiphers.clear();
localWifiConfiguration1.allowedProtocols.clear();
if(type.equals("wt")) { //wifi连接
localWifiConfiguration1.SSID = ("\"" + ssid + "\"");
WifiConfiguration localWifiConfiguration2 = isExsits(ssid);
if(localWifiConfiguration2 != null) {
mWifiManager.removeNetwork(localWifiConfiguration2.networkId); //从列表中删除指定的网络配置网络
}
if(paramInt == 1) { //没有密码
localWifiConfiguration1.wepKeys[0] = "";
localWifiConfiguration1.allowedKeyManagement.set(0);
localWifiConfiguration1.wepTxKeyIndex = 0;
} else if(paramInt == 2) { //简单密码
localWifiConfiguration1.hiddenSSID = true;
localWifiConfiguration1.wepKeys[0] = ("\"" + passawrd + "\"");
} else { //wap加密
localWifiConfiguration1.preSharedKey = ("\"" + passawrd + "\"");
localWifiConfiguration1.hiddenSSID = true;
localWifiConfiguration1.allowedAuthAlgorithms.set(0);
localWifiConfiguration1.allowedGroupCiphers.set(2);
localWifiConfiguration1.allowedKeyManagement.set(1);
localWifiConfiguration1.allowedPairwiseCiphers.set(1);
localWifiConfiguration1.allowedGroupCiphers.set(3);
localWifiConfiguration1.allowedPairwiseCiphers.set(2);
}
}else {//"ap" wifi热点
localWifiConfiguration1.SSID = ssid;
localWifiConfiguration1.allowedAuthAlgorithms.set(1);
localWifiConfiguration1.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
localWifiConfiguration1.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
localWifiConfiguration1.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
localWifiConfiguration1.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
localWifiConfiguration1.allowedKeyManagement.set(0);
localWifiConfiguration1.wepTxKeyIndex = 0;
if (paramInt == 1) { //没有密码
localWifiConfiguration1.wepKeys[0] = "";
localWifiConfiguration1.allowedKeyManagement.set(0);
localWifiConfiguration1.wepTxKeyIndex = 0;
} else if (paramInt == 2) { //简单密码
localWifiConfiguration1.hiddenSSID = true;//网络上不广播ssid
localWifiConfiguration1.wepKeys[0] = passawrd;
} else if (paramInt == 3) {//wap加密
localWifiConfiguration1.preSharedKey = passawrd;
localWifiConfiguration1.allowedAuthAlgorithms.set(0);
localWifiConfiguration1.allowedProtocols.set(1);
localWifiConfiguration1.allowedProtocols.set(0);
localWifiConfiguration1.allowedKeyManagement.set(1);
localWifiConfiguration1.allowedPairwiseCiphers.set(2);
localWifiConfiguration1.allowedPairwiseCiphers.set(1);
}
}
return localWifiConfiguration1;
}
/**获取热点名**/
public String getApSSID() {
try {
Method localMethod = this.mWifiManager.getClass().getDeclaredMethod("getWifiApConfiguration", new Class[0]);
if (localMethod == null) return null;
Object localObject1 = localMethod.invoke(this.mWifiManager,new Object[0]);
if (localObject1 == null) return null;
WifiConfiguration localWifiConfiguration = (WifiConfiguration) localObject1;
if (localWifiConfiguration.SSID != null) return localWifiConfiguration.SSID;
Field localField1 = WifiConfiguration.class .getDeclaredField("mWifiApProfile");
if (localField1 == null) return null;
localField1.setAccessible(true);
Object localObject2 = localField1.get(localWifiConfiguration);
localField1.setAccessible(false);
if (localObject2 == null) return null;
Field localField2 = localObject2.getClass().getDeclaredField("SSID");
localField2.setAccessible(true);
Object localObject3 = localField2.get(localObject2);
if (localObject3 == null) return null;
localField2.setAccessible(false);
String str = (String) localObject3;
return str;
} catch (Exception localException) {
}
return null;
}
/**获取wifi名**/
public String getBSSID() {
if (this.mWifiInfo == null)
return "NULL";
return this.mWifiInfo.getBSSID();
}
/**得到配置好的网络 **/
public List getConfiguration() {
return this.mWifiConfiguration;
}
/**获取ip地址**/
public int getIPAddress() {
return (mWifiInfo == null) ? 0 : mWifiInfo.getIpAddress();
}
/**获取物理地址(Mac)**/
public String getMacAddress() {
return (mWifiInfo == null) ? "NULL" : mWifiInfo.getMacAddress();
}
/**获取网络id**/
public int getNetworkId() {
return (mWifiInfo == null) ? 0 : mWifiInfo.getNetworkId();
}
/**获取热点创建状态**/
public int getWifiApState() {
try {
int i = ((Integer) this.mWifiManager.getClass()
.getMethod("getWifiApState", new Class[0])
.invoke(this.mWifiManager, new Object[0])).intValue();
return i;
} catch (Exception localException) {
}
return 4; //未知wifi网卡状态
}
/**获取wifi连接信息**/
public WifiInfo getWifiInfo() {
return this.mWifiManager.getConnectionInfo();
}
/** 得到网络列表**/
public List getWifiList() {
return this.mWifiList;
}
/**查看扫描结果**/
public StringBuilder lookUpScan() {
StringBuilder localStringBuilder = new StringBuilder();
for (int i = 0; i < mWifiList.size(); i++)
{
localStringBuilder.append("Index_"+new Integer(i + 1).toString() + ":");
//将ScanResult信息转换成一个字符串包
//其中把包括:BSSID、SSID、capabilities、frequency、level
localStringBuilder.append((mWifiList.get(i)).toString());
localStringBuilder.append("\n");
}
return localStringBuilder;
}
/** 设置wifi搜索结果 **/
public void setWifiList() {
this.mWifiList = this.mWifiManager.getScanResults();
}
/**开始搜索wifi**/
public void startScan() {
this.mWifiManager.startScan();
}
/**得到接入点的BSSID**/
public String GetBSSID() {
return (mWifiInfo == null) ? "NULL" : mWifiInfo.getBSSID();
}
}
创建WiFi热点需要先获取到wifi的服务,再配置热点名称、密码等等,然后再通过反射调用setWifiApEnabled方法来创建热点。因为wifi和热点不能同时打开,所以打开热点的时候需要调用wifiManager.setWifiEnabled(false); 关闭wifi
public void stratWifiAp() {
Method method1 = null;
try {
method1 = mWifiManager.getClass().getMethod("setWifiApEnabled",
WifiConfiguration.class, boolean.class);
WifiConfiguration netConfig = new WifiConfiguration();
netConfig.SSID = mSSID;
netConfig.preSharedKey = mPasswd;
netConfig.allowedAuthAlgorithms
.set(WifiConfiguration.AuthAlgorithm.OPEN);
netConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
netConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
netConfig.allowedKeyManagement
.set(WifiConfiguration.KeyMgmt.WPA_PSK);
netConfig.allowedPairwiseCiphers
.set(WifiConfiguration.PairwiseCipher.CCMP);
netConfig.allowedPairwiseCiphers
.set(WifiConfiguration.PairwiseCipher.TKIP);
netConfig.allowedGroupCiphers
.set(WifiConfiguration.GroupCipher.CCMP);
netConfig.allowedGroupCiphers
.set(WifiConfiguration.GroupCipher.TKIP);
method1.invoke(mWifiManager, netConfig, true);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
参考文章:
Android开发之蓝牙通信
Android BulutoothChat
Android中WiFi开发总结