Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信


前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主从关系的客户端和服务端了,本文相关链接需要去google的API上查看,需要FQ的

Bluetooth Low Energy:http://developer.android.com/guide/topics/connectivity/bluetooth-le.html

但是我们依然没有讲到BLE(低功耗蓝牙),放心,下一篇就回讲到,跟前面的基本上很大的不同,我们今天来看下客户端和服务端的实现

我们以上篇为栗子:

Android BLE与终端通信(二)——Android Bluetooth基础搜索蓝牙设备显示列表

一.蓝牙数据传输

蓝牙数据传输其实跟我们的 Socket(套接字)有点类似,如果有不懂的,可以百度一下概念,我们只要知道是这么回事就可以了,在网络中使用Socket和ServerSocket控制客户端和服务端来数据读写。而蓝牙通讯也是由客户端和服务端来完成的,蓝牙客户端Socket是BluetoothSocket,蓝牙服务端Socket是BluetoothServerSocket,这两个类都在android.bluetooth包下,而且无论是BluetoothSocket还是BluetoothServerSocket,我们都需要一个UUID(标识符),这个UUID在上篇也是有提到,而且他的格式也是固定的:

UUID:XXXXXXXX(8)-XXXX(4)-XXXX(4)-XXXX(4)-XXXXXXXXXXXX(12)

第一段是8位,中间三段式4位,最后一段是12位,UUID相当于Socket的端口,而蓝牙地址则相当于Socket的IP

1.activity_main.xml

<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" >

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="btnSearch"
        android:text="搜索蓝牙设备" />

    <ListView
        android:id="@+id/lvDevices"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

2.实现步骤

1.声明

我们需要的东西
  // 本地蓝牙适配器
    private BluetoothAdapter mBluetoothAdapter;
    // 列表
    private ListView lvDevices;
    // 存储搜索到的蓝牙
    private List<String> bluetoothDevices = new ArrayList<String>();
    // listview的adapter
    private ArrayAdapter<String> arrayAdapter;
    // UUID.randomUUID()随机获取UUID
    private final UUID MY_UUID = UUID
     .fromString("db764ac8-4b08-7f25-aafe-59d03c27bae3");
    // 连接对象的名称
    private final String NAME = "LGL";
    // 这里本身即是服务端也是客户端,需要如下类
    private BluetoothSocket clientSocket;
    private BluetoothDevice device;
    // 输出流_客户端需要往服务端输出
    private OutputStream os;

2.初始化

// 获取本地蓝牙适配器
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // 判断手机是否支持蓝牙
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();
            finish();
        }

        // 判断是否打开蓝牙
        if (!mBluetoothAdapter.isEnabled()) {
            // 弹出对话框提示用户是后打开
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, 1);
            // 不做提示,强行打开
            // mBluetoothAdapter.enable();
        }
        // 初始化listview
        lvDevices = (ListView) findViewById(R.id.lvDevices);
        lvDevices.setOnItemClickListener(this);

        // 获取已经配对的设备
        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter
                .getBondedDevices();

        // 判断是否有配对过的设备
        if (pairedDevices.size() > 0) {
            for (BluetoothDevice device : pairedDevices) {
                // 遍历到列表中
                bluetoothDevices.add(device.getName() + ":"
                        + device.getAddress() + "\n");
            }
        }

        // adapter
        arrayAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, android.R.id.text1,
                bluetoothDevices);
        lvDevices.setAdapter(arrayAdapter);

        /**
         * 异步搜索蓝牙设备——广播接收
         */
        // 找到设备的广播
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        // 注册广播
        registerReceiver(receiver, filter);
        // 搜索完成的广播
        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        // 注册广播
        registerReceiver(receiver, filter);
    }

3.点击搜索

public void btnSearch(View v) {
        // 设置进度条
        setProgressBarIndeterminateVisibility(true);
        setTitle("正在搜索...");
        // 判断是否在搜索,如果在搜索,就取消搜索
        if (mBluetoothAdapter.isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
        }
        // 开始搜索
        mBluetoothAdapter.startDiscovery();
    }

4.搜索设备

private final BroadcastReceiver receiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            // 收到的广播类型
            String action = intent.getAction();
            // 发现设备的广播
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // 从intent中获取设备
                BluetoothDevice device = intent
                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // 判断是否配对过
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    // 添加到列表
                    bluetoothDevices.add(device.getName() + ":"
                            + device.getAddress() + "\n");
                    arrayAdapter.notifyDataSetChanged();

                }
                // 搜索完成
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
                    .equals(action)) {
                // 关闭进度条
                setProgressBarIndeterminateVisibility(true);
                setTitle("搜索完成!");
            }
        }
    };

