前言
上篇文章我们介绍到了开发经典蓝牙和单片机通讯的过程,安卓通讯之《蓝牙单片机通讯助手》①集成工作 ,我们这里还要兼容最新的安卓6.0及以上的系统,因为从6.0以后的权限机制和以往的不一样,我们需要在代码中向用户询问权限。而且在6.0运行蓝牙,还需要加上获取到此刻的地理位置的权限,这是谷歌加的~~,所以我们先把运行的权限弄好先,再扫描设备、连接设备和双向通讯。
权限问题(兼容安卓6.0及以上)
很多小伙伴问我,为什么你以前写的安卓高版本的蓝牙App现在在高版本的安卓机就不可以获取到附近的蓝牙设备啦?这也是我上面提到的:在6.0运行蓝牙,还需要加上获取到此刻的地理位置的权限。所以我们先把权限弄好。我下面是用郭神封装好的权限代码。你们如有不懂,去CSDN搜索郭霖6.0就有当天的直播权限封装视频解说。
private void checkpermission() {
//判断是否APi等于或大于23,即6.0
if(Build.VERSION.SDK_INT >= 23){
//所需要的权限加进去
requestRuntimePermission(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION
,Manifest.permission.ACCESS_FINE_LOCATION
,Manifest.permission.BLUETOOTH
,Manifest.permission.BLUETOOTH_ADMIN}, new PermissionListener() {
//权限回调
@Override
public void onGranted() {
//全部都授权就跳转下个Activity
Log.i("权限全部都同意了","-----==");
}
//某个没有被授权则强制退出程序
@Override
public void onDenied(List<String> deniedPermission) {
for(String persmission:deniedPermission){
Toast.makeText(SplashActivity.this,"基于你的安卓版本大于6.0,未授予该权限导致不能运行,强制退出:"+persmission,Toast.LENGTH_LONG).show();
} }
});
}else {
//手机在6.0以下,则不需要获取到地理位置权限,直接跳过
}
}
打开蓝牙扫描工作
打开蓝牙工作,相信大家都会,用户在此打开蓝牙的是否,要用 intent回调。在这里,我们还需要写进一个蓝牙广播接收器,来监听系统发出来的已经搜索到的附近蓝牙设备,并且获取该数据的名字和蓝牙地址。
代码部分说明:
我这里用的是一个listVIew控件来展示当前可以连接的蓝牙设备。点击某一项,则把该项的蓝牙名字和id传下去到一个Activity的通讯工作。中间的是一个进度条之类的,自定义控件。
package com.example.xuhong.bluetoothassistant_master;
import android.app.Activity;
import android.app.ListActivity;
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.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import com.example.xuhong.bluetoothassistant_master.adapter.DeviceListAdapter;
import com.example.xuhong.bluetoothassistant_master.ui.WhorlView;
public class DeviceScanActivity extends ListActivity {
private TextView mtv_show;
// 调试用
private static final String TAG = "DeviceScanActivity";
// 开启蓝牙请求码
private static final int REQUEST_ENABLE = 0;
// 停止扫描蓝牙消息头
private static final int WHAT_CANCEL_DISCOVERY = 1;
// 判断蓝牙列表
private static final int WHAT_DEVICE_UPDATE = 2;
// 扫描间隔时间
private static final int SCAN_PERIOD = 30 * 1000;
//实例化Adapter
private DeviceListAdapter mLeDeviceListAdapter = null;
// 蓝牙适配器
private BluetoothAdapter mBluetoothAdapter = null;
// 进度条
private WhorlView mWhorlView = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_device_scan);
init();
}
@Override
protected void onStart() {
super.onStart();
mLeDeviceListAdapter = new DeviceListAdapter(this);
// 设置列表适配器,注:调用此方法必须继承ListActivity
setListAdapter(mLeDeviceListAdapter);
scanDevice(true);
mWhorlView.setVisibility(View.VISIBLE);
}
@Override
protected void onPause() {
scanDevice(false);
super.onPause();
}
@Override
protected void onDestroy() {
unregReceiver();
super.onDestroy();
}
//回调函数
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if ( resultCode == Activity.RESULT_CANCELED) {
finish();
Process.killProcess(Process.myPid());
}
switch (requestCode){
case REQUEST_ENABLE:
if ( mBluetoothAdapter.isEnabled()) {
registerReceiver();
scanDevice(true);
}
break;
}
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
if (device == null) {
return;
}
Intent intent = new Intent(this, MainActivity.class);
Bundle bundle = new Bundle();
bundle.putParcelable("device", device);
intent.putExtras(bundle);
scanDevice(false);
startActivity(intent);
finish();
}
/**
* 消息处理者
*/
private Handler mHandler = new Handler(new Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case WHAT_DEVICE_UPDATE:
mLeDeviceListAdapter.addDevice((BluetoothDevice) msg.obj);
// 刷新列表
mLeDeviceListAdapter.notifyDataSetChanged();
break;
case WHAT_CANCEL_DISCOVERY:
mWhorlView.setVisibility(View.GONE);
mtv_show.setText("搜索完毕!");
break;
default:
break;
}
return false;
}
});
/**
* 初始化
*/
private void init() {
mtv_show= (TextView) findViewById(R.id.tv_show);
mWhorlView = (WhorlView) findViewById(R.id.whorl_view);
// 开启动画
mWhorlView.start();
// 初始化本地蓝牙设备
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 检测蓝牙设备是否开启,如果未开启,发起Intent并回调
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE);
}
registerReceiver();
}
/**
* 是否扫描蓝牙设备
*/
private void scanDevice(boolean enable) {
if (enable) {
Log.d(TAG, "[1]--> startDiscovery()");
// 开启扫描
mBluetoothAdapter.startDiscovery();
// 延时30s后取消扫描动作
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothAdapter.cancelDiscovery();
Log.d(TAG, "[2]--> cancelDiscovery()");
// 发送消息
mHandler.sendEmptyMessage(WHAT_CANCEL_DISCOVERY);
}
}, SCAN_PERIOD);
} else {
Log.d(TAG, "[3]--> cancelDiscovery()");
// 停止扫描
mBluetoothAdapter.cancelDiscovery();
}
}
/**
* 注册广播接收器
*/
private void registerReceiver() {
registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
}
/**
* 注销广播接收器
*/
private void unregReceiver() {
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
}
/**
* 广播接收器接收返回的蓝牙信息
*/
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//未配对的设备
if (BluetoothDevice.ACTION_FOUND == action) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "[4] --> " + device.getName() + "------" + device.getAddress());
if (device != null) {
//发送消息
mHandler.sendMessage(mHandler.obtainMessage(WHAT_DEVICE_UPDATE, device));
}
}
}
};
}
通讯工作
我这里用的是一个spinner控件和一个button,怎么使用去看看相关博文,我这里只是供用户选择发送到哪一个数据。因为单片机通讯都是基本的 十六进制 0x00格式,所以我这里只是规范下发送的格式,你们也可以完全按照自己的想法设计UI。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.ToggleButton;
import com.example.xuhong.bluetoothassistant_master.adapter.ChatListAdapter;
import com.example.xuhong.bluetoothassistant_master.data.ChatListData;
import com.example.xuhong.bluetoothassistant_master.util.Toaster;
// 2016.09.15. 徐宏 编写
public class MainActivity extends Activity {
private ListView mChatListView;
//列表
private List<ChatListData> mList = new ArrayList<>();
private ChatListAdapter chatListAdapter;
private static final String TAG = "MainActivity";
// uuid
private static final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";
private Spinner mSpinner;
private String[] data = new String[]{"选择 0x01", "选择 0x02", "选择 0x03", "选择 0x04", "选择 0x05", "选择 0x06", "选择 0x07" , "选择 0x08", "选择 0x09"};
private Byte sendData = null;
private String mRecieve =null;
private ArrayAdapter<String> adapter;
//获得系统的适配器
private BluetoothDevice mBluetoothDevice = null;
//创建socket
private BluetoothSocket mSocket = null;
//io流
private OutputStream mOutS = null;
private static final int CONNECT_SUCCED =10;
private static final int mRecieve_SUCCED = 20;
private InputStream input =null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//全屏显示
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
initview();
}
private void initview() {
mSpinner = (Spinner) findViewById(R.id.mSpinner);
mChatListView = (ListView)findViewById(R.id.mChatListView);
chatListAdapter=new ChatListAdapter(MainActivity.this,mList);
mChatListView.setAdapter(chatListAdapter);
adapter= new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data);
mSpinner.setAdapter(adapter);
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int id, long l) {
switch (id){
case 0:
sendData=1;
Log.i(TAG,"点击0");
break;
case 1:
sendData=2;
Log.i(TAG,"点击1");
break;
case 2:
sendData=3;
Log.i(TAG,"点击2");
break;
case 3:
sendData=4;
Log.i(TAG,"点击3");
break;
case 4:
sendData=5;
Log.i(TAG,"点击4");
break;
case 5:
sendData=6;
Log.i(TAG,"点击5");
break;
case 6:
sendData=7;
Log.i(TAG,"点击6");
break;
case 7:
sendData=8;
Log.i(TAG,"点击7");
break;
case 8:
sendData=9;
Log.i(TAG,"点击8");
break;
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
}
//添加右边文本
private void addRightItem(String text) {
ChatListData date = new ChatListData();
date.setType(ChatListAdapter.VALUE_RIGHT_TEXT);
date.setText(text);
mList.add(date);
//通知adapter刷新
adapter.notifyDataSetChanged();
//滚动到底部
mChatListView.setSelection(mChatListView.getBottom());
}
//添加左边文本
private void addLeftItem(String text) {
ChatListData date = new ChatListData();
date.setType(ChatListAdapter.VALUE_LEFT_TEXT);
date.setText(text);
mList.add(date);
//通知adapter刷新
adapter.notifyDataSetChanged();
//滚动到底部
mChatListView.setSelection(mChatListView.getBottom());
}
public void btn_send(View v){
writeStream(sendData);
addRightItem(sendData+"");
}
@Override
protected void onStart() {
super.onStart();
initDevice();
}
@Override
protected void onStop() {
close();
super.onStop();
}
private void initDevice() {
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
mBluetoothDevice = bundle.getParcelable("device");
if (mBluetoothDevice != null) {
new Thread(mConnRun).start();
}
}
}
private Runnable mConnRun = new Runnable() {
@Override
public void run() {
connect();
}
};
private void connect() {
UUID uuid = UUID.fromString(SPP_UUID);
try {
mSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(uuid);
} catch (IOException e) {
if (mSocket != null) {
try {
mSocket.close();
} catch (IOException e1) {
Log.e(TAG, e1.getMessage());
}
}
}
try {
mSocket.connect();
} catch (IOException e) {
if (mSocket != null) {
try {
mSocket.close();
} catch (IOException e1) {
Log.e(TAG, e1.getMessage());
}
}
}
try {
mOutS = mSocket.getOutputStream();
input=mSocket.getInputStream();
handler.sendEmptyMessage(CONNECT_SUCCED);
} catch (IOException e) {
if (mOutS != null) {
try {
mOutS.close();
} catch (IOException e1) {
Log.e(TAG, e.getMessage());
}
}
if (mSocket != null) {
try {
mSocket.close();
} catch (IOException e1) {
Log.e(TAG, e.getMessage());
}
}
}
}
private void close() {
if (mOutS != null) {
try {
mOutS.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
if (mSocket != null) {
try {
mSocket.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
}
private void writeStream(byte data) {
try {
if (mOutS != null) {
mOutS.write(data);
Log.i(TAG,"输出---->>>是:"+data);
mOutS.flush();
}
} catch (IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toaster.shortToastShow(MainActivity.this, "连接超时");
MainActivity.this.finish();
}
});
}
}
public Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
break;
case mRecieve_SUCCED:
addLeftItem(mRecieve);
break;
case CONNECT_SUCCED:
Log.i(TAG,"接收成功");
new MyThread().start(); //开启下面的线程
break;
}
}
};
//新开的一个接收的线程
class MyThread extends Thread{
@Override
public void run() {
while (true){
try {
//获取到的数据
int read = input.read();
//因为不允许在子线程更新UI所以用handler
handler.sendEmptyMessage(mRecieve_SUCCED);
Log.i(TAG+"数据是",Integer.toHexString(read));
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG,"异常"+e);
}
}}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode== KeyEvent.KEYCODE_BACK){
AlertDialog.Builder alertDialog=new AlertDialog.Builder(MainActivity.this);
alertDialog.setTitle("确认!");
alertDialog.setMessage(" 确定退出智能锁控制吗");
alertDialog.setPositiveButton("否",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
}
});
alertDialog.setNegativeButton("是", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
MainActivity.this.finish();
}
});
alertDialog.show();
}
return false;
}
}
运行效果:
完美通讯~~