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

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
< manifest xmlns: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-permission android:name = "android.permission.BLUETOOTH_ADMIN" />
< uses-permission android: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 >
                 < action android:name = "android.intent.action.MAIN" />
 
                 < category android: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
< LinearLayout xmlns: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
package com.example.thecaseforbluetooth;
 
import java.util.ArrayList;
import java.util.Set;
 
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.Menu;
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.Toast;
import android.widget.ToggleButton;
 
public class BluetoothActivity extends Activity {
     public Button searchBtn; //搜索蓝牙设备
     public Button exitBtn; //退出应用
     public Button discoverBtn; //设置可被发现
     public ToggleButton openBtn; //开关蓝牙设备
     public Button serverbtn;
     public ListView listView; //蓝牙设备清单
     public ArrayAdapter<String> adapter;
     public ArrayList<String> list = new ArrayList<String>();
     private BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     Set<BluetoothDevice> bondDevices ;
     public Context context ;
     @Override
     public void 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 = new ArrayAdapter<String>( this , android.R.layout.simple_list_item_1,list);
         listView.setAdapter(adapter);
         openBtn.setChecked( false );
         //注册广播接收信号
         IntentFilter intent = new IntentFilter();
         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( new OnClickListener() {
             @Override
             public void 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( new OnClickListener() {
             
             @Override
             public void onClick(View v) {
                 // TODO Auto-generated method stub
                 BluetoothActivity. this .finish();
             }
         });
         //设置蓝牙设备可发现
         discoverBtn.setOnClickListener( new OnClickListener() {
             
             @Override
             public void onClick(View v) {
                 // TODO Auto-generated method stub
                 Intent discoverIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                 discoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300 );
                 startActivity(discoverIntent);
             }
         });
         //开关蓝牙设备
         openBtn.setOnClickListener( new OnClickListener() {
             
             @Override
             public void onClick(View v) {
                 // TODO Auto-generated method stub
                 if (openBtn.isChecked() == true ){
                     bluetoothAdapter.disable();
                 }
                 else if (openBtn.isChecked() == false ){
                     bluetoothAdapter.enable();
                 }
             }
         });
         //作为服务端开启
         serverbtn.setOnClickListener( new OnClickListener() {
             
             @Override
             public void onClick(View v) {
                 // TODO Auto-generated method stub
                 ServerThread serverThread = new ServerThread(bluetoothAdapter, context);
                 Toast.makeText(context, "server 端启动" , 5000 ).show();
                 serverThread.start();
             }
         });
         listView.setOnItemClickListener( new ItemClickListener());
     }
     @Override
     public void 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 = new Intent(
                     BluetoothAdapter.ACTION_REQUEST_ENABLE);
             startActivityForResult(enableIntent, 3 );
         }
          
     }
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.main, menu);
         return true ;
     }
     
     private final BroadcastReceiver searchReceiver = new BroadcastReceiver() {
         
         @Override
         public void 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();
              }
             
         }
     };
       public class ItemClickListener implements OnItemClickListener {
 
         @Override
         public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                 long arg3) {
             // 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 = new ClientThread(btDev, context);
              clientThread.start();
         }}
}
Bluetoothprotocol.java 
?
1
2
3
4
5
6
7
8
package com.example.thecaseforbluetooth;
 
public interface Bluetoothprotocol {
     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" ;
}
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
package com.example.thecaseforbluetooth;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
 
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
 
public class ServerThread extends Thread {
     public BluetoothServerSocket mserverSocket;
     public BluetoothAdapter bluetoothAdapter;
     public BluetoothSocket socket;
     public Context context;
     public ServerThread(BluetoothAdapter bluetoothAdapter,Context context) {
         this .bluetoothAdapter = bluetoothAdapter;
         this .context = context;
     }
 
     public void run() {
         
         try {
             /* 创建一个蓝牙服务器
              * 参数分别:服务器名称、UUID   */
             mserverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(Bluetoothprotocol.PROTOCOL_SCHEME_RFCOMM,
                     UUID.fromString( "00001101-0000-1000-8000-00805F9B34FB" ));      
//          /* 接受客户端的连接请求 */
             socket = mserverSocket.accept();
             //下面代码作者偷懒,读写另外起一个线程最好
             //接收数据
              byte [] buffer = new byte [ 1024 ];
                 int bytes;
                 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 = new byte [bytes];
                             for ( int i= 0 ; i<bytes; i++)
                             {
                                 buf_data[i] = buffer[i];
                             }
                             String s = new String(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
package com.example.thecaseforbluetooth;
 
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
 
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.widget.Toast;
 
public class ClientThread extends Thread {
     public BluetoothSocket socket;
     public BluetoothDevice device;
     public Context context;
     public ClientThread(BluetoothDevice device,Context context){
         this .device = device;
         this .context = context;
     }
     public void 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();
         }
     }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值