蓝牙聊天工具

网上有很多关于蓝牙聊天工具的示例代码,对比参考并加入了自己的理解和创新,自己也做了一个蓝牙聊天工具,总体感觉还可以,下面进行分析一下。
首先定义了 一个Activity主界面,进行聊天信息的输出和发送 。先将聊天界面的XML布局文件贴出来:

activity_bluetooth_chat.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout  android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
    <ListView  android:id="@+id/chat_window_LV" android:layout_width="match_parent" android:layout_height="match_parent" android:transcriptMode="alwaysScroll" android:stackFromBottom="true" android:layout_weight="2">
    </ListView>
    <LinearLayout  android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="bottom">
        <Button  android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="p" android:onClick="takePhotoClicked"/>
        <EditText  android:id="@+id/input_ET" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="bottom"/>
        <Button  android:text="send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onSendBtnClicked"/>
    </LinearLayout>
</LinearLayout>

包括一个ListView和一个LineLayout布局(一个EditText和一个Button),该ListView用来显示聊天信息的,由一个ArrayAdapter类进行管理,每当收到或者发送 一条信息时,调用add(Object obj)方法可以同步显示出来,另外属性android:transcriptMode=”alwaysScroll”表示当内容 逐渐增多时,会出现滚动条进行帮助显示。 android:stackFromBottom=”true”表示item是从底部开始添加的。而android:layout_gravity=”bottom”>属性表示 该部件的位置,此处是在parent的底部。
聊天记录的ListView里每个item的layout为:

talk_note.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView  android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20dp" xmlns:android="http://schemas.android.com/apk/res/android"></TextView>

下面来看主Activity,先给出用到的数据成员:

public class BluetoothChat extends AppCompatActivity {
   private static final String TAG = "BluetoothChat";

    public static boolean mBoundListenConnection = false;

    private static final String path = "/DCIM/camera/";

    /* @brief: the flag indicate that the window is in behind */
    public static  boolean WINDOW_BEHIND_FLAG  = true;


    /* @brief: the handler flag used to decide the action will be taken */
    public static final int NEW_DEVICE_READY_TO_CONNECT = 0;

    /* * @Brief: * the intent request code */
    private static final int REQUEST_CODE_ENABLE_BLUETOOTH = 1001;
    private static final int REQUEST_CODE_SCAN_DEVICES  = 1002;
    public static final int REQUEST_CODE_CAMERA_ACTION = 1003;
    private static final int REQUEST_CODE_PHOTO_list = 1004;


    private BluetoothAdapter mBluetoothChatAdapter = null;
    private static BluetoothChatDevice mBluetoothChatDevice;

    private  ArrayAdapter<String> messageArrayAdapter;

    private EditText inputArea;
    public BlueChatHandler mBluetoothChatHandler = new BlueChatHandler(this);

    private Messenger bluetoothChatMessenger = new Messenger(mBluetoothChatHandler);
    private Messenger listenMessenger;

给出onCreate方法中做的工作 :

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bluetooth_chat);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        inputArea = (EditText)findViewById(R.id.input_ET);

        ListView message  = (ListView)findViewById(R.id.chat_window_LV);
        messageArrayAdapter = new ArrayAdapter<String>(this,R.layout.talk_note);
        message.setAdapter(messageArrayAdapter);

        mBluetoothChatHandler.post(mRunnable);

        mBluetoothChatDevice =
                new BluetoothChatDevice(this,mBluetoothChatHandler);

        mBluetoothChatAdapter = BluetoothAdapter.getDefaultAdapter();

        if(mBluetoothChatAdapter==null) {
            finish();
            return;
        }
        if (!mBluetoothChatAdapter.isEnabled()) {
            Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(i, REQUEST_CODE_ENABLE_BLUETOOTH);
        }else {
            if (!mBoundListenConnection) {
                Intent listenForConnection = new Intent(this, ListenForConnectionService.class);
                bindService(listenForConnection, mServiceConnection, BIND_AUTO_CREATE);
            }
        }
        // Eula.show(this);
    }

首先初始化聊天记录 的ListView message和输入部件EditText inputArea。并将message与一个ArrayAdapter messageArrayAdapter关联 起来,进而可以实时同步 聊天信息。初始化Ui thread的handler,并将其 post到一个Runnable对象中,使程序先执行此run()方法:

    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            Eula.show(BluetoothChat.this);
        }
    };

此处不是本文的重点不做介绍。其实就是一个用户协议,accept后下次打开该 应用时,就不会出现AlertDialog窗口。

