欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-02-17 15:38:40
一、首先说明:蓝牙通信必须用手机测试,因为avd里没有相关的硬件,会报错! 好了,看看最后的效果图:   二、概述: 1.判断是否支持b...

一、首先说明:蓝牙通信必须用手机测试,因为avd里没有相关的硬件,会报错!

好了,看看最后的效果图:

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

二、概述:

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

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