上篇文章我们介绍到了开发经典蓝牙和单片机通讯的过程,安卓通讯之《蓝牙单片机通讯助手》①集成工作 ,我们这里还要兼容最新的安卓6.0及以上的系统,因为从6.0以后的权限机制和以往的不一样,我们需要在代码中向用户询问权限。而且在6.0运行蓝牙,还需要加上获取到此刻的地理位置的权限,这是谷歌加的~~,所以我们先把运行的权限弄好先,再扫描设备、连接设备和双向通讯。



很多小伙伴问我,为什么你以前写的安卓高版本的蓝牙App现在在高版本的安卓机就不可以获取到附近的蓝牙设备啦?这也是我上面提到的:在6.0运行蓝牙,还需要加上获取到此刻的地理位置的权限。所以我们先把权限弄好。我下面是用郭神封装好的权限代码。你们如有不懂,去CSDN搜索郭霖6.0就有当天的直播权限封装视频解说。


  1. private void checkpermission() {
  2. //判断是否APi等于或大于23,即6.0
  3. if(Build.VERSION.SDK_INT >= 23){
  4. //所需要的权限加进去
  5. requestRuntimePermission(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION
  6. ,Manifest.permission.ACCESS_FINE_LOCATION
  7. ,Manifest.permission.BLUETOOTH
  8. ,Manifest.permission.BLUETOOTH_ADMIN}, new PermissionListener() {
  9. //权限回调
  10. @Override
  11. public void onGranted() {
  12. //全部都授权就跳转下个Activity
  13. Log.i("权限全部都同意了","-----==");
  14. }
  15. //某个没有被授权则强制退出程序
  16. @Override
  17. public void onDenied(List<String> deniedPermission) {
  18. for(String persmission:deniedPermission){
  19. Toast.makeText(SplashActivity.this,"基于你的安卓版本大于6.0,未授予该权限导致不能运行,强制退出:"+persmission,Toast.LENGTH_LONG).show();
  20. } }
  21. });
  22. }else {
  23. //手机在6.0以下,则不需要获取到地理位置权限,直接跳过
  24. }
  25. }


打开蓝牙工作,相信大家都会,用户在此打开蓝牙的是否,要用 intent回调。在这里,我们还需要写进一个蓝牙广播接收器,来监听系统发出来的已经搜索到的附近蓝牙设备,并且获取该数据的名字和蓝牙地址。


我这里用的是一个listVIew控件来展示当前可以连接的蓝牙设备。点击某一项,则把该项的蓝牙名字和id传下去到一个Activity的通讯工作。中间的是一个进度条之类的,自定义控件。


