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

时间:2022-12-23 10:36:06

欢迎转载,但请注明出处!谢谢。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:

      

 package org.fiu.bluetoothdemos;

 import org.fiu.bluetoothchatdemos.R;

 import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View; public class MainActivity extends ActionBarActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} /**
* 建立服务器
*
* @param view
*/
public void btn_server(View view) {
startActivity(new Intent(this, ServerActivity.class));
} /**
* 建立客户端
*
* @param view
*/
public void btn_client(View view) {
startActivity(new Intent(this, ClientActivity.class));
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.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.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

      3.1.2 layout:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="org.fiu.bluetoothdemos.MainActivity" > <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="btn_server"
android:text="开启服务器" /> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="btn_client"
android:text="开启客户端" /> </LinearLayout>

    3.2 服务器端代码:

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

      

 package org.fiu.bluetoothdemos;

 import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask; import org.fiu.bluetoothchatdemos.R; import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView; /**
* 服务器
*
* @author c
*
*/
public class ServerActivity extends Activity {
private EditText et_msg;
private BluetoothServer server;
private List<String> msgs = new ArrayList<String>();
private TimerTask task = new TimerTask() { @Override
public void run() {
synchronized (msgs) {
msgs = server.getMsgs();
}
runOnUiThread(new Runnable() {
public void run() {
msgAdapter.notifyDataSetChanged();
}
});
} };
private MyAdapter msgAdapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat); et_msg = (EditText) findViewById(R.id.et_msg);
server = new BluetoothServer(this);
server.start(); ListView lv_msg = (ListView) findViewById(R.id.lv_msg);
msgAdapter = new MyAdapter();
lv_msg.setAdapter(msgAdapter); Timer timer = new Timer();
timer.schedule(task, 0, 1000);
} public class MyAdapter extends BaseAdapter { @Override
public int getCount() {
// TODO Auto-generated method stub
return msgs.size();
} @Override
public Object getItem(int position) {
return msgs.get(position);
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = null;
if (convertView != null) {
tv = (TextView) convertView;
} else {
tv = new TextView(ServerActivity.this);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(
AbsListView.LayoutParams.MATCH_PARENT,
AbsListView.LayoutParams.WRAP_CONTENT);
tv.setLayoutParams(params);
}
tv.setTag(msgs.get(position));
tv.setText(msgs.get(position));
return tv;
} } /**
* 发送
*
* @param view
*/
public void btn_send(View view) {
String msg = et_msg.getText().toString().trim();
send(msg);
} /**
* 发送消息到客户端
*
* @param msg
*/
private void send(String msg) {
server.send(msg);
synchronized (msgs) {
msgs.add("服务器发送:" + msg);
}
}
}

      3.2.2 服务器页面layout

      

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:orientation="vertical" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:orientation="horizontal" > <EditText
android:id="@+id/et_msg"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="chatMessage" /> <Button
android:layout_width="20dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="btn_send"
android:text="send" />
</LinearLayout> <ListView
android:id="@+id/lv_msg"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView> </LinearLayout>

      3.2.3 服务器蓝牙管理类

      

 package org.fiu.bluetoothdemos;

 import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast; /**
* 蓝牙服务器
*
* @author weizh
*
*/
public class BluetoothServer {
/**
* 消息集合
*/
private List<String> listMsg = new ArrayList<String>();
/**
* 是否工作中
*/
private boolean isWorking = false;
/**
* bluetooth name
*/
private String name = "FIUBluetoothServer";
/**
* spp well-known UUID
*/
public static final UUID MY_UUID = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final String TAG = "BluetoothServer";
/**
* 蓝牙服务器socket
*/
private BluetoothServerSocket bluetoothServerSocket;
/**
* 客户端socket
*/
private BluetoothSocket mClientSocket; Context context; public BluetoothServer(Context context) {
this.context = context;
} /**
* 开启服务器
*/
public void start() {
listen();
} /**
* 开始监听
*/
private void listen() {
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
// 判断是否有蓝牙设备
if (!BluetoothUtils.checkBluetoothExists()) {
throw new RuntimeException("bluetooth module not exists.");
}
// 打开设备
if (!BluetoothUtils.openBluetoothDevice()) {
return;
}
try {
if (bluetoothServerSocket == null) {
bluetoothServerSocket = BluetoothAdapter
.getDefaultAdapter()
.listenUsingRfcommWithServiceRecord(name,
MY_UUID);
}
isWorking = true;
while (isWorking) {
mClientSocket = bluetoothServerSocket.accept();
Log.i(TAG, "客户端已连接:"
+ mClientSocket.getRemoteDevice().getName());
myHandler.sendEmptyMessage(0x01);
new ClientWorkingThread(mClientSocket).start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start(); } private Handler myHandler = new Handler() { @Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Toast.makeText(context,
"客户端已连接:" + mClientSocket.getRemoteDevice().getName(), 0)
.show();
} }; /**
* 停止
*/
public void stop() {
isWorking = false;
if (bluetoothServerSocket != null) {
try {
bluetoothServerSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
bluetoothServerSocket = null;
}
}
if (mClientSocket != null) {
try {
mClientSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
mClientSocket = null;
}
}
} /**
* 客户端socket工作类
*
* @author weizh
*
*/
private class ClientWorkingThread extends Thread {
/**
* 客户端socket
*/
private BluetoothSocket mClientSocket; public ClientWorkingThread(BluetoothSocket clientSocket) {
this.mClientSocket = clientSocket;
} @Override
public void run() {
try {
InputStream inputStream = mClientSocket.getInputStream();// 输入流
// 从输入流中取出数据,插入消息条中
byte[] buffer = new byte[1024];
while (isWorking) {
int read = inputStream.read(buffer);
if (read != -1) {
// 有内容
// 判断是否取得的消息填充满了buffer,未到字符串结尾符;如果不是,证明读取到了一条信息,并且信息是完整的,这个完整的前提是不能粘包,不粘包可以使用flush进行处理。
StringBuilder sb = new StringBuilder();
if (read < buffer.length) {
String msg = new String(buffer, 0, read);
sb.append(msg);
} else {
byte[] tempBytes = new byte[1024 * 4];
while (read == buffer.length
&& buffer[read - 1] != 0x7f) {
read = inputStream.read(buffer);
}
String msg = new String(buffer, 0, read);
sb.append(msg);
}
Log.i(TAG, "服务器收到:" + sb.toString());
synchronized (listMsg) {
listMsg.add("客户端发送:" + sb.toString());
}
}
// try {
// Thread.sleep(300);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 工作完毕,关闭socket
try {
mClientSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
} /**
* 返回listMsg
*
* @return
*/
public List<String> getMsgs() {
synchronized (listMsg) {
return listMsg;
}
} /**
* 发送消息
*
* @param msg
*/
public void send(String msg) {
if (mClientSocket != null) {
try {
mClientSocket.getOutputStream().write(msg.getBytes());
mClientSocket.getOutputStream().flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

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

      3.3.1 客户端页面代码:

 package org.fiu.bluetoothdemos;

 import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask; import org.fiu.bluetoothchatdemos.R;
import org.fiu.bluetoothdemos.ServerActivity.MyAdapter; import android.app.Activity;
import android.app.AlertDialog;
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.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.AbsoluteLayout;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView; /**
* 客户端
*
* @author c
*
*/
public class ClientActivity extends Activity {
/**
* 被发现的设备
*/
private List<BluetoothDevice> discoverDevices = new ArrayList<BluetoothDevice>();
/**
* 蓝牙客户端
*/
private BluetoothClient bluetoothClient;
/**
* tag
*/
public final String TAG = "ClientActivity";
/**
* 搜索对话框
*/
private AlertDialog dlgSearch;
/**
* adapter
*/
private BaseAdapter adapter;
private EditText et_msg;
private List<String> msgs = new ArrayList<String>();
private TimerTask task = new TimerTask() { @Override
public void run() {
synchronized (msgs) {
msgs = bluetoothClient.getMsgs();
}
runOnUiThread(new Runnable() {
public void run() {
msgAdapter.notifyDataSetChanged();
}
});
} };
private MyAdapter msgAdapter;
/**
* 设备搜索广播
*/
private BroadcastReceiver receiver = new BroadcastReceiver() { @Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case BluetoothDevice.ACTION_FOUND:
// 发现设备,添加到列表,刷新列表
discoverDevices.add((BluetoothDevice) intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
if (adapter != null) {
adapter.notifyDataSetChanged();
}
break;
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
// 开始搜索
Log.i(TAG, "开始搜索设备");
discoverDevices.clear();
// 弹出对话框
if (dlgSearch == null) {
dlgSearch = new AlertDialog.Builder(ClientActivity.this)
.create();
// 自定义对话框
View view = LayoutInflater.from(ClientActivity.this)
.inflate(R.layout.dialog_search, null);
ListView lv_devices = (ListView) view
.findViewById(R.id.lv_devices);
adapter = new DevicesAdapter(ClientActivity.this);
lv_devices.setAdapter(adapter);
lv_devices
.setOnItemClickListener(new OnItemClickListener() { @Override
public void onItemClick(AdapterView<?> parent,
View view, int position, long id) {
// 项点击时,进行连接
BluetoothDevice device = (BluetoothDevice) view
.getTag();
bluetoothClient.connect(device);
dlgSearch.dismiss();
dlgSearch = null; }
});
dlgSearch.setView(view);
dlgSearch.setCancelable(true);// 可以按back键取消
dlgSearch.setCanceledOnTouchOutside(false);// 不可以按空白地方取消
}
dlgSearch.show();
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
// 结束搜索
Log.i(TAG, "结束搜索设备");
break; default:
break;
}
} }; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat); bluetoothClient = new BluetoothClient(); et_msg = (EditText) findViewById(R.id.et_msg); ListView lv_msg = (ListView) findViewById(R.id.lv_msg);
msgAdapter = new MyAdapter();
lv_msg.setAdapter(msgAdapter); Timer timer = new Timer();
timer.schedule(task, 0, 1000); // 搜索蓝牙设备
bluetoothClient.start();
} public class MyAdapter extends BaseAdapter { @Override
public int getCount() {
// TODO Auto-generated method stub
return msgs.size();
} @Override
public Object getItem(int position) {
return msgs.get(position);
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = null;
if (convertView != null) {
tv = (TextView) convertView;
} else {
tv = new TextView(ClientActivity.this);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(
AbsListView.LayoutParams.MATCH_PARENT,
AbsListView.LayoutParams.WRAP_CONTENT);
tv.setLayoutParams(params);
}
tv.setTag(msgs.get(position));
tv.setText(msgs.get(position));
return tv;
} } @Override
protected void onResume() {
super.onResume();
registerReceiver();
} private void registerReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
registerReceiver(receiver, filter);
} @Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
} /**
* 设备adapter
*
* @author c
*
*/
private class DevicesAdapter extends BaseAdapter { private Context context; public DevicesAdapter(Context context) {
this.context = context;
} @Override
public int getCount() {
return discoverDevices.size();
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return discoverDevices.get(position);
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
} @Override
public View getView(final int position, View convertView,
ViewGroup parent) {
TextView tv = null;
if (convertView != null) {
tv = (TextView) convertView;
} else {
tv = new TextView(context);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(
AbsListView.LayoutParams.MATCH_PARENT,
AbsListView.LayoutParams.WRAP_CONTENT);
tv.setLayoutParams(params);
}
tv.setTag(discoverDevices.get(position));
tv.setText(discoverDevices.get(position).getName());
tv.setFocusable(false);
tv.setFocusableInTouchMode(false);
// tv.setOnClickListener(new OnClickListener() {
//
// @Override
// public void onClick(View v) {
// // 项点击时,进行连接
// bluetoothClient.connect(discoverDevices.get(position));
// dlgSearch.dismiss();
// dlgSearch = null;
// }
// });
return tv;
} } /**
* 发送
*
* @param view
*/
public void btn_send(View view) {
String msg = et_msg.getText().toString().trim();
send(msg);
} /**
* 发送消息到客户端
*
* @param msg
*/
private void send(String msg) {
bluetoothClient.send(msg);
synchronized (msgs) {
msgs.add("客户端发送:" + msg);
}
}
}

      3.3.2 客户端页面layout  

    

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:orientation="vertical" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:orientation="horizontal" > <EditText
android:id="@+id/et_msg"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="chatMessage" /> <Button
android:layout_width="20dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="btn_send"
android:text="send" />
</LinearLayout> <ListView
android:id="@+id/lv_msg"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView> </LinearLayout>

      3.3.3 客户端蓝牙管理类

    

 package org.fiu.bluetoothdemos;

 import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log; /**
* 蓝牙服务器
*
* @author weizh
*
*/
public class BluetoothClient {
private static final String TAG = "BluetoothClient";
/**
* 消息集合
*/
private List<String> listMsg = new ArrayList<String>();
/**
* 是否工作中
*/
private boolean isWorking = false;
/**
* spp well-known UUID
*/
public final UUID uuid = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
/**
* 客户端socket
*/
private BluetoothSocket mClientSocket; public BluetoothClient() { } /**
* 开启服务器
*/
public void start() {
startDiscovery();
} /**
* 开始检查设备
*/
private void startDiscovery() {
if (!BluetoothUtils.checkBluetoothExists()) {
throw new RuntimeException("bluetooth module not exists.");
}
// 打开设备
if (!BluetoothUtils.openBluetoothDevice()) {
return;
}
// 开始扫描设备
BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter();
defaultAdapter.startDiscovery();
} OutputStream outputStream;
private InputStream inputStream; /**
* 停止
*/
public void stop() {
isWorking = false;
if (mClientSocket != null) {
try {
mClientSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
mClientSocket = null;
}
}
} /**
* 客户端socket工作类
*
* @author weizh
*
*/
private class ClientWorkingThread extends Thread { public ClientWorkingThread() {
} @SuppressLint("NewApi")
@Override
public void run() {
try {
// 从输入流中取出数据,插入消息条中
byte[] buffer = new byte[1024];
while (isWorking) {
int read = inputStream.read(buffer);
if (read != -1) {
// 有内容
// 判断是否取得的消息填充满了buffer,未到字符串结尾符;如果不是,证明读取到了一条信息,并且信息是完整的,这个完整的前提是不能粘包,不粘包可以使用flush进行处理。
StringBuilder sb = new StringBuilder();
if (read < buffer.length) {
String msg = new String(buffer, 0, read);
sb.append(msg);
} else {
byte[] tempBytes = new byte[1024 * 4];
while (read == buffer.length
&& buffer[read - 1] != 0x7f) {
read = inputStream.read(buffer);
}
String msg = new String(buffer, 0, read);
sb.append(msg);
}
Log.i(TAG, "客户端收到:" + sb.toString());
synchronized (listMsg) {
listMsg.add("服务器发送:" + sb.toString());
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 工作完毕,关闭socket
try {
mClientSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
} /**
* 返回listMsg
*
* @return
*/
public List<String> getMsgs() {
synchronized (listMsg) {
return listMsg;
}
} /**
* 发送消息
*
* @param msg
*/
public void send(final String msg) {
new Thread(new Runnable() { @Override
public void run() {
if (mClientSocket != null) {
try {
if (outputStream != null) {
byte[] bytes = msg.getBytes();
outputStream.write(bytes);
outputStream.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start(); } /**
* 进行连接
*
* @param device
*/
@SuppressLint("NewApi")
public void connect(final BluetoothDevice device) {
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
try {
mClientSocket = device
.createRfcommSocketToServiceRecord(BluetoothServer.MY_UUID);
mClientSocket.connect();
isWorking = true;
try {
outputStream = mClientSocket.getOutputStream();
inputStream = mClientSocket.getInputStream();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
new ClientWorkingThread().start(); } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i(TAG, "连接失败");
}
}
}).start();
} }

      3.4 其它用到的布局和类

        3.4.1 dialog_search

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal" > <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="正在搜索……" /> <ProgressBar
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true" />
</RelativeLayout> <View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:background="@android:color/darker_gray" /> <ListView
android:id="@+id/lv_devices"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp" >
</ListView> </LinearLayout>

3.5 BluetoothUtils.java:蓝牙帮助类

 package org.fiu.bluetoothdemos;

 import java.util.Locale;
import java.util.Set; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; /**
* 蓝牙帮助模块
*
* @author c
*
*/
public class BluetoothUtils {
/**
* 检查蓝牙模块是否存在
*
* @return
*/
public static boolean checkBluetoothExists() {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter
.getDefaultAdapter();
if (bluetoothAdapter != null) {
return true;
}
return false;
} /**
* 打开蓝牙模块
*
* @return
*/
public static boolean openBluetoothDevice() {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter
.getDefaultAdapter();
if (!bluetoothAdapter.isEnabled()) {
if (bluetoothAdapter.enable()) {
return true;
}
} else {
return true;
}
return false;
} /**
* 开启蓝牙模块扫描
*
* @return
*/
public static void startDiscovery() {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter
.getDefaultAdapter();
if (!bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.startDiscovery();
}
} /**
* Convert hex string to byte[] 把为字符串转化为字节数组
*
* @param hexString
* the hex string
* @return byte[]
*/
public static byte[] hexStringToBytes(String hexString) {
hexString = hexString.replaceAll(" ", "");
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase(Locale.getDefault());
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
} /**
* Convert char to byte
*
* @param c
* char
* @return byte
*/
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
} /**
* 获取已配对的蓝牙设备集合
*
* @return
*/
public static Set<BluetoothDevice> getBondedDevices() {
return BluetoothAdapter.getDefaultAdapter().getBondedDevices();
} /**
* 检测当前device是否已经bonded过
*
* @param device
* @return
*/
public static boolean isBonded(BluetoothDevice device) {
if (checkBluetoothExists()) {
// 连接之前先确定是否已经bond过,配对过
Set<BluetoothDevice> bondedDevices = BluetoothAdapter
.getDefaultAdapter().getBondedDevices();
if (bondedDevices != null) {
for (BluetoothDevice bluetoothDevice : bondedDevices) {
if (bluetoothDevice.getAddress()
.equals(device.getAddress())) {
// 该device已经bond过
return true;
}
}
}
}
return false;
}
}

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

    

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.fiu.bluetoothchatdemos"
android:versionCode="1"
android:versionName="1.0" > <uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="org.fiu.bluetoothdemos.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="org.fiu.bluetoothdemos.ServerActivity" />
<activity android:name="org.fiu.bluetoothdemos.ClientActivity" />
</application> </manifest>

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

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

    

Android端简易蓝牙聊天通讯App(原创)的更多相关文章

  1. 【视频】零基础学Android开发:蓝牙聊天室APP(四)

    零基础学Android开发:蓝牙聊天室APP第四讲 4.1 ListView控件的使用 4.2 BaseAdapter具体解释 4.3 ListView分布与滚动事件 4.4 ListView事件监听 ...

  2. 【视频】零基础学Android开发:蓝牙聊天室APP(二)

    零基础学Android开发:蓝牙聊天室APP第二讲 2.1 课程内容应用场景 2.2 Android UI设计 2.3 组件布局:LinearLayout和RelativeLayout 2.4 Tex ...

  3. 【视频】零基础学Android开发:蓝牙聊天室APP(三)

    零基础学Android开发:蓝牙聊天室APP第三讲 3.1 ImageView.ImageButton控件具体解释 3.2 GridView控件具体解释 3.3 SimpleAdapter适配器具体解 ...

  4. 【视频】零基础学Android开发:蓝牙聊天室APP(一)

    零基础学Android开发:蓝牙聊天室APP第一讲 1. Android介绍与环境搭建:史上最高效Android入门学习 1.1 Google的大小战略 1.2 物联网与云计算 1.3 智能XX设备 ...

  5. Android一对多蓝牙连接示例APP

    一对多蓝牙连接示例,基于Google BluetoothChat修改,实现一对多聊天(一个服务端.多个客户端),类似聊天室. 主要功能: 客户端的发出的消息所有终端都能收到(由服务端转发) 客户端之间 ...

  6. 基于Android Classic Bluetooth的蓝牙聊天软件

    代码地址如下:http://www.demodashi.com/demo/12133.html BluetoothChat 基于Android Classic Bluetooth的蓝牙聊天软件,目前仅 ...

  7. Android蓝牙串口通讯【转】

    本文转载自:http://blog.sina.com.cn/s/blog_631e3f2601012ixi.html Android蓝牙串口通讯 闲着无聊玩起了Android蓝牙模块与单片机蓝牙模块的 ...

  8. 基于Android 平台简易即时通讯的研究与设计&lbrack;转&rsqb;

    摘要:论文简单介绍Android 平台的特性,主要阐述了基于Android 平台简易即时通讯(IM)的作用和功能以及实现方法.(复杂的通讯如引入视频音频等可以考虑AnyChat SDK~)关键词:An ...

  9. Android - 传统蓝牙通信聊天

    Android -传统蓝牙通信聊天 技术:java+Android4.4+jdk1.8 运行环境:Android4.4.Android7.0 概述 Android 传统蓝牙的使用,包括开关蓝牙.搜索设 ...

随机推荐

  1. js&colon;数据结构笔记7--哈希表

    哈希表(散列表):通过哈希函数将键值映射为一个字典; 哈希函数:依赖键值的数据类型来构建一个哈希函数: 一个基本的哈希表:(按字符串计算键值) function HashTable() { this. ...

  2. Android使用SimpleAdapter

    SimpleAdapter的使用步骤如下: 声明ListView,并进行初始化操作 准备数据集,一般用list来实现,当然也可以使用数组 为listview适配simpleadapter 如下代码: ...

  3. 互联网视频直播技术(广电总局、优酷土豆、XX直播)

    互联网直播是目前最火的技术之一,涵盖了很多方面的知识(网络,CDN,GPU,算法,图像处理),以下我介绍互联网直播的大体框架和关键技术点: 一.前端视频流协议 前端流主要包括UDP.RTMP.RTSP ...

  4. String&comma;StringBuffer&comma;StringBudilder区别--2019-04-13

    String,StringBuffer,StringBudilder区别: 1String 是字符串常量,创建内容不可以变, final修饰意味着String类型不能被继承,减少被修改的可能,从而最大 ...

  5. TensorFlow进阶(二)---张量的操作

    张量操作 在tensorflow中,有很多操作张量的函数,有生成张量.创建随机张量.张量类型与形状变换和张量的切片与运算 生成张量 固定值张量 tf.zeros(shape, dtype=tf.flo ...

  6. Vmware tools install

    Vmware tools 1◆ 下载 2◆ diagram   Ifcfg-eth0   =====>关闭防火墙 systemctl stop firewalld.service   ===== ...

  7. HBase核心知识点总结

    一.HBase介绍 1.基本概念 HBase是一种Hadoop数据库,经常被描述为一种稀疏的,分布式的,持久化的,多维有序映射,它基于行键.列键和时间戳建立索引,是一个可以随机访问的存储和检索数据的平 ...

  8. 【BZOJ1017】&lbrack;JSOI2008&rsqb;魔兽地图(动态规划)

    [BZOJ1017][JSOI2008]魔兽地图(动态规划) 题面 BZOJ 洛谷 题解 状态设一下,\(f[i][j][k]\)表示第\(i\)个物品,有\(j\)个用于合成,总花费为\(k\)的最 ...

  9. Parquet 格式文件

    Apache Parquet是Hadoop生态圈中一种新型列式存储格式,它可以兼容Hadoop生态圈中大多数计算框架(Hadoop.Spark等),被多种查询引擎支持(Hive.Impala.Dril ...

  10. 关于ErrorPage

    JSP里创建一个网页test.jsp, 让这个网页上面出现一个错误, 再创建一个切换页面error.jsp, 使test.jsp如果出现错误就切换到error.jsp上, 但是怎么试都是出现一个网页上 ...