然后定义了一个BluetoothChatDevice的实例mBluetoothChatDevice,该类是自己写的一个类,将UI thread的Handler和该Context通过其构造 函数传递给该类。

接着调用mBluetoothChatAdapter = BluetoothAdapter.getDefaultAdapter();获得该设备的蓝牙 接口,并判断是否支持蓝牙,如果不 支持 蓝牙则 返回值mBluetoothChatAdapter ==null。
接着通过调用蓝牙接口的isEnabled()方法判断蓝牙是否已经打开。如果已经打开则启动一个service来监听是否有蓝牙设备接入;如果没有打开则执行打开蓝牙activity,并在打开后启动监听蓝牙设备接入服务。

public static boolean WINDOW_BEHIND_FLAG = true;为indicate当前applicaton是否visible。如果visible则直接将聊天 信息同步到ListView message中,如果Invisible,则会出现Toast提示信息。

public static boolean mBoundListenConnection = false;为是否bind了监听设备接入的服务,由于是在onCreate方法中启动服务的。所以在onDestory方法中unbind服务。

要想bind一个service必须 具有一个ServiceConnection,给出此数据成员的代码:

private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            listenMessenger = new Messenger(service);
            Message msg = Message.obtain(null,ListenForConnectionService.START_LISTEN_HANDLER_MESS,bluetoothChatMessenger);
            mBoundListenConnection = true;
            try {
                listenMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBoundListenConnection = false;
            try {
                listenMessenger.send(Message.obtain(null, ListenForConnectionService.STOP_LISTEN_HANDLER_MESS));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

在方法public void onServiceConnected(ComponentName name, IBinder service) 中得到一个Messenger对象,此对象 由Ibinder service初始化,是监听蓝牙接入的Handler,因此进行listenMessenger.send(msg)时,会在监听service中收到消息,并启动监听thread,并且将该包含UI thread的handler的Messenger发给 绑定的服务中,这样在此监听 服务中 就可以向UI thread传递消息了。
看看这段断码后看onActivityResult方法

 public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
          case R.id.scan_for_blueDevice:
              Intent i = new Intent(this,BluetoothDeviceList.class);
              startActivityForResult(i, REQUEST_CODE_SCAN_DEVICES);
              break;
          case R.id.visibleBluetooth:
              Intent scanIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
              scanIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
              Log.d(TAG,"type"+scanIntent.getType());
              startActivity(scanIntent);
              break;
          default:
              break;
        }
        return super.onOptionsItemSelected(item);
    }

此段完成了启动来源可发现的activity,以及启动 蓝牙搜索设备的activity。

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case REQUEST_CODE_ENABLE_BLUETOOTH:
                if(resultCode==RESULT_CANCELED) {
                    Toast.makeText(this,"the bluetooth device is not enable",Toast.LENGTH_LONG).show();
                    finish();
                    return;
                }
                if(!mBoundListenConnection) {
                    Intent listenForConnection = new Intent(this, ListenForConnectionService.class);
                    bindService(listenForConnection, mServiceConnection, BIND_AUTO_CREATE);
                }

                break;
            case REQUEST_CODE_SCAN_DEVICES:
                String address =  data.getExtras().getString(BluetoothDeviceList.SELECTED_BLUETOOTH_DEVICE_ADDR);
                BluetoothDevice bluetoothDevice = mBluetoothChatAdapter.getRemoteDevice(address);
                mBluetoothChatDevice.connect(bluetoothDevice);
                break;
            default:
                super.onActivityResult(requestCode, resultCode, data);
                break;
        }

    }

给出UI thead的Handler:

private class BlueChatHandler extends Handler {

        private Context context;