5.客户端实现已经发送数据流


    // 客户端
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        // 先获得蓝牙的地址和设备名
        String s = arrayAdapter.getItem(position);
        // 单独解析地址
        String address = s.substring(s.indexOf(":") + 1).trim();

        // 主动连接蓝牙
        try {
            // 判断是否在搜索,如果在搜索,就取消搜索
            if (mBluetoothAdapter.isDiscovering()) {
                mBluetoothAdapter.cancelDiscovery();
            }
            try {
                // 判断是否可以获得
                if (device == null) {
                    // 获得远程设备
                    device = mBluetoothAdapter.getRemoteDevice(address);
                }
                // 开始连接
                if (clientSocket == null) {
                    clientSocket = device
                            .createRfcommSocketToServiceRecord(MY_UUID);
                    // 连接
                    clientSocket.connect();
                    // 获得输出流
                    os = clientSocket.getOutputStream();

                }
            } catch (Exception e) {
                // TODO: handle exception
            }
            // 如果成功获得输出流
            if (os != null) {
                os.write("Hello Bluetooth!".getBytes("utf-8"));
            }

        } catch (Exception e) {
            // TODO: handle exception
        }
    }

6.Handler服务

// 服务端,需要监听客户端的线程类
    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            Toast.makeText(MainActivity.this, String.valueOf(msg.obj),
                    Toast.LENGTH_SHORT).show();
            super.handleMessage(msg);
        }
    };

7.服务端读取数据流