扫描

  1. package com.example.xuhong.bluetoothassistant_master;
  2. import android.app.Activity;
  3. import android.app.ListActivity;
  4. import android.bluetooth.BluetoothAdapter;
  5. import android.bluetooth.BluetoothDevice;
  6. import android.content.BroadcastReceiver;
  7. import android.content.Context;
  8. import android.content.Intent;
  9. import android.content.IntentFilter;
  10. import android.os.Bundle;
  11. import android.os.Handler;
  12. import android.os.Handler.Callback;
  13. import android.os.Message;
  14. import android.os.Process;
  15. import android.util.Log;
  16. import android.view.View;
  17. import android.widget.ListView;
  18. import android.widget.TextView;
  19. import com.example.xuhong.bluetoothassistant_master.adapter.DeviceListAdapter;
  20. import com.example.xuhong.bluetoothassistant_master.ui.WhorlView;
  21. public class DeviceScanActivity extends ListActivity {
  22. private TextView mtv_show;
  23. // 调试用
  24. private static final String TAG = "DeviceScanActivity";
  25. // 开启蓝牙请求码
  26. private static final int REQUEST_ENABLE = 0;
  27. // 停止扫描蓝牙消息头
  28. private static final int WHAT_CANCEL_DISCOVERY = 1;
  29. // 判断蓝牙列表
  30. private static final int WHAT_DEVICE_UPDATE = 2;
  31. // 扫描间隔时间
  32. private static final int SCAN_PERIOD = 30 * 1000;
  33. //实例化Adapter
  34. private DeviceListAdapter mLeDeviceListAdapter = null;
  35. // 蓝牙适配器
  36. private BluetoothAdapter mBluetoothAdapter = null;
  37. // 进度条
  38. private WhorlView mWhorlView = null;
  39. @Override
  40. public void onCreate(Bundle savedInstanceState) {
  41. super.onCreate(savedInstanceState);
  42. setContentView(R.layout.activity_device_scan);
  43. init();
  44. }
  45. @Override
  46. protected void onStart() {
  47. super.onStart();
  48. mLeDeviceListAdapter = new DeviceListAdapter(this);
  49. // 设置列表适配器,注:调用此方法必须继承ListActivity
  50. setListAdapter(mLeDeviceListAdapter);
  51. scanDevice(true);
  52. mWhorlView.setVisibility(View.VISIBLE);
  53. }
  54. @Override
  55. protected void onPause() {
  56. scanDevice(false);
  57. super.onPause();
  58. }
  59. @Override
  60. protected void onDestroy() {
  61. unregReceiver();
  62. super.onDestroy();
  63. }
  64. //回调函数
  65. @Override
  66. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  67. if ( resultCode == Activity.RESULT_CANCELED) {
  68. finish();
  69. Process.killProcess(Process.myPid());
  70. }
  71. switch (requestCode){
  72. case REQUEST_ENABLE:
  73. if ( mBluetoothAdapter.isEnabled()) {
  74. registerReceiver();
  75. scanDevice(true);
  76. }
  77. break;
  78. }
  79. }
  80. @Override
  81. protected void onListItemClick(ListView l, View v, int position, long id) {
  82. super.onListItemClick(l, v, position, id);
  83. BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
  84. if (device == null) {
  85. return;
  86. }
  87. Intent intent = new Intent(this, MainActivity.class);
  88. Bundle bundle = new Bundle();
  89. bundle.putParcelable("device", device);
  90. intent.putExtras(bundle);
  91. scanDevice(false);
  92. startActivity(intent);
  93. finish();
  94. }
  95. /**
  96. * 消息处理者
  97. */
  98. private Handler mHandler = new Handler(new Callback() {
  99. @Override
  100. public boolean handleMessage(Message msg) {
  101. switch (msg.what) {
  102. case WHAT_DEVICE_UPDATE:
  103. mLeDeviceListAdapter.addDevice((BluetoothDevice) msg.obj);
  104. // 刷新列表
  105. mLeDeviceListAdapter.notifyDataSetChanged();
  106. break;
  107. case WHAT_CANCEL_DISCOVERY:
  108. mWhorlView.setVisibility(View.GONE);
  109. mtv_show.setText("搜索完毕!");
  110. break;
  111. default:
  112. break;
  113. }
  114. return false;
  115. }
  116. });
  117. /**
  118. * 初始化
  119. */
  120. private void init() {
  121. mtv_show= (TextView) findViewById(R.id.tv_show);
  122. mWhorlView = (WhorlView) findViewById(R.id.whorl_view);
  123. // 开启动画
  124. mWhorlView.start();
  125. // 初始化本地蓝牙设备
  126. mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  127. // 检测蓝牙设备是否开启,如果未开启,发起Intent并回调
  128. if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
  129. Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  130. startActivityForResult(enableIntent, REQUEST_ENABLE);
  131. }
  132. registerReceiver();
  133. }
  134. /**
  135. * 是否扫描蓝牙设备
  136. */
  137. private void scanDevice(boolean enable) {
  138. if (enable) {
  139. Log.d(TAG, "[1]--> startDiscovery()");
  140. // 开启扫描
  141. mBluetoothAdapter.startDiscovery();
  142. // 延时30s后取消扫描动作
  143. mHandler.postDelayed(new Runnable() {
  144. @Override
  145. public void run() {
  146. mBluetoothAdapter.cancelDiscovery();
  147. Log.d(TAG, "[2]--> cancelDiscovery()");
  148. // 发送消息
  149. mHandler.sendEmptyMessage(WHAT_CANCEL_DISCOVERY);
  150. }
  151. }, SCAN_PERIOD);
  152. } else {
  153. Log.d(TAG, "[3]--> cancelDiscovery()");
  154. // 停止扫描
  155. mBluetoothAdapter.cancelDiscovery();
  156. }
  157. }
  158. /**
  159. * 注册广播接收器
  160. */
  161. private void registerReceiver() {
  162. registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
  163. }
  164. /**
  165. * 注销广播接收器
  166. */
  167. private void unregReceiver() {
  168. if (mReceiver != null) {
  169. unregisterReceiver(mReceiver);
  170. }
  171. }
  172. /**
  173. * 广播接收器接收返回的蓝牙信息
  174. */
  175. private BroadcastReceiver mReceiver = new BroadcastReceiver() {
  176. @Override
  177. public void onReceive(Context context, Intent intent) {
  178. String action = intent.getAction();
  179. //未配对的设备
  180. if (BluetoothDevice.ACTION_FOUND == action) {
  181. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  182. Log.d(TAG, "[4] --> " + device.getName() + "------" + device.getAddress());
  183. if (device != null) {
  184. //发送消息
  185. mHandler.sendMessage(mHandler.obtainMessage(WHAT_DEVICE_UPDATE, device));
  186. }
  187. }
  188. }
  189. };
  190. }



