android实现蓝牙聊天功能
项目地址:https://gitee.com/chen_yuan_fan/androidlanya.git
mainActivity
package android.myapplication; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.inputmethod.EditorInfo; import android.widget.*; import android.view.*; public class MainActivity extends AppCompatActivity { public static final int MESSAGE_STATE_CHANGE = 1; public static final int MESSAGE_READ = 2; public static final int MESSAGE_WRITE = 3; public static final int MESSAGE_DEVICE_NAME = 4; public static final int MESSAGE_TOAST = 5; public static final String DEVICE_NAME = "device_name"; public static final String TOAST = "toast"; public static final int REQUEST_CONNECT_DEVICE = 1; public static final int REQUEST_ENABLE_BT = 2; private ListView mConversationView; private EditText mOutEditText; private Button mSendButton; private String mConnectedDeviceName = null; private ArrayAdapter<String> mConversationArrayAdapter; private StringBuffer mOutStringBuffer; private BluetoothAdapter mBluetoothAdapter; private ChatService mChatService = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 得到本地蓝牙适配器 mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter(); // 若当前设备不支持蓝牙功能 if(mBluetoothAdapter == null){ Toast.makeText(this,"蓝牙不可用",Toast.LENGTH_LONG).show(); finish(); return; } } @Override public void onStart(){ super.onStart(); if(!mBluetoothAdapter.isEnabled()){ // 若当前设备蓝牙功能未开启,则开启蓝牙 Intent enableIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent,REQUEST_ENABLE_BT); } else{ if(mChatService==null) setupChat(); } } @Override public synchronized void onResume(){ super.onResume(); if(mChatService != null) if(mChatService.getState() == ChatService.STATE_NONE) mChatService.start(); } @Override public synchronized void onPause(){ super.onPause(); } @Override public synchronized void onStop(){ super.onStop(); } @Override public synchronized void onDestroy(){ super.onDestroy(); if(mChatService != null) mChatService.stop(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. switch (item.getItemId()){ case R.id.scan: Intent serverIntent=new Intent(this,DeviceList.class); startActivityForResult(serverIntent,REQUEST_CONNECT_DEVICE); return true; case R.id.discoverable: ensureDiscoverable(); return true; case R.id.BtOpen: if (!mBluetoothAdapter.isEnabled()) { Intent turnOn = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(turnOn, REQUEST_ENABLE_BT); } return true; case R.id.BtOff: mBluetoothAdapter.disable(); return true; } return false; } private void ensureDiscoverable(){ if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE){ Intent discoverableIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra( BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300); startActivity(discoverableIntent); } } private void sendMessage(String message){ if(mChatService.getState() != ChatService.STATE_CONNECTED){ Toast.makeText(this,R.string.not_connected,Toast.LENGTH_SHORT).show(); return; } if(message.length() > 0){ byte[] send=message.getBytes(); mChatService.write(send); mOutStringBuffer.setLength(0); mOutEditText.setText(mOutStringBuffer); } } private void setupChat(){ mConversationArrayAdapter=new ArrayAdapter<String>(this, R.layout.list_item); mConversationView=(ListView)findViewById(R.id.list_conversation); mConversationView.setAdapter(mConversationArrayAdapter); mOutEditText=(EditText)findViewById(R.id.edit_text_out); mSendButton = (Button)findViewById(R.id.button_send); mSendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String message = mOutEditText.getText().toString(); sendMessage(message); } }); mChatService = new ChatService(this,mHandler); mOutStringBuffer=new StringBuffer(""); } private final Handler mHandler=new Handler(){ @Override public void handleMessage(Message msg){ switch (msg.what){ case MESSAGE_STATE_CHANGE: switch (msg.arg1){ case ChatService.STATE_CONNECTED: mConversationArrayAdapter.clear(); break; case ChatService.STATE_CONNECTING: break; case ChatService.STATE_LISTEN: case ChatService.STATE_NONE: break; }break; case MESSAGE_WRITE: byte[]writeBuf =(byte[])msg.obj; String writeMessage=new String(writeBuf); mConversationArrayAdapter.add("我: " + writeMessage); break; case MESSAGE_READ: byte[]readBuf =(byte[])msg.obj; String readMessage=new String(readBuf,0,msg.arg1); mConversationArrayAdapter.add(mConnectedDeviceName+": " +readMessage); break; case MESSAGE_DEVICE_NAME: mConnectedDeviceName=msg.getData().getString(DEVICE_NAME); Toast.makeText(getApplicationContext(), "链接到"+mConnectedDeviceName,Toast.LENGTH_SHORT) .show(); break; case MESSAGE_TOAST: Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),Toast.LENGTH_SHORT) .show(); break; } } }; public void onActivityResult(int requesstCode, int resultCode, Intent data){ switch (requesstCode){ case REQUEST_CONNECT_DEVICE: if(resultCode==Activity.RESULT_OK){ String address=data.getExtras().getString(DeviceList.EXTRA_DEVICE_ADDRESS); BluetoothDevice device=mBluetoothAdapter.getRemoteDevice(address); mChatService.connect(device); } break; case REQUEST_ENABLE_BT: if(resultCode == Activity.RESULT_OK){ setupChat(); }else { Toast.makeText(this, "bt_not_enable_leaving", Toast.LENGTH_SHORT).show(); finish(); } } } }
chatService
package android.myapplication; /** * Created by Excalibur on 2017/5/30. */ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; public class ChatService { private static final String NAME="MainActivity"; // UUID-->通用唯一识别码,能唯一地辨识咨询 private static final UUID MY_UUID=UUID.fromString( "00001101-0000-1000-8000-00805F9B34FB");//串口 // "fa87c0d0-afac-11de-8a39-0800200c9a66"); private final BluetoothAdapter mAdapter; private final Handler mHandler ; private AcceptThread mAcceptThread; private ConnectThread mConnectThread; private ConnectedThread mConnectedThread; private int mState; public static final int STATE_NONE = 0; public static final int STATE_LISTEN = 1; public static final int STATE_CONNECTING = 2; public static final int STATE_CONNECTED = 3; public ChatService(Context context, Handler handler){ mAdapter=BluetoothAdapter.getDefaultAdapter(); mState = STATE_NONE; mHandler = handler; } private synchronized void setState(int state){ mState = state; mHandler.obtainMessage(MainActivity.MESSAGE_STATE_CHANGE,state,-1) .sendToTarget(); } public synchronized int getState(){ return mState; } public synchronized void start(){ if(mConnectThread !=null){ mConnectThread.cancel(); mConnectThread=null; } if(mConnectedThread !=null){ mConnectedThread.cancel(); mConnectedThread=null; } if (mAcceptThread==null){ mAcceptThread=new AcceptThread(); mAcceptThread.start(); } setState(STATE_LISTEN); } // 取消 Connecting Connected状态下的相关线程,然后运行新的mConnectThread线程 public synchronized void connect(BluetoothDevice device){ if(mState == STATE_CONNECTED){ if(mConnectThread !=null){ mConnectThread.cancel(); mConnectThread=null; } } if(mConnectedThread !=null) { mConnectedThread.cancel(); mConnectedThread = null; } if(mAcceptThread != null){ mAcceptThread.cancel(); mAcceptThread =null; } mConnectThread = new ConnectThread(device); mConnectThread.start(); setState(STATE_CONNECTING); } // 开启一个ConnectThread来管理对应的当前连接。之前取消任意现存的mConnectThread // mConnectThread,mAcceptThread线程,然后开启新的mConnectThread,传入当前 // 刚刚接受的socket连接,最后通过Handler来通知UI连接 public synchronized void connected(BluetoothSocket socket, BluetoothDevice device){ if(mConnectThread !=null){ mConnectThread.cancel(); mConnectThread=null; } if(mConnectedThread !=null){ mConnectedThread.cancel(); mConnectedThread=null; } if(mAcceptThread !=null){ mAcceptThread.cancel(); mAcceptThread=null; } mConnectedThread=new ConnectedThread(socket); mConnectedThread.start(); Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_DEVICE_NAME); Bundle bundle = new Bundle(); bundle.putString(MainActivity.DEVICE_NAME,device.getName()); msg.setData(bundle); mHandler.sendMessage(msg); setState(STATE_CONNECTED); } // 停止所有相关线程,设当前状态为none public synchronized void stop(){ if(mConnectThread !=null){ mConnectThread.cancel(); mConnectThread=null; } if(mConnectedThread !=null){ mConnectedThread.cancel(); mConnectedThread=null; } if(mAcceptThread !=null){ mAcceptThread.cancel(); mAcceptThread=null; } setState(STATE_NONE); } // 在STATE_CONNECTED状态下,调用mConnectedThread里的write方法,写入byte public void write(byte[]out){ ConnectedThread r; synchronized (this){ if(mState != STATE_CONNECTED) return; r = mConnectedThread; } r.write(out); } // 连接失败的时候处理,通知UI,并设为STATE_LISTEN状态 private void connectionFailed(){ setState(STATE_LISTEN); Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST); Bundle bundle=new Bundle(); bundle.putString(MainActivity.TOAST,"链接不到设备"); msg.setData(bundle); mHandler.sendMessage(msg); ChatService.this.start(); } // 当连接失去的时候,设为STATE_LISTEN private void connectionLost(){ setState(STATE_LISTEN); Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST); Bundle bundle=new Bundle(); bundle.putString(MainActivity.TOAST,"设备链接中断"); msg.setData(bundle); mHandler.sendMessage(msg); ChatService.this.start(); } // 创建监听线程,准备接受新连接。使用阻塞方式,调用BluetoothServerSocket.accept() private class AcceptThread extends Thread{ private final BluetoothServerSocket mmServerSocket; public AcceptThread(){ BluetoothServerSocket tmp = null; try{ tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID); }catch (IOException e){} mmServerSocket = tmp; } public void run(){ BluetoothSocket socket= null; while(mState != STATE_CONNECTED){ try{ socket = mmServerSocket.accept(); }catch (IOException e) { break; } if(socket != null){ connected(socket,socket.getRemoteDevice()); try{ mmServerSocket.close(); }catch (IOException e){} } } } public void cancel(){ try{ mmServerSocket.close(); }catch (IOException e){} } } // 连接线程,专门用来对外发出连接对方蓝牙的请求并进行处理 // 构造函数里通过BluetoothDevice.createRfcommSocketToServiceRecord(), // 从待连接的device产生BluetoothSocket,然后在run方法中connect // 成功后调用 BluetoothChatService的connnected()方法,定义cancel()在关闭线程时能关闭socket private class ConnectThread extends Thread{ private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device){ // Use a temporary object that is later assigned to mmSocket, // because mmSocket is final mmDevice=device; BluetoothSocket tmp = null; // Get a BluetoothSocket to connect with the given BluetoothDevice try{ // MY_UUID is the app's UUID string, also used by the server code tmp = device.createRfcommSocketToServiceRecord(MY_UUID); }catch (IOException e){} mmSocket = tmp; } public void run(){ // Cancel discovery because it will slow down the connection mAdapter.cancelDiscovery(); try{ // Connect the device through the socket. This will block // until it succeeds or throws an exception mmSocket.connect(); }catch (IOException e){ connectionFailed(); // Unable to connect; close the socket and get out try{ mmSocket.close(); }catch (IOException e2){} //ChatService.this.start(); return; } synchronized(ChatService.this){ mConnectedThread = null; } connected(mmSocket,mmDevice); } public void cancel(){ /* try{ mmSocket.close(); }catch (IOException e){}*/ } } // 双方蓝牙连接后一直运行的线程。构造函数中设置输入输出流。 // Run方法中使用阻塞模式的InputStream.read()循环读取输入流 // 然后psot到UI线程中更新聊天信息。也提供了write()将聊天消息写入输出流传输至对方, // 传输成功后回写入UI线程。最后cancel()关闭连接的socket private class ConnectedThread extends Thread{ private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; public ConnectedThread(BluetoothSocket socket){ mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut=null; // Get the input and output streams, using temp objects because // member streams are final try{ tmpIn=mmSocket.getInputStream(); tmpOut=mmSocket.getOutputStream(); }catch (IOException e){} mmInStream = tmpIn; mmOutStream = tmpOut; } public void run(){ byte[]buffer=new byte[1024]; int bytes; while (true){ try{ bytes = mmInStream.read(buffer); mHandler.obtainMessage(MainActivity.MESSAGE_READ,bytes,-1,buffer).sendToTarget(); }catch (IOException e){ connectionLost(); break; } } } public void write(byte[]buffer){ try{ mmOutStream.write(buffer); }catch (IOException e){ Log.d("MainActivity","Send Fail"); } mHandler.obtainMessage(MainActivity.MESSAGE_WRITE,buffer).sendToTarget(); } public void cancel(){ try{ mmSocket.close(); }catch (IOException e){} } } }
DeviceList
package android.myapplication; import java.util.Set; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; import android.widget.Toast; /** * Created by Excalibur on 2017/6/1. * 用于显示蓝牙设备列表,并返回蓝牙设备信息 */ public class DeviceList extends AppCompatActivity{ public static String EXTRA_DEVICE_ADDRESS="device_address"; private BluetoothAdapter mBtAdapter; private ArrayAdapter<String>mPairedDevicesArrayAdapter; private ArrayAdapter<String>mNewDevicesArrayAdapter; private IntentFilter filter = new IntentFilter(); private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.device_list); setResult(Activity.RESULT_CANCELED); progressBar = (ProgressBar)findViewById(R.id.processbar); Button scanButton=(Button)findViewById(R.id.button_scan); scanButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { doDiscovery(); view.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); } }); mPairedDevicesArrayAdapter=new ArrayAdapter<String>(this, R.layout.list_item); mNewDevicesArrayAdapter=new ArrayAdapter<String>(this, R.layout.list_item); ListView pairedListView=(ListView)findViewById(R.id.paired_devices); pairedListView.setAdapter(mPairedDevicesArrayAdapter); pairedListView.setOnItemClickListener(mDeviceClickListen); ListView newDeviceListView=(ListView)findViewById(R.id.new_devices); newDeviceListView.setAdapter(mNewDevicesArrayAdapter); newDeviceListView.setOnItemClickListener(mDeviceClickListen); filter.addAction(BluetoothDevice.ACTION_FOUND); filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(mReceiver,filter); mBtAdapter=BluetoothAdapter.getDefaultAdapter(); Set<BluetoothDevice>pairedDevices=mBtAdapter.getBondedDevices(); if(pairedDevices.size()>0){ for(BluetoothDevice device : pairedDevices){ mPairedDevicesArrayAdapter.add(device.getName()+"\n" +device.getAddress()); } }else{ String noDevices=getResources().getText(R.string.none_paired) .toString(); mPairedDevicesArrayAdapter.add(noDevices); } } @Override protected void onDestroy(){ super.onDestroy(); if(mBtAdapter!=null){ mBtAdapter.cancelDiscovery(); } this.unregisterReceiver(mReceiver); } private void doDiscovery(){ if(mBtAdapter.isDiscovering()) mBtAdapter.cancelDiscovery(); mBtAdapter.startDiscovery(); } private OnItemClickListener mDeviceClickListen=new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { mBtAdapter.cancelDiscovery(); String info=((TextView) view).getText().toString(); String address=info.substring(info.length()-17); Intent intent =new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS,address); setResult(Activity.RESULT_OK,intent); finish(); } }; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action=intent.getAction(); if(BluetoothDevice.ACTION_FOUND.equals(action)){ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(device.getBondState() != BluetoothDevice.BOND_BONDED){ mNewDevicesArrayAdapter.add(device.getName()+"\n" + device.getAddress()); } }else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED .equals(action)){ progressBar.setVisibility(View.GONE); Toast.makeText(DeviceList.this,"搜索完毕",Toast.LENGTH_SHORT).show(); if(mNewDevicesArrayAdapter.getCount()==0){ String noDevices=getResources().getText( R.string.none_found).toString(); mNewDevicesArrayAdapter.add(noDevices); } } } }; }

浙公网安备 33010602011771号