// 线程服务类
    private class AcceptThread extends Thread {
        private BluetoothServerSocket serverSocket;
        private BluetoothSocket socket;
        // 输入 输出流
        private OutputStream os;
        private InputStream is;

        public AcceptThread() {
            try {
                serverSocket = mBluetoothAdapter
                        .listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            // 截获客户端的蓝牙消息
            try {
                socket = serverSocket.accept(); // 如果阻塞了,就会一直停留在这里
                is = socket.getInputStream();
                os = socket.getOutputStream();
                // 不断接收请求,如果客户端没有发送的话还是会阻塞
                while (true) {
                    // 每次只发送128个字节
                    byte[] buffer = new byte[128];
                    // 读取
                    int count = is.read();
                    // 如果读取到了,我们就发送刚才的那个Toast
                    Message msg = new Message();
                    msg.obj = new String(buffer, 0, count, "utf-8");
                    handler.sendMessage(msg);
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    }

8.开启服务

首先要声明
        //启动服务
        ac = new AcceptThread();
        ac.start();

MainActivity完整代码

package com.lgl.bluetoothget;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnItemClickListener {

    // 本地蓝牙适配器
    private BluetoothAdapter mBluetoothAdapter;
    // 列表
    private ListView lvDevices;
    // 存储搜索到的蓝牙
    private List<String> bluetoothDevices = new ArrayList<String>();
    // listview的adapter
    private ArrayAdapter<String> arrayAdapter;
    // UUID.randomUUID()随机获取UUID
    private final UUID MY_UUID = UUID
     .fromString("db764ac8-4b08-7f25-aafe-59d03c27bae3");
    // 连接对象的名称
    private final String NAME = "LGL";

    // 这里本身即是服务端也是客户端,需要如下类
    private BluetoothSocket clientSocket;
    private BluetoothDevice device;
    // 输出流_客户端需要往服务端输出
    private OutputStream os;
    //线程类的实例
    private AcceptThread ac;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView() {

        // 获取本地蓝牙适配器
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // 判断手机是否支持蓝牙
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();
            finish();
        }

        // 判断是否打开蓝牙
        if (!mBluetoothAdapter.isEnabled()) {
            // 弹出对话框提示用户是后打开
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, 1);
            // 不做提示,强行打开
            // mBluetoothAdapter.enable();
        }
        // 初始化listview
        lvDevices = (ListView) findViewById(R.id.lvDevices);
        lvDevices.setOnItemClickListener(this);

        // 获取已经配对的设备
        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter
                .getBondedDevices();

        // 判断是否有配对过的设备
        if (pairedDevices.size() > 0) {
            for (BluetoothDevice device : pairedDevices) {
                // 遍历到列表中
                bluetoothDevices.add(device.getName() + ":"
                        + device.getAddress() + "\n");
            }
        }

        // adapter
        arrayAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, android.R.id.text1,
                bluetoothDevices);
        lvDevices.setAdapter(arrayAdapter);

        //启动服务
        ac = new AcceptThread();
        ac.start();

        /**
         * 异步搜索蓝牙设备——广播接收
         */
        // 找到设备的广播
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        // 注册广播
        registerReceiver(receiver, filter);
        // 搜索完成的广播
        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        // 注册广播
        registerReceiver(receiver, filter);
    }

    public void btnSearch(View v) {
        // 设置进度条
        setProgressBarIndeterminateVisibility(true);
        setTitle("正在搜索...");
        // 判断是否在搜索,如果在搜索,就取消搜索
        if (mBluetoothAdapter.isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
        }
        // 开始搜索
        mBluetoothAdapter.startDiscovery();
    }

    // 广播接收器
    private final BroadcastReceiver receiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            // 收到的广播类型
            String action = intent.getAction();
            // 发现设备的广播
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // 从intent中获取设备
                BluetoothDevice device = intent
                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // 判断是否配对过
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    // 添加到列表
                    bluetoothDevices.add(device.getName() + ":"
                            + device.getAddress() + "\n");
                    arrayAdapter.notifyDataSetChanged();

                }
                // 搜索完成
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
                    .equals(action)) {
                // 关闭进度条
                setProgressBarIndeterminateVisibility(true);
                setTitle("搜索完成!");
            }
        }
    };

    // 客户端
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        // 先获得蓝牙的地址和设备名
        String s = arrayAdapter.getItem(position);
        // 单独解析地址
        String address = s.substring(s.indexOf(":") + 1).trim();

        // 主动连接蓝牙
        try {
            // 判断是否在搜索,如果在搜索,就取消搜索
            if (mBluetoothAdapter.isDiscovering()) {
                mBluetoothAdapter.cancelDiscovery();
            }
            try {
                // 判断是否可以获得
                if (device == null) {
                    // 获得远程设备
                    device = mBluetoothAdapter.getRemoteDevice(address);
                }
                // 开始连接
                if (clientSocket == null) {
                    clientSocket = device
                            .createRfcommSocketToServiceRecord(MY_UUID);
                    // 连接
                    clientSocket.connect();
                    // 获得输出流
                    os = clientSocket.getOutputStream();

                }
            } catch (Exception e) {
                // TODO: handle exception
            }
            // 如果成功获得输出流
            if (os != null) {
                os.write("Hello Bluetooth!".getBytes("utf-8"));
            }

        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    // 服务端,需要监听客户端的线程类
    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            Toast.makeText(MainActivity.this, String.valueOf(msg.obj),
                    Toast.LENGTH_SHORT).show();
            super.handleMessage(msg);
        }
    };

    // 线程服务类
    private class AcceptThread extends Thread {
        private BluetoothServerSocket serverSocket;
        private BluetoothSocket socket;
        // 输入 输出流
        private OutputStream os;
        private InputStream is;

        public AcceptThread() {
            try {
                serverSocket = mBluetoothAdapter
                        .listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            // 截获客户端的蓝牙消息
            try {
                socket = serverSocket.accept(); // 如果阻塞了,就会一直停留在这里
                is = socket.getInputStream();
                os = socket.getOutputStream();
                // 不断接收请求,如果客户端没有发送的话还是会阻塞
                while (true) {
                    // 每次只发送128个字节
                    byte[] buffer = new byte[128];
                    // 读取
                    int count = is.read();
                    // 如果读取到了,我们就发送刚才的那个Toast
                    Message msg = new Message();
                    msg.obj = new String(buffer, 0, count, "utf-8");
                    handler.sendMessage(msg);
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    }
}

Google的API上其实已经说的很详细了的,这里我再提供一份PDF学习文档,可以更加直观的了解

PDF文档下载地址:http://download.csdn.net/detail/qq_26787115/9416162

Demo下载地址:http://download.csdn.net/detail/qq_26787115/9416158

Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信的更多相关文章

  1. Netty入门之客户端与服务端通信(二)

    Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ...

  2. 基于开源SuperSocket实现客户端和服务端通信项目实战

    一.课程介绍 本期带给大家分享的是基于SuperSocket的项目实战,阿笨在实际工作中遇到的真实业务场景,请跟随阿笨的视角去如何实现打通B/S与C/S网络通讯,如果您对本期的<基于开源Supe ...

  3. netty-3.客户端与服务端通信

    (原) 第三篇,客户端与服务端通信 以下例子逻辑: 如果客户端连上服务端,服务端控制台就显示,XXX个客户端地址连接上线. 第一个客户端连接成功后,客户端控制台不显示信息,再有其它客户端再连接上线,则 ...

  4. Python进阶----SOCKET套接字基础, 客户端与服务端通信, 执行远端命令.

    Python进阶----SOCKET套接字基础, 客户端与服务端通信, 执行远端命令. 一丶socket套接字 什么是socket套接字: ​ ​  ​ 专业理解: socket是应用层与TCP/IP ...

  5. Netty入门——客户端与服务端通信

    Netty简介Netty是一个基于JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞.基于事件驱动.高性能.高可靠性和高可定制性.换句话说,Netty是一个NIO框架,使用它可以简单快速 ...

  6. java Socket通信,客户端与服务端相互发消息

    1.通信过程 网络分为应用层,http.ssh.telnet就是属于这一类,建立在传输层的基础上.其实就是定义了各自的编码解码格式,分层如下: 2.Socket连接 上述通信都要先在传输层有建立连接的 ...

  7. 实验09——java基于TCP实现客户端与服务端通信

    TCP通信         需要先创建连接 - 并且在创建连接的过程中 需要经过三次握手        底层通过 流 发送数据 数据没有大小限制        可靠的传输机制 - 丢包重发 包的顺序的 ...

  8. Python socket编程客户端与服务端通信

    [本文出自天外归云的博客园] 目标:实现客户端与服务端的socket通信,消息传输. 客户端 客户端代码: from socket import socket,AF_INET,SOCK_STREAM ...

  9. python实现两台不同主机之间进行通信(客户端和服务端)——Socket

    大家好,我是辰哥~ 今天教大家通过Python进行Socket网络编程 (做一个聊天程序) 可以实现在不同的主机(电脑)之间进行通话. 具体效果如何,接着往下看 可以看到客户端(上方)向服务器端(下方 ...

随机推荐

  1. 生活沉思录 via 哲理小故事

    本文转载:http://www.cnblogs.com/willick/p/3174803.html 1.小托蒂的悲剧 意大利小男孩托蒂,有一只十分奇怪的眼睛,因为从生理上看,这是一只完全正常的眼睛, ...

  2. 03_dbcp数据源依赖jar包,DBCP中API介绍,不同过dbcp方式使用dbcp数据库连接池,通过配置文件使用dbcp数据库连接池

     DBCP数据源 使用DBCP数据源,需要导入两个jar包 Commons-dbcp.jar:连接池的实现 Common-pool.jar:连接池实现的依赖库. 导入mysql的jar包. DBC ...

  3. achartengine之折线图

    问题在文章的最后,大致说来就是折线图,如果点的个数大于3个的时候,不是所有的点都显示对应的值的,这是为什么呢,本来以为是小问题,但两天了还没找到原因) 将前两天的折线图代码做了小量修改,形成一个类似于 ...

  4. Java的访问权限详解(3+1)public private protected default

    Java使用三个关键字在类的内部设定访问权限:public.private.protected.这些访问指定词(access specifier)决定了紧跟其后被定义的成员(方法或属性)可以被谁使用. ...

  5. Java在Linux下 不能处理图形的解决办法 Can't connect to X11 window server

    java在图形处理时调用了本地的图形处理库.在利用Java作图形处理(比如:图片缩放,图片签名,生成报表)时,如果运行在windows上不会出问题.如果将程序移植到Linux/Unix上的时候有可能出 ...

  6. android自定义xmls文件属性

    在使用到自定义View的xml布局文件中需要加入xmlns:前缀=http://schemas.android.com/apk/res/你的自定义View所在的包路径. 下面是一个简单的例子: 结构图 ...

  7. 最简单的基于FFmpeg的解码器-纯净版(不包含libavformat)

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  8. 【Unity Shaders】Transparency —— 使用渲染队列进行深度排序

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  9. 【一天一道LeetCode】#107. Binary Tree Level Order Traversal II

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 来源: htt ...

  10. Windows Server2012R2 添加Microsoft .NET Framework 3.5 功能失败的解决方法

    最近部署了一台Windows Server2012R2的虚机,在安装sharepoint foundation 2013之前安装必备的组件的时候安装到一半报错了没有安装成功,后来发现原来是系统的.NE ...