android蓝牙通讯开发(详细)「建议收藏」

android蓝牙通讯开发(详细)「建议收藏」新建一个工程之后,我们可以先看到界面左边的项目栏,我们可以看到,除了app目录以外,大多数的文件和目录都是自动生成的,我们也不需要对他们进行修改,而app目录之下的文件才是我们工作的重点。下面,我先对

大家好,又见面了,我是你们的朋友全栈君。

新建一个工程之后,我们可以先看到界面左边的项目栏,我们可以看到,除了app目录以外,大多数的文件和目录都是自动生成的,我们也不需要对他们进行修改,而app目录之下的文件才是我们工作的重点。下面,我先对app目录下的内容进行一些讲解。

1.AndroidManifest.xml

这是整个项目的配置文件,我们在程序中定义的四大组件都需要在这里注册,另外,也可以在这里给应用程序添加权限声明。

2.java

这个是放置我们所有java代码的地方。

3.res

这个项目中所使用到的所有图片、布局、字符串资源都要存放在这个项目。其中,drawable文件夹和mipmap文件夹都是用来存放图片资源的。layout文件夹是用来存放布局资源的,values文件夹是存放字符串等资源的。

现在,我开始介绍一下关于android 蓝牙的通信的知识。

首先,我们要在一个页面中打开和关闭蓝牙。我们可以点击layout下的布局文件,先在其中添加两个按钮。在布局文件中,我们可以使用两种方法来调出两个按钮。第一种就是直接在design界面,点击Palette,找到Button,点击然后拖动到旁边的界面上。第二种就是在Text界面直接打入代码,例如:

1 <Button
android:id="@+id/bt" 2 android:layout_width="match_parent" 3 android:layout_height="wrap_content" 4 tools:ignore="MissingConstraints" />

其中,layout_width和layout_height是必须要有的属性,设置为match_parent表示充满父控件空间,wrap_content表示根据控件自身大小显示。id则是这个控件的名称。然后我们开始设置这个按钮的点击事件。在我们的java文件中先定义一个Button对象,然后使用Button的对象调用findViewId方法。方法里的参数为控件的id。然后就设置点击事件。

复制代码
button.findViewById(R.id.bt);
button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                
            }
        });
复制代码

在onClick方法中添加事件的代码即可。

 

蓝牙的开启

首先,要在新建项目中的AndroidManifest.xml中声明两个权限:BLUETOOTH权限和BLUETOOTH_ADMIN权限。其中,BLUETOOTH权限用于请求连接和传送数据;BLUETOOTH_ADMIN权限用于启动设备、发现或进行蓝牙设置,如果要拥有该权限,必须现拥有BLUETOOTH权限。

 因为android 6.0之后采用新的权限机制来保护用户的隐私,如果我们设置的targetSdkVersion大于或等于23,则需要另外添加ACCESS_COARSE_LOCATION和ACCESS_FINE_LOCATION权限,否则,可能会出现搜索不到蓝牙设备的问题。

复制代码
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>

 
复制代码

然后要在Activity中,添加

BluetoothAdapter blueadapter = BluetoothAdapter.getDefaultAdapter();
//获取蓝牙适配器

来获取蓝牙适配器的对象

在按钮的点击事件中添加以下代码来开启蓝牙

if(blueadapter==bull)
//表示手机不支持蓝牙
return;

 

 

复制代码
 if (!blueadapter.isEnabled())
        //判断本机蓝牙是否打开
        {//如果没打开,则打开蓝牙
        blueadapter.enable();
        }

 
复制代码

同理,使用disable()可以关闭蓝牙。

搜索蓝牙

接下来是搜索蓝牙设备,如果想要改变自己蓝牙能否被搜索的状态,可以使用以下的代码来使自己的蓝牙设备可被发现

复制代码
if (blueadapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) //不在可被搜索的范围
        {
        Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
        discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);//设置本机蓝牙在300秒内可见
        startActivity(discoverableIntent);
        }
复制代码