        public BlueChatHandler(Context c) {
            context = c;
        }

        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case NEW_DEVICE_READY_TO_CONNECT:
                    BluetoothSocket socket = (BluetoothSocket)msg.obj;
                    mBluetoothChatDevice.chat(socket);
                    break;
                case BluetoothChatDevice.SEND_MESSAGE:
                    byte[] bytes = (byte[])msg.obj;
                    String tmp = new String(bytes);
                   messageArrayAdapter.add(mBluetoothChatAdapter.getName()+": "+tmp);
                    break;
                case BluetoothChatDevice.RECEIVE_MESSAGE:
                // Bitmap bitmap;
                    byte[] mbytes = (byte[])msg.obj;
                    /* if (mbytes.length != 0) { bitmap = BitmapFactory.decodeByteArray(mbytes, 0, mbytes.length); } else { return; }*/
                    String mtmp = new String(mbytes,0,msg.arg1);
                    messageArrayAdapter.add(BluetoothChatDevice.connectedDeviceName+": "+mtmp);
                   // messageArrayAdapter.add(bitmap);
                    if(WINDOW_BEHIND_FLAG)
                        Toast.makeText(BluetoothChat.this,BluetoothChatDevice.connectedDeviceName+": " +
                                " "+mtmp,Toast.LENGTH_LONG).show();
                    break;
                case BluetoothChatDevice.TOAST_MESSAGE:
                    switch (msg.arg1) {
                        case BluetoothChatDevice.TOAST_MESSAGE_UNCONNECT:
                            Toast.makeText(context, msg.obj.toString() + " unconnected",
                                    Toast.LENGTH_SHORT).show();
                            break;
                        case BluetoothChatDevice.TOAST_MESSAGE_CONNECT:
                            Toast.makeText(context,msg.obj.toString()+" connect",
                                    Toast.LENGTH_SHORT).show();
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }

给出BluetoothChatdevice类:


/** * Created by almo.liu on 2016/4/20. */
public class BluetoothChatDevice implements Serializable {

    private static final String TAG = "BluetoothChatDevice";

    public static final int IN_CHATTING_FLAG = 0;
    public static final int IN_CONNECTING_FLAG = 1;
    public static final int IN_CONNECTED_FLAG = 2;
    public static final int NO_EVENT_HAPPEN = -1;

    public static int STATE = NO_EVENT_HAPPEN;

    public static final int RECEIVE_MESSAGE = 11;
    public static final int SEND_MESSAGE  = 12;
    public static final int TOAST_MESSAGE = 13;
    public static final int TOAST_MESSAGE_CONNECT = 14;
    public static final int TOAST_MESSAGE_UNCONNECT = 15;

    private Context mContext;
    private Handler mBluetoothChatDeviceHandler;

    public static String connectedDeviceName = null;

    private ConnectingThread mConnectingThread;
    private ChatThread mChatThread;

    public BluetoothChatDevice(Context context,Handler handler) {
        mContext = context;
        mBluetoothChatDeviceHandler = handler;
    }

    public synchronized void chat(BluetoothSocket bluetoothSocket) {
        if(mChatThread!=null)
            mChatThread.cancel();

        mChatThread = new ChatThread(bluetoothSocket);
        mChatThread.start();
    }

    public synchronized void connect(BluetoothDevice bluetoothDevice) {

        if(mConnectingThread!=null)
            mConnectingThread.cancel();

        mConnectingThread = new ConnectingThread(bluetoothDevice);
        mConnectingThread.start();
    }

    public void send(byte[] bytes) {
        if(mChatThread!=null)
            mChatThread.write(bytes);
        else
            Toast.makeText(mContext,"please connected a device!",Toast.LENGTH_SHORT).show();
    }

    private void connectionLost() {

        mBluetoothChatDeviceHandler.obtainMessage(TOAST_MESSAGE,TOAST_MESSAGE_UNCONNECT,
                1,connectedDeviceName).sendToTarget();
        connectedDeviceName = null;
    }

    private void successConnect() {
        mBluetoothChatDeviceHandler.obtainMessage(TOAST_MESSAGE,TOAST_MESSAGE_CONNECT,
                0,connectedDeviceName).sendToTarget();
    }

    private class ConnectingThread extends Thread {

        BluetoothSocket socket;

        public ConnectingThread(BluetoothDevice bluetoothDevice) {
            BluetoothSocket bluetoothSocket = null;
            STATE = IN_CONNECTING_FLAG;
            try {
                bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(
                        ListenForConnectionService.MY_UUID_INSECURE);
            } catch (IOException e) {
                STATE = NO_EVENT_HAPPEN;
            }
            socket = bluetoothSocket;
        }

        @Override
        public void run() {
            try {
                socket.connect();
            } catch (IOException e) {
               STATE = NO_EVENT_HAPPEN;
                mConnectingThread = null;
                return;
            }
            STATE = IN_CONNECTED_FLAG;
            mConnectingThread = null;
            chat(socket);
        }

