学习内容
Ø 蓝牙的基本概念
Ø Android中蓝牙的应用
能力目标
Ø 了解蓝牙的基本概念
Ø 掌握Android中蓝牙的应用
Ø 掌握如何使用Android中Wi-Fi
本章简介
蓝牙是一种重要的短距离无线通信技术,它被广泛应用于各种设备,比如计算机、手机、汽车等,支持设备之间的近距离通信,从而是数据传输更加快捷有效。Wi-Fi是一种高速的无线通信协议,它具有传输速度高,传输距离长的特点。通过WiFi,手机、PDA、电脑等移动设备可以以无线方式连接网络。本节中我们主要来学习Android开发中如何调用系统中蓝牙以及wifi的功能。
11.1 蓝牙简介
蓝牙(Bluettoth)是目前使用最广泛的一种短距离(10M)无线通信协议之一,广泛应用于各种设备中,比如手机、计算机、耳机、鼠标、键盘等。
蓝牙采用了分散式网络结构以及快跳频和短包技术,支持点对点及点对多点的通信,工作在全球通用的2.4GHz频度。根据不同的蓝牙版本,传输速度会差很多,例如:最新的蓝牙3.0传输速度为3Mb/s,而未来的蓝牙4.0技术从理论上可达到60Mb/s。
蓝牙协议分为核心协议层、电缆替代协议层、电话控制协议层、采纳的其它协议层等4层,蓝牙的核心协议包括基带、链路管理、逻辑链路控制和适应协议四部分。其中链路管理(LMP)负责蓝牙组件间连接的建立。逻辑链路控制与适应协议(L2CAP)位于基带协议层上,属于数据链路层,是一个为高层传输和应用层协议屏蔽基带协议的适配协议。
蓝牙技术作为目前比较常用的无线通信技术,早已经成为手机的标配之一,基于Android的手机设备也不例外。但遗憾的是模拟器不支持蓝牙程序的调试,蓝牙程序必须运行在真机上,且必须是在Android版本2.0以上的真机上。
Android中蓝牙有关的类和接口都位于android.bluetooth包中,如下表11-1-1所示。
表11-1-1 蓝牙功能包
功能包 |
说明 |
BluetoothAdapter |
本地蓝牙适配器 |
BluetoothClass |
蓝牙类,主要包括服务和设备 |
BluetoothClass.Device |
蓝牙设备类 |
BluetoothClass.Device.Major |
蓝牙设备管理器 |
BluetoothClass.Service |
有关蓝牙的服务类 |
BluetoothDevice |
远程蓝牙设备 |
BluetoothServerSocket |
监听蓝牙连接的类 |
BluetoothSocket |
蓝牙连接类 |
这些蓝牙API允许应用程序扫描、连接和断开其它蓝牙设备,包括编写和修改本地服务的SDP协议数据库和查询其它蓝牙设备上的SDP协议数据库,以及在Android上建立RFCOMM协议的连接并连接到其它指定设备上。
11.2 蓝牙的打开、关闭及搜索
通过11.1小节的学习我们知道Android中与蓝牙相关的类和接口都定义在了android.bluetooth包中,我们常用的主要是BluetoothAdapter和BluetoothDevice两个类。其中BluetoothAdapter类的对象代表了本地的蓝牙适配器;BluetoothDevice代表了一个远程的Bluetooth设备。
扫描已经配对的蓝牙设备时,包括手机和电脑配对,必须得通过手动完成,不能通过代码完成,我们应该把主要的精力放在配对完成之后的操作上来。核心步骤如下:
(1) 获得BluetoothAdapter对象;
(2) 判断当前设备中是否拥有蓝牙设备;
(3) 判断当前设备中的蓝牙设备是否已经打开,如果没有打开的话,要打开;
(4) 得到所有已经配对的蓝牙设备对象BluetoothDevice;
在使用蓝牙之前,需要在功能清单文件AndroidManifest.xm中添加如下权限,
<uses-permission android:name=”android.permission.BLUETOOTH”/>
<uses-permission android:name=”android.permission.BLUETOOTH_ADMIN”/>
BluetoothAdapter是蓝牙的核心类,下面的代码创建了BluetoothAdapter对象:
adapter = BluetoothAdapter.getDefaultAdapter();
通过代码还可以直接打开系统的蓝牙设置界面,代码如下:
Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enable, 22);
或直接使用enable()方法打开蓝牙功能,代码如下:
要关闭蓝牙,可以使用如下的代码:
adapter.disable();
蓝牙设备打开之后,还需要让其它的蓝牙设备可以搜索到自己,蓝牙才能使用,要想让别人能够搜索到自己,需要在程序中加入如下代码:
Intent discoveryIntent =
new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(discoveryIntent,22);
每一个蓝牙设备由BluetoothDevice描述,需要定义一个List对象,来保存搜索到的蓝牙设备,具体代码如下:
private List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
接着调用BluetoothAdapter的startDiscovery()方法就可以搜索附近的蓝牙设备了。系统会在每搜索到一个蓝牙设备时发送一个广播,通过接收这个广播,可以获得搜索到的蓝牙设备信息。当搜索完成时还会发送一个广播,可以在该广播接收器中做一些收尾工作。
示例11.1:
演示了上述蓝牙的常用操作,Activity类的详细代码如下所示:
public class DiscoveryActivity extends ListActivity {
private Handler handler = null;
private BluetoothAdapter adapter = null;// 蓝牙适配器对象
private List<BluetoothDevice> devices = null;// 用来存储搜索到的蓝牙设备
private volatile boolean discoveryFinished;// 表示搜索是否完成
private Runnable discoveryWorkder = new Runnable() {
public void run() {
adapter.startDiscovery();// 开始搜索
while (true) {
if (discoveryFinished) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
};
// 搜索蓝牙设备时调用
private BroadcastReceiver foundReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
// 获得搜索结果数据
BluetoothDevice device =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 将结果添加到设备列表中
devices.add(device);
// 显示列表
showDevices();
}
};
// 搜索完成时调用
private BroadcastReceiver discoveryReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 卸载注册的接收器
unregisterReceiver(foundReceiver);
unregisterReceiver(this);
discoveryFinished = true;
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(LayoutParams.FLAG_BLUR_BEHIND, LayoutParams.FLAG_BLUR_BEHIND);
setContentView(R.layout.discovery);
devices = new ArrayList<BluetoothDevice>();
adapter = BluetoothAdapter.getDefaultAdapter();
handler = new Handler();
if (adapter != null) {
System.out.println("本机拥有蓝牙设备");
// 如果蓝牙适配器没有打开,则打开
if (!adapter.isEnabled()) {
adapter.enable();
}
// 注册discoveryReceiver接收器
IntentFilter discoveryFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(discoveryReceiver, discoveryFilter);
// 注册foundReceiver接收器
IntentFilter foundFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(foundReceiver, foundFilter);
// 显示一个对话框,正在搜索蓝牙设备
SamplesUtils.indeterminate(DiscoveryActivity.this,
handler, "正在扫描...",
discoveryWorkder,
new OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
for (; adapter.isDiscovering();) {
adapter.cancelDiscovery();
}
discoveryFinished = true;
}
}, true);
}else {
System.out.println("本机没有蓝牙设备");
}
}
// 显示搜索设备列表
protected void showDevices() {
List<String> list = new ArrayList<String>();
for (int i = 0, size = devices.size(); i < size; ++i) {
StringBuilder builder = new StringBuilder();
BluetoothDevice device = devices.get(i);
builder.append(device.getName()).append(" : ").append(device.getAddress());
list.add(builder.toString());
}
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
handler.post(new Runnable() {
public void run() {
setListAdapter(adapter);
}
});
}
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent result = new Intent();
result.putExtra(BluetoothDevice.EXTRA_DEVICE, devices.get(position));
setResult(RESULT_OK, result);
finish();
}
}
将程序部署到真机上运行测试程序,结果如下图10.1.1和图10.1.2所示。
11.3 Wi-Fi入门
Wi-Fi的全称是Wireless Fidelity,是一种高速的无线通信协议。Wi-Fi最大的优点是传输速度高,传输速度可以达到11M/S,另外Wi-Fi的有效传输距离也很长。
Wi-Fi的频段在世界范围内是无需任何电信运营执照就可以免费使用,因此WLAN无线设备提供了一个世界范围内可用的、费用极低且数据带宽极高的无线空中接口。用户可以在Wi-Fi覆盖区域内快速浏览网页、随时随地接听、拨打电话。而其它一些基于WLAN的宽带数据应用,如流媒体、网络游戏等功能更是值得用户期待。有了Wi-Fi功能,我们打电话(包括国际长途)、浏览网页、收发电子邮件、音乐下载、数码照片传递等,再也无需担心速度慢和花费高的问题。现在Wi-Fi在国内的覆盖范围越来越广泛,比如高级宾馆、豪华住宅区、飞机场以及咖啡厅之类的场所都有Wi-Fi接口。当我们去旅游、办公时,就可以在这些场所使用我们的移动设备尽情网上冲浪了。
实际上,对于Wi-Fi并不需要过多的控制,当成功连接Wi-Fi后,就可以直接通过IP在Wi-Fi设备之间进行通信了。一般只需要控制打开或关闭Wi-Fi以及获得一些与Wi-Fi相关的信息,基本上来自请求端的信息都是可见的,比如连接速度、IP地址、完成状态等。不幸的是Wi-Fi功能不能在Android模拟器上测试,得使用支持Wi-Fi功能的Android真机才行,就算在有Wi-Fi功能的真机上也需要先通过Wi-Fi和其它Wi-Fi设备连接后,才能获得Wi-Fi相关的信息。
Android中编写Wi-Fi程序,主要涉及以下几个类和接口。
Ø ScanResult:主要用来描述已经检测出的接入点,包括接入点的地址、接入点的名称、身份认证、频率、信号强度等信息。
Ø WifiConfiguration:Wi-Fi网络的配置,包括安全配置等。
Ø WifiManager:提供了管理Wi-Fi连接的大部分API,它主要包括如下内容
(1) 已经配置好的网络清单。这个清单可以查看和修改,而且可以修改个别记录的属性。
(2) 当连接中有活动的Wi-Fi网络时,可以建立或关闭这个连接,并且可以查询有关网络的状态信息。
(3) 对接入点的扫描结果包含足够的信息来决定需要与什么接入点建立连接。
(4) 定义了许多常量来表示Wi-Fi状态的改变。
Ø WifiInfo:Wi-Fi无线连接的描述,包括接入点、网络连接状态、隐藏的接入点、IP地址、连接速度、MAC地址、网络ID、信号强度等信息。
示例11.2
演示如何开关闭Wi-Fi以及获取Wi-Fi相关信息,这些信息包括:MAC地址、接入点的BSSID、IP地址、网络ID等。
布局文件的详细代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<CheckBox
android:id="@+id/chkOpenCloseWifi"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvScanResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvWifiInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp" />
<TextView
android:id="@+id/tvWifiConfigurations"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textSize="20sp" />
</LinearLayout>
Activity类代码如下:
public class WIFIActivity extends Activity implements OnCheckedChangeListener {
private WifiManager manager;
private WifiInfo wifiInfo;
private CheckBox chkOpenCloseWifiBox;
private List<WifiConfiguration> wifiConfigurations;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wifi);
manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifiInfo = manager.getConnectionInfo();
chkOpenCloseWifiBox = (CheckBox) findViewById(R.id.chkOpenCloseWifi);
TextView tvWifiConfigurations = (TextView) findViewById(R.id.tvWifiConfigurations);
TextView tvWifiInfo = (TextView) findViewById(R.id.tvWifiInfo);
chkOpenCloseWifiBox.setOnCheckedChangeListener(this);
if (manager.isWifiEnabled()) {
chkOpenCloseWifiBox.setText("Wifi已开启");
chkOpenCloseWifiBox.setChecked(true);
} else {
chkOpenCloseWifiBox.setText("Wifi已关闭");
chkOpenCloseWifiBox.setChecked(false);
}
// 获得Wifi信息
StringBuffer sb = new StringBuffer();
sb.append("Wifi信息\n");
sb.append("MAC地址:" + wifiInfo.getMacAddress() + "\n");
sb.append("接入点的BSSID:" + wifiInfo.getBSSID() + "\n");
sb.append("IP地址(int):" + wifiInfo.getIpAddress() + "\n");
sb.append("IP地址(Hex):" + Integer.toHexString(wifiInfo.getIpAddress()) + "\n");
sb.append("IP地址:" + ipIntToString(wifiInfo.getIpAddress()) + "\n");
sb.append("网络ID:" + wifiInfo.getNetworkId() + "\n");
tvWifiInfo.setText(sb.toString());
// 得到配置好的网络
wifiConfigurations = manager.getConfiguredNetworks();
tvWifiConfigurations.setText("已连接的无线网络\n");
for (WifiConfiguration wifiConfiguration : wifiConfigurations) {
tvWifiConfigurations.setText(tvWifiConfigurations.getText() + wifiConfiguration.SSID + "\n");
}
}
private String ipIntToString(int ip) {
try {
byte[] bytes = new byte[4];
bytes[0] = (byte) (0xff & ip);
bytes[1] = (byte) ((0xff00 & ip) >> 8);
bytes[2] = (byte) ((0xff0000 & ip) >> 16);
bytes[3] = (byte) ((0xff000000 & ip) >> 24);
return Inet4Address.getByAddress(bytes).getHostAddress();
} catch (Exception e) {
return "";
}
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked){
if (isChecked) {
manager.setWifiEnabled(true);
chkOpenCloseWifiBox.setText("Wifi已开启");
} else {
manager.setWifiEnabled(false);
chkOpenCloseWifiBox.setText("Wifi已关闭");
}
}
}
在有Wi-Fi接入点的环境中,将本程序部署到真机上进行测试,程序运行效果如下图11.1.3所示。
1:实现一个蓝牙搜索程序
训练技能点
利用BluetoothDevice实现蓝牙设备搜索
需求说明
蓝牙实现的功能是在两台或多台设备之间传传输数据,因此我们要想使用蓝牙设备,首先需要能够搜索到对应的蓝牙设备,然后才能完成数据的传输。本实训要求大家参考11.2节的内容实现一个蓝牙搜索程序,当搜索到别的蓝牙设备后,要求以Toast的形式给用户弹出提示信息。
2:获取Wi-Fi相关信息
训练技能点
如何获取Wi-Fi相关信息
需求说明
Wi-Fi的连接信息在实际的应用中是很有用的,以连接速度为例,当我们可以在程序中根据连接速度的快慢做不同的工作,比如速度比较快时上传或下载资源、慢时浏览网页等。再比如,当我们的程序需要网络时,可以根据Wi-Fi的完成状态,来判断用户是否联网,如果没有联网给用户以相应的提示。本示例要实现的功能就是获取Wi-Fi的所有信息,然后显现给用户。
巩固练习
一、选择题
1. 蓝牙工作的频度是()
A. 1.8GHz
B. 2.4GHz
C. 3.2GHz
D. 3.0GHz
2. 下列说法中正确的是( )
A. WIFI的全称是Wireless Fidelety
B. WIFI的频段在世界范围内是无需任何电信运营执照就可以免费使用
C. 当成功连接WIFI后,就可以直接通过IP在WIFI设备之间进行通信了
D. ScanResult类主要用来描述已经检测出的接入点
二、上机练习
编写一程序,实现对Wi-Fi和蓝牙开启、关闭状态的控制。