蓝牙聊天工具

来源:互联网 发布:如何查看知乎提问者 编辑:程序博客网 时间:2024/04/30 14:28

网上有很多关于蓝牙聊天工具的示例代码,对比参考并加入了自己的理解和创新,自己也做了一个蓝牙聊天工具,总体感觉还可以,下面进行分析一下。
首先定义了 一个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;    }}
0 0
原创粉丝点击