我这里用的是一个spinner控件和一个button,怎么使用去看看相关博文,我这里只是供用户选择发送到哪一个数据。因为单片机通讯都是基本的 十六进制 0x00格式,所以我这里只是规范下发送的格式,你们也可以完全按照自己的想法设计UI。


通讯工作


  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.OutputStream;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import java.util.UUID;
  7. import android.app.AlertDialog;
  8. import android.content.DialogInterface;
  9. import android.os.Bundle;
  10. import android.app.Activity;
  11. import android.bluetooth.BluetoothDevice;
  12. import android.bluetooth.BluetoothSocket;
  13. import android.os.Handler;
  14. import android.os.Message;
  15. import android.util.Log;
  16. import android.view.KeyEvent;
  17. import android.view.View;
  18. import android.view.Window;
  19. import android.view.WindowManager;
  20. import android.widget.AdapterView;
  21. import android.widget.ArrayAdapter;
  22. import android.widget.ImageView;
  23. import android.widget.ListView;
  24. import android.widget.Spinner;
  25. import android.widget.ToggleButton;
  26. import com.example.xuhong.bluetoothassistant_master.adapter.ChatListAdapter;
  27. import com.example.xuhong.bluetoothassistant_master.data.ChatListData;
  28. import com.example.xuhong.bluetoothassistant_master.util.Toaster;
  29. // 2016.09.15. 徐宏 编写
  30. public class MainActivity extends Activity {
  31. private ListView mChatListView;
  32. //列表
  33. private List<ChatListData> mList = new ArrayList<>();
  34. private ChatListAdapter chatListAdapter;
  35. private static final String TAG = "MainActivity";
  36. // uuid
  37. private static final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";
  38. private Spinner mSpinner;
  39. private String[] data = new String[]{"选择 0x01", "选择 0x02", "选择 0x03", "选择 0x04", "选择 0x05", "选择 0x06", "选择 0x07" , "选择 0x08", "选择 0x09"};
  40. private Byte sendData = null;
  41. private String mRecieve =null;
  42. private ArrayAdapter<String> adapter;
  43. //获得系统的适配器
  44. private BluetoothDevice mBluetoothDevice = null;
  45. //创建socket
  46. private BluetoothSocket mSocket = null;
  47. //io流
  48. private OutputStream mOutS = null;
  49. private static final int CONNECT_SUCCED =10;
  50. private static final int mRecieve_SUCCED = 20;
  51. private InputStream input =null;
  52. protected void onCreate(Bundle savedInstanceState) {
  53. super.onCreate(savedInstanceState);
  54. //全屏显示
  55. requestWindowFeature(Window.FEATURE_NO_TITLE);
  56. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  57. setContentView(R.layout.activity_main);
  58. initview();
  59. }
  60. private void initview() {
  61. mSpinner = (Spinner) findViewById(R.id.mSpinner);
  62. mChatListView = (ListView)findViewById(R.id.mChatListView);
  63. chatListAdapter=new ChatListAdapter(MainActivity.this,mList);
  64. mChatListView.setAdapter(chatListAdapter);
  65. adapter= new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data);
  66. mSpinner.setAdapter(adapter);
  67. mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
  68. @Override
  69. public void onItemSelected(AdapterView<?> adapterView, View view, int id, long l) {
  70. switch (id){
  71. case 0:
  72. sendData=1;
  73. Log.i(TAG,"点击0");
  74. break;
  75. case 1:
  76. sendData=2;
  77. Log.i(TAG,"点击1");
  78. break;
  79. case 2:
  80. sendData=3;
  81. Log.i(TAG,"点击2");
  82. break;
  83. case 3:
  84. sendData=4;
  85. Log.i(TAG,"点击3");
  86. break;
  87. case 4:
  88. sendData=5;
  89. Log.i(TAG,"点击4");
  90. break;
  91. case 5:
  92. sendData=6;
  93. Log.i(TAG,"点击5");
  94. break;
  95. case 6:
  96. sendData=7;
  97. Log.i(TAG,"点击6");
  98. break;
  99. case 7:
  100. sendData=8;
  101. Log.i(TAG,"点击7");
  102. break;
  103. case 8:
  104. sendData=9;
  105. Log.i(TAG,"点击8");
  106. break;
  107. }
  108. }
  109. @Override
  110. public void onNothingSelected(AdapterView<?> adapterView) {
  111. }
  112. });
  113. }
  114. //添加右边文本
  115. private void addRightItem(String text) {
  116. ChatListData date = new ChatListData();
  117. date.setType(ChatListAdapter.VALUE_RIGHT_TEXT);
  118. date.setText(text);
  119. mList.add(date);
  120. //通知adapter刷新
  121. adapter.notifyDataSetChanged();
  122. //滚动到底部
  123. mChatListView.setSelection(mChatListView.getBottom());
  124. }
  125. //添加左边文本
  126. private void addLeftItem(String text) {
  127. ChatListData date = new ChatListData();
  128. date.setType(ChatListAdapter.VALUE_LEFT_TEXT);
  129. date.setText(text);
  130. mList.add(date);
  131. //通知adapter刷新
  132. adapter.notifyDataSetChanged();
  133. //滚动到底部
  134. mChatListView.setSelection(mChatListView.getBottom());
  135. }
  136. public void btn_send(View v){
  137. writeStream(sendData);
  138. addRightItem(sendData+"");
  139. }
  140. @Override
  141. protected void onStart() {
  142. super.onStart();
  143. initDevice();
  144. }
  145. @Override
  146. protected void onStop() {
  147. close();
  148. super.onStop();
  149. }
  150. private void initDevice() {
  151. Bundle bundle = getIntent().getExtras();
  152. if (bundle != null) {
  153. mBluetoothDevice = bundle.getParcelable("device");
  154. if (mBluetoothDevice != null) {
  155. new Thread(mConnRun).start();
  156. }
  157. }
  158. }
  159. private Runnable mConnRun = new Runnable() {
  160. @Override
  161. public void run() {
  162. connect();
  163. }
  164. };
  165. private void connect() {
  166. UUID uuid = UUID.fromString(SPP_UUID);
  167. try {
  168. mSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(uuid);
  169. } catch (IOException e) {
  170. if (mSocket != null) {
  171. try {
  172. mSocket.close();
  173. } catch (IOException e1) {
  174. Log.e(TAG, e1.getMessage());
  175. }
  176. }
  177. }
  178. try {
  179. mSocket.connect();
  180. } catch (IOException e) {
  181. if (mSocket != null) {
  182. try {
  183. mSocket.close();
  184. } catch (IOException e1) {
  185. Log.e(TAG, e1.getMessage());
  186. }
  187. }
  188. }
  189. try {
  190. mOutS = mSocket.getOutputStream();
  191. input=mSocket.getInputStream();
  192. handler.sendEmptyMessage(CONNECT_SUCCED);
  193. } catch (IOException e) {
  194. if (mOutS != null) {
  195. try {
  196. mOutS.close();
  197. } catch (IOException e1) {
  198. Log.e(TAG, e.getMessage());
  199. }
  200. }
  201. if (mSocket != null) {
  202. try {
  203. mSocket.close();
  204. } catch (IOException e1) {
  205. Log.e(TAG, e.getMessage());
  206. }
  207. }
  208. }
  209. }
  210. private void close() {
  211. if (mOutS != null) {
  212. try {
  213. mOutS.close();
  214. } catch (IOException e) {
  215. Log.e(TAG, e.getMessage());
  216. }
  217. }
  218. if (mSocket != null) {
  219. try {
  220. mSocket.close();
  221. } catch (IOException e) {
  222. Log.e(TAG, e.getMessage());
  223. }
  224. }
  225. }
  226. private void writeStream(byte data) {
  227. try {
  228. if (mOutS != null) {
  229. mOutS.write(data);
  230. Log.i(TAG,"输出---->>>是:"+data);
  231. mOutS.flush();
  232. }
  233. } catch (IOException e) {
  234. runOnUiThread(new Runnable() {
  235. @Override
  236. public void run() {
  237. Toaster.shortToastShow(MainActivity.this, "连接超时");
  238. MainActivity.this.finish();
  239. }
  240. });
  241. }
  242. }
  243. public Handler handler = new Handler() {
  244. @Override
  245. public void handleMessage(Message msg) {
  246. super.handleMessage(msg);
  247. switch (msg.what) {
  248. case 1:
  249. break;
  250. case mRecieve_SUCCED:
  251. addLeftItem(mRecieve);
  252. break;
  253. case CONNECT_SUCCED:
  254. Log.i(TAG,"接收成功");
  255. new MyThread().start(); //开启下面的线程
  256. break;
  257. }
  258. }
  259. };
  260. //新开的一个接收的线程
  261. class MyThread extends Thread{
  262. @Override
  263. public void run() {
  264. while (true){
  265. try {
  266. //获取到的数据
  267. int read = input.read();
  268. //因为不允许在子线程更新UI所以用handler
  269. handler.sendEmptyMessage(mRecieve_SUCCED);
  270. Log.i(TAG+"数据是",Integer.toHexString(read));
  271. } catch (IOException e) {
  272. e.printStackTrace();
  273. Log.i(TAG,"异常"+e);
  274. }
  275. }}
  276. }
  277. @Override
  278. public boolean onKeyDown(int keyCode, KeyEvent event) {
  279. if(keyCode== KeyEvent.KEYCODE_BACK){
  280. AlertDialog.Builder alertDialog=new AlertDialog.Builder(MainActivity.this);
  281. alertDialog.setTitle("确认!");
  282. alertDialog.setMessage(" 确定退出智能锁控制吗");
  283. alertDialog.setPositiveButton("否",new DialogInterface.OnClickListener() {
  284. @Override
  285. public void onClick(DialogInterface arg0, int arg1) {
  286. }
  287. });
  288. alertDialog.setNegativeButton("是", new DialogInterface.OnClickListener() {
  289. @Override
  290. public void onClick(DialogInterface arg0, int arg1) {
  291. MainActivity.this.finish();
  292. }
  293. });
  294. alertDialog.show();
  295. }
  296. return false;
  297. }
  298. }

这里写图片描述


完美通讯~~

版权声明:本文为corehouse原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/corehouse/p/13770905.html