Android 实现蓝牙客户端与服务器端通信
DarHalstead
8年前
<p>一、首先说明:蓝牙通信必须用手机测试,因为avd里没有相关的硬件,会报错!<br> 好了,看看最后的效果图:<br> <img alt="这里写图片描述" src="https://simg.open-open.com/show/b5130eb960a81a86030f73ee9d5bf11e.png"><br> <img alt="这里写图片描述" src="https://simg.open-open.com/show/6a17c0e0bd3e6cc7624882a4c7ec28f7.png"></p> <p>二、概述:<br> 1.判断是否支持Bluetooth</p> <pre> <code class="language-java">BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if(bluetoothAdapter == null) { //the device doesn't support bluetooth } else { //the device support bluetooth }</code></pre> <p>2.如果支持,打开Bluetooth</p> <pre> <code class="language-java">if(!bluetoothAdapter.isEnable()) { Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent,REQUEST_ENABLE_BT); }</code></pre> <p>3.监视Bluetooth打开状态</p> <pre> <code class="language-java">BroadcastReceiver bluetoothState = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String stateExtra = BluetoothAdapter.EXTRA_STATE; int state = intent.getIntExtra(stateExtra, -1); switch(state) { case BluetoothAdapter.STATE_TURNING_ON: break; case BluetoothAdapter.STATE_ON: break; case BluetoothAdapter.STATE_TURNING_OFF: break; case BluetoothAdapter.STATE_OFF: break; } } } registerReceiver(bluetoothState,new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));</code></pre> <p>4.设置本地设备可以被其它设备搜索</p> <pre> <code class="language-java">Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); startActivityForResult(discoveryIntent,REQUEST_DISCOVERY); BroadcastReceiver discovery = new BroadcastReceiver() { @Override public void onRecevie(Content context, Intent intent) { String scanMode = BluetoothAdapter.EXTRA_SCAN_MODE; String preScanMode = BluetoothAdapter.EXTRA_PREVIOUS_SCAN_MODE; int mode = intent.getIntExtra(scanMode); } } registerReceiver(discovery,new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);</code></pre> <p>5.搜索设备<br> 开始搜索 bluetoothAdapter.startDiscovery();<br> 停止搜索 bluetoothAdapter.cancelDiscovery();</p> <p>当发现一个设备时,系统会发出ACTION_FOUND广播消息,我们可以实现接收这个消息的BroadcastReceiver</p> <pre> <code class="language-java">BroadcastReceiver deviceFound = new BroadcastReceiver() { @Override public void onReceiver(Content content, Intent intent) { String remoteDeviceName = intent.getStringExtra(BluetoothAdapter.EXTRA_NAME); BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothAdapter.EXTRA_DEVICE); } } registerReceiver(deviceFound, new IntentFilter(BluetoothAdapter.ACTION_FOUND);</code></pre> <p>6.连接设备</p> <p>连接两个蓝牙设备要分别实现服务器端(BluetoothServerSocket)和客户端(BluetoothSocket),这点与J2SE中的</p> <p>ServerSocket和Socket很类似。</p> <p>BluetoothServerSocket在服务器端调用方法accept()监听,当有客户端请求到来时,accept()方法返回BluetoothSocket,客户端得到后,两端便可以通信。通过InputStream和OutputStream来实现数据的传输。</p> <p>accept方法是阻塞的,所以不能放在UI线程中,当用到BluetoothServerSocket和BluetoothSocket时,通常把它们放在各自的新线程中。</p> <p>三、如何实现<br> 以下是开发中的几个关键步骤:<br> 1)首先开启蓝牙<br> 2)搜索可用设备<br> 3)创建蓝牙socket,获取输入输出流<br> 4)读取和写入数据<br> 5)断开连接关闭蓝牙</p> <p>1、因为有页面切换,这里我使用了TabHost,但原来的效果不好,没有动画,那只好自己复写了</p> <pre> <code class="language-java">/** * 带有动画效果的TabHost * * @Project App_Bluetooth * @Package com.android.bluetooth * @author chenlin * @version 1.0 * @Date 2013年6月2日 * @Note TODO */ public class AnimationTabHost extends TabHost { private int mCurrentTabID = 0;//当前的tabId private final long mDuration = 400;//动画时间 public AnimationTabHost(Context context) { this(context, null); } public AnimationTabHost(Context context, AttributeSet attrs) { super(context, attrs); } /** * 切换动画 */ @Override public void setCurrentTab(int index) { //向右平移 if (index > mCurrentTabID) { TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f); translateAnimation.setDuration(mDuration); getCurrentView().startAnimation(translateAnimation); //向左平移 } else if (index < mCurrentTabID) { TranslateAnimation translateAnimation = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f); translateAnimation.setDuration(mDuration); getCurrentView().startAnimation(translateAnimation); } super.setCurrentTab(index); //-----方向平移------------------------------ if (index > mCurrentTabID) { TranslateAnimation translateAnimation = new TranslateAnimation( // Animation.RELATIVE_TO_PARENT, 1.0f,// RELATIVE_TO_SELF Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f); translateAnimation.setDuration(mDuration); getCurrentView().startAnimation(translateAnimation); } else if (index < mCurrentTabID) { TranslateAnimation translateAnimation = new TranslateAnimation( Animation.RELATIVE_TO_PARENT, -1.0f, Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f); translateAnimation.setDuration(mDuration); getCurrentView().startAnimation(translateAnimation); } mCurrentTabID = index; } }</code></pre> <p>2、先搭建好主页,使用复写的TabHost滑动,如何滑动,根据状态,有三种状态</p> <pre> <code class="language-java">/** * 主页 * * @Project App_Bluetooth * @Package com.android.bluetooth * @author chenlin * @version 1.0 * @Date 2013年6月2日 */ @SuppressWarnings("deprecation") public class BluetoothActivity extends TabActivity { static AnimationTabHost mTabHost;//动画tabhost static String BlueToothAddress;//蓝牙地址 static Type mType = Type.NONE;//类型 static boolean isOpen = false; //类型: enum Type { NONE, SERVICE, CILENT }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initTab(); } private void initTab() { //初始化 mTabHost = (AnimationTabHost) getTabHost(); //添加tab mTabHost.addTab(mTabHost.newTabSpec("Tab1").setIndicator("设备列表", getResources().getDrawable(android.R.drawable.ic_menu_add)) .setContent(new Intent(this, DeviceActivity.class))); mTabHost.addTab(mTabHost.newTabSpec("Tab2").setIndicator("会话列表", getResources().getDrawable(android.R.drawable.ic_menu_add)) .setContent(new Intent(this, ChatActivity.class))); //添加监听 mTabHost.setOnTabChangedListener(new OnTabChangeListener() { public void onTabChanged(String tabId) { if (tabId.equals("Tab1")) { //TODO } } }); //默认在第一个tabhost上面 mTabHost.setCurrentTab(0); } public void onActivityResult(int requestCode, int resultCode, Intent data) { Toast.makeText(this, "address:", Toast.LENGTH_SHORT).show(); } }</code></pre> <p>3、有了主页,就开始分别实现两个列表页面,一个是寻找设备页面DeviceActivity.java,另一个是会话页面ChatActivity.java<br> 1)设备页面DeviceActivity.java</p> <pre> <code class="language-java">/** * 发现的设备列表 * @Project App_Bluetooth * @Package com.android.bluetooth * @author chenlin * @version 1.0 * @Date 2013年6月2日 * @Note TODO */ public class DeviceActivity extends Activity { private ListView mListView; //数据 private ArrayList<DeviceBean> mDatas; private Button mBtnSearch, mBtnService; private ChatListAdapter mAdapter; //蓝牙适配器 private BluetoothAdapter mBtAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.devices); initDatas(); initViews(); registerBroadcast(); init(); } private void initDatas() { mDatas = new ArrayList<DeviceBean>(); mAdapter = new ChatListAdapter(this, mDatas); mBtAdapter = BluetoothAdapter.getDefaultAdapter(); } /** * 列出所有的蓝牙设备 */ private void init() { Log.i("tag", "mBtAdapter=="+ mBtAdapter); //根据适配器得到所有的设备信息 Set<BluetoothDevice> deviceSet = mBtAdapter.getBondedDevices(); if (deviceSet.size() > 0) { for (BluetoothDevice device : deviceSet) { mDatas.add(new DeviceBean(device.getName() + "\n" + device.getAddress(), true)); mAdapter.notifyDataSetChanged(); mListView.setSelection(mDatas.size() - 1); } } else { mDatas.add(new DeviceBean("没有配对的设备", true)); mAdapter.notifyDataSetChanged(); mListView.setSelection(mDatas.size() - 1); } } /** * 注册广播 */ private void registerBroadcast() { //设备被发现广播 IntentFilter discoveryFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(mReceiver, discoveryFilter); // 设备发现完成 IntentFilter foundFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(mReceiver, foundFilter); } /** * 初始化视图 */ private void initViews() { mListView = (ListView) findViewById(R.id.list); mListView.setAdapter(mAdapter); mListView.setFastScrollEnabled(true); mListView.setOnItemClickListener(mDeviceClickListener); mBtnSearch = (Button) findViewById(R.id.start_seach); mBtnSearch.setOnClickListener(mSearchListener); mBtnService = (Button) findViewById(R.id.start_service); mBtnService.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { BluetoothActivity.mType = Type.SERVICE; BluetoothActivity.mTabHost.setCurrentTab(1); } }); } /** * 搜索监听 */ private OnClickListener mSearchListener = new OnClickListener() { @Override public void onClick(View arg0) { if (mBtAdapter.isDiscovering()) { mBtAdapter.cancelDiscovery(); mBtnSearch.setText("重新搜索"); } else { mDatas.clear(); mAdapter.notifyDataSetChanged(); init(); /* 开始搜索 */ mBtAdapter.startDiscovery(); mBtnSearch.setText("ֹͣ停止搜索"); } } }; /** * 点击设备监听 */ private OnItemClickListener mDeviceClickListener = new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { DeviceBean bean = mDatas.get(position); String info = bean.message; String address = info.substring(info.length() - 17); BluetoothActivity.BlueToothAddress = address; AlertDialog.Builder stopDialog = new AlertDialog.Builder(DeviceActivity.this); stopDialog.setTitle("连接");//标题 stopDialog.setMessage(bean.message); stopDialog.setPositiveButton("连接", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mBtAdapter.cancelDiscovery(); mBtnSearch.setText("重新搜索"); BluetoothActivity.mType = Type.CILENT; BluetoothActivity.mTabHost.setCurrentTab(1); dialog.cancel(); } }); stopDialog.setNegativeButton("取消", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { BluetoothActivity.BlueToothAddress = null; dialog.cancel(); } }); stopDialog.show(); } }; /** * 发现设备广播 */ private final BroadcastReceiver mReceiver = 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); // 如果绑定的状态不一样 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { mDatas.add(new DeviceBean(device.getName() + "\n" + device.getAddress(), false)); mAdapter.notifyDataSetChanged(); mListView.setSelection(mDatas.size() - 1); } // 如果搜索完成了 } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { setProgressBarIndeterminateVisibility(false); if (mListView.getCount() == 0) { mDatas.add(new DeviceBean("û没有发现蓝牙设备", false)); mAdapter.notifyDataSetChanged(); mListView.setSelection(mDatas.size() - 1); } mBtnSearch.setText("重新搜索"); } } }; @Override public void onStart() { super.onStart(); if (!mBtAdapter.isEnabled()) { Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, 3); } } @Override protected void onDestroy() { super.onDestroy(); if (mBtAdapter != null) { mBtAdapter.cancelDiscovery(); } this.unregisterReceiver(mReceiver); } }</code></pre> <p>2)会话页面ChatActivity.java</p> <pre> <code class="language-java">/** * 会话界面 * * @Project App_Bluetooth * @Package com.android.bluetooth * @author chenlin * @version 1.0 * @Date 2013年3月2日 * @Note TODO */ public class ChatActivity extends Activity implements OnItemClickListener, OnClickListener { private static final int STATUS_CONNECT = 0x11; private ListView mListView; private ArrayList<DeviceBean> mDatas; private Button mBtnSend;// 发送按钮 private Button mBtnDisconn;// 断开连接 private EditText mEtMsg; private DeviceListAdapter mAdapter; /* 一些常量,代表服务器的名称 */ public static final String PROTOCOL_SCHEME_L2CAP = "btl2cap"; public static final String PROTOCOL_SCHEME_RFCOMM = "btspp"; public static final String PROTOCOL_SCHEME_BT_OBEX = "btgoep"; public static final String PROTOCOL_SCHEME_TCP_OBEX = "tcpobex"; // 蓝牙服务端socket private BluetoothServerSocket mServerSocket; // 蓝牙客户端socket private BluetoothSocket mSocket; // 设备 private BluetoothDevice mDevice; private BluetoothAdapter mBluetoothAdapter; // --线程类----------------- private ServerThread mServerThread; private ClientThread mClientThread; private ReadThread mReadThread; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.chat); initDatas(); initViews(); initEvents(); } private void initEvents() { mListView.setOnItemClickListener(this); // 发送信息 mBtnSend.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { String text = mEtMsg.getText().toString(); if (!TextUtils.isEmpty(text)) { // 发送信息 sendMessageHandle(text); mEtMsg.setText(""); mEtMsg.clearFocus(); // 隐藏软键盘 InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); manager.hideSoftInputFromWindow(mEtMsg.getWindowToken(), 0); } else Toast.makeText(ChatActivity.this, "发送内容不能为空!", Toast.LENGTH_SHORT).show(); } }); // 关闭会话 mBtnDisconn.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { if (BluetoothActivity.mType == Type.CILENT) { shutdownClient(); } else if (BluetoothActivity.mType == Type.SERVICE) { shutdownServer(); } BluetoothActivity.isOpen = false; BluetoothActivity.mType = Type.NONE; Toast.makeText(ChatActivity.this, "已断开连接!", Toast.LENGTH_SHORT).show(); } }); } private void initViews() { mListView = (ListView) findViewById(R.id.list); mListView.setAdapter(mAdapter); mListView.setFastScrollEnabled(true); mEtMsg = (EditText) findViewById(R.id.MessageText); mEtMsg.clearFocus(); mBtnSend = (Button) findViewById(R.id.btn_msg_send); mBtnDisconn = (Button) findViewById(R.id.btn_disconnect); } private void initDatas() { mDatas = new ArrayList<DeviceBean>(); mAdapter = new DeviceListAdapter(this, mDatas); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); } /** * 信息处理 */ private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { String info = (String) msg.obj; switch (msg.what) { case STATUS_CONNECT: Toast.makeText(ChatActivity.this, info, 0).show(); break; } if (msg.what == 1) { mDatas.add(new DeviceBean(info, true)); mAdapter.notifyDataSetChanged(); mListView.setSelection(mDatas.size() - 1); }else { mDatas.add(new DeviceBean(info, false)); mAdapter.notifyDataSetChanged(); mListView.setSelection(mDatas.size() - 1); } } }; @Override public void onResume() { super.onResume(); if (BluetoothActivity.isOpen) { Toast.makeText(this, "连接已经打开,可以通信。如果要再建立连接,请先断开", Toast.LENGTH_SHORT).show(); return; } if (BluetoothActivity.mType == Type.CILENT) { String address = BluetoothActivity.BlueToothAddress; if (!"".equals(address)) { mDevice = mBluetoothAdapter.getRemoteDevice(address); mClientThread = new ClientThread(); mClientThread.start(); BluetoothActivity.isOpen = true; } else { Toast.makeText(this, "address is null !", Toast.LENGTH_SHORT).show(); } } else if (BluetoothActivity.mType == Type.SERVICE) { mServerThread = new ServerThread(); mServerThread.start(); BluetoothActivity.isOpen = true; } } // 客户端线程 private class ClientThread extends Thread { public void run() { try { mSocket = mDevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); Message msg = new Message(); msg.obj = "请稍候,正在连接服务器:" + BluetoothActivity.BlueToothAddress; msg.what = STATUS_CONNECT; mHandler.sendMessage(msg); mSocket.connect(); msg = new Message(); msg.obj = "已经连接上服务端!可以发送信息。"; msg.what = STATUS_CONNECT; mHandler.sendMessage(msg); // 启动接受数据 mReadThread = new ReadThread(); mReadThread.start(); } catch (IOException e) { Message msg = new Message(); msg.obj = "连接服务端异常!断开连接重新试一试。"; msg.what = STATUS_CONNECT; mHandler.sendMessage(msg); } } }; // 开启服务器 private class ServerThread extends Thread { public void run() { try { // 创建一个蓝牙服务器 参数分别:服务器名称、UUID mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(PROTOCOL_SCHEME_RFCOMM, UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); Message msg = new Message(); msg.obj = "请稍候,正在等待客户端的连接..."; msg.what = STATUS_CONNECT; mHandler.sendMessage(msg); /* 接受客户端的连接请求 */ mSocket = mServerSocket.accept(); msg = new Message(); msg.obj = "客户端已经连接上!可以发送信息。"; msg.what = STATUS_CONNECT; mHandler.sendMessage(msg); // 启动接受数据 mReadThread = new ReadThread(); mReadThread.start(); } catch (IOException e) { e.printStackTrace(); } } }; /* 停止服务器 */ private void shutdownServer() { new Thread() { public void run() { if (mServerThread != null) { mServerThread.interrupt(); mServerThread = null; } if (mReadThread != null) { mReadThread.interrupt(); mReadThread = null; } try { if (mSocket != null) { mSocket.close(); mSocket = null; } if (mServerSocket != null) { mServerSocket.close(); mServerSocket = null; } } catch (IOException e) { Log.e("server", "mserverSocket.close()", e); } }; }.start(); } /* ͣ停止客户端连接 */ private void shutdownClient() { new Thread() { public void run() { if (mClientThread != null) { mClientThread.interrupt(); mClientThread = null; } if (mReadThread != null) { mReadThread.interrupt(); mReadThread = null; } if (mSocket != null) { try { mSocket.close(); } catch (IOException e) { e.printStackTrace(); } mSocket = null; } }; }.start(); } // 发送数据 private void sendMessageHandle(String msg) { if (mSocket == null) { Toast.makeText(this, "没有连接", Toast.LENGTH_SHORT).show(); return; } try { OutputStream os = mSocket.getOutputStream(); os.write(msg.getBytes()); mDatas.add(new DeviceBean(msg, false)); mAdapter.notifyDataSetChanged(); mListView.setSelection(mDatas.size() - 1); } catch (IOException e) { e.printStackTrace(); } } // 读取数据 private class ReadThread extends Thread { public void run() { byte[] buffer = new byte[1024]; int bytes; InputStream is = null; try { is = mSocket.getInputStream(); while (true) { if ((bytes = is.read(buffer)) > 0) { byte[] buf_data = new byte[bytes]; for (int i = 0; i < bytes; i++) { buf_data[i] = buffer[i]; } String s = new String(buf_data); Message msg = new Message(); msg.obj = s; msg.what = 1; mHandler.sendMessage(msg); } } } catch (IOException e1) { e1.printStackTrace(); } finally { try { is.close(); } catch (IOException e1) { e1.printStackTrace(); } } } } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { } @Override public void onClick(View view) { } @Override protected void onDestroy() { super.onDestroy(); if (BluetoothActivity.mType == Type.CILENT) { shutdownClient(); } else if (BluetoothActivity.mType == Type.SERVICE) { shutdownServer(); } BluetoothActivity.isOpen = false; BluetoothActivity.mType = Type.NONE; } }</code></pre> <p>三、相关代码下载<br> 链接:<a href="/misc/goto?guid=4959674210430759325">http://pan.baidu.com/s/1eSNRvzG</a> 密码:awgv</p> <p> </p> <p>来自: <a href="/misc/goto?guid=4959674210524390322" rel="nofollow">http://blog.csdn.net/lovoo/article/details/51576246</a></p> <p> </p>