        public synchronized void cancel() {
          try {
                socket.close();
                mConnectingThread = null;
                STATE = NO_EVENT_HAPPEN;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private class ChatThread extends Thread {

        private BluetoothSocket socket;

        private InputStream inputStream;
        private OutputStream outputStream;

        public ChatThread(BluetoothSocket b) {

            InputStream tmp_input = null;
            OutputStream tmp_output = null;
            STATE = IN_CHATTING_FLAG;
            socket = b;
            try {
                tmp_input= socket.getInputStream();
                tmp_output = socket.getOutputStream();
            } catch (IOException e) {
                Log.d(TAG,"exception in create out and in stream");
                STATE = NO_EVENT_HAPPEN;
            }
            connectedDeviceName = socket.getRemoteDevice().getName();
            successConnect();
            outputStream = tmp_output;
            inputStream = tmp_input;
        }

        public synchronized void write(byte[] bytes) {
            if(mChatThread==null)
                return;
            try {
                outputStream.write(bytes);
            } catch (IOException e) {
                return;
            }
            mBluetoothChatDeviceHandler.obtainMessage(SEND_MESSAGE,bytes).sendToTarget();
        }

        @Override
        public void run() {
            byte[] buffer = new byte[1024];
            int num = 0;
            while(true) {
                try {
                    num = inputStream.read(buffer);
                    Log.d(TAG,new String(buffer));
                } catch (IOException e) {
                    Log.d(TAG, "exception in read: " + num);
                    mChatThread  = null;
                    break;
                }
                mBluetoothChatDeviceHandler.obtainMessage(RECEIVE_MESSAGE,num,0,buffer).sendToTarget();
            }
            connectionLost();
            STATE = NO_EVENT_HAPPEN;
            mChatThread = null;
        }

        public synchronized void cancel() {
           try {
                STATE = NO_EVENT_HAPPEN;
                socket.close();
                mChatThread = null;
           } catch (IOException e) {
               e.printStackTrace();
           }
        }
    }
}

给出监听设备接入的service:

public class ListenForConnectionService extends Service {

    private static final String TAG = "ListenFor";

    public static final UUID MY_UUID_SECURE =
            UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
    public static final UUID MY_UUID_INSECURE =
            UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");

    private static boolean loop_listen = false;
    private  BluetoothAdapter mListenAdapter ;
    private  BluetoothServerSocket mListenBluetoothServerSocket;

    public  static final int START_LISTEN_HANDLER_MESS = 1;
    public static final int  STOP_LISTEN_HANDLER_MESS = 2;

    private Messenger blueChatMessenger;

    private Handler listenHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case START_LISTEN_HANDLER_MESS:
                    blueChatMessenger = (Messenger)msg.obj;
                    listen();
                    break;
                case STOP_LISTEN_HANDLER_MESS:
                    cancelListen();
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    };



    Messenger listenMessenger = new Messenger(listenHandler);

    @Override
    public void onCreate() {
        super.onCreate();
/* mListenAdapter = BluetoothAdapter.getDefaultAdapter(); BluetoothServerSocket tmp; try { tmp = mListenAdapter.listenUsingInsecureRfcommWithServiceRecord("test",MY_UUID_INSECURE); } catch (IOException e) { return; } mListenBluetoothServerSocket = tmp; */
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        loop_listen = false;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return listenMessenger.getBinder();
    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
    }

    private Runnable listenRunnable = new Runnable() {
        @Override
        public void run() {
            BluetoothSocket socket = null;
            while(loop_listen) {
                if(mListenBluetoothServerSocket==null) {
                    mListenAdapter = BluetoothAdapter.getDefaultAdapter();
                    BluetoothServerSocket tmp;
                    Log.d(TAG,"null++++++++++++++++++");
                    try {
                        tmp = mListenAdapter.listenUsingInsecureRfcommWithServiceRecord("test",MY_UUID_INSECURE);
                    } catch (IOException e) {
                        return;
                    }
                    mListenBluetoothServerSocket = tmp;
                    continue;
                }
                try {
                    socket = mListenBluetoothServerSocket.accept();
                } catch (IOException e) {
                    loop_listen = false;
                    break;
                }
                if(socket!=null) {
                    Message msg = Message.obtain(null,BluetoothChat.NEW_DEVICE_READY_TO_CONNECT,socket);
                    try {
                        blueChatMessenger.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    };


    /* the method called to contact with the service */
    public void listen() {
        loop_listen = true;
        Thread listenThread = new Thread(listenRunnable);
        listenThread.start();
    }

    public void cancelListen() {
        loop_listen = false;
    }

}

你可能感兴趣的:(蓝牙,聊天工具)