在开始学Android开发的时候,由于其他的需要,本着学习的心态,再网上找了一个资源,做了一个手机蓝牙串口的调试助手。
程序搭建原理简介:
蓝牙通信技术可以在短距离传输中以无线电的方式传递数据,借助蓝牙通信技术来创造便利性,智能性和可控性[8]。实现Android应用程序与蓝牙设备连接,类似TCP连接,构建Android蓝牙通信连接两个关键类是BluetoothSocket和BluetoothServerSocket[9]。
在Android蓝牙通信连接的开发中,实现基本功能蓝牙通信软件,则需要在软件实现服务端和客户端两种机制[10]。这种通信构建需要在服务端程序根据UUID(通用唯一识别码)调用listenUsingRfcommWithServiceRecord(String,UUID)获得BluetoothServerSocket对象,在子线程调用accept()方法开启监听线程。在客户端也根据UUID(通用唯一识别码)调用createRfcommSocketToServiceRecord(UUID)获取BluetoothSocket,在子线程调用connect()方法发起请求连接。
构建蓝牙通信连接后,不管是服务器还是客户端机制,彼此都会连接好的BluetoothSocket对象,获取对应的输入字节流(inputstream)和输出字节流(outputstream),可调用它们的read(byte[])和write(byte[])方法来分别实现对数据流的读和写,实现蓝牙数据通信功能。
程序蓝牙通信搭建原理框图:
本项目所用的权限:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
接下来大体过一下代码,代码 基本都有注释,基本应该都能看的懂,由于时间原因就不一一介绍了:
构建蓝牙的基本服务类BluetoothChatService.class
/** * 这个类做了所有设置和管理与其它蓝牙设备连接的工作。 * 它有一个线程监听传入连接,一个线程与设备进行连接,还有一个线程负责连接后的数据传输。 * */ public class BluetoothChatService { // Debugging private static final String TAG = "BluetoothChatService"; private static final boolean D = true; // 创建服务器套接字时SDP记录的名称 private static final String NAME = "BluetoothChat"; // 该应用的唯一UUID private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // 成员变量 private final BluetoothAdapter mAdapter; private final Handler mHandler; private AcceptThread mAcceptThread; private ConnectThread mConnectThread; private ConnectedThread mConnectedThread; private int mState; // 指示当前连接状态的常量 public static final int STATE_NONE = 0; // we're doing nothing public static final int STATE_LISTEN = 1; // 现在正在侦听传入连接 public static final int STATE_CONNECTING = 2; // 现在启动传出连接 public static final int STATE_CONNECTED = 3; // 现在连接到远程设备 //蓝牙接收延迟时间 private int delay_time; public void setDelay_time(int delay_time) { this.delay_time = delay_time; } /** * 构造函数。 准备新的BluetoothChat会话。 * @param context The UI Activity Context * @param handler A Handler to send messages back to the UI Activity */ public BluetoothChatService(Context context, Handler handler) { mAdapter = BluetoothAdapter.getDefaultAdapter(); mState = STATE_NONE; mHandler = handler; delay_time=100; } /** * 设置聊天连接的当前状态 * @param state 定义当前连接状态的整数 */ private synchronized void setState(int state) { if (D) Log.d(TAG, "setState() " + mState + " -> " + state); mState = state; // Give the new state to the Handler so the UI Activity can update mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, state, -1).sendToTarget(); } /** * 返回当前连接状态。 */ public synchronized int getState() { return mState; } /** * 启动聊天服务。 特别地启动AcceptThread以在侦听(服务器)模式下开始会话。 * 由Activity onResume()调用 */ public synchronized void start() { if (D) Log.d(TAG, "start"); // 取消尝试建立连接的任何线程 if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} // 取消当前运行连接的任何线程 if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} // 启动线程以监听BluetoothServerSocket if (mAcceptThread == null) { mAcceptThread = new AcceptThread(); mAcceptThread.start(); } setState(STATE_LISTEN); } /** * 启动ConnectThread以启动与远程设备的连接。 * @param device 要连接的BluetoothDevice */ public synchronized void connect(BluetoothDevice device) { if (D) Log.d(TAG, "connect to: " + device); // 取消尝试建立连接的任何线程 if (mState == STATE_CONNECTING) { if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} } // 取消当前运行连接的任何线程 if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} // 启动线程以与给定设备连接 mConnectThread = new ConnectThread(device); mConnectThread.start(); setState(STATE_CONNECTING); } /** * 启动ConnectedThread以开始管理蓝牙连接 * @param socket 在其上进行连接的BluetoothSocket * @param device 已连接的BluetoothDevice */ public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) { if (D) Log.d(TAG, "connected"); // 取消完成连接的线程 if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} // 取消当前运行连接的任何线程 if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} // 取消接受线程,因为我们只想连接到一个设备 if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;} // 启动线程以管理连接并执行传输 mConnectedThread = new ConnectedThread(socket); mConnectedThread.start(); //将连接的设备的名称发送回UI活动 Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME); Bundle bundle = new Bundle(); bundle.putString(BluetoothChat.DEVICE_NAME, device.getName()); msg.setData(bundle); mHandler.sendMessage(msg); setState(STATE_CONNECTED); } /** * Stop all threads */ public synchronized void stop() { if (D) Log.d(TAG, "stop"); if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;} setState(STATE_NONE); } /** * 以线程锁(不同步)方式写入ConnectedThread * @param out 要写入的字节 * @see ConnectedThread#write(byte[]) */ public void write(byte[] out) { //创建临时对象 ConnectedThread r; // 同步ConnectedThread的副本 synchronized (this) { if (mState != STATE_CONNECTED) return; r = mConnectedThread; } //执行写入不同步 r.write(out); } /** * 指示连接尝试失败并通知UI活动. */ private void connectionFailed() { setState(STATE_LISTEN); // 将失败消息发送回活动 Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString(BluetoothChat.TOAST, "无法连接设备"); msg.setData(bundle); mHandler.sendMessage(msg); } /** * 指示连接已丢失并通知UI活动 */ private void connectionLost() { setState(STATE_LISTEN); // 将失败消息发送回活动 Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString(BluetoothChat.TOAST, "丢失设备连接"); msg.setData(bundle); mHandler.sendMessage(msg); } /** * 此线程在侦听传入连接时运行。 它的行为像一个服务器端。 * 它运行直到接受连接 * (或直到取消). */ private class AcceptThread extends Thread { // 本地服务器套接字 private final BluetoothServerSocket mmServerSocket; public AcceptThread() { BluetoothServerSocket tmp = null; // 创建一个新的侦听服务器套接字 try { tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (IOException e) { Log.e(TAG, "listen() failed", e); } mmServerSocket = tmp; } public void run() { if (D) Log.d(TAG, "BEGIN mAcceptThread" + this); setName("AcceptThread"); BluetoothSocket socket = null; // 如果我们没有连接,请监听服务器插座 while (mState != STATE_CONNECTED) { try { // 这是一个阻塞调用,只会在成功的连接或异常返回 socket = mmServerSocket.accept(); } catch (IOException e) { Log.e(TAG, "accept() failed", e); break; } // 如果连接被接受 if (socket != null) { synchronized (BluetoothChatService.this) { switch (mState) { case STATE_LISTEN: case STATE_CONNECTING: // 状况正常。 启动连接的线程。 connected(socket, socket.getRemoteDevice()); break; case STATE_NONE: case STATE_CONNECTED: //未准备就绪或已连接。 终止新套接字。 try { socket.close(); } catch (IOException e) { Log.e(TAG, "Could not close unwanted socket", e); } break; } } } } if (D) Log.i(TAG, "END mAcceptThread"); } public void cancel() { if (D) Log.d(TAG, "cancel " + this); try { mmServerSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of server failed", e); } } } /** * 尝试与设备建立传出连接时,此线程运行。类似于一个客户端 * 它直通; 连接 * 成功或失败。 */ private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { mmDevice = device; BluetoothSocket tmp = null; // 获取与给定的蓝牙设备的连接的BluetoothSocket try { tmp = device.createRfcommSocketToServiceRecord(MY_UUID); } catch (IOException e) { Log.e(TAG, "create() failed", e); } mmSocket = tmp; } public void run() { Log.i(TAG, "BEGIN mConnectThread"); setName("ConnectThread"); // 始终取消发现,因为它会减慢连接速度 mAdapter.cancelDiscovery(); //连接到BluetoothSocket try { // 这是一个阻塞调用,只会在成功的连接或异常返回 mmSocket.connect(); } catch (IOException e) { connectionFailed(); // Close the socket try { mmSocket.close(); } catch (IOException e2) { Log.e(TAG, "unable to close() socket during connection failure", e2); } // 启动服务以重新启动侦听模式 BluetoothChatService.this.start(); return; } // 重置ConnectThread,因为我们完成了 synchronized (BluetoothChatService.this) { mConnectThread = null; } // 启动连接的线程 connected(mmSocket, mmDevice); } public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of connect socket failed", e); } } } /** * 此线程在与远程设备的连接期间运行。 * 它处理所有传入和传出传输。 */ private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; public ConnectedThread(BluetoothSocket socket) { Log.d(TAG, "create ConnectedThread"); mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; // 获取BluetoothSocket输入和输出流 try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { Log.e(TAG, "temp sockets not created", e); } mmInStream = tmpIn; mmOutStream = tmpOut; } public void run() { Log.i(TAG, "BEGIN mConnectedThread"); byte[] buffer = new byte[1024]; int bytes; while (true) { try { while(mmInStream.available()==0){ } try { Thread.sleep(delay_time); //当有数据流入时,线程休眠一段时间,默认100ms } catch (InterruptedException e) { e.printStackTrace(); } bytes = mmInStream.read(buffer); //从字节流中读取数据填充到字节数组,返回读取数据的长度 mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); //将消息传回主界面 } catch (IOException e) { Log.e(TAG, "disconnected", e); connectionLost(); break; } } } /** * 写入连接的OutStream。 * @param buffer The bytes to write */ public void write(byte[] buffer) { try { mmOutStream.write(buffer); //将发送的消息共享回UI活动 mHandler.obtainMessage(BluetoothChat.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); } } } }
主要显示视图UI类 BluetoothChat.class类
/** * 显示通信信息的主Activity。 */ public class BluetoothChat extends Activity { // Debugging private static final String TAG = "BluetoothChat"; private static final boolean D = true; //返回页面标志 private boolean exit =false; // 来自BluetoothChatService Handler的消息类型 public static final int MESSAGE_STATE_CHANGE = 1; public static final int MESSAGE_READ = 2; public static final int MESSAGE_WRITE = 3; public static final int MESSAGE_DEVICE_NAME = 4; public static final int MESSAGE_TOAST = 5; // 来自BluetoothChatService Handler的关键名 public static final String DEVICE_NAME = "device_name"; public static final String TOAST = "toast"; // Intent请求代码 private static final int REQUEST_CONNECT_DEVICE = 1; private static final int REQUEST_ENABLE_BT = 2; // 布局视图 private TextView mTitle; private TextView mConversationView; private TextView outcount; private TextView incount; private TextView view; // 声明复选按钮 private CheckBox in16; private CheckBox autosend; private CheckBox out16; // 声明button按钮 private Button mSendButton; private Button search; private Button disc; // 用来保存存储的文件名 public String filename = ""; // 保存用数据缓存 private String fmsg = ""; // 计数用 private int countin = 0; private int countout = 0; // 已连接设备的名称 private String mConnectedDeviceName = null; // 输出流缓冲区 private StringBuffer mOutStringBuffer; // 本地蓝牙适配器 private BluetoothAdapter mBluetoothAdapter = null; // 用于通信的服务 private BluetoothChatService mChatService = null; // CheckBox用 private boolean inhex = true; private boolean outhex = true; private boolean auto = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (D) Log.e(TAG, "+++ ON CREATE +++"); // 设置窗口布局 requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); setContentView(R.layout.activity_bluetooth_chat_layout); getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title); //布局控件初始化函数,注册相关监听器 init(); // 获取本地蓝牙适配器 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 如果没有蓝牙适配器,则不支持 if (mBluetoothAdapter == null) { Toast.makeText(this, "蓝牙不可用", Toast.LENGTH_LONG).show(); finish(); return; } search.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { search(); } }); disc.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(BluetoothChat.this, "该设备已设置为可在300秒内发现,且可连接", Toast.LENGTH_SHORT).show(); ensureDiscoverable(); } }); } public void search(){ Intent serverIntent = new Intent(BluetoothChat.this, device.class); startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); } private void init(){ // 通过findViewById获得CheckBox对象 in16 = (CheckBox) findViewById(R.id.in16); autosend = (CheckBox) findViewById(R.id.autosend); out16 = (CheckBox) findViewById(R.id.out16); // 注册事件监听器 in16.setOnCheckedChangeListener(listener); autosend.setOnCheckedChangeListener(listener); out16.setOnCheckedChangeListener(listener); // 获得button的对象 search = (Button) findViewById(R.id.search); disc = (Button) findViewById(R.id.discoverable1); mSendButton = (Button) findViewById(R.id.button_send); //获取选择控件的值 // 设置custom title mTitle = (TextView) findViewById(R.id.title_left_text); mTitle.setText(R.string.activity_name); mTitle = (TextView) findViewById(R.id.title_right_text); view = (TextView) findViewById(R.id.edit_text_out); } // 响应事件监听 private OnCheckedChangeListener listener = new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // in16被选中 if (buttonView.getId() == R.id.in16) { if (isChecked) { Toast.makeText(BluetoothChat.this, "16进制显示", Toast.LENGTH_SHORT).show(); inhex = true; } else inhex = false; } //out16选中 if (buttonView.getId() == R.id.out16) { if (isChecked) { Toast.makeText(BluetoothChat.this, "16进制发送", Toast.LENGTH_SHORT).show(); outhex = true; } else outhex = false; } // 自动发送被选中 if (buttonView.getId() == R.id.autosend) { if (isChecked) { Toast.makeText(BluetoothChat.this, "自动发送", Toast.LENGTH_SHORT).show(); auto = true; } else auto = false; } } }; @Override public void onStart() { super.onStart(); if (D) Log.e(TAG, "++ ON START ++"); //如果BT未打开,请求启用。 // 然后在onActivityResult期间调用setupChat() if (!mBluetoothAdapter.isEnabled()) { Intent enableIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, REQUEST_ENABLE_BT); // 否则,设置聊天会话 } else { if (mChatService == null) setupChat(); else { try { mChatService.wait(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } //300秒内搜索 private void ensureDiscoverable() { if (D) Log.d(TAG, "ensure discoverable"); if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent discoverableIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); //设置本机蓝牙可让发现 discoverableIntent.putExtra( BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivity(discoverableIntent); } } //自动发送 private Runnable runnable = new Runnable() { @Override public void run() { String message = view.getText().toString(); sendMessage(message); // 初始化输出流缓冲区 mOutStringBuffer = new StringBuffer(""); } }; //初始化 private void setupChat() { Log.d(TAG, "setupChat()"); mConversationView = (TextView) findViewById(R.id.in); mConversationView.setMovementMethod(ScrollingMovementMethod .getInstance());// 使TextView接收区可以滚动 outcount = (TextView) findViewById(R.id.outcount); incount = (TextView) findViewById(R.id.incount); outcount.setText("0"); incount.setText("0"); mSendButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { // 使用编辑文本小部件的内容发送消息 TextView view = (TextView) findViewById(R.id.edit_text_out); String message = view.getText().toString(); sendMessage(message); } }); // 初始化BluetoothChatService以执行app_incon_bluetooth连接 mChatService = new BluetoothChatService(this, mHandler); //初始化外发消息的缓冲区 mOutStringBuffer = new StringBuffer(""); } //重写发送函数,参数不同。 private void sendMessage(String message) { // 确保已连接 if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) { Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT) .show(); return; } // 检测是否有字符串发送 if (message.length() > 0) { // 获取 字符串并告诉BluetoothChatService发送 if (outhex == true) { byte[] send = Data_syn.hexStr2Bytes(message); mChatService.write(send);//回调service } else if (outhex == false) { byte[] send = message.getBytes(); mChatService.write(send);//回调service } // 清空输出缓冲区 mOutStringBuffer.setLength(0); } else { Toast.makeText(this,"发送内容不能为空", Toast.LENGTH_SHORT).show(); } } // 该Handler从BluetoothChatService中获取信息 private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_STATE_CHANGE: if (D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1); switch (msg.arg1) { case BluetoothChatService.STATE_CONNECTED: mTitle.setText(R.string.title_connected_to); mTitle.append(mConnectedDeviceName); mConversationView.setText(null); break; case BluetoothChatService.STATE_CONNECTING: mTitle.setText(R.string.title_connecting); break; case BluetoothChatService.STATE_LISTEN: case BluetoothChatService.STATE_NONE: mTitle.setText(R.string.title_not_connected); break; } break; case MESSAGE_WRITE: byte[] writeBuf = (byte[]) msg.obj; // 自动发送 if (auto == true) { // 自动发送模块 mHandler.postDelayed(runnable, 1000); } else if (auto == false) { mHandler.removeCallbacks(runnable); } // 发送计数 if (outhex == true) { String writeMessage = Data_syn.Bytes2HexString(writeBuf); countout += writeMessage.length() / 2; outcount.setText("" + countout); } else if (outhex == false) { String writeMessage = null; try { writeMessage = new String(writeBuf, "GBK"); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } countout += writeMessage.length(); outcount.setText("" + countout); } break; case MESSAGE_READ: byte[] readBuf = (byte[]) msg.obj; //检错误码计算函数 if (inhex == true) { String readMessage = " " + Data_syn.bytesToHexString(readBuf, msg.arg1); fmsg += readMessage; mConversationView.append(readMessage); // 接收计数,更显UI countin += readMessage.length() / 2; incount.setText("" + countin); } else if (inhex == false) { String readMessage = null; try { readMessage = new String(readBuf, 0, msg.arg1, "GBK"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } fmsg += readMessage; mConversationView.append(readMessage); // 接收计数,更新UI countin += readMessage.length(); incount.setText("" + countin); } break; case MESSAGE_DEVICE_NAME: // 保存已连接设备的名称 mConnectedDeviceName = msg.getData().getString(DEVICE_NAME); Toast.makeText(getApplicationContext(), "连接到 " + mConnectedDeviceName, Toast.LENGTH_SHORT) .show(); break; case MESSAGE_TOAST: Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST), Toast.LENGTH_SHORT) .show(); break; } } }; //返回该Activity回调函数 public void onActivityResult(int requestCode, int resultCode, Intent data) { if (D) Log.d(TAG, "onActivityResult " + resultCode); switch (requestCode) { //search返回的 case REQUEST_CONNECT_DEVICE: // DeviceListActivity返回时要连接的设备 if (resultCode == Activity.RESULT_OK) { // 获取设备的MAC地址 String address = data.getExtras().getString( device.EXTRA_DEVICE_ADDRESS); // 获取BLuetoothDevice对象 BluetoothDevice device = mBluetoothAdapter .getRemoteDevice(address); // 尝试连接到设备 mChatService.connect(device); } break; //start返回的(遇到蓝牙不可用退出) case REQUEST_ENABLE_BT: // 当启用蓝牙的请求返回时 if (resultCode == Activity.RESULT_OK) { //蓝牙已启用,因此设置聊天会话 setupChat();//初始化文本 } else { // 用户未启用蓝牙或发生错误 Log.d(TAG, "BT not enabled"); Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show(); finish(); } } } // 保存按键响应函数 public void onSaveButtonClicked(View v) { Save(); } // 清屏按键响应函数 public void onClearButtonClicked(View v) { fmsg = ""; mConversationView.setText(null); view.setText(null); return; } // 清除计数按键响应函数 public void onClearCountButtonClicked(View v) { countin = 0; countout = 0; outcount.setText("0"); incount.setText("0"); return; } // 保存功能实现 private void Save() { // 显示对话框输入文件名 LayoutInflater factory = LayoutInflater.from(BluetoothChat.this); // 图层模板生成器句柄 final View DialogView = factory.inflate(R.layout.sname, null); // 用sname.xml模板生成视图模板 new AlertDialog.Builder(BluetoothChat.this).setTitle("文件名") .setView(DialogView) // 设置视图模板 .setPositiveButton("确定", new DialogInterface.OnClickListener() // 确定按键响应函数 { public void onClick(DialogInterface dialog, int whichButton) { EditText text1 = (EditText) DialogView .findViewById(R.id.sname); // 得到文件名输入框句柄 filename = text1.getText().toString(); // 得到文件名 try { if (Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED)) { // 如果SD卡已准备好 filename = filename + ".txt"; // 在文件名末尾加上.txt File sdCardDir = Environment .getExternalStorageDirectory(); // 得到SD卡根目录 File BuildDir = new File(sdCardDir, "/BluetoothSave"); // 打开BluetoothSave目录,如不存在则生成 if (BuildDir.exists() == false) BuildDir.mkdirs(); File saveFile = new File(BuildDir, filename); // 新建文件句柄,如已存在仍新建文档 FileOutputStream stream = new FileOutputStream( saveFile); // 打开文件输入流 stream.write(fmsg.getBytes()); stream.close(); Toast.makeText(BluetoothChat.this, "存储成功!", Toast.LENGTH_SHORT) .show(); } else { Toast.makeText(BluetoothChat.this, "没有存储卡!", Toast.LENGTH_LONG) .show(); } } catch (IOException e) { return; } } }) .setNegativeButton("取消", // 取消按键响应函数,直接退出对话框不做任何处理 new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }).show(); // 显示对话框 } @Override public synchronized void onResume() { super.onResume(); if (D) Log.e(TAG, "+ ON RESUME +"); // 在onResume()中执行此检查包括在onStart()期间未启用BT的情况, // 因此我们暂停启用它... // onResume()将在ACTION_REQUEST_ENABLE活动时被调用返回. if (mChatService != null) { // 只有状态是STATE_NONE,我们知道我们还没有启动蓝牙 if (mChatService.getState() == BluetoothChatService.STATE_NONE) { // 启动BluetoothChat服务 mChatService.start(); } } } @Override public synchronized void onPause() { super.onPause(); if (D) Log.e(TAG, "- ON PAUSE -"); } @Override public void onStop() { super.onStop(); if (D) Log.e(TAG, "-- ON STOP --"); } @Override public void onDestroy() { super.onDestroy(); // 停止蓝牙通信连接服务 if (mChatService != null) mChatService.stop(); if (D) Log.e(TAG, "--- ON DESTROY ---"); } @Override public void onBackPressed() { exit(); } public void exit() { exit = true; if(exit == true) { this.finish(); } } }
DeviceListActivity.class,蓝牙配对列表显示,点击返回给主界面,主要为一个listview
/** * 这个 Activity显示为一个对话框. 它列出了所有已配对的设备和之后在该地区搜索到的设备. 当一个设备被用户选择后, * 该设备的MAC地址会在result Intent中被传回到父 Activity. */ public class DeviceListActivity extends Activity { // Debugging private static final String TAG = "DeviceListActivity"; private static final boolean D = true; // Return Intent extra public static String EXTRA_DEVICE_ADDRESS = "device_address"; public static String EXTRA_LEGAL = "legal"; // Member fields private BluetoothAdapter mBtAdapter; private ArrayAdapter<String> mPairedDevicesArrayAdapter; private ArrayAdapter<String> mNewDevicesArrayAdapter; private boolean isexist =false; //自动配对结果 private boolean result=false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 设置窗口 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.device_list); // 设置结果CANCELED使用户退出 setResult(Activity.RESULT_CANCELED); // 初始化阵列适配器。 一个用于已配对的设备,一个用于新发现的设备,一个用于测试专用的设备 mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name); mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name); // 为配对的设备查找和设置ListView ListView pairedListView = (ListView) findViewById(R.id.paired_devices); pairedListView.setAdapter(mPairedDevicesArrayAdapter); pairedListView.setOnItemClickListener(mDeviceClickListener); // 为新发现的设备找到并设置ListView ListView newDevicesListView = (ListView) findViewById(R.id.new_devices); newDevicesListView.setAdapter(mNewDevicesArrayAdapter); newDevicesListView.setOnItemClickListener(mDeviceClickListener); // 发现设备时注册广播 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(mReceiver, filter); // 发现完成后注册广播 filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(mReceiver, filter); //自动配对请求注册广播 filter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST); this.registerReceiver(mReceiver, filter); // 获取本地蓝牙适配器 mBtAdapter = BluetoothAdapter.getDefaultAdapter(); // 获取一组当前配对的设备 Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices(); // 如果有配对的设备,请将每个设备添加到ArrayAdapter TextView textView = (TextView) findViewById(R.id.title_paired_devices); if (pairedDevices.size() > 0) { textView.setVisibility(View.VISIBLE); for (BluetoothDevice device : pairedDevices) { isexist=true; mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } } if (isexist==false) { textView.setVisibility(View.GONE); String noDevices = getResources().getText(R.string.none_paired) .toString(); mPairedDevicesArrayAdapter.add(noDevices); } } @Override protected void onStart() { super.onStart(); doDiscovery(); } @Override protected void onDestroy() { super.onDestroy(); // 确保我们不再进行发现 if (mBtAdapter != null) { mBtAdapter.cancelDiscovery(); } //取消注册广播侦听器 this.unregisterReceiver(mReceiver); } /** * 使用BluetoothAdapter启动设备发现 */ private void doDiscovery() { if (D) Log.d(TAG, "doDiscovery()"); // 在标题中指示扫描 setProgressBarIndeterminateVisibility(true); setTitle(R.string.scanning); // 开启新装置的副标题 findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE); // 如果我们已经发现,停止它 if (mBtAdapter.isDiscovering()) { mBtAdapter.cancelDiscovery(); } //从BluetoothAdapter请求发现 mBtAdapter.startDiscovery(); } // ListView中其他设备的点击监听器 private OnItemClickListener mDeviceClickListener = new OnItemClickListener() { public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) { //取消发现,我们m_about来连接 mBtAdapter.cancelDiscovery(); // 获取设备的MAC地址,这是视图中最后17个字符 String info = ((TextView) v).getText().toString(); String address = info.substring(info.length() - 17); // 创建结果意图并包括MAC地址 Intent intent = new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS, address); intent.putExtra(EXTRA_LEGAL,false); // 设置结果并完成此活动 setResult(Activity.RESULT_OK, intent); finish(); } }; //ListView中专用测试设备的点击监听器 private OnItemClickListener mZYDeviceClickListener = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //取消发现,我们m_about来连接 mBtAdapter.cancelDiscovery(); // 获取设备的MAC地址,这是视图中最后17个字符 String info = ((TextView) view).getText().toString(); String address = info.substring(info.length() - 17); // 创建结果意图并包括MAC地址 Intent intent = new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS, address); intent.putExtra(EXTRA_LEGAL,true); // 设置结果并完成此活动 setResult(Activity.RESULT_OK, intent); finish(); } }; // BroadcastReceiver,侦听发现的设备,并在发现完成时更改标题 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); //从Intent获取BluetoothDevice对象 BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 发现找到设备时 if (BluetoothDevice.ACTION_FOUND.equals(action)) { String devicename =device.getName(); while (devicename==null){ devicename =device.getName(); } // 该设备没有配对,加载到新的通用蓝牙设备的列表 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } // 再次得到的action,会等于PAIRING_REQUEST,进行配对操作 }else if(BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)){ String devicename =device.getName(); while (devicename==null){ devicename =device.getName(); } Log.e(TAG,"接收到配对广播"); } // 发现完成后,更改活动标题 else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { setProgressBarIndeterminateVisibility(false); setTitle(R.string.select_device); if (mNewDevicesArrayAdapter.getCount() == 0) { String noDevices = getResources().getText( R.string.none_found).toString(); mNewDevicesArrayAdapter.add(noDevices); } } } };
数据转换工具类 主要设置十六进制数据为ascll数据的转化
// 将字节数组转化为16进制字符串,确定长度 public static String bytesToHexString(byte[] bytes, int a) { String result = ""; for (int i = 0; i < a; i++) { String hexString = Integer.toHexString(bytes[i] & 0xFF);// 将高24位置0 if (hexString.length() == 1) { hexString = '0' + hexString; } result += hexString.toUpperCase(); } return result; } // 将字节数组转化为16进制字符串,不确定长度 public static String Bytes2HexString(byte[] b) { String ret = ""; for (int i =0; i < b.length; i++) { String hex = Integer.toHexString(b[i] & 0xFF);// 将高24位置0 if (hex.length() == 1) { hex = '0' + hex; } ret += hex.toUpperCase(); } return ret; } // 将16进制字符串转化为字节数组 public static byte[] hexStr2Bytes(String paramString) { int i = paramString.length() / 2; byte[] arrayOfByte = new byte[i]; int j = 0; while (true) { if (j >= i) return arrayOfByte; int k = 1 + j * 2; int l = k + 1; arrayOfByte[j] = (byte) (0xFF & Integer.decode( "0x" + paramString.substring(j * 2, k) + paramString.substring(k, l)).intValue()); ++j; } }
最后附上运行的app界面截图