当前位置: 移动技术网 > 移动技术>移动开发>Android > Android实现蓝牙客户端与服务器端通信示例

Android实现蓝牙客户端与服务器端通信示例

2019年07月24日  | 移动技术网移动技术  | 我要评论

一、首先说明:蓝牙通信必须用手机测试,因为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下载:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网