PS:最近同学问我蓝牙的事,因此自己也就脑补了一下蓝牙...
学习内容:
1.如何实现蓝牙通信技术...
蓝牙通信其实是手机里很常用的一种通信方式,现在的手机中是必然存在蓝牙的,蓝牙通信也是有一部分优点的,功耗低,安全性比较高,但是缺点想必大家都知道,传输的速率也确实是不快,相比于Wifi通信,确实不值一提...虽然影响范围并不高,但是既然蓝牙存在,那么还是有必要知道蓝牙是如何进行通信的...蓝牙通信有两种方式,最常用的就是使用socket套接字来实现蓝牙通信...
蓝牙通信原理:蓝牙通信的原理很简单,一个设备作为服务端,另一个设备作为客户端,服务端对外暴露,客户端通过发送连接请求,服务端进行响应,然后返回一个BluetoothSocket套接字来管理这个连接...那么服务端个客户端就会共享一个RFCOMM端口,进行数据传递...听起来其实蛮简单的,实现过程还是有点复杂的...这里我还是直接上一个代码吧...
这个是Activity.java文件...这是个非常长的代码块,看完谁都头痛...我们还是进行分块解释...先把这些代码都略过...还是看最下面的解释...
package com.qualcomm.bluetoothclient; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Set; import java.util.UUID; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.Toast; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; public class ClientActivity extends Activity implements OnItemClickListener { private Context mContext; private BluetoothAdapter mBluetoothAdapter; // Bluetooth适配器 private BluetoothDevice device; // 蓝牙设备 private ListView mListView; private ArrayList<ChatMessage> list; private ClientAdapter clientAdapter; // ListView适配器 private Button disconnect = null, sendButton = null; private EditText editText = null; private BluetoothSocket socket; // 客户端socket private ClientThread mClientThread; // 客户端运行线程 private ReadThread mReadThread; // 读取流线程 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } // 变量初始化 private void init() { // TODO Auto-generated method stub mContext = this; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//获取本地蓝牙... list = new ArrayList<ChatMessage>();// 初始化list clientAdapter = new ClientAdapter(mContext, list); //适配器,用来限制如何显示ListView... mListView = (ListView) findViewById(R.id.list); mListView.setFastScrollEnabled(true); mListView.setAdapter(clientAdapter); mListView.setOnItemClickListener(this); // 注册receiver监听,注册广播... IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); // 获取已经配对过的蓝牙设备 Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();//获取发现的蓝牙设备的基本信息... if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { //将手机名字和物理地址放入到listview中... list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), true)); clientAdapter.notifyDataSetChanged(); //重新绘制ListView mListView.setSelection(list.size() - 1); } } else { list.add(new ChatMessage("没有已经配对过的设备", true)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } editText = (EditText) findViewById(R.id.edit); editText.setEnabled(false); editText.clearFocus(); sendButton = (Button) findViewById(R.id.btn_send); sendButton.setEnabled(false); sendButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub String msg = editText.getText().toString(); if (msg.length() > 0) { sendMessageHandler(msg); editText.setText(""); editText.clearFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);//定义一个输入法对象...通过Context.INPUT_METHOD_SERVICE获取实例... //当EditText没有焦点的时候,阻止输入法的弹出...其实就是在没点击EditText获取焦点的时候,没有输入法的显示... imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); } else { Toast.makeText(mContext, "发送内容不能为空", Toast.LENGTH_SHORT).show(); } } }); disconnect = (Button) findViewById(R.id.disconnect); disconnect.setEnabled(false); disconnect.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub // 关闭相关服务 closeClient(); BluetoothMsg.isOpen = false; BluetoothMsg.serviceOrCilent = BluetoothMsg.ServerOrCilent.NONE; Toast.makeText(mContext, "连接已断开", Toast.LENGTH_SHORT).show(); } }); } @Override protected void onStart() { // TODO Auto-generated method stub super.onStart(); if (mBluetoothAdapter != null) { //本地蓝牙存在... if (!mBluetoothAdapter.isEnabled()) { //判断蓝牙是否被打开... // 发送打开蓝牙的意图,系统会弹出一个提示对话框 Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, RESULT_FIRST_USER); // 设置蓝牙的可见性,最大值3600秒,默认120秒,0表示永远可见(作为客户端,可见性可以不设置,服务端必须要设置) //打开本机的蓝牙功能,持续的时间是永远可见... Intent displayIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); displayIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0); startActivity(displayIntent); // 直接打开蓝牙 mBluetoothAdapter.enable(); } } } @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); // 扫描 scanDevice(); } /** * 蓝牙设备扫描过程中(mBluetoothAdapter.startDiscovery())会发出的消息 * ACTION_FOUND 扫描到远程设备 * ACTION_DISCOVERY_FINISHED 扫描结束 */ private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object from the Intent // 通过EXTRA_DEVICE附加域来得到一个BluetoothDevice设备 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // If it's already paired, skip it, because it's been listed already // 如果这个设备是不曾配对过的,添加到list列表 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), false)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } // When discovery is finished, change the Activity title } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { setProgressBarIndeterminateVisibility(false); if (mListView.getCount() == 0) { list.add(new ChatMessage("没有发现蓝牙设备", false)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } } } }; // Handler更新UI private Handler LinkDetectedHandler = new Handler() { @Override public void handleMessage(Message msg) { //Toast.makeText(mContext, (String)msg.obj, Toast.LENGTH_SHORT).show(); if(msg.what==1) { list.add(new ChatMessage((String)msg.obj, true)); } else { list.add(new ChatMessage((String)msg.obj, false)); } clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } }; // 当连接上服务器的时候才可以选择发送数据和断开连接 private Handler refreshUI = new Handler() { public void handleMessage(Message msg) { if (msg.what == 0) { disconnect.setEnabled(true); sendButton.setEnabled(true); editText.setEnabled(true); } } }; // 开启客户端连接服务端 private class ClientThread extends Thread { @Override public void run() { // TODO Auto-generated method stub if (device != null) { try { socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); // 连接 Message msg = new Message(); msg.obj = "请稍候,正在连接服务器: "+ BluetoothMsg.BlueToothAddress; msg.what = 0; LinkDetectedHandler.sendMessage(msg); // 通过socket连接服务器,这是一个阻塞过程,直到连接建立或者连接失效 socket.connect(); Message msg2 = new Message(); msg2.obj = "已经连接上服务端!可以发送信息"; msg2.what = 0; LinkDetectedHandler.sendMessage(msg2); // 更新UI界面 Message uiMessage = new Message(); uiMessage.what = 0; refreshUI.sendMessage(uiMessage); // 可以开启读数据线程 mReadThread = new ReadThread(); mReadThread.start(); } catch (IOException e) { // TODO Auto-generated catch block // socket.connect()连接失效 Message msg = new Message(); msg.obj = "连接服务端异常!断开连接重新试一试。"; msg.what = 0; LinkDetectedHandler.sendMessage(msg); } } } } // 通过socket获取InputStream流 private class ReadThread extends Thread { @Override public void run() { // TODO Auto-generated method stub byte[] buffer = new byte[1024]; int bytes; InputStream is = null; try { is = socket.getInputStream(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } while(true) { try { if ((bytes = is.read(buffer)) > 0) { byte[] data = new byte[bytes]; for (int i = 0; i < data.length; i++) { data[i] = buffer[i]; } String s = new String(data); Message msg = new Message(); msg.obj = s; msg.what = 1; LinkDetectedHandler.sendMessage(msg); } } catch (IOException e) { // TODO Auto-generated catch block try { is.close(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } break; } } } } // 发送数据 private void sendMessageHandler(String msg) { if (socket == null) { Toast.makeText(mContext, "没有可用的连接", Toast.LENGTH_SHORT).show(); return; } //如果连接上了,那么获取输出流... try { OutputStream os = socket.getOutputStream(); os.write(msg.getBytes()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } list.add(new ChatMessage(msg, false)); //将数据存放到list中 clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } // 停止服务 private void closeClient() { new Thread() { public void run() { if (mClientThread != null) { mClientThread.interrupt(); mClientThread = null; } if (mReadThread != null) { mReadThread.interrupt(); mReadThread = null; } try { if (socket != null) { socket.close(); socket = null; } } catch (IOException e) { // TODO: handle exception } } }.start(); } // 扫描设备 private void scanDevice() { // TODO Auto-generated method stub if (mBluetoothAdapter.isDiscovering()) { //如果正在处于扫描过程... mBluetoothAdapter.cancelDiscovery(); //取消扫描... } else { list.clear(); clientAdapter.notifyDataSetChanged(); // 每次扫描前都先判断一下是否存在已经配对过的设备 Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), true)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } } else { list.add(new ChatMessage("No devices have been paired", true)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } /* 开始搜索 */ mBluetoothAdapter.startDiscovery(); } } @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { // TODO Auto-generated method stub ChatMessage item = list.get(arg2); //item保存着message和一个boolean数值... String info = item.getMessage(); //单纯获取message的信息... String address = info.substring(info.length() - 17);//获取MAC地址...其实就是硬件地址... BluetoothMsg.BlueToothAddress = address; // 停止扫描 // BluetoothAdapter.startDiscovery()很耗资源,在尝试配对前必须中止它 mBluetoothAdapter.cancelDiscovery(); // 通过Mac地址去尝试连接一个设备 device = mBluetoothAdapter.getRemoteDevice(BluetoothMsg.BlueToothAddress); mClientThread = new ClientThread(); //开启新的线程... mClientThread.start(); BluetoothMsg.isOpen = true; } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); if (mBluetoothAdapter != null) { mBluetoothAdapter.cancelDiscovery(); // 关闭蓝牙 mBluetoothAdapter.disable(); } unregisterReceiver(mReceiver); closeClient(); } }
这个是我自定义了一个类,用来判断蓝牙的连接类型...
package com.qualcomm.bluetoothclient; public class BluetoothMsg { /** * 蓝牙连接类型 * */ public enum ServerOrCilent { NONE, SERVICE, CILENT }; //蓝牙连接方式 public static ServerOrCilent serviceOrCilent = ServerOrCilent.NONE; //连接蓝牙地址 public static String BlueToothAddress = null, lastblueToothAddress = null; //通信线程是否开启 public static boolean isOpen = false; }
这个是保存我们发送的数据信息的自定义类...
package com.qualcomm.bluetoothclient; /* * 这里定义一个类,用来保存我们发送的数据信息... * */ public class ChatMessage { private String message; private boolean isSiri; public ChatMessage(String message, boolean siri) { // TODO Auto-generated constructor stub this.message = message; this.isSiri = siri; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public boolean isSiri() { return isSiri; } public void setSiri(boolean isSiri) { this.isSiri = isSiri; } }
最后这个是适配器...因为我这里使用到了ListView,因此我需要使用一个适配器来设置ListView以何种方式显示在屏幕上...
package com.qualcomm.bluetoothclient; import java.util.ArrayList; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; /* * * * * * */ public class ClientAdapter extends BaseAdapter { private ArrayList<ChatMessage> list; private LayoutInflater mInflater; public ClientAdapter(Context context, ArrayList<ChatMessage> messages) { // TODO Auto-generated constructor stub this.list = messages; this.mInflater = LayoutInflater.from(context); } @Override public int getCount() { // TODO Auto-generated method stub return list.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return list.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public int getItemViewType(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ViewHolder viewHolder = null; ChatMessage message = list.get(position); if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item, null); viewHolder = new ViewHolder((View)convertView.findViewById(R.id.list_child) , (TextView)convertView.findViewById(R.id.chat_msg)); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } if (message.isSiri()) { viewHolder.child.setBackgroundResource(R.drawable.msgbox_rec); } else { viewHolder.child.setBackgroundResource(R.drawable.msgbox_send); } viewHolder.msg.setText(message.getMessage()); return convertView; } class ViewHolder { protected View child; protected TextView msg; public ViewHolder(View child, TextView msg){ this.child = child; this.msg = msg; } } }
在这里我进行正式的解释...先说第二部分..第二部分是一些初始化的操作...也就是获取本地蓝牙,注册广播,设置监听的一些过程...因为实现通信,我们首先要判断我们的手机是否有蓝牙...获取到本机的蓝牙... 也就是这句话是实现通信的第一步...mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();我们才能够打开蓝牙...然后进行操作...详细解释在代码中...
//→_→ 第二部分... // 变量初始化 private void init() { // TODO Auto-generated method stub mContext = this; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//获取本地蓝牙...实现通信的第一步... list = new ArrayList<ChatMessage>();// 初始化list,由于我们最后会将数据以列表的形式进行显示,因此使用ListView... clientAdapter = new ClientAdapter(mContext, list); //适配器,用来限制如何显示ListView... mListView = (ListView) findViewById(R.id.list); mListView.setFastScrollEnabled(true); //使用快速滑动功能..目的是能够快速滑动到指定位置... mListView.setAdapter(clientAdapter); mListView.setOnItemClickListener(this); /* 下面是注册receiver监听,注册广播...说一下为什么要注册广播... * 因为蓝牙的通信,需要进行设备的搜索,搜索到设备后我们才能够实现连接..如果没有搜索,那还谈什么连接... * 因此我们需要搜索,搜索的过程中系统会自动发出三个广播...这三个广播为: * ACTION_DISCOVERY_START:开始搜索... * ACTION_DISCOVERY_FINISH:搜索结束... * ACTION_FOUND:正在搜索...一共三个过程...因为我们需要对这三个响应过程进行接收,然后实现一些功能,因此 * 我们需要对广播进行注册...知道广播的人应该都知道,想要对广播进行接收,必须进行注册,否则是接收不到的... * */ IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); // 定义一个集合,来保存已经配对过的设备... Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); if (pairedDevices.size() > 0) { //如果存在设备... for (BluetoothDevice device : pairedDevices) {//遍历... //将手机名字和物理地址放入到listview中... list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), true)); clientAdapter.notifyDataSetChanged(); //重新绘制ListView mListView.setSelection(list.size() - 1); //设置list保存的信息的位置...说白了该条信息始终在上一条信息的下方... } } else { list.add(new ChatMessage("没有已经配对过的设备", true)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } editText = (EditText) findViewById(R.id.edit); editText.setEnabled(false); editText.clearFocus(); //设置没有焦点..也就是无法输入任何文字... sendButton = (Button) findViewById(R.id.btn_send); sendButton.setEnabled(false); sendButton.setOnClickListener(new OnClickListener() {//设置监听,只有获取到焦点后才能进行此过程... @Override public void onClick(View arg0) { // TODO Auto-generated method stub String msg = editText.getText().toString(); if (msg.length() > 0) { //调用第五部分线程,通过线程发送我们输入的文本... sendMessageHandler(msg); //发送完清空... editText.setText(""); editText.clearFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);//定义一个输入法对象...通过Context.INPUT_METHOD_SERVICE获取实例... //当EditText没有焦点的时候,阻止输入法的弹出...其实就是在没点击EditText获取焦点的时候,没有输入法的显示... imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); } else { Toast.makeText(mContext, "发送内容不能为空", Toast.LENGTH_SHORT).show(); } } }); disconnect = (Button) findViewById(R.id.disconnect); disconnect.setEnabled(false); //为断开连接设置监听... disconnect.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub // 关闭相关服务,调用第六部分的函数... closeClient(); //表示蓝牙状态需要关闭... BluetoothMsg.isOpen = false; BluetoothMsg.serviceOrCilent = BluetoothMsg.ServerOrCilent.NONE; Toast.makeText(mContext, "连接已断开", Toast.LENGTH_SHORT).show(); } }); }
接着就是第三部分了...我们获取到了本地的蓝牙设备,我们就需要把蓝牙进行打开了..只有开启了蓝牙,才能够进行搜索,连接等操作..因此这一步是实现蓝牙通信的关键...
//→_→ 第三部分... @Override protected void onStart() { // TODO Auto-generated method stub super.onStart(); if (mBluetoothAdapter != null) { //本地蓝牙存在... if (!mBluetoothAdapter.isEnabled()) { //判断蓝牙是否被打开... // 发送打开蓝牙的意图,系统会弹出一个提示对话框,打开蓝牙是需要传递intent的... // intent这个重要的东西,大家应该都知道,它能够实现应用之间通信和交互.... Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); //打开本机的蓝牙功能...使用startActivityForResult()方法...这里我们开启的这个Activity是需要它返回执行结果给主Activity的... startActivityForResult(enableIntent, RESULT_FIRST_USER); Intent displayIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); // 设置蓝牙的可见性,最大值3600秒,默认120秒,0表示永远可见(作为客户端,可见性可以不设置,服务端必须要设置) displayIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0); //这里只需要开启另一个activity,让其一直显示蓝牙...没必要把信息返回..因此调用startActivity() startActivity(displayIntent); // 直接打开蓝牙 mBluetoothAdapter.enable();//这步才是真正打开蓝牙的部分.... } } } //这步就是当线程从Pause状态到Avtive状态要执行的过程... @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); // 扫描 scanDevice(); //调用第七部分的扫描过程... }
然后是第四部分....第四部分是对广播响应后的接收过程..这个过程也是重要的,因为我们在开启蓝牙后扫描的时候,如果有设备可以连接,我们得做一些操作让用户知道有设备可以连接,总不能有设备连接,我们什么也不告诉用户,这就不合理吧....因此我们在这一步是需要给用户反馈信息的...让用户知道下一步应该做什么...这一部分倒是很简单,没复杂的东西..
//→_→ 第四部分... private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); //获取当前正在执行的动作... if (BluetoothDevice.ACTION_FOUND.equals(action)) //正在搜索过程... { // 通过EXTRA_DEVICE附加域来得到一个BluetoothDevice设备 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 如果这个设备是不曾配对过的,添加到list列表 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), false)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) //搜索结束后的过程... { setProgressBarIndeterminateVisibility(false); //这步是如果设备过多是否显示滚动条... if (mListView.getCount() == 0) { list.add(new ChatMessage("没有发现蓝牙设备", false)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } } } };
第五部分就是线程部分了,所有的线程部分...涉及UI的更新,数据信息的显示,将数据进行发送,接收服务器返回的数据信息...这步是实现数据传递的关键...
//→_→ 第五部分... private Handler LinkDetectedHandler = new Handler() { @Override public void handleMessage(Message msg) { //这步多了一个判断..判断的是ListView保存的数据是我们要传输给服务端的数据,还是那些我们定义好的提示数据... if(msg.what==1) { list.add(new ChatMessage((String)msg.obj, true)); } else { list.add(new ChatMessage((String)msg.obj, false)); } clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } }; // Handler更新UI... // 当连接上服务器的时候才可以选择发送数据和断开连接,并且要对界面进行刷新操作... private Handler refreshUI = new Handler() { public void handleMessage(Message msg) { if (msg.what == 0) { disconnect.setEnabled(true); sendButton.setEnabled(true); editText.setEnabled(true); } } }; // 开启客户端连接服务端,一个新的线程... private class ClientThread extends Thread { @Override public void run() { // TODO Auto-generated method stub if (device != null) { try { /* 下面这步也是关键,我们如果想要连接服务器,我们需要调用方法createRfcommSocketToServiceRecord * 参数00001101-0000-1000-8000-00805F9B34FB表示的是默认的蓝牙串口...通过传递参数调用方法, * 会返回给我们一个套接字..这一步就是获取套接字,实现连接的过程... * * */ socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); // 连接 Message msg = new Message(); msg.obj = "请稍候,正在连接服务器: "+ BluetoothMsg.BlueToothAddress; msg.what = 0; LinkDetectedHandler.sendMessage(msg); //调用线程,显示msg信息... // 通过socket连接服务器,正式形成连接...这是一个阻塞过程,直到连接建立或者连接失效... socket.connect(); //如果实现了连接,那么服务端和客户端就共享一个RFFCOMM信道... Message msg2 = new Message(); msg2.obj = "已经连接上服务端!可以发送信息"; msg2.what = 0; LinkDetectedHandler.sendMessage(msg2); //调用线程,显示msg信息... // 如果连接成功了...这步就会执行...更新UI界面...否则走catch(IOException e) Message uiMessage = new Message(); uiMessage.what = 0; refreshUI.sendMessage(uiMessage); // 可以开启读数据线程 mReadThread = new ReadThread(); mReadThread.start(); } catch (IOException e) { // TODO Auto-generated catch block // socket.connect()连接失效 Message msg = new Message(); msg.obj = "连接服务端异常!断开连接重新试一试。"; msg.what = 0; LinkDetectedHandler.sendMessage(msg); } } } } // 通过socket获取InputStream流.. private class ReadThread extends Thread { @Override public void run() { // TODO Auto-generated method stub byte[] buffer = new byte[1024]; int bytes; InputStream is = null; try { is = socket.getInputStream(); //获取服务器发过来的所有字节... } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } while(true) { try {//读取过程,将数据信息保存在ListView中... if ((bytes = is.read(buffer)) > 0) { byte[] data = new byte[bytes]; for (int i = 0; i < data.length; i++) { data[i] = buffer[i]; } String s = new String(data); Message msg = new Message(); msg.obj = s; msg.what = 1; //这里的meg.what=1...表示的是服务器发送过来的数据信息.. LinkDetectedHandler.sendMessage(msg); } } catch (IOException e) { // TODO Auto-generated catch block try { is.close(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } break; } } } } //这一步表示的是发送数据的过程... private void sendMessageHandler(String msg) { if (socket == null) { Toast.makeText(mContext, "没有可用的连接", Toast.LENGTH_SHORT).show(); return; } //如果连接上了,那么获取输出流... try { OutputStream os = socket.getOutputStream(); os.write(msg.getBytes());//获取所有的自己然后往外发送... } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } list.add(new ChatMessage(msg, false)); //将数据存放到list中 clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); }
第六部分就是一个停止服务的一个过程,就不细说了,第七部分就是一个扫描的过程,扫描附近的设别...
// →_→ 第七部分... // 扫描设备 private void scanDevice() { // TODO Auto-generated method stub if (mBluetoothAdapter.isDiscovering()) { //如果正在处于扫描过程... mBluetoothAdapter.cancelDiscovery(); //取消扫描... } else { list.clear(); clientAdapter.notifyDataSetChanged(); // 每次扫描前都先判断一下是否存在已经配对过的设备 Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); if (pairedDevices.size() > 0) { //如果存在配对过的设备,那么就直接遍历集合,然后显示.. for (BluetoothDevice device : pairedDevices) { list.add(new ChatMessage(device.getName() + "\n" + device.getAddress(), true)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } } else { //如果没有就进行添加... list.add(new ChatMessage("No devices have been paired", true)); clientAdapter.notifyDataSetChanged(); mListView.setSelection(list.size() - 1); } /* 开始搜索 */ mBluetoothAdapter.startDiscovery(); } }
第八部分为连接过程做了一些初始化的工作,获取其他设备的物理地址,通过物理地址实现连接...至于第九部分就是销过程了,第一部分就是变量的定义过程...就没什么好说的了...
//→_→ 第八部分... @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { // TODO Auto-generated method stub ChatMessage item = list.get(arg2); //item保存着message和一个boolean数值... String info = item.getMessage(); //单纯获取message的信息... String address = info.substring(info.length() - 17);//获取MAC地址...其实就是硬件地址...因为连接设别必然从物理地址下手... BluetoothMsg.BlueToothAddress = address;//赋值 // 停止扫描 // BluetoothAdapter.startDiscovery()很耗资源,在尝试配对前必须中止它 mBluetoothAdapter.cancelDiscovery(); // 通过Mac地址去尝试连接一个设备, device = mBluetoothAdapter.getRemoteDevice(BluetoothMsg.BlueToothAddress); mClientThread = new ClientThread(); mClientThread.start(); //开启新的线程...开始连接...这里只是做了一些初始化的工作..连接过程还是ClientThread线程... BluetoothMsg.isOpen = true; }
xml文件我就不贴了,直接放一个源码吧...这个源码包含一个客户端,一个服务端...(注意,这个测试需要在两个真机上测试,模拟器是没有作用的...)
源码地址:http://files.cnblogs.com/files/RGogoing/Bluetooth.zip