Android Bluetooth 学习(2)应用层实现蓝牙设备查找、tcp_ip通信

来源:互联网 发布:java 实现onvif 编辑:程序博客网 时间:2024/06/05 02:20

Bluetooth结构

    1、JAVA层
    
frameworks/base/core/java/android/bluetooth/

   包含了bluetooth的JAVA类。

    2、JNI层

    frameworks/base/core/jni/android_bluetooth_开头的文件

   
   定义了bluez通过JNI到上层的接口。

   frameworks/base/core/jni/android_server_bluetoothservice.cpp

   调用硬件适配层的接口system/bluetooth/bluedroid/bluetooth.c

    3、bluez库

    external/bluez/

    
   这是bluez用户空间的库,开源的bluetooth代码,包括很多协议,生成libbluetooth.so。

    
    4、硬件适配层

    system/bluetooth/bluedroid/bluetooth.c

  
   包含了对硬件操作的接口

   system/bluetooth/data/*

   一些配置文件,复制到/etc/bluetooth/。

   还有其他一些测试代码和工具。


一、简略介绍Bluetooth开发使用到的类

1、BluetoothAdapter,蓝牙适配器,可判断蓝牙设备是否可用等功能。
常用方法列举如下:
    cancelDiscovery() ,取消搜索过程,在进行蓝牙设备搜索时,如果调用该方法会停止搜索。(搜索过程会持续12秒)
    disable()关闭蓝牙,也就是我们常说的禁用蓝牙。
    enable()打开蓝牙,这个方法打开蓝牙但不会弹出提示,正常流程操作下,我们会让系统提示用户是否打开蓝牙设备。如下两行代码可轻松搞定。

    Intent enabler=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enabler,reCode);//同startActivity(enabler);(在主Activity启动一个二级Activity,reCode一般等于3,一定记得要在AndroidManifest.xml里面添加蓝牙权限
    getAddress()获取本地蓝牙地址
    getDefaultAdapter()获取默认BluetoothAdapter,实际上,也只有这一种方法获取BluetoothAdapter
    getName()获取本地蓝牙名称
    getRemoteDevice(String address)根据蓝牙地址获取远程蓝牙设备
    getState()获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)
    isDiscovering()判断当前是否正在查找设备,是返回true
    isEnabled()判断蓝牙是否打开,已打开返回true,否则,返回false
    listenUsingRfcommWithServiceRecord(String name,UUID uuid)根据名称,UUID创建并返回    
    BluetoothServerSocket,这是创建BluetoothSocket服务器端的第一步
    startDiscovery()开始搜索,这是搜索的第一步

 2.BluetoothDevice看名字就知道,这个类描述了一个蓝牙设备
    createRfcommSocketToServiceRecord(UUIDuuid)根据UUID创建并返回一个BluetoothSocket
    这个方法也是我们获取BluetoothDevice的目的——创建BluetoothSocket
    这个类其他的方法,如getAddress(),getName(),同BluetoothAdapter
    这个类有几个隐藏方法,涉及到蓝牙的自动配对,setPin,createBond,cancelPairingUserInput,等方法(需要通过java的反射,调用这几个隐藏方法)

3.BluetoothServerSocket如果去除了Bluetooth相信大家一定再熟悉不过了,既然是Socket,方法就应该都差不多,
    这个类一种只有三个方法
    两个重载的accept(),accept(inttimeout)两者的区别在于后面的方法指定了过时时间,需要注意的是,执行这两个方法的时候,直到接收到了客户端的请求(或是过期之后),都会阻塞线程,应该放在新线程里运行!
    还有一点需要注意的是,这两个方法都返回一个BluetoothSocket,最后的连接也是服务器端与客户端的两个BluetoothSocket的连接
    close()关闭!

4.BluetoothSocket,跟BluetoothServerSocket相对,是客户端
    一共5个方法,不出意外,都会用到
    close(),关闭
    connect()连接
    getInptuStream()获取输入流
    getOutputStream()获取输出流
    getRemoteDevice()获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备

二、蓝牙设备的发现、查找。

1.基于安全性考虑,设置开启可被搜索后,Android系统会默认给出120秒的时间,其他设备能在这120秒内搜索到它。 
    Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); 
    startActivityForResult(enalbe,REQUEST_DISCOVERABLE); 
2.搜索蓝牙设备 

    BluetoothAdapter _bluetooth = BluetoothAdapter.getDefaultAdapter();
    _bluetooth.startDiscovery();
3.关闭蓝牙设备
    BluetoothAdapter _bluetooth = BluetoothAdapter.getDefaultAdapter();
    _bluetooth.disable();
4.创建蓝牙客户端
    BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID.fromString(UUID号));
    socket.connect();
4.创建蓝牙服务端
    BluetoothServerSocket _serverSocket = _bluetooth.listenUsingRfcommWithServiceRecord(服务端名称,UUID.fromeString(UUID号));
    BluetoothSocket socket = _serverSocket.accept();
    InputStream inputStream = socket.getInputStream();
三、相关代码实现
注:tcp_ip模块需要在系统setting模块中,完成蓝牙的配对,只有配对成功的,才能进行socket通信(具体如何配对和如何自动配对,将在bluetoot3或者4中进行讲解
AndridManifest.xml


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.thecaseforbluetooth"
    android:versionCode="1"
    android:versionName="1.0">
 
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH"/>
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".BluetoothActivity"
            android:label="@string/title_activity_bluetooth">
            <intent-filter>
                <actionandroid:name="android.intent.action.MAIN"/>
 
                <categoryandroid:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
 
</manifest>
main.xml 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/btnSearch"
  android:text="查找设备"
/>
<Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/btnExit"
  android:text="退出应用"
/>
<Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/btnDis"
  android:text="设置可被发现"
/>
<Button
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/btnserver"
  android:text="启动服务端"
/>
<ToggleButton
android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/tbtnSwitch"
  android:text="开/关 蓝牙设备"
/>
<ListView
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:id="@+id/lvDevices"
   
/>
</LinearLayout>
BluetoothActivity.java 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
packagecom.example.thecaseforbluetooth;
 
importjava.util.ArrayList;
importjava.util.Set;
 
importandroid.app.Activity;
importandroid.bluetooth.BluetoothAdapter;
importandroid.bluetooth.BluetoothDevice;
importandroid.content.BroadcastReceiver;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.content.IntentFilter;
importandroid.os.Bundle;
importandroid.view.Menu;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.AdapterView;
importandroid.widget.AdapterView.OnItemClickListener;
importandroid.widget.ArrayAdapter;
importandroid.widget.Button;
importandroid.widget.ListView;
importandroid.widget.Toast;
importandroid.widget.ToggleButton;
 
publicclass BluetoothActivity extendsActivity {
    publicButton searchBtn;//搜索蓝牙设备
    publicButton exitBtn;//退出应用
    publicButton discoverBtn;//设置可被发现
    publicToggleButton openBtn;//开关蓝牙设备
    publicButton serverbtn;
    publicListView listView;//蓝牙设备清单
    publicArrayAdapter<String> adapter;
    publicArrayList<String> list =newArrayList<String>();
    privateBluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    Set<BluetoothDevice> bondDevices ;
    publicContext context ;
    @Override
    publicvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        searchBtn = (Button)findViewById(R.id.btnSearch);
        exitBtn = (Button)findViewById(R.id.btnExit);
        discoverBtn = (Button)findViewById(R.id.btnDis);
        openBtn = (ToggleButton)findViewById(R.id.tbtnSwitch);
        serverbtn = (Button)findViewById(R.id.btnserver);
        listView = (ListView)findViewById(R.id.lvDevices);
        context = getApplicationContext();
        adapter = newArrayAdapter<String>(this, android.R.layout.simple_list_item_1,list);
        listView.setAdapter(adapter);
        openBtn.setChecked(false);
        //注册广播接收信号
        IntentFilter intent = newIntentFilter();
        intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver来取得搜索结果
        intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);//每当扫描模式变化的时候,应用程序可以为通过ACTION_SCAN_MODE_CHANGED值来监听全局的消息通知。比如,当设备停止被搜寻以后,该消息可以被系统通知給应用程序。
        intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//每当蓝牙模块被打开或者关闭,应用程序可以为通过ACTION_STATE_CHANGED值来监听全局的消息通知。
        registerReceiver(searchReceiver, intent);
        //显示已配对设备以及搜索未配对设备
        searchBtn.setOnClickListener(newOnClickListener() {
            @Override
            publicvoid onClick(View v) {
                // TODO Auto-generated method stub
                if(bluetoothAdapter.isDiscovering()){
                    bluetoothAdapter.cancelDiscovery();
                }
                list.clear();
                bondDevices = bluetoothAdapter.getBondedDevices();
                 
                for(BluetoothDevice device : bondDevices) {
                    String str = "  已配对完成   " + device.getName() +" "
                    + device.getAddress();
                    list.add(str);
                    adapter.notifyDataSetChanged();
                }
                bluetoothAdapter.startDiscovery();
            }
        });
        //退出应用
        exitBtn.setOnClickListener(newOnClickListener() {
             
            @Override
            publicvoid onClick(View v) {
                // TODO Auto-generated method stub
                BluetoothActivity.this.finish();
            }
        });
        //设置蓝牙设备可发现
        discoverBtn.setOnClickListener(newOnClickListener() {
             
            @Override
            publicvoid onClick(View v) {
                // TODO Auto-generated method stub
                Intent discoverIntent = newIntent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                discoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
                startActivity(discoverIntent);
            }
        });
        //开关蓝牙设备
        openBtn.setOnClickListener(newOnClickListener() {
             
            @Override
            publicvoid onClick(View v) {
                // TODO Auto-generated method stub
                if(openBtn.isChecked() == true){
                    bluetoothAdapter.disable();
                }
                elseif(openBtn.isChecked() == false){
                    bluetoothAdapter.enable();
                }
            }
        });
        //作为服务端开启
        serverbtn.setOnClickListener(newOnClickListener() {
             
            @Override
            publicvoid onClick(View v) {
                // TODO Auto-generated method stub
                ServerThread serverThread = newServerThread(bluetoothAdapter, context);
                Toast.makeText(context,"server 端启动",5000).show();
                serverThread.start();
            }
        });
        listView.setOnItemClickListener(newItemClickListener());
    }
    @Override
    publicvoid onStart() {
        super.onStart();
        // If BT is not on, request that it be enabled.
        if(bluetoothAdapter == null){
            Toast.makeText(context,"蓝牙设备不可用",5000).show();
        }
         if(!bluetoothAdapter.isEnabled()) {
            Intent enableIntent = newIntent(
                    BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent,3);
        }
          
    }
    @Override
    publicboolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        returntrue;
    }
     
    privatefinal BroadcastReceiver searchReceiver = newBroadcastReceiver() {
         
        @Override
        publicvoid onReceive(Context context, Intent intent) {
            // TODO Auto-generated method stub
             String action = intent.getAction();
             BluetoothDevice device = null;
             if(BluetoothDevice.ACTION_FOUND.equals(action)){
                 device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                 if(device.getBondState() == BluetoothDevice.BOND_NONE) {
                 Toast.makeText(context, device.getName()+"",5000).show();
                 String str = " 未配对完成   " + device.getName() +" "
                 + device.getAddress();
                 if(list.indexOf(str) == -1)// 防止重复添加
                     list.add(str);
                 }
                 adapter.notifyDataSetChanged();
             }
             
        }
    };
      publicclass ItemClickListener implementsOnItemClickListener {
 
        @Override
        publicvoid onItemClick(AdapterView<?> arg0, View arg1, intarg2,
                longarg3) {
            // TODO Auto-generated method stub
             if(bluetoothAdapter.isDiscovering())
                 bluetoothAdapter.cancelDiscovery();
             String str = list.get(arg2);
             String address = str.substring(str.length() - 17);
             BluetoothDevice btDev = bluetoothAdapter.getRemoteDevice(address);
             ClientThread clientThread = newClientThread(btDev, context);
             clientThread.start();
        }}
}
Bluetoothprotocol.java 
?
1
2
3
4
5
6
7
8
packagecom.example.thecaseforbluetooth;
 
publicinterface Bluetoothprotocol {
    publicstatic final String PROTOCOL_SCHEME_L2CAP = "btl2cap";
    publicstatic final String PROTOCOL_SCHEME_RFCOMM = "btspp";
    publicstatic final String PROTOCOL_SCHEME_BT_OBEX = "btgoep";
    publicstatic final String PROTOCOL_SCHEME_TCP_OBEX = "tcpobex";
}
ServerThread.java 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
packagecom.example.thecaseforbluetooth;
 
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.OutputStream;
importjava.util.UUID;
 
importandroid.bluetooth.BluetoothAdapter;
importandroid.bluetooth.BluetoothServerSocket;
importandroid.bluetooth.BluetoothSocket;
importandroid.content.Context;
importandroid.os.Message;
importandroid.util.Log;
importandroid.widget.Toast;
 
publicclass ServerThread extendsThread {
    publicBluetoothServerSocket mserverSocket;
    publicBluetoothAdapter bluetoothAdapter;
    publicBluetoothSocket socket;
    publicContext context;
    publicServerThread(BluetoothAdapter bluetoothAdapter,Context context) {
        this.bluetoothAdapter = bluetoothAdapter;
        this.context = context;
    }
 
    publicvoid run() {
         
        try{
            /* 创建一个蓝牙服务器
             * 参数分别:服务器名称、UUID   */
            mserverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(Bluetoothprotocol.PROTOCOL_SCHEME_RFCOMM,
                    UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));      
