前面文章讲解了android的蓝牙基本用法,本文讲得深入些,探讨下蓝牙方面的隐藏api。用过android系统设置(setting)的人都知道蓝牙搜索之后可以建立配对和解除配对,但是这两项功能的函数没有在sdk中给出,那么如何去使用这两项功能呢?本文利用java的反射机制去调用这两项功能对应的函数:createbond和removebond,具体的发掘和实现步骤如下:
1.使用git工具下载platform/packages/apps/settings.git,在setting源码中查找关于建立配对和解除配对的api,知道这两个api的宿主(bluetoothdevice);
2.使用反射机制对bluetoothdevice枚举其所有方法和常量,看看是否存在:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
static public void printallinform( class clsshow) { try { // 取得所有方法 method[] hidemethod = clsshow.getmethods(); int i = 0 ; for (; i < hidemethod.length; i++) { log.e( "method name" , hidemethod[i].getname()); } // 取得所有常量 field[] allfields = clsshow.getfields(); for (i = 0 ; i < allfields.length; i++) { log.e( "field name" , allfields[i].getname()); } } catch (securityexception e) { // throw new runtimeexception(e.getmessage()); e.printstacktrace(); } catch (illegalargumentexception e) { // throw new runtimeexception(e.getmessage()); e.printstacktrace(); } catch (exception e) { // todo auto-generated catch block e.printstacktrace(); } } |
结果如下:
11-29 09:19:12.012: method name(452): cancelbondprocess
11-29 09:19:12.020: method name(452): cancelpairinguserinput
11-29 09:19:12.020: method name(452): createbond
11-29 09:19:12.020: method name(452): createinsecurerfcommsocket
11-29 09:19:12.027: method name(452): createrfcommsocket
11-29 09:19:12.027: method name(452): createrfcommsockettoservicerecord
11-29 09:19:12.027: method name(452): createscosocket
11-29 09:19:12.027: method name(452): describecontents
11-29 09:19:12.035: method name(452): equals
11-29 09:19:12.035: method name(452): fetchuuidswithsdp
11-29 09:19:12.035: method name(452): getaddress
11-29 09:19:12.035: method name(452): getbluetoothclass
11-29 09:19:12.043: method name(452): getbondstate
11-29 09:19:12.043: method name(452): getname
11-29 09:19:12.043: method name(452): getservicechannel
11-29 09:19:12.043: method name(452): gettruststate
11-29 09:19:12.043: method name(452): getuuids
11-29 09:19:12.043: method name(452): hashcode
11-29 09:19:12.043: method name(452): isbluetoothdock
11-29 09:19:12.043: method name(452): removebond
11-29 09:19:12.043: method name(452): setpairingconfirmation
11-29 09:19:12.043: method name(452): setpasskey
11-29 09:19:12.043: method name(452): setpin
11-29 09:19:12.043: method name(452): settrust
11-29 09:19:12.043: method name(452): tostring
11-29 09:19:12.043: method name(452): writetoparcel
11-29 09:19:12.043: method name(452): convertpintobytes
11-29 09:19:12.043: method name(452): getclass
11-29 09:19:12.043: method name(452): notify
11-29 09:19:12.043: method name(452): notifyall
11-29 09:19:12.043: method name(452): wait
11-29 09:19:12.051: method name(452): wait
11-29 09:19:12.051: method name(452): wait
3.如果枚举发现api存在(sdk却隐藏),则自己实现调用方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/** * 与设备配对 参考源码:platform/packages/apps/settings.git * /settings/src/com/android/settings/bluetooth/cachedbluetoothdevice.java */ static public boolean createbond( class btclass,bluetoothdevice btdevice) throws exception { method createbondmethod = btclass.getmethod( "createbond" ); boolean returnvalue = ( boolean ) createbondmethod.invoke(btdevice); return returnvalue.booleanvalue(); } /** * 与设备解除配对 参考源码:platform/packages/apps/settings.git * /settings/src/com/android/settings/bluetooth/cachedbluetoothdevice.java */ static public boolean removebond( class btclass,bluetoothdevice btdevice) throws exception { method removebondmethod = btclass.getmethod( "removebond" ); boolean returnvalue = ( boolean ) removebondmethod.invoke(btdevice); return returnvalue.booleanvalue(); } |
此处注意:sdk之所以不给出隐藏的api肯定有其原因,也许是出于安全性或者是后续版本兼容性的考虑,因此不能保证隐藏api能在所有android平台上很好地运行。
本文程序运行效果如下图所示:
main.xml源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?xml version= "1.0" encoding= "utf-8" ?> <linearlayout xmlns:android= "http://schemas.android.com/apk/res/android" android:orientation= "vertical" android:layout_width= "fill_parent" android:layout_height= "fill_parent" > <linearlayout android:id= "@+id/linearlayout01" android:layout_height= "wrap_content" android:layout_width= "fill_parent" > <button android:layout_height= "wrap_content" android:id= "@+id/btnsearch" android:text= "search" android:layout_width= "160dip" ></button> <button android:layout_height= "wrap_content" android:layout_width= "160dip" android:text= "show" android:id= "@+id/btnshow" ></button> </linearlayout> <linearlayout android:id= "@+id/linearlayout02" android:layout_width= "wrap_content" android:layout_height= "wrap_content" ></linearlayout> <listview android:id= "@+id/listview01" android:layout_width= "fill_parent" android:layout_height= "fill_parent" > </listview> </linearlayout> |
工具类clsutils.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
|
package com.testreflect; import java.lang.reflect.field; import java.lang.reflect.method; import android.bluetooth.bluetoothdevice; import android.util.log; public class clsutils { /** * 与设备配对 参考源码:platform/packages/apps/settings.git * /settings/src/com/android/settings/bluetooth/cachedbluetoothdevice.java */ static public boolean createbond( class btclass,bluetoothdevice btdevice) throws exception { method createbondmethod = btclass.getmethod( "createbond" ); boolean returnvalue = ( boolean ) createbondmethod.invoke(btdevice); return returnvalue.booleanvalue(); } /** * 与设备解除配对 参考源码:platform/packages/apps/settings.git * /settings/src/com/android/settings/bluetooth/cachedbluetoothdevice.java */ static public boolean removebond( class btclass,bluetoothdevice btdevice) throws exception { method removebondmethod = btclass.getmethod( "removebond" ); boolean returnvalue = ( boolean ) removebondmethod.invoke(btdevice); return returnvalue.booleanvalue(); } /** * * @param clsshow */ static public void printallinform( class clsshow) { try { // 取得所有方法 method[] hidemethod = clsshow.getmethods(); int i = 0 ; for (; i < hidemethod.length; i++) { log.e( "method name" , hidemethod[i].getname()); } // 取得所有常量 field[] allfields = clsshow.getfields(); for (i = 0 ; i < allfields.length; i++) { log.e( "field name" , allfields[i].getname()); } } catch (securityexception e) { // throw new runtimeexception(e.getmessage()); e.printstacktrace(); } catch (illegalargumentexception e) { // throw new runtimeexception(e.getmessage()); e.printstacktrace(); } catch (exception e) { // todo auto-generated catch block e.printstacktrace(); } } } |
主程序testreflect.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
|
package com.testreflect; import java.util.arraylist; import java.util.list; 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.util.log; import android.view.view; import android.widget.adapterview; import android.widget.arrayadapter; import android.widget.button; import android.widget.listview; import android.widget.toast; public class testreflect extends activity { button btnsearch, btnshow; listview lvbtdevices; arrayadapter<string> adtdevices; list<string> lstdevices = new arraylist<string>(); bluetoothdevice btdevice; bluetoothadapter btadapt; @override public void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); setcontentview(r.layout.main); btnsearch = (button) this .findviewbyid(r.id.btnsearch); btnsearch.setonclicklistener( new clickevent()); btnshow = (button) this .findviewbyid(r.id.btnshow); btnshow.setonclicklistener( new clickevent()); lvbtdevices = (listview) this .findviewbyid(r.id.listview01); adtdevices = new arrayadapter<string>(testreflect. this , android.r.layout.simple_list_item_1, lstdevices); lvbtdevices.setadapter(adtdevices); lvbtdevices.setonitemclicklistener( new itemclickevent()); btadapt = bluetoothadapter.getdefaultadapter(); // 初始化本机蓝牙功能 if (btadapt.getstate() == bluetoothadapter.state_off) // 开蓝牙 btadapt.enable(); // 注册receiver来获取蓝牙设备相关的结果 intentfilter intent = new intentfilter(); intent.addaction(bluetoothdevice.action_found); intent.addaction(bluetoothdevice.action_bond_state_changed); registerreceiver(searchdevices, intent); } private broadcastreceiver searchdevices = new broadcastreceiver() { public void onreceive(context context, intent intent) { string action = intent.getaction(); bundle b = intent.getextras(); object[] lstname = b.keyset().toarray(); // 显示所有收到的消息及其细节 for ( int i = 0 ; i < lstname.length; i++) { string keyname = lstname[i].tostring(); log.e(keyname, string.valueof(b.get(keyname))); } // 搜索设备时,取得设备的mac地址 if (bluetoothdevice.action_found.equals(action)) { bluetoothdevice device = intent .getparcelableextra(bluetoothdevice.extra_device); if (device.getbondstate() == bluetoothdevice.bond_none) { string str = "未配对|" + device.getname() + "|" + device.getaddress(); lstdevices.add(str); // 获取设备名称和mac地址 adtdevices.notifydatasetchanged(); } } } }; class itemclickevent implements adapterview.onitemclicklistener { @override public void onitemclick(adapterview<?> arg0, view arg1, int arg2, long arg3) { btadapt.canceldiscovery(); string str = lstdevices.get(arg2); string[] values = str.split( "//|" ); string address=values[ 2 ]; btdevice = btadapt.getremotedevice(address); try { if (values[ 0 ].equals( "未配对" )) { toast.maketext(testreflect. this , "由未配对转为已配对" , 500 ).show(); clsutils.createbond(btdevice.getclass(), btdevice); } else if (values[ 0 ].equals( "已配对" )) { toast.maketext(testreflect. this , "由已配对转为未配对" , 500 ).show(); clsutils.removebond(btdevice.getclass(), btdevice); } } catch (exception e) { // todo auto-generated catch block e.printstacktrace(); } } } /** * 按键处理 * @author gv * */ class clickevent implements view.onclicklistener { @override public void onclick(view v) { if (v == btnsearch) { //搜索附近的蓝牙设备 lstdevices.clear(); object[] lstdevice = btadapt.getbondeddevices().toarray(); for ( int i = 0 ; i < lstdevice.length; i++) { bluetoothdevice device=(bluetoothdevice)lstdevice[i]; string str = "已配对|" + device.getname() + "|" + device.getaddress(); lstdevices.add(str); // 获取设备名称和mac地址 adtdevices.notifydatasetchanged(); } // 开始搜索 settitle( "本机蓝牙地址:" + btadapt.getaddress()); btadapt.startdiscovery(); } else if (v==btnshow){ //显示bluetoothdevice的所有方法和常量,包括隐藏api clsutils.printallinform(btdevice.getclass()); } } } } |
希望本文实例能够对大家进行android程序开发有一定的借鉴帮助作用。