AndroidStudio蓝牙开发实例——基于Android6.0

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

AndroidStudio蓝⽛开发实例——基于Android6.0
因项⽬需要做⼀个Android 的蓝⽛app来通过⼿机蓝⽛传输数据以及控制飞⾏器,在此,我对这段时间⾥写的蓝⽛app的代码进⾏知识梳理和出现错误的总结。

该应⽤的Compile Sdk Version 和targetSdkVersion均为26,Min Sdk Version为22,基于Android studio平台开发。

⼀、声明蓝⽛权限
⾸先,要在新建项⽬中的AndroidManifest.xml中声明两个权限:BLUETOOTH权限和BLUETOOTH_ADMIN权限。

其中,BLUETOOTH权限⽤于请求连接和传送数据;BLUETOOTH_ADMIN权限⽤于启动设备、发现或进⾏蓝⽛设置,如果要拥有该权限,必须现拥有BLUETOOTH权限。

其次,因为android 6.0之后采⽤新的权限机制来保护⽤户的隐私,如果我们设置的targetSdkVersion⼤于或等于23,则需要另外添加ACCESS_COARSE_LOCATION和ACCESS_FINE_LOCATION权限,否则,可能会出现搜索不到蓝⽛设备的问题。

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
⼆、启动和关闭蓝⽛
1.⾸先,要获取BluetoothAdapter蓝⽛适配器的对象,然后检测设备是否⽀持蓝⽛。

BluetoothAdapter blueadapter = BluetoothAdapter.getDefaultAdapter();
//获取蓝⽛适配器
if(blueadapter==bull)
//表⽰⼿机不⽀持蓝⽛
return;
2.启动蓝⽛功能:isEnable()⽅法⽤来检查蓝⽛当前状态,如果⽅法返回false,则蓝⽛没启动。

enable()⽅法⽤来打开本地蓝⽛适配器。

if (!blueadapter.isEnabled())
//判断本机蓝⽛是否打开
{//如果没打开,则打开蓝⽛
blueadapter.enable();
}
3.使⽤disable()可以关闭本地蓝⽛适配器。

三、发现蓝⽛设备
1.开启当前蓝⽛的可见性
Android 设备默认是不能被搜索的,如果想要本机设备可被搜索,可以以BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE动作为startActivity()⽅法的参数,这个⽅法会提交⼀个开启蓝⽛可见的请求。

默认的情况下,设备在120秒内可以被搜索,也可以⾃定义⼀个间隔时间,但是规定的最⼤值为300秒,0秒则表⽰设备可以⼀直被搜索,⾃定义时间通过EXTRA_DISCOVERABLE_DURATION来定义,代码如下。

if (blueadapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) //不在可被搜索的范围
{
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);//设置本机蓝⽛在300秒内可见
startActivity(discoverableIntent);
}
2.调⽤startDiscover()搜索蓝⽛
开启蓝⽛后,调⽤startDiscover()⽅法搜索蓝⽛,注意,只有开启了蓝⽛可见性的设备才会响应。

该搜索过程为异步操作,调⽤后讲以⼴播的机制返回搜索到的对象,搜索的过程⼀般为12秒,搜索过程页⾯会显⽰搜索到的设备。

public void doDiscovry() {
if (blueadapter.isDiscovering()) {
//判断蓝⽛是否正在扫描,如果是调⽤取消扫描⽅法;如果不是,则开始扫描
blueadapter.cancelDiscovery();
} else
blueadapter.startDiscovery();
}
3.注册⼴播
通过blueadapter.startDiscovery()来搜索蓝⽛设备,要获取到搜索的结果需要注册⼴播。

定义⼀个列表
public ArrayAdapter adapter;
ListView listView = (ListView) findViewById(R.id.list);//控件列表
//定义⼀个列表,存蓝⽛设备的地址。

public ArrayList<String> arrayList=new ArrayList<>();
//定义⼀个列表,存蓝⽛设备地址,⽤于显⽰。

public ArrayList<String> deviceName=new ArrayList<>();
将搜索到的显⽰在控件列表上
adapter = new ArrayAdapter(this, yout.simple_expandable_list_item_1, deviceName);
listView.setAdapter(adapter);
定义⼴播和处理⼴播消息
IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);//注册⼴播接收信号
registerReceiver(bluetoothReceiver, intentFilter);//⽤BroadcastReceiver 来取得结果
private final BroadcastReceiver bluetoothReceiver = new 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);
deviceName.add("设备名:"+device.getName()+"\n" +"设备地址:"+device.getAddress() + "\n");//将搜索到的蓝⽛名称和地址添加到列表。

arrayList.add( device.getAddress());//将搜索到的蓝⽛地址添加到列表。

adapter.notifyDataSetChanged();//更新
}
}
};
搜索完设备后,要记得注销⼴播。

注册后的⼴播对象在其他地⽅有强引⽤,如果不取消,activity会释放不了资源。

protected void onDestroy(){
super.onDestroy();//解除注册
unregisterReceiver(bluetoothReceiver);
}
4.了解targetSdkVersion是否⼤于或等于23
若是⼤于或等于23,除了添加了蓝⽛权限外,还要动态获取位置权限,才能将搜索到的蓝⽛设备显⽰出来。

若是⼩于,则不需要动态获取权限。

动态申请权限,⽹上例⼦如下。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == RESULT_OK) {
textView.setText("打开蓝⽛成功");
}
if (resultCode == RESULT_CANCELED) {
textView.setText("放弃打开蓝⽛");
}
} else {
textView.setText("蓝⽛异常");
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_COARSE_LOCATION:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
}
break;
}
}
四、配对蓝⽛设备
蓝⽛的配对和连接有两种⽅式。

