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>