如果想搜索别人的设备,就可以在按钮的点击事件中调用startDiscover()来搜索蓝牙

复制代码
public void doDiscovry() {
    if (blueadapter.isDiscovering()) {
        //判断蓝牙是否正在扫描,如果是调用取消扫描方法;如果不是,则开始扫描
        blueadapter.cancelDiscovery();
    } else
        blueadapter.startDiscovery();

}
复制代码

我们要想获得搜索到的结果,就需要注册广播

首先,要定义一个列表控件listview和一个集合适配器和两个ArrayList集合类的对象,这两个对象都是用来存设备的地址,只是有一个不是用来显示出来的。在主类中定义以下的集合适配器和两个ArrayList集合类的对象,然后在onCreate中定义适配器

复制代码
public ArrayAdapter adapter;
ListView listView = (ListView) findViewById(R.id.list);//控件 列表
//定义一个列表,存蓝牙设备的地址。
public ArrayList<String> arrayList=new ArrayList<>();
//定义一个列表,存蓝牙设备地址,用于显示。
public ArrayList<String> deviceName=new ArrayList<>();

//定义适配器
adapter = new ArrayAdapter(this, android.R.layout.simple_expandable_list_item_1, deviceName);

//给列表添加适配器
listView.setAdapter(adapter);
复制代码

然后定义广播以及处理广播的消息

复制代码
IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);//注册广播接收信号
registerReceiver(bluetoothReceiver, intentFilter);//用BroadcastReceiver 来取得结果

private final BroadcastReceiver bluetoothReceiver = 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);
            deviceName.add("设备名:"+device.getName()+"\n" +"设备地址:"+device.getAddress() + "\n");//将搜索到的蓝牙名称和地址添加到列表。
            arrayList.add( device.getAddress());//将搜索到的蓝牙地址添加到列表。
            adapter.notifyDataSetChanged();//更新
        }
    }
 };
复制代码

搜索完设备后,要记得注销广播。注册后的广播对象在其他地方有强引用,如果不取消,activity会释放不了资源 。

protected void onDestroy(){
    super.onDestroy();//解除注册
    unregisterReceiver(bluetoothReceiver);
}

4.了解targetSdkVersion是否大于或等于23

       若是大于或等于23,除了添加了蓝牙权限外,还要动态获取位置权限,才能将搜索到的蓝牙设备显示出来。若是小于,则不需要动态获取权限。
动态申请权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public 
void 
applypermission() {
        
if 
(Build.VERSION.SDK_INT >= 
23
) {
            
//检查是否已经给了权限
            
int 
checkpermission = ContextCompat.checkSelfPermission(getApplicationContext(),
                    
Manifest.permission.ACCESS_FINE_LOCATION);
            
if 
(checkpermission != PackageManager.PERMISSION_GRANTED) {
//没有给权限
                
Log.e(
"permission"

"动态申请"
);
                
//参数分别是当前活动,权限字符串数组,requestcode
                
ActivityCompat.requestPermissions(WiFiMainActivity.
this

new 
String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 
1
);
            
}
        
}
    
}
 
    
@Override
    