//          /* 接受客户端的连接请求 */
            socket = mserverSocket.accept();
            //下面代码作者偷懒,读写另外起一个线程最好
            //接收数据
             byte[] buffer = newbyte[1024];
                intbytes;
                InputStream mmInStream = null;
                 
                try{
                    mmInStream = socket.getInputStream();
                }catch(IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }  
                System.out.println("zhoulc server");
                while(true){
                     if( (bytes = mmInStream.read(buffer)) > 0)
                        {
                            byte[] buf_data = newbyte[bytes];
                            for(inti=0; i<bytes; i++)
                            {
                                buf_data[i] = buffer[i];
                            }
                            String s = newString(buf_data);
                            System.out.println(s+"zhoulc server is in");
                
                }
        }catch(Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
}
     
}
ClientThread.java 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
packagecom.example.thecaseforbluetooth;
 
importjava.io.IOException;
importjava.io.OutputStream;
importjava.util.UUID;
 
importandroid.bluetooth.BluetoothDevice;
importandroid.bluetooth.BluetoothSocket;
importandroid.content.Context;
importandroid.widget.Toast;
 
publicclass ClientThread extendsThread {
    publicBluetoothSocket socket;
    publicBluetoothDevice device;
    publicContext context;
    publicClientThread(BluetoothDevice device,Context context){
        this.device = device;
        this.context = context;
    }
    publicvoid run() {
        try{
            //创建一个Socket连接:只需要服务器在注册时的UUID号
            socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
            //连接
            socket.connect();
            //下面代码作者偷懒,读写另外起一个线程最好
            //发送数据
            if(socket == null)
            {
                Toast.makeText(context,"链接失败",5000).show();
                return;
            }
            System.out.println("zhoulc client");
            while(true){
                try{
                    System.out.println("zhoulc client is in");
                    String msg = "hello everybody I am client";
                    OutputStream os = socket.getOutputStream();
                    os.write(msg.getBytes());
                }catch(IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }          
            }
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }
}
0 0
原创粉丝点击