Android实现蓝牙客户端与服务器端通信示例
一、首先说明:蓝牙通信必须用手机测试,因为avd里没有相关的硬件,会报错!
好了,看看最后的效果图:
二、概述:
1.判断是否支持bluetooth
bluetoothadapter bluetoothadapter = bluetoothadapter.getdefaultadapter(); if(bluetoothadapter == null) { //the device doesn't support bluetooth } else { //the device support bluetooth }
2.如果支持,打开bluetooth
if(!bluetoothadapter.isenable()) { intent enableintent = new intent(bluetoothadapter.action_request_enable); startactivityforresult(enableintent,request_enable_bt); }
3.监视bluetooth打开状态
broadcastreceiver bluetoothstate = new broadcastreceiver() { public void onreceive(context context, intent intent) { string stateextra = bluetoothadapter.extra_state; int state = intent.getintextra(stateextra, -1); switch(state) { case bluetoothadapter.state_turning_on: break; case bluetoothadapter.state_on: break; case bluetoothadapter.state_turning_off: break; case bluetoothadapter.state_off: break; } } } registerreceiver(bluetoothstate,new intentfilter(bluetoothadapter.action_state_changed));
4.设置本地设备可以被其它设备搜索
intent discoveryintent = new intent(bluetoothadapter.action_request_discoverable); startactivityforresult(discoveryintent,request_discovery); broadcastreceiver discovery = new broadcastreceiver() { @override public void onrecevie(content context, intent intent) { string scanmode = bluetoothadapter.extra_scan_mode; string prescanmode = bluetoothadapter.extra_previous_scan_mode; int mode = intent.getintextra(scanmode); } } registerreceiver(discovery,new intentfilter(bluetoothadapter.action_scan_mode_changed);
5.搜索设备
开始搜索 bluetoothadapter.startdiscovery();
停止搜索 bluetoothadapter.canceldiscovery();
当发现一个设备时,系统会发出action_found广播消息,我们可以实现接收这个消息的broadcastreceiver
broadcastreceiver devicefound = new broadcastreceiver() { @override public void onreceiver(content content, intent intent) { string remotedevicename = intent.getstringextra(bluetoothadapter.extra_name); bluetoothdevice remotedevice = intent.getparcelableextra(bluetoothadapter.extra_device); } } registerreceiver(devicefound, new intentfilter(bluetoothadapter.action_found);
6.连接设备
连接两个蓝牙设备要分别实现服务器端(bluetoothserversocket)和客户端(bluetoothsocket),这点与j2se中的
serversocket和socket很类似。
bluetoothserversocket在服务器端调用方法accept()监听,当有客户端请求到来时,accept()方法返回bluetoothsocket,客户端得到后,两端便可以通信。通过inputstream和outputstream来实现数据的传输。
accept方法是阻塞的,所以不能放在ui线程中,当用到bluetoothserversocket和bluetoothsocket时,通常把它们放在各自的新线程中。
三、如何实现
以下是开发中的几个关键步骤:
1)首先开启蓝牙
2)搜索可用设备
3)创建蓝牙socket,获取输入输出流
4)读取和写入数据
5)断开连接关闭蓝牙
1、因为有页面切换,这里我使用了tabhost,但原来的效果不好,没有动画,那只好自己复写了
/** * 带有动画效果的tabhost * * @project app_bluetooth * @package com.android.bluetooth * @author chenlin * @version 1.0 * @date 2013年6月2日 * @note todo */ public class animationtabhost extends tabhost { private int mcurrenttabid = 0;//当前的tabid private final long mduration = 400;//动画时间 public animationtabhost(context context) { this(context, null); } public animationtabhost(context context, attributeset attrs) { super(context, attrs); } /** * 切换动画 */ @override public void setcurrenttab(int index) { //向右平移 if (index > mcurrenttabid) { translateanimation translateanimation = new translateanimation(animation.relative_to_self, 0f, animation.relative_to_self, -1.0f, animation.relative_to_self, 0f, animation.relative_to_self, 0f); translateanimation.setduration(mduration); getcurrentview().startanimation(translateanimation); //向左平移 } else if (index < mcurrenttabid) { translateanimation translateanimation = new translateanimation( animation.relative_to_self, 0f, animation.relative_to_self, 1.0f, animation.relative_to_self, 0f, animation.relative_to_self, 0f); translateanimation.setduration(mduration); getcurrentview().startanimation(translateanimation); } super.setcurrenttab(index); //-----方向平移------------------------------ if (index > mcurrenttabid) { translateanimation translateanimation = new translateanimation( // animation.relative_to_parent, 1.0f,// relative_to_self animation.relative_to_parent, 0f, animation.relative_to_parent, 0f, animation.relative_to_parent, 0f); translateanimation.setduration(mduration); getcurrentview().startanimation(translateanimation); } else if (index < mcurrenttabid) { translateanimation translateanimation = new translateanimation( animation.relative_to_parent, -1.0f, animation.relative_to_parent, 0f, animation.relative_to_parent, 0f, animation.relative_to_parent, 0f); translateanimation.setduration(mduration); getcurrentview().startanimation(translateanimation); } mcurrenttabid = index; } }
2、先搭建好主页,使用复写的tabhost滑动,如何滑动,根据状态,有三种状态
/** * 主页 * * @project app_bluetooth * @package com.android.bluetooth * @author chenlin * @version 1.0 * @date 2013年6月2日 */ @suppresswarnings("deprecation") public class bluetoothactivity extends tabactivity { static animationtabhost mtabhost;//动画tabhost static string bluetoothaddress;//蓝牙地址 static type mtype = type.none;//类型 static boolean isopen = false; //类型: enum type { none, service, cilent }; @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); inittab(); } private void inittab() { //初始化 mtabhost = (animationtabhost) gettabhost(); //添加tab mtabhost.addtab(mtabhost.newtabspec("tab1").setindicator("设备列表", getresources().getdrawable(android.r.drawable.ic_menu_add)) .setcontent(new intent(this, deviceactivity.class))); mtabhost.addtab(mtabhost.newtabspec("tab2").setindicator("会话列表", getresources().getdrawable(android.r.drawable.ic_menu_add)) .setcontent(new intent(this, chatactivity.class))); //添加监听 mtabhost.setontabchangedlistener(new ontabchangelistener() { public void ontabchanged(string tabid) { if (tabid.equals("tab1")) { //todo } } }); //默认在第一个tabhost上面 mtabhost.setcurrenttab(0); } public void onactivityresult(int requestcode, int resultcode, intent data) { toast.maketext(this, "address:", toast.length_short).show(); } }
3、有了主页,就开始分别实现两个列表页面,一个是寻找设备页面deviceactivity.java,另一个是会话页面chatactivity.java
1)设备页面deviceactivity.java
/** * 发现的设备列表 * @project app_bluetooth * @package com.android.bluetooth * @author chenlin * @version 1.0 * @date 2013年6月2日 * @note todo */ public class deviceactivity extends activity { private listview mlistview; //数据 private arraylist<devicebean> mdatas; private button mbtnsearch, mbtnservice; private chatlistadapter madapter; //蓝牙适配器 private bluetoothadapter mbtadapter; @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.devices); initdatas(); initviews(); registerbroadcast(); init(); } private void initdatas() { mdatas = new arraylist<devicebean>(); madapter = new chatlistadapter(this, mdatas); mbtadapter = bluetoothadapter.getdefaultadapter(); } /** * 列出所有的蓝牙设备 */ private void init() { log.i("tag", "mbtadapter=="+ mbtadapter); //根据适配器得到所有的设备信息 set<bluetoothdevice> deviceset = mbtadapter.getbondeddevices(); if (deviceset.size() > 0) { for (bluetoothdevice device : deviceset) { mdatas.add(new devicebean(device.getname() + "\n" + device.getaddress(), true)); madapter.notifydatasetchanged(); mlistview.setselection(mdatas.size() - 1); } } else { mdatas.add(new devicebean("没有配对的设备", true)); madapter.notifydatasetchanged(); mlistview.setselection(mdatas.size() - 1); } } /** * 注册广播 */ private void registerbroadcast() { //设备被发现广播 intentfilter discoveryfilter = new intentfilter(bluetoothdevice.action_found); this.registerreceiver(mreceiver, discoveryfilter); // 设备发现完成 intentfilter foundfilter = new intentfilter(bluetoothadapter.action_discovery_finished); this.registerreceiver(mreceiver, foundfilter); } /** * 初始化视图 */ private void initviews() { mlistview = (listview) findviewbyid(r.id.list); mlistview.setadapter(madapter); mlistview.setfastscrollenabled(true); mlistview.setonitemclicklistener(mdeviceclicklistener); mbtnsearch = (button) findviewbyid(r.id.start_seach); mbtnsearch.setonclicklistener(msearchlistener); mbtnservice = (button) findviewbyid(r.id.start_service); mbtnservice.setonclicklistener(new onclicklistener() { @override public void onclick(view arg0) { bluetoothactivity.mtype = type.service; bluetoothactivity.mtabhost.setcurrenttab(1); } }); } /** * 搜索监听 */ private onclicklistener msearchlistener = new onclicklistener() { @override public void onclick(view arg0) { if (mbtadapter.isdiscovering()) { mbtadapter.canceldiscovery(); mbtnsearch.settext("重新搜索"); } else { mdatas.clear(); madapter.notifydatasetchanged(); init(); /* 开始搜索 */ mbtadapter.startdiscovery(); mbtnsearch.settext("ֹͣ停止搜索"); } } }; /** * 点击设备监听 */ private onitemclicklistener mdeviceclicklistener = new onitemclicklistener() { public void onitemclick(adapterview<?> parent, view view, int position, long id) { devicebean bean = mdatas.get(position); string info = bean.message; string address = info.substring(info.length() - 17); bluetoothactivity.bluetoothaddress = address; alertdialog.builder stopdialog = new alertdialog.builder(deviceactivity.this); stopdialog.settitle("连接");//标题 stopdialog.setmessage(bean.message); stopdialog.setpositivebutton("连接", new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int which) { mbtadapter.canceldiscovery(); mbtnsearch.settext("重新搜索"); bluetoothactivity.mtype = type.cilent; bluetoothactivity.mtabhost.setcurrenttab(1); dialog.cancel(); } }); stopdialog.setnegativebutton("取消", new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int which) { bluetoothactivity.bluetoothaddress = null; dialog.cancel(); } }); stopdialog.show(); } }; /** * 发现设备广播 */ 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) { mdatas.add(new devicebean(device.getname() + "\n" + device.getaddress(), false)); madapter.notifydatasetchanged(); mlistview.setselection(mdatas.size() - 1); } // 如果搜索完成了 } else if (bluetoothadapter.action_discovery_finished.equals(action)) { setprogressbarindeterminatevisibility(false); if (mlistview.getcount() == 0) { mdatas.add(new devicebean("û没有发现蓝牙设备", false)); madapter.notifydatasetchanged(); mlistview.setselection(mdatas.size() - 1); } mbtnsearch.settext("重新搜索"); } } }; @override public void onstart() { super.onstart(); if (!mbtadapter.isenabled()) { intent enableintent = new intent(bluetoothadapter.action_request_enable); startactivityforresult(enableintent, 3); } } @override protected void ondestroy() { super.ondestroy(); if (mbtadapter != null) { mbtadapter.canceldiscovery(); } this.unregisterreceiver(mreceiver); } }
2)会话页面chatactivity.java
/** * 会话界面 * * @project app_bluetooth * @package com.android.bluetooth * @author chenlin * @version 1.0 * @date 2013年3月2日 * @note todo */ public class chatactivity extends activity implements onitemclicklistener, onclicklistener { private static final int status_connect = 0x11; private listview mlistview; private arraylist<devicebean> mdatas; private button mbtnsend;// 发送按钮 private button mbtndisconn;// 断开连接 private edittext metmsg; private devicelistadapter madapter; /* 一些常量,代表服务器的名称 */ public static final string protocol_scheme_l2cap = "btl2cap"; public static final string protocol_scheme_rfcomm = "btspp"; public static final string protocol_scheme_bt_obex = "btgoep"; public static final string protocol_scheme_tcp_obex = "tcpobex"; // 蓝牙服务端socket private bluetoothserversocket mserversocket; // 蓝牙客户端socket private bluetoothsocket msocket; // 设备 private bluetoothdevice mdevice; private bluetoothadapter mbluetoothadapter; // --线程类----------------- private serverthread mserverthread; private clientthread mclientthread; private readthread mreadthread; @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.chat); initdatas(); initviews(); initevents(); } private void initevents() { mlistview.setonitemclicklistener(this); // 发送信息 mbtnsend.setonclicklistener(new onclicklistener() { @override public void onclick(view arg0) { string text = metmsg.gettext().tostring(); if (!textutils.isempty(text)) { // 发送信息 sendmessagehandle(text); metmsg.settext(""); metmsg.clearfocus(); // 隐藏软键盘 inputmethodmanager manager = (inputmethodmanager) getsystemservice(context.input_method_service); manager.hidesoftinputfromwindow(metmsg.getwindowtoken(), 0); } else toast.maketext(chatactivity.this, "发送内容不能为空!", toast.length_short).show(); } }); // 关闭会话 mbtndisconn.setonclicklistener(new onclicklistener() { @override public void onclick(view view) { if (bluetoothactivity.mtype == type.cilent) { shutdownclient(); } else if (bluetoothactivity.mtype == type.service) { shutdownserver(); } bluetoothactivity.isopen = false; bluetoothactivity.mtype = type.none; toast.maketext(chatactivity.this, "已断开连接!", toast.length_short).show(); } }); } private void initviews() { mlistview = (listview) findviewbyid(r.id.list); mlistview.setadapter(madapter); mlistview.setfastscrollenabled(true); metmsg = (edittext) findviewbyid(r.id.messagetext); metmsg.clearfocus(); mbtnsend = (button) findviewbyid(r.id.btn_msg_send); mbtndisconn = (button) findviewbyid(r.id.btn_disconnect); } private void initdatas() { mdatas = new arraylist<devicebean>(); madapter = new devicelistadapter(this, mdatas); mbluetoothadapter = bluetoothadapter.getdefaultadapter(); } /** * 信息处理 */ private handler mhandler = new handler() { @override public void handlemessage(message msg) { string info = (string) msg.obj; switch (msg.what) { case status_connect: toast.maketext(chatactivity.this, info, 0).show(); break; } if (msg.what == 1) { mdatas.add(new devicebean(info, true)); madapter.notifydatasetchanged(); mlistview.setselection(mdatas.size() - 1); }else { mdatas.add(new devicebean(info, false)); madapter.notifydatasetchanged(); mlistview.setselection(mdatas.size() - 1); } } }; @override public void onresume() { super.onresume(); if (bluetoothactivity.isopen) { toast.maketext(this, "连接已经打开,可以通信。如果要再建立连接,请先断开", toast.length_short).show(); return; } if (bluetoothactivity.mtype == type.cilent) { string address = bluetoothactivity.bluetoothaddress; if (!"".equals(address)) { mdevice = mbluetoothadapter.getremotedevice(address); mclientthread = new clientthread(); mclientthread.start(); bluetoothactivity.isopen = true; } else { toast.maketext(this, "address is null !", toast.length_short).show(); } } else if (bluetoothactivity.mtype == type.service) { mserverthread = new serverthread(); mserverthread.start(); bluetoothactivity.isopen = true; } } // 客户端线程 private class clientthread extends thread { public void run() { try { msocket = mdevice.createrfcommsockettoservicerecord(uuid.fromstring("00001101-0000-1000-8000-00805f9b34fb")); message msg = new message(); msg.obj = "请稍候,正在连接服务器:" + bluetoothactivity.bluetoothaddress; msg.what = status_connect; mhandler.sendmessage(msg); msocket.connect(); msg = new message(); msg.obj = "已经连接上服务端!可以发送信息。"; msg.what = status_connect; mhandler.sendmessage(msg); // 启动接受数据 mreadthread = new readthread(); mreadthread.start(); } catch (ioexception e) { message msg = new message(); msg.obj = "连接服务端异常!断开连接重新试一试。"; msg.what = status_connect; mhandler.sendmessage(msg); } } }; // 开启服务器 private class serverthread extends thread { public void run() { try { // 创建一个蓝牙服务器 参数分别:服务器名称、uuid mserversocket = mbluetoothadapter.listenusingrfcommwithservicerecord(protocol_scheme_rfcomm, uuid.fromstring("00001101-0000-1000-8000-00805f9b34fb")); message msg = new message(); msg.obj = "请稍候,正在等待客户端的连接..."; msg.what = status_connect; mhandler.sendmessage(msg); /* 接受客户端的连接请求 */ msocket = mserversocket.accept(); msg = new message(); msg.obj = "客户端已经连接上!可以发送信息。"; msg.what = status_connect; mhandler.sendmessage(msg); // 启动接受数据 mreadthread = new readthread(); mreadthread.start(); } catch (ioexception e) { e.printstacktrace(); } } }; /* 停止服务器 */ private void shutdownserver() { new thread() { public void run() { if (mserverthread != null) { mserverthread.interrupt(); mserverthread = null; } if (mreadthread != null) { mreadthread.interrupt(); mreadthread = null; } try { if (msocket != null) { msocket.close(); msocket = null; } if (mserversocket != null) { mserversocket.close(); mserversocket = null; } } catch (ioexception e) { log.e("server", "mserversocket.close()", e); } }; }.start(); } /* ͣ停止客户端连接 */ private void shutdownclient() { new thread() { public void run() { if (mclientthread != null) { mclientthread.interrupt(); mclientthread = null; } if (mreadthread != null) { mreadthread.interrupt(); mreadthread = null; } if (msocket != null) { try { msocket.close(); } catch (ioexception e) { e.printstacktrace(); } msocket = null; } }; }.start(); } // 发送数据 private void sendmessagehandle(string msg) { if (msocket == null) { toast.maketext(this, "没有连接", toast.length_short).show(); return; } try { outputstream os = msocket.getoutputstream(); os.write(msg.getbytes()); mdatas.add(new devicebean(msg, false)); madapter.notifydatasetchanged(); mlistview.setselection(mdatas.size() - 1); } catch (ioexception e) { e.printstacktrace(); } } // 读取数据 private class readthread extends thread { public void run() { byte[] buffer = new byte[1024]; int bytes; inputstream is = null; try { is = msocket.getinputstream(); while (true) { if ((bytes = is.read(buffer)) > 0) { byte[] buf_data = new byte[bytes]; for (int i = 0; i < bytes; i++) { buf_data[i] = buffer[i]; } string s = new string(buf_data); message msg = new message(); msg.obj = s; msg.what = 1; mhandler.sendmessage(msg); } } } catch (ioexception e1) { e1.printstacktrace(); } finally { try { is.close(); } catch (ioexception e1) { e1.printstacktrace(); } } } } @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { } @override public void onclick(view view) { } @override protected void ondestroy() { super.ondestroy(); if (bluetoothactivity.mtype == type.cilent) { shutdownclient(); } else if (bluetoothactivity.mtype == type.service) { shutdownserver(); } bluetoothactivity.isopen = false; bluetoothactivity.mtype = type.none; } }
三、相关代码下载
demo下载:http://xiazai.jb51.net/201701/yuanma/app_bluetooth_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。