⼀种是每个设备作为⼀个客户端去连接⼀个服务端,向对⽅发起连接。

另⼀种则是作为服务端来接收客户端发来连接的消息。

蓝⽛之间的数
据传输采⽤的是和TCP传输类似的传输机制。

1.作为客户端连接
⾸先要获取⼀个代表远程设备BluetoothDevice的对象,然后使⽤该BluetoothDevice的对象来获取⼀个BluetoothSocket对象。

BluetoothSocket对象调⽤connect()可以建⽴连
接。

蓝⽛连接整个过程需要在⼦线程中执⾏的,并且要将 scoket.connect()放在⼀个新的⼦线程中,因为如果将这个⽅法也放在同⼀个⼦线程中解决的话,就会永远报错read
failed, socket might closed or timeout, read ret: -1;借鉴⽹上的⽅法:再开⼀个⼦线程专门执⾏socket.connect()⽅法,问题可以解决;
另外,借鉴⽹上⽅法和建议,在获得socket的时候,尽量不使⽤uuid⽅式;因为这样虽然能够获取到socket 但是不能进⾏⾃动,所以使⽤的前提是已经配对了的设备连接;
使⽤反射的⽅式,能够⾃动提⽰配对,也适合⼿机间通信。

final BluetoothSocket socket = (BluetoothSocket) device.getClass().getDeclaredMethod("createRfcommSocket", new Class[]{int.class}).invoke(device, 1);
代码中的device需要把注册⼴播时的device作为参数传进线程中。

注意,传进来的device的值要为远程设备的地址,若不是或有出⼊,则可能会出现NullPointerException异
常,并提⽰尝试调⽤⼀个空的对象。

为了解决这个问题,可以把显⽰获得的device名字、地址和传⼊线程的device的地址分在不同的集合类。

传⼊线程的device使⽤只有设备地
址的集合类。

在连接蓝⽛之前,还要先取消蓝⽛设备的扫描,否则容易连接失败。

adapter.cancelDiscovery();//adapter为获取到的蓝⽛适配器
socket.connect();//连接
2.作为服务端连接
服务端接收连接需要使⽤BluetoothServerSocket类,它的作⽤是监听进来的连接,在⼀个连接被接收之后,会返回⼀个BluetoothSocket对象,这个对象可以⽤来和客户端进
⾏通信。

与客户端⼀样,服务端也要在⼦线程中实现。

通过调⽤listenUsingRfcommWithServiceRecord(String,UUID)⽅法可以得到⼀个BluetoothServerSocket的对象,然后再⽤
这个对象来调⽤accept()来返回⼀个BluetoothSocket对象。

由于accept()是个阻塞的⽅法,它会直到接收到⼀个连接或异常之后才会返回,所以要放在⼦线程中。

bluetoothServerSocket=bluetoothAdapter.listenUsingRfcommWithServiceRecord(bluetoothAdapter.getDefaultAdapter().getName(), UUID.fromString("00001101-0000-1000-8000-00805F9B34FB" //bluetoothServerSocket= (BluetoothServerSocket) bluetoothAdapter.getClass().getMethod("listenUsingRfcommOn",new Class[]{int.class}).invoke(bluetoothAdapter,10);
socket=bluetoothServerSocket.accept();//接收连接
代码中注释掉的内容是通过反射的⽅式来接收,由于我使⽤时出现了异常,所以暂时不考虑这个⽅法。

还有,与TCP不同的是,这个连接时只允许⼀个客户端连接,因此在BluetoothServerSocket对象接收到⼀个连接请求时就要⽴刻调⽤close()⽅法把服务端关闭。

五、客户端发送数据
当两个设备成功连接之后,双⽅都会有⼀个BluetoothSocket对象,这时,就可以在设备之间传送数据了。

1.使⽤getOutputStream()⽅法来获取输出流来处理传输。

2.调⽤write()。

os = socket.getOutputStream();//获取输出流
if (os != null) {//判断输出流是否为空
os.write(message.getBytes("UTF-8"));
}
os.flush();//将输出流的数据强制提交
os.close();//关闭输出流
}
将输出流中的数据提交后,要记得关闭输出流,否则,可能会造成只能发送⼀次数据。

六、服务端接收数据
1.使⽤getInputStream()⽅法来获取输⼊流来处理传输。

2.调⽤read()。

InputStream im=null;
im=bluetoothSocket.getInputStream();
byte buf[] = new byte[1024];
if (is != null) {
is.read(buf, 0, buf.length);//读取发来的数据
String message = new String(buf);//把发来的数据转化为String类型
BuletoothMainActivity.UpdateRevMsg(message);//更新信息在显⽰⽂本框
is.close();//关闭输⼊流
使⽤服务端接收数据时,要先从客户端向服务端发起连接,只有接收到连接请求之后,才会返回⼀个BluetoothSocket对象。

有BluetoothSocket对象才能获取到输⼊流。

下⾯是将接收到数据显⽰在界⾯的⽅法:
在Activity中定义Handler类的对象handler。

public static void UpdateRevMsg(String revMsg) {
mRevMsg=revMsg;
handler.post(RefreshTextView);
}
private static Runnable RefreshTextView=new Runnable() {
@Override
public void run() {
textView2.setText(mRevMsg);
}
};。

相关文档
最新文档