public 
void 
onRequestPermissionsResult(
int 
requestCode, 
@NonNull 
String[] permissions, 
@NonNull 
int
[] grantResults) {
        
super
.onRequestPermissionsResult(requestCode, permissions, grantResults);
        
if 
(grantResults[
0
] == PackageManager.PERMISSION_GRANTED) {
            
Toast.makeText(WiFiMainActivity.
this

"已授权"
, Toast.LENGTH_SHORT).show();
        

else 
{
            
Toast.makeText(WiFiMainActivity.
this

"拒绝授权"
, Toast.LENGTH_SHORT).show();
        
}
 
    
}

 配对蓝牙设备

 蓝牙的配对和连接有两种方式。一种是每个设备作为一个客户端去连接一个服务端,向对方发起连接。另一种则是作为服务端来接收客户端发来连接的消息。蓝牙之间的数据传输采用的是和TCP传输类似的传输机制。

1.作为客户端连接
       首先要获取一个代表远程设备BluetoothDevice的对象,然后使用该BluetoothDevice的对象来获取一个BluetoothSocket对象。BluetoothSocket对象调用connect()可以建立连接。

       蓝牙连接整个过程需要在子线程中执行的,并且要将 scoket.connect()放在一个新的子线程中,因为如果将这个方法也放在同一个子线程中解决的话,就会永远报错read failed, socket might closed or timeout, read ret: -1;借鉴网上的方法:再开一个子线程专门执行socket.connect()方法,问题可以解决;

       另外,借鉴网上方法和建议,在获得socket的时候 ,尽量不使用uuid方式;因为这样虽然能够获取到socket 但是不能进行自动,所以使用的前提是已经配对了的设备连接;

       使用反射的方式,能够自动提示配对,也适合手机间通信。

首先,在设置列表中显示的蓝牙的点击事件,其中ClientThread是连接,发送和接收的线程类

复制代码
//定义列表Item的点击事件
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
                device = adapter.getRemoteDevice(arrayList.get(i));
                mge = editText.getText().toString() + "\r\n";//获得编辑文本框里的文字
                clientThread = new ClientThread(device, mge, context);
                clientThread.start();
            }


        });
复制代码

然后定义一个线程类,在他的构造方法中接收listview点击事件里传来的参数

class ClientThread extends Thread {

public void run() {
}
}

在run方法中输入以下代码

final BluetoothSocket socket = (BluetoothSocket) device.getClass().getDeclaredMethod("createRfcommSocket", new Class[]{int.class}).invoke(device, 1);

   代码中的device需要把注册广播时的device作为参数传进线程中。注意,传进来的device的值要为远程设备的地址,若不是或有出入,则可能会出现NullPointerException异常,并提示尝试调用一个空的对象。为了解决这个问题,可以把显示获得的device名字、地址和传入线程的device的地址分在不同的集合类。传入线程的device使用只有设备地址的集合类。

       在连接蓝牙之前,还要先取消蓝牙设备的扫描,否则容易连接失败。

 

adapter.cancelDiscovery();//adapter为获取到的蓝牙适配器
socket.connect();//连接

 

2.作为服务端连接
       服务端接收连接需要使用BluetoothServerSocket类,它的作用是监听进来的连接,在一个连接被接收之后,会返回一个BluetoothSocket对象,这个对象可以用来和客户端进行通信。

       与客户端一样,服务端也要在子线程中实现。通过调用listenUsingRfcommWithServiceRecord(String,UUID)方法可以得到一个BluetoothServerSocket的对象,然后再用这个对象来调用accept()来返回一个BluetoothSocket对象。由于accept()是个阻塞的方法,它会直到接收到一个连接或异常之后才会返回,所以要放在子线程中。

bluetoothServerSocket=bluetoothAdapter.listenUsingRfcommWithServiceRecord(bluetoothAdapter.getDefaultAdapter().getName(), UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
//bluetoothServerSocket= (BluetoothServerSocket) bluetoothAdapter.getClass().getMethod("listenUsingRfcommOn",new Class[]{int.class}).invoke(bluetoothAdapter,10);
socket=bluetoothServerSocket.accept();//接收连接

 这个连接时只允许一个客户端连接,因此在BluetoothServerSocket对象接收到一个连接请求时就要立刻调用close()方法把服务端关闭。

当两个设备成功连接之后,双方都会有一个BluetoothSocket对象,这时,就可以在设备之间传送数据了。

       1.使用getOutputStream()方法来获取输出流来处理传输。

       2.调用write()。

复制代码
复制代码
os = socket.getOutputStream();//获取输出流
if (os != null) {//判断输出流是否为空
    os.write(message.getBytes("UTF-8"));
}
os.flush();//将输出流的数据强制提交
os.close();//关闭输出流
}
复制代码
复制代码

 3.使用getInputStream()方法来获取输出流来处理传输。

