• 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

        

  • 相关阅读:
    你可能不知道的css-doodle
    js变量提升与函数提升的详细过程
    绑定Github上的个人博客到Godaddy域名
    基于Github&Hexo的个人博客搭建过程
    github提交代码contributions不显示小绿块
    从零开始学 Web 系列教程
    从零开始学 Web 之 Vue.js(六)Vue的组件
    从零开始学 Web 之 Vue.js(五)Vue的动画
    从零开始学 Web 之 Vue.js(四)Vue的Ajax请求和跨域
    CSS(二)- 选择器
  • 原文地址:https://www.cnblogs.com/weizhxa/p/5792775.html
Copyright © 2020-2023  润新知