Android端简易蓝牙聊天通讯App(原创)

欢迎转载,但请注明出处!谢谢。http://www.cnblogs.com/weizhxa/p/5792775.html

  最近公司在做一个蓝牙串口通讯的App,有一个固定的蓝牙设备,需要实现手机连接相互交换数据。以前没怎么做过蓝牙开发,故查看Android App Guide的蓝牙篇,发现有个chat示例,故此做了点研究。在研究的基础上进行了此App的实现。

  1、App特点:

    1.1 App中同时存在服务器与客户端,任意手机可以作为服务器或者客户端;

    1.2 客户端可以进行蓝牙环境扫描;

    1.3 诸多异常处理……均未做,O(∩_∩)O。demo了,主要学习的是蓝牙技术嘛。

  2、实现过程中的总结:

    2.1 蓝牙串口通讯,谷歌给出了一个固定UUID: 00001101-0000-1000-8000-00805F9B34FB,大多数蓝牙串口设备使用此UUID作为连接用UUID,此UUID在BluetoothDevice的createRfcommSocketToServiceRecord方法中有提到。具体可以看api doc。

  3、具体实现代码:

    3.1 客户端服务器选择页:MainActivity。此页仅进行客户端与服务器端选择使用。

      3.1.1 Activity:

      

 1 package org.fiu.bluetoothdemos;
 2 
 3 import org.fiu.bluetoothchatdemos.R;
 4 
 5 import android.content.Intent;
 6 import android.os.Bundle;
 7 import android.support.v7.app.ActionBarActivity;
 8 import android.view.Menu;
 9 import android.view.MenuItem;
10 import android.view.View;
11 
12 public class MainActivity extends ActionBarActivity {
13 
14     @Override
15     protected void onCreate(Bundle savedInstanceState) {
16         super.onCreate(savedInstanceState);
17         setContentView(R.layout.activity_main);
18     }
19 
20     /**
21      * 建立服务器
22      * 
23      * @param view
24      */
25     public void btn_server(View view) {
26         startActivity(new Intent(this, ServerActivity.class));
27     }
28 
29     /**
30      * 建立客户端
31      * 
32      * @param view
33      */
34     public void btn_client(View view) {
35         startActivity(new Intent(this, ClientActivity.class));
36     }
37 
38     @Override
39     public boolean onCreateOptionsMenu(Menu menu) {
40         // Inflate the menu; this adds items to the action bar if it is present.
41         getMenuInflater().inflate(R.menu.main, menu);
42         return true;
43     }
44 
45     @Override
46     public boolean onOptionsItemSelected(MenuItem item) {
47         // Handle action bar item clicks here. The action bar will
48         // automatically handle clicks on the Home/Up button, so long
49         // as you specify a parent activity in AndroidManifest.xml.
50         int id = item.getItemId();
51         if (id == R.id.action_settings) {
52             return true;
53         }
54         return super.onOptionsItemSelected(item);
55     }
56 }

 

      3.1.2 layout:

 

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical"
 6     android:paddingBottom="@dimen/activity_vertical_margin"
 7     android:paddingLeft="@dimen/activity_horizontal_margin"
 8     android:paddingRight="@dimen/activity_horizontal_margin"
 9     android:paddingTop="@dimen/activity_vertical_margin"