4.创建一个新的线程来read()输入流,这里是接收16进制数然后转换为10进制数显示

复制代码
ew Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                while (true) {
                                    int count = 0;
                                    while (count == 0) {
                                        count = is.available();
                                    }
                                    byte buf[] = new byte[count];
                                    if (buf != null) {
                                        is.read(buf);
                                       // BuletoothMainActivity.num++;
                                        String message = BuletoothMainActivity.bytesToIntString(buf);

                                        BuletoothMainActivity.UpdateRevMsg(message);
                                    }

                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }).start();
复制代码
复制代码
public static String bytesToIntString(byte[] bytes) {//16进制转10进制以字符串形式输出显示
        String result = "";
        for (int i = 0; i < bytes.length; i++) {
            String hexString = Integer.toString(bytes[i] & 0xFF);
            if (hexString.length() == 1) {
                hexString = '0' + hexString;
            }
            result += hexString.toUpperCase();
        }
        return result;
    }
复制代码

 

public static void UpdateRevMsg(String revMsg) {
    mRevMsg=revMsg;
    handler.post(RefreshTextView);
}

private static Runnable RefreshTextView=new Runnable() {
    @Override
    public void run() {
        textView2.setText(mRevMsg);
    }
};

 

在服务端发送和接收也类似上面。

转自:https://www.cnblogs.com/lwkdbk/p/10644838.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/154515.html原文链接:https://javaforall.net

(0)
上一篇 2022年7月1日 下午8:16
下一篇 2022年7月1日 下午8:36


相关推荐

  • 火山工场AI Agent产业园举办开园后首场活动,上百人免费领“龙虾”

    火山工场AI Agent产业园举办开园后首场活动,上百人免费领“龙虾”

    2026年3月16日
    2
  • c++map set_get post区别

    c++map set_get post区别setset的数据结构set的操作函数erasefindcount重载操作符multisetmapmap的数据结构map的构造函数map的操作函数erasecleanfind重载运算符其他操作函数multimap)setset是一种关联式容器,其特性如下:set以RBTree作为底层容器所得元素的只有key没有value,value就是key不允许出现键值重复所有的元素都会被自动排…

    2025年9月26日
    7
  • grok账号怎么支付充值会员?

    grok账号怎么支付充值会员?

    2026年3月15日
    2
  • 三自由度机械臂的三维设计

    三自由度机械臂的三维设计三自由度机械臂的三维设计背景底座背景写本篇文章的时候只是模型设计完成以及部分零件进行了采购,最终是否能实现不能确定。继续之前的智能车制作,我的想法是一个多功能的智能小车它至少得有手有眼吧,所以继摄像头云台的制作结束后我又进行了机械臂的设计,最初对于机械臂的了解真的是非常的少,当时的想法是一个关节一个舵机呗,大概是这样:但是我觉得这样有点丑,而且这样的话用的舵机也不少,不是很物美价廉,所有我…

    2022年6月7日
    34
  • php 字符串转换时间_php 字符时间如何转换「建议收藏」

    php 字符串转换时间_php 字符时间如何转换「建议收藏」php字符时间转换的方法:1、通过php中的“strtotime()”函数将任何英文文本的日期时间描述解析为时间戳;2、使用php中的“mktime()”函数从日期取得时间戳即可。本文操作环境:windows7系统、PHP5.6版,DELLG3电脑。php字符串转时间戳PHP提供了函数可以方便的将各种形式的日期转换为时间戳,该类函数主要是:strtotime():将任何英文文本的日期时间描述解…

    2022年6月2日
    32
  • ILSVRC2015_VID数据集说明

    ILSVRC2015_VID数据集说明参考 https blog csdn net weixin article details 数据主页 http bvisionweb1 cs unc edu ilsvrc2015 download videos 3j16 php vid 数据下载 http bvisionweb1 cs unc edu ilsvrc2015 ILSVRC2015 VID tar gz

    2026年3月26日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号