10     tools:context="org.fiu.bluetoothdemos.MainActivity" >
11 
12     <Button
13         android:layout_width="match_parent"
14         android:layout_height="wrap_content"
15         android:onClick="btn_server"
16         android:text="开启服务器" />
17 
18     <Button
19         android:layout_width="match_parent"
20         android:layout_height="wrap_content"
21         android:onClick="btn_client"
22         android:text="开启客户端" />
23 
24 </LinearLayout>

    3.2 服务器端代码:

      3.2.1 服务器页面:点击进入服务器页面后,直接使用上面所说UUID进行服务器建立,等待客户端连接工作。拥有一个消息显示框和发送EditText,可以与客户端进行交互。内部拥有一个BluetoothServer管理类,这个类主要管理了服务器的蓝牙操作。

      

  1 package org.fiu.bluetoothdemos;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 import java.util.Timer;
  6 import java.util.TimerTask;
  7 
  8 import org.fiu.bluetoothchatdemos.R;
  9 
 10 import android.app.Activity;
 11 import android.os.Bundle;
 12 import android.view.View;
 13 import android.view.ViewGroup;
 14 import android.widget.AbsListView;
 15 import android.widget.BaseAdapter;
 16 import android.widget.EditText;
 17 import android.widget.ListView;
 18 import android.widget.TextView;
 19 
 20 /**
 21  * 服务器
 22  * 
 23  * @author c
 24  * 
 25  */
 26 public class ServerActivity extends Activity {
 27     private EditText et_msg;
 28     private BluetoothServer server;
 29     private List<String> msgs = new ArrayList<String>();
 30     private TimerTask task = new TimerTask() {
 31 
 32         @Override
 33         public void run() {
 34             synchronized (msgs) {
 35                 msgs = server.getMsgs();
 36             }
 37             runOnUiThread(new Runnable() {
 38                 public void run() {
 39                     msgAdapter.notifyDataSetChanged();
 40                 }
 41             });
 42         }
 43 
 44     };
 45     private MyAdapter msgAdapter;
 46 
 47     @Override
 48     protected void onCreate(Bundle savedInstanceState) {
 49         super.onCreate(savedInstanceState);
 50         setContentView(R.layout.activity_chat);
 51 
 52         et_msg = (EditText) findViewById(R.id.et_msg);
 53         server = new BluetoothServer(this);
 54         server.start();
 55 
 56         ListView lv_msg = (ListView) findViewById(R.id.lv_msg);
 57         msgAdapter = new MyAdapter();
 58         lv_msg.setAdapter(msgAdapter);
 59 
 60         Timer timer = new Timer();
 61         timer.schedule(task, 0, 1000);
 62     }
 63 
 64     public class MyAdapter extends BaseAdapter {
 65 
 66         @Override
 67         public int getCount() {
 68             // TODO Auto-generated method stub
 69             return msgs.size();
 70         }
 71 
 72         @Override
 73         public Object getItem(int position) {
 74             return msgs.get(position);
 75         }
 76 
 77         @Override
 78         public long getItemId(int position) {
 79             // TODO Auto-generated method stub
 80             return position;
 81         }
 82 
 83         @Override
 84         public View getView(int position, View convertView, ViewGroup parent) {
 85             TextView tv = null;
 86             if (convertView != null) {
 87                 tv = (TextView) convertView;
 88             } else {
 89                 tv = new TextView(ServerActivity.this);
 90                 AbsListView.LayoutParams params = new AbsListView.LayoutParams(
 91                         AbsListView.LayoutParams.MATCH_PARENT,
 92                         AbsListView.LayoutParams.WRAP_CONTENT);
 93                 tv.setLayoutParams(params);
 94             }
 95             tv.setTag(msgs.get(position));
 96             tv.setText(msgs.get(position));
 97             return tv;
 98         }
 99 
100     }
101 
102     /**
103      * 发送
104      * 
105      * @param view
106      */
107     public void btn_send(View view) {
108         String msg = et_msg.getText().toString().trim();
109         send(msg);
110     }
111 
112     /**
113      * 发送消息到客户端
114      * 
115      * @param msg
116      */
117     private void send(String msg) {
118         server.send(msg);
119         synchronized (msgs) {
120             msgs.add("服务器发送:" + msg);
121         }
122     }
123 }

      3.2.2 服务器页面layout

      

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:layout_margin="5dp"
 6     android:orientation="vertical" >
 7 
 8     <LinearLayout
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:layout_marginBottom="5dp"
12         android:orientation="horizontal" >
13 
14         <EditText
15             android:id="@+id/et_msg"
16             android:layout_width="0dp"
17             android:layout_height="wrap_content"
18             android:layout_weight="3"
19             android:hint="chatMessage" />
20 
21         <Button
22             android:layout_width="20dp"
23             android:layout_height="wrap_content"
24             android:layout_weight="1"
25             android:onClick="btn_send"
26             android:text="send" />
27     </LinearLayout>
28 
29     <ListView
30         android:id="@+id/lv_msg"
31         android:layout_width="match_parent"
32         android:layout_height="match_parent" >
33     </ListView>
34 
35 </LinearLayout>

      3.2.3 服务器蓝牙管理类

      

  1 package org.fiu.bluetoothdemos;
  2 
  3 import java.io.IOException;
  4 import java.io.InputStream;
  5 import java.util.ArrayList;
  6 import java.util.List;
  7 import java.util.UUID;
  8 
  9 import android.bluetooth.BluetoothAdapter;
 10 import android.bluetooth.BluetoothServerSocket;
 11 import android.bluetooth.BluetoothSocket;
 12 import android.content.Context;
 13 import android.os.Handler;
 14 import android.os.Looper;
 15 import android.os.Message;
 16 import android.util.Log;
 17 import android.widget.Toast;
 18 
 19 /**
 20  * 蓝牙服务器
 21  * 
 22  * @author weizh
 23  * 
 24  */
 25 public class BluetoothServer {
 26     /**
 27      * 消息集合
 28      */
 29     private List<String> listMsg = new ArrayList<String>();
 30     /**
 31      * 是否工作中
 32      */
 33     private boolean isWorking = false;
 34     /**
 35      * bluetooth name
 36      */
 37     private String name = "FIUBluetoothServer";
 38     /**
 39      * spp well-known UUID
 40      */
 41     public static final UUID MY_UUID = UUID
 42             .fromString("00001101-0000-1000-8000-00805F9B34FB");
 43     private static final String TAG = "BluetoothServer";
 44     /**
 45      * 蓝牙服务器socket
 46      */
 47     private BluetoothServerSocket bluetoothServerSocket;
 48     /**
 49      * 客户端socket
 50      */
 51     private BluetoothSocket mClientSocket;
 52 
 53     Context context;
 54 
 55     public BluetoothServer(Context context) {
 56         this.context = context;
 57     }
 58 
 59     /**
 60      * 开启服务器
 61      */
 62     public void start() {
 63         listen();
 64     }
 65 
 66     /**
 67      * 开始监听
 68      */
 69     private void listen() {
 70         new Thread(new Runnable() {
 71 
 72             @Override
 73             public void run() {
 74                 // TODO Auto-generated method stub
 75                 // 判断是否有蓝牙设备
 76                 if (!BluetoothUtils.checkBluetoothExists()) {
 77                     throw new RuntimeException("bluetooth module not exists.");
 78                 }
 79                 // 打开设备
 80                 if (!BluetoothUtils.openBluetoothDevice()) {
 81                     return;
 82                 }
 83                 try {
 84                     if (bluetoothServerSocket == null) {
 85                         bluetoothServerSocket = BluetoothAdapter
 86                                 .getDefaultAdapter()
 87                                 .listenUsingRfcommWithServiceRecord(name,
 88                                         MY_UUID);
 89                     }
 90                     isWorking = true;
 91                     while (isWorking) {
 92                         mClientSocket = bluetoothServerSocket.accept();
 93                         Log.i(TAG, "客户端已连接:"
 94                                 + mClientSocket.getRemoteDevice().getName());
 95                         myHandler.sendEmptyMessage(0x01);
 96                         new ClientWorkingThread(mClientSocket).start();
 97                     }
 98                 } catch (IOException e) {
 99                     // TODO Auto-generated catch block
100                     e.printStackTrace();
101                 }
102             }
103         }).start();
104 
105     }
106 
107     private Handler myHandler = new Handler() {
108 
109         @Override
110         public void handleMessage(Message msg) {
111             super.handleMessage(msg);
112             Toast.makeText(context,
113                     "客户端已连接:" + mClientSocket.getRemoteDevice().getName(), 0)
114                     .show();
115         }
116 
117     };
118 
119     /**
120      * 停止
121      */
122     public void stop() {
123         isWorking = false;
124         if (bluetoothServerSocket != null) {
125             try {
126                 bluetoothServerSocket.close();
127             } catch (IOException e) {
128                 // TODO Auto-generated catch block
129                 e.printStackTrace();
130             } finally {
131                 bluetoothServerSocket = null;
132             }
133         }
134         if (mClientSocket != null) {
135             try {
136                 mClientSocket.close();
137             } catch (IOException e) {
138                 // TODO Auto-generated catch block
139                 e.printStackTrace();
140             } finally {
141                 mClientSocket = null;
142             }
143         }
144     }
145 
146     /**
147      * 客户端socket工作类
148      * 
149      * @author weizh
150      * 
151      */
152     private class ClientWorkingThread extends Thread {
153         /**
154          * 客户端socket
155          */
156         private BluetoothSocket mClientSocket;
157 
158         public ClientWorkingThread(BluetoothSocket clientSocket) {
159             this.mClientSocket = clientSocket;
160         }
161 
162         @Override
163         public void run() {
164             try {
165                 InputStream inputStream = mClientSocket.getInputStream();// 输入流
166                 // 从输入流中取出数据,插入消息条中
167                 byte[] buffer = new byte[1024];
168                 while (isWorking) {
169                     int read = inputStream.read(buffer);
170                     if (read != -1) {
171                         // 有内容
172                         // 判断是否取得的消息填充满了buffer,未到字符串结尾符;如果不是,证明读取到了一条信息,并且信息是完整的,这个完整的前提是不能粘包,不粘包可以使用flush进行处理。
173                         StringBuilder sb = new StringBuilder();
174                         if (read < buffer.length) {
175                             String msg = new String(buffer, 0, read);
176                             sb.append(msg);
177                         } else {
178                             byte[] tempBytes = new byte[1024 * 4];
179                             while (read == buffer.length
180                                     && buffer[read - 1] != 0x7f) {
181                                 read = inputStream.read(buffer);
182                             }
183                             String msg = new String(buffer, 0, read);
184                             sb.append(msg);
185                         }
186                         Log.i(TAG, "服务器收到:" + sb.toString());
187                         synchronized (listMsg) {
188                             listMsg.add("客户端发送:" + sb.toString());
189                         }
190                     }
191                     // try {
192                     // Thread.sleep(300);
193                     // } catch (InterruptedException e) {
194                     // // TODO Auto-generated catch block
195                     // e.printStackTrace();
196                     // }
197                 }
198             } catch (IOException e) {
199                 // TODO Auto-generated catch block
200                 e.printStackTrace();
201             }
202             // 工作完毕,关闭socket
203             try {
204                 mClientSocket.close();
205             } catch (IOException e) {
206                 // TODO Auto-generated catch block
207                 e.printStackTrace();
208             }
209 
210         }
211     }
212 
213     /**
214      * 返回listMsg
215      * 
216      * @return
217      */
218     public List<String> getMsgs() {
219         synchronized (listMsg) {
220             return listMsg;
221         }
222     }
223 
224     /**
225      * 发送消息
226      * 
227      * @param msg
228      */
229     public void send(String msg) {
230         if (mClientSocket != null) {
231             try {
232                 mClientSocket.getOutputStream().write(msg.getBytes());
233                 mClientSocket.getOutputStream().flush();
234             } catch (IOException e) {
235                 // TODO Auto-generated catch block
236                 e.printStackTrace();
237             }
238         }
239     }
240 }

     3.3 客户端页面:客户端除了具有消息框和发送消息框外,加载后首先可以进行蓝牙环境扫描,并且和选择。demo中偷了懒,没在选择后停止蓝牙扫描工作等诸多小细节……

      3.3.1 客户端页面代码:

  1 package org.fiu.bluetoothdemos;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 import java.util.Timer;
  6 import java.util.TimerTask;
  7 
  8 import org.fiu.bluetoothchatdemos.R;
  9 import org.fiu.bluetoothdemos.ServerActivity.MyAdapter;
 10 
 11 import android.app.Activity;
 12 import android.app.AlertDialog;
 13 import android.bluetooth.BluetoothAdapter;
 14 import android.bluetooth.BluetoothDevice;
 15 import android.content.BroadcastReceiver;
 16 import android.content.Context;
 17 import android.content.Intent;
 18 import android.content.IntentFilter;
 19 import android.os.Bundle;
 20 import android.util.Log;
 21 import android.view.LayoutInflater;
 22 import android.view.View;
 23 import android.view.View.OnClickListener;
 24 import android.view.ViewGroup;
 25 import android.view.ViewGroup.LayoutParams;
 26 import android.widget.AbsListView;
 27 import android.widget.AbsoluteLayout;
 28 import android.widget.AdapterView;
 29 import android.widget.AdapterView.OnItemClickListener;
 30 import android.widget.ArrayAdapter;
 31 import android.widget.BaseAdapter;
 32 import android.widget.EditText;
 33 import android.widget.LinearLayout;
 34 import android.widget.ListView;
 35 import android.widget.TextView;
 36 
 37 /**
 38  * 客户端
 39  * 
 40  * @author c
 41  * 
 42  */
 43 public class ClientActivity extends Activity {
 44     /**
 45      * 被发现的设备
 46      */
 47     private List<BluetoothDevice> discoverDevices = new ArrayList<BluetoothDevice>();
 48     /**
 49      * 蓝牙客户端
 50      */
 51     private BluetoothClient bluetoothClient;
 52     /**
 53      * tag
 54      */
 55     public final String TAG = "ClientActivity";
 56     /**
 57      * 搜索对话框
 58      */
 59     private AlertDialog dlgSearch;
 60     /**
 61      * adapter
 62      */
 63     private BaseAdapter adapter;
 64     private EditText et_msg;
 65     private List<String> msgs = new ArrayList<String>();
 66     private TimerTask task = new TimerTask() {
 67 
 68         @Override
 69         public void run() {
 70             synchronized (msgs) {
 71                 msgs = bluetoothClient.getMsgs();
 72             }
 73             runOnUiThread(new Runnable() {
 74                 public void run() {
 75                     msgAdapter.notifyDataSetChanged();
 76                 }
 77             });
 78         }
 79 
 80     };
 81     private MyAdapter msgAdapter;
 82     /**
 83      * 设备搜索广播
 84      */
 85     private BroadcastReceiver receiver = new BroadcastReceiver() {
 86 
 87         @Override
 88         public void onReceive(Context context, Intent intent) {
 89             String action = intent.getAction();
 90             switch (action) {
 91             case BluetoothDevice.ACTION_FOUND:
 92                 // 发现设备,添加到列表,刷新列表
 93                 discoverDevices.add((BluetoothDevice) intent
 94                         .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
 95                 if (adapter != null) {
 96                     adapter.notifyDataSetChanged();
 97                 }
 98                 break;
 99             case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
100                 // 开始搜索
101                 Log.i(TAG, "开始搜索设备");
102                 discoverDevices.clear();
103                 // 弹出对话框
104                 if (dlgSearch == null) {
105                     dlgSearch = new AlertDialog.Builder(ClientActivity.this)
106                             .create();
107                     // 自定义对话框
108                     View view = LayoutInflater.from(ClientActivity.this)
109                             .inflate(R.layout.dialog_search, null);
110                     ListView lv_devices = (ListView) view
111                             .findViewById(R.id.lv_devices);
112                     adapter = new DevicesAdapter(ClientActivity.this);
113                     lv_devices.setAdapter(adapter);
114                     lv_devices
115                             .setOnItemClickListener(new OnItemClickListener() {
116 
117                                 @Override
118                                 public void onItemClick(AdapterView<?> parent,
119                                         View view, int position, long id) {
120                                     // 项点击时,进行连接
121                                     BluetoothDevice device = (BluetoothDevice) view
122                                             .getTag();
123                                     bluetoothClient.connect(device);
124                                     dlgSearch.dismiss();
125                                     dlgSearch = null;
126 
127                                 }
128                             });
129                     dlgSearch.setView(view);
130                     dlgSearch.setCancelable(true);// 可以按back键取消
131                     dlgSearch.setCanceledOnTouchOutside(false);// 不可以按空白地方取消
132                 }
133                 dlgSearch.show();
134                 break;
135             case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
136                 // 结束搜索
137                 Log.i(TAG, "结束搜索设备");
138                 break;
139 
140             default:
141                 break;
142             }
143         }
144 
145     };
146 
147     @Override
148     protected void onCreate(Bundle savedInstanceState) {
149         super.onCreate(savedInstanceState);
150         setContentView(R.layout.activity_chat);
151 
152         bluetoothClient = new BluetoothClient();
153 
154         et_msg = (EditText) findViewById(R.id.et_msg);
155 
156         ListView lv_msg = (ListView) findViewById(R.id.lv_msg);
157         msgAdapter = new MyAdapter();
158         lv_msg.setAdapter(msgAdapter);
159 
160         Timer timer = new Timer();
161         timer.schedule(task, 0, 1000);
162 
163         // 搜索蓝牙设备
164         bluetoothClient.start();
165     }
166 
167     public class MyAdapter extends BaseAdapter {
168 
169         @Override
170         public int getCount() {
171             // TODO Auto-generated method stub
172             return msgs.size();
173         }
174 
175         @Override
176         public Object getItem(int position) {
177             return msgs.get(position);
178         }
179 
180         @Override
181         public long getItemId(int position) {
182             // TODO Auto-generated method stub
183             return position;
184         }
185 
186         @Override
187         public View getView(int position, View convertView, ViewGroup parent) {
188             TextView tv = null;
189             if (convertView != null) {
190                 tv = (TextView) convertView;
191             } else {
192                 tv = new TextView(ClientActivity.this);
193                 AbsListView.LayoutParams params = new AbsListView.LayoutParams(
194                         AbsListView.LayoutParams.MATCH_PARENT,
195                         AbsListView.LayoutParams.WRAP_CONTENT);
196                 tv.setLayoutParams(params);
197             }
198             tv.setTag(msgs.get(position));
199             tv.setText(msgs.get(position));
200             return tv;
201         }
202 
203     }
204 
205     @Override
206     protected void onResume() {
207         super.onResume();
208         registerReceiver();
209     }
210 
211     private void registerReceiver() {
212         IntentFilter filter = new IntentFilter();
213         filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
214         filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
215         filter.addAction(BluetoothDevice.ACTION_FOUND);
216         registerReceiver(receiver, filter);
217     }
218 
219     @Override
220     protected void onPause() {
221         super.onPause();
222         unregisterReceiver(receiver);
223     }
224 
225     /**
226      * 设备adapter
227      * 
228      * @author c
229      * 
230      */
231     private class DevicesAdapter extends BaseAdapter {
232 
233         private Context context;
234 
235         public DevicesAdapter(Context context) {
236             this.context = context;
237         }
238 
239         @Override
240         public int getCount() {
241             return discoverDevices.size();
242         }
243 
244         @Override
245         public Object getItem(int position) {
246             // TODO Auto-generated method stub
247             return discoverDevices.get(position);
248         }
249 
250         @Override
251         public long getItemId(int position) {
252             // TODO Auto-generated method stub
253             return position;
254         }
255 
256         @Override
257         public View getView(final int position, View convertView,
258                 ViewGroup parent) {
259             TextView tv = null;
260             if (convertView != null) {
261                 tv = (TextView) convertView;
262             } else {
263                 tv = new TextView(context);
264                 AbsListView.LayoutParams params = new AbsListView.LayoutParams(
265                         AbsListView.LayoutParams.MATCH_PARENT,
266                         AbsListView.LayoutParams.WRAP_CONTENT);
267                 tv.setLayoutParams(params);
268             }
269             tv.setTag(discoverDevices.get(position));
270             tv.setText(discoverDevices.get(position).getName());
271             tv.setFocusable(false);
272             tv.setFocusableInTouchMode(false);
273             // tv.setOnClickListener(new OnClickListener() {
274             //
275             // @Override
276             // public void onClick(View v) {
277             // // 项点击时,进行连接
278             // bluetoothClient.connect(discoverDevices.get(position));
279             // dlgSearch.dismiss();
280             // dlgSearch = null;
281             // }
282             // });
283             return tv;
284         }
285 
286     }
287 
288     /**
289      * 发送
290      * 
291      * @param view
292      */
293     public void btn_send(View view) {
294         String msg = et_msg.getText().toString().trim();
295         send(msg);
296     }
297 
298     /**
299      * 发送消息到客户端
300      * 
301      * @param msg
302      */
303     private void send(String msg) {
304         bluetoothClient.send(msg);
305         synchronized (msgs) {
306             msgs.add("客户端发送:" + msg);
307         }
308     }
309 }

      3.3.2 客户端页面layout  

    

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:layout_margin="5dp"
 6     android:orientation="vertical" >
 7 
 8     <LinearLayout
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:layout_marginBottom="5dp"
12         android:orientation="horizontal" >
13 
14         <EditText
15             android:id="@+id/et_msg"
16             android:layout_width="0dp"
17             android:layout_height="wrap_content"
18             android:layout_weight="3"
19             android:hint="chatMessage" />
20 
21         <Button
22             android:layout_width="20dp"
23             android:layout_height="wrap_content"
24             android:layout_weight="1"
25             android:onClick="btn_send"
26             android:text="send" />
27     </LinearLayout>
28 
29     <ListView
30         android:id="@+id/lv_msg"
31         android:layout_width="match_parent"
32         android:layout_height="match_parent" >
33     </ListView>
34 
35 </LinearLayout>

 

      3.3.3 客户端蓝牙管理类

    

  1 package org.fiu.bluetoothdemos;
  2 
  3 import java.io.IOException;
  4 import java.io.InputStream;
  5 import java.io.OutputStream;
  6 import java.util.ArrayList;
  7 import java.util.List;
  8 import java.util.UUID;
  9 
 10 import android.annotation.SuppressLint;
 11 import android.bluetooth.BluetoothAdapter;
 12 import android.bluetooth.BluetoothDevice;
 13 import android.bluetooth.BluetoothSocket;
 14 import android.util.Log;
 15 
 16 /**
 17  * 蓝牙服务器
 18  * 
 19  * @author weizh
 20  * 
 21  */
 22 public class BluetoothClient {
 23     private static final String TAG = "BluetoothClient";
 24     /**
 25      * 消息集合
 26      */
 27     private List<String> listMsg = new ArrayList<String>();
 28     /**
 29      * 是否工作中
 30      */
 31     private boolean isWorking = false;
 32     /**
 33      * spp well-known UUID
 34      */
 35     public final UUID uuid = UUID
 36             .fromString("00001101-0000-1000-8000-00805F9B34FB");
 37     /**
 38      * 客户端socket
 39      */
 40     private BluetoothSocket mClientSocket;
 41 
 42     public BluetoothClient() {
 43 
 44     }
 45 
 46     /**
 47      * 开启服务器
 48      */
 49     public void start() {
 50         startDiscovery();
 51     }
 52 
 53     /**
 54      * 开始检查设备
 55      */
 56     private void startDiscovery() {
 57         if (!BluetoothUtils.checkBluetoothExists()) {
 58             throw new RuntimeException("bluetooth module not exists.");
 59         }
 60         // 打开设备
 61         if (!BluetoothUtils.openBluetoothDevice()) {
 62             return;
 63         }
 64         // 开始扫描设备
 65         BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter();
 66         defaultAdapter.startDiscovery();
 67     }
 68 
 69     OutputStream outputStream;
 70     private InputStream inputStream;
 71 
 72     /**
 73      * 停止
 74      */
 75     public void stop() {
 76         isWorking = false;
 77         if (mClientSocket != null) {
 78             try {
 79                 mClientSocket.close();
 80             } catch (IOException e) {
 81                 // TODO Auto-generated catch block
 82                 e.printStackTrace();
 83             } finally {
 84                 mClientSocket = null;
 85             }
 86         }
 87     }
 88 
 89     /**
 90      * 客户端socket工作类
 91      * 
 92      * @author weizh
 93      * 
 94      */
 95     private class ClientWorkingThread extends Thread {
 96 
 97         public ClientWorkingThread() {
 98         }
 99 
100         @SuppressLint("NewApi")
101         @Override
102         public void run() {
103             try {
104                 // 从输入流中取出数据,插入消息条中
105                 byte[] buffer = new byte[1024];
106                 while (isWorking) {
107                     int read = inputStream.read(buffer);
108                     if (read != -1) {
109                         // 有内容
110                         // 判断是否取得的消息填充满了buffer,未到字符串结尾符;如果不是,证明读取到了一条信息,并且信息是完整的,这个完整的前提是不能粘包,不粘包可以使用flush进行处理。
111                         StringBuilder sb = new StringBuilder();
112                         if (read < buffer.length) {
113                             String msg = new String(buffer, 0, read);
114                             sb.append(msg);
115                         } else {
116                             byte[] tempBytes = new byte[1024 * 4];
117                             while (read == buffer.length
118                                     && buffer[read - 1] != 0x7f) {
119                                 read = inputStream.read(buffer);
120                             }
121                             String msg = new String(buffer, 0, read);
122                             sb.append(msg);
123                         }
124                         Log.i(TAG, "客户端收到:" + sb.toString());
125                         synchronized (listMsg) {
126                             listMsg.add("服务器发送:" + sb.toString());
127                         }
128                     }
129                 }
130             } catch (IOException e) {
131                 // TODO Auto-generated catch block
132                 e.printStackTrace();
133             }
134             // 工作完毕,关闭socket
135             try {
136                 mClientSocket.close();
137             } catch (IOException e) {
138                 // TODO Auto-generated catch block
139                 e.printStackTrace();
140             }
141 
142         }
143     }
144 
145     /**
146      * 返回listMsg
147      * 
148      * @return
149      */
150     public List<String> getMsgs() {
151         synchronized (listMsg) {
152             return listMsg;
153         }
154     }
155 
156     /**
157      * 发送消息
158      * 
159      * @param msg
160      */
161     public void send(final String msg) {
162         new Thread(new Runnable() {
163 
164             @Override
165             public void run() {
166                 if (mClientSocket != null) {
167                     try {
168                         if (outputStream != null) {
169                             byte[] bytes = msg.getBytes();
170                             outputStream.write(bytes);
171                             outputStream.flush();
172                         }
173                     } catch (IOException e) {
174                         // TODO Auto-generated catch block
175                         e.printStackTrace();
176                     }
177                 }
178             }
179         }).start();
180 
181     }
182 
183     /**
184      * 进行连接
185      * 
186      * @param device
187      */
188     @SuppressLint("NewApi")
189     public void connect(final BluetoothDevice device) {
190         new Thread(new Runnable() {
191 
192             @Override
193             public void run() {
194                 // TODO Auto-generated method stub
195                 try {
196                     mClientSocket = device
197                             .createRfcommSocketToServiceRecord(BluetoothServer.MY_UUID);
198                     mClientSocket.connect();
199                     isWorking = true;
200                     try {
201                         outputStream = mClientSocket.getOutputStream();
202                         inputStream = mClientSocket.getInputStream();
203                     } catch (IOException e1) {
204                         // TODO Auto-generated catch block
205                         e1.printStackTrace();
206                     }
207                     new ClientWorkingThread().start();
208 
209                 } catch (IOException e) {
210                     // TODO Auto-generated catch block
211                     e.printStackTrace();
212                     Log.i(TAG, "连接失败");
213                 }
214             }
215         }).start();
216     }
217 
218 }

 

      3.4 其它用到的布局和类

        3.4.1 dialog_search

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <RelativeLayout
 8         android:layout_width="match_parent"
 9         android:layout_height="wrap_content"
10         android:layout_margin="5dp"
11         android:orientation="horizontal" >
12 
13         <TextView
14             android:layout_width="wrap_content"
15             android:layout_height="wrap_content"
16             android:layout_alignParentLeft="true"
17             android:text="正在搜索……" />
18 
19         <ProgressBar
20             android:id="@+id/progressBar1"
21             style="?android:attr/progressBarStyleSmall"
22             android:layout_width="wrap_content"
23             android:layout_height="wrap_content"
24             android:layout_alignParentRight="true" />
25     </RelativeLayout>
26 
27     <View
28         android:layout_width="match_parent"
29         android:layout_height="2dp"
30         android:layout_marginBottom="2dp"
31         android:layout_marginTop="2dp"
32         android:background="@android:color/darker_gray" />
33 
34     <ListView
35         android:id="@+id/lv_devices"
36         android:layout_width="match_parent"
37         android:layout_height="match_parent"
38         android:layout_margin="5dp" >
39     </ListView>
40 
41 </LinearLayout>

3.5 BluetoothUtils.java:蓝牙帮助类

  1 package org.fiu.bluetoothdemos;
  2 
  3 import java.util.Locale;
  4 import java.util.Set;
  5 
  6 import android.bluetooth.BluetoothAdapter;
  7 import android.bluetooth.BluetoothDevice;
  8 
  9 /**
 10  * 蓝牙帮助模块
 11  * 
 12  * @author c
 13  * 
 14  */
 15 public class BluetoothUtils {
 16     /**
 17      * 检查蓝牙模块是否存在
 18      * 
 19      * @return
 20      */
 21     public static boolean checkBluetoothExists() {
 22         BluetoothAdapter bluetoothAdapter = BluetoothAdapter
 23                 .getDefaultAdapter();
 24         if (bluetoothAdapter != null) {
 25             return true;
 26         }
 27         return false;
 28     }
 29 
 30     /**
 31      * 打开蓝牙模块
 32      * 
 33      * @return
 34      */
 35     public static boolean openBluetoothDevice() {
 36         BluetoothAdapter bluetoothAdapter = BluetoothAdapter
 37                 .getDefaultAdapter();
 38         if (!bluetoothAdapter.isEnabled()) {
 39             if (bluetoothAdapter.enable()) {
 40                 return true;
 41             }
 42         } else {
 43             return true;
 44         }
 45         return false;
 46     }
 47 
 48     /**
 49      * 开启蓝牙模块扫描
 50      * 
 51      * @return
 52      */
 53     public static void startDiscovery() {
 54         BluetoothAdapter bluetoothAdapter = BluetoothAdapter
 55                 .getDefaultAdapter();
 56         if (!bluetoothAdapter.isDiscovering()) {
 57             bluetoothAdapter.startDiscovery();
 58         }
 59     }
 60 
 61     /**
 62      * Convert hex string to byte[] 把为字符串转化为字节数组
 63      * 
 64      * @param hexString
 65      *            the hex string
 66      * @return byte[]
 67      */
 68     public static byte[] hexStringToBytes(String hexString) {
 69         hexString = hexString.replaceAll(" ", "");
 70         if (hexString == null || hexString.equals("")) {
 71             return null;
 72         }
 73         hexString = hexString.toUpperCase(Locale.getDefault());
 74         int length = hexString.length() / 2;
 75         char[] hexChars = hexString.toCharArray();
 76         byte[] d = new byte[length];
 77         for (int i = 0; i < length; i++) {
 78             int pos = i * 2;
 79             d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
 80         }
 81         return d;
 82     }
 83 
 84     /**
 85      * Convert char to byte
 86      * 
 87      * @param c
 88      *            char
 89      * @return byte
 90      */
 91     private static byte charToByte(char c) {
 92         return (byte) "0123456789ABCDEF".indexOf(c);
 93     }
 94 
 95     /**
 96      * 获取已配对的蓝牙设备集合
 97      * 
 98      * @return
 99      */
100     public static Set<BluetoothDevice> getBondedDevices() {
101         return BluetoothAdapter.getDefaultAdapter().getBondedDevices();
102     }
103 
104     /**
105      * 检测当前device是否已经bonded过
106      * 
107      * @param device
108      * @return
109      */
110     public static boolean isBonded(BluetoothDevice device) {
111         if (checkBluetoothExists()) {
112             // 连接之前先确定是否已经bond过,配对过
113             Set<BluetoothDevice> bondedDevices = BluetoothAdapter
114                     .getDefaultAdapter().getBondedDevices();
115             if (bondedDevices != null) {
116                 for (BluetoothDevice bluetoothDevice : bondedDevices) {
117                     if (bluetoothDevice.getAddress()
118                             .equals(device.getAddress())) {
119                         // 该device已经bond过
120                         return true;
121                     }
122                 }
123             }
124         }
125         return false;
126     }
127 }

 

3.6 AndroidManifest.xml:别忘了添加蓝牙权限

    

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="org.fiu.bluetoothchatdemos"
 4     android:versionCode="1"
 5     android:versionName="1.0" >
 6 
 7     <uses-sdk
 8         android:minSdkVersion="8"
 9         android:targetSdkVersion="21" />
10 
11     <uses-permission android:name="android.permission.BLUETOOTH" />
12     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
13 
14     <application
15         android:allowBackup="true"
16         android:icon="@drawable/ic_launcher"
17         android:label="@string/app_name"
18         android:theme="@style/AppTheme" >
19         <activity
20             android:name="org.fiu.bluetoothdemos.MainActivity"
21             android:label="@string/app_name" >
22             <intent-filter>
23                 <action android:name="android.intent.action.MAIN" />
24 
25                 <category android:name="android.intent.category.LAUNCHER" />
26             </intent-filter>
27         </activity>
28         <activity android:name="org.fiu.bluetoothdemos.ServerActivity" />
29         <activity android:name="org.fiu.bluetoothdemos.ClientActivity" />
30     </application>
31 
32 </manifest>

 

    好了,此文章及至这里,如果有问题,欢迎留言。

    整包资源:http://download.csdn.net/detail/weizhhf/9736822

    

 

posted @ 2016-08-21 15:02  weizhxa  阅读(8192)  评论(0编辑  收藏  举报