上周参照网上代码,做了USB的初步探测程序,工作正常 。今天从硬件部拿到了一段例程,原本打算参考它来完善自己的程序。但运行之后总是报错,逐步跟进错误,进而发现了一个匪疑所思的问题。调试一天也未发现原因以及解决办法。在此做记录,留待以后解决或从网上得到帮助。
问题程序:
UsbHid.java:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class UsbHid extends Activity{ private static UsbHid usbHid = null; Context context; private static final String TAG = "UsbHid"; private UsbManager myUsbManager; private UsbDevice myUsbDevice; private boolean HasUsbDevice; ... private UsbHid(Context context) { this.context = context; myUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); HasUsbDevice = enumerateDevice(); Log.d(TAG, "UsbHid init"); } public bollean enumerateDevice() { if (myUsbManager == null) return -1; HashMap<String, UsbDevice> deviceList = myUsbManager.getDeviceList(); if (!deviceList.isEmpty()) { ... return true; } return false; } public static UsbHid getInstance(Context context) { if (usbHid == null) { usbHid = new UsbHid(context); } return usbHid; } public boolean getUsbDevice() { return HasUsbDevice; } ... }
MainActivity.java:
public class MainActivity extends ActionBarActivity { UsbHid usbHid = null; UsbManager myUsbManager; UsbDevice myUsbDevice; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); usbHid = UsbHid.getInstance(this); if (usbHid.getUsbDevice()) { usbHid.ConnectUsbHid(); this.setTitle("已连接USB设备"); } else { this.setTitle("末检测到USB设备"); } if (usbHid.getUsbDevice()) { UsbReadThread thread = new UsbReadThread(); thread.start(); } } ... }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
以上程序从原理上分析应该没有问题,但实际中deviceList.isEmpty()总是返回null。
更为奇怪的是,若将UsbHid.java中的
myUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
HasUsbDevice = enumerateDevice();
移到MainActivity.java中,则没有问题,可以正常枚举设备。
前一天写的以上内容,今天原本打算暂时绕过以上问题,继续进行后续开发,毕竟这个问题不是不可避免的。但还是觉得应该再基于这个问题研究一下,因为这样才能够提高水平,丰富经验。
进行反复修改尝试,根据现象判断,最终发现了问题所在:
UsbHid.java:
public static UsbHid getInstance(Context context) {
if (usbHid == null) {
usbHid = new UsbHid(context);
}
return usbHid;
}
就是这句导致的问题!将这句程序封掉后,可以枚举到设备,程序可以正常工作了。
具体原因尚不清楚,但初步分析应该是由于usbHid是静态变量(private static UsbHid usbHid = null;),放在全局存储区,因此回收的时候并没有马上释放掉。下次进程再启动时,它并不是null,而是上次一分配过的值。基于这个想法,做了实验,将上面这段代码改写如下:
public static UsbHid getInstance(Context context) {
if (usbHid == null) {
usbHid = new UsbHid(context);
return usbHid;
}
return null;
}
改写程序的目的是测试是否每次都返回usbHid而非null。宏观上从程序运行现象来看应该是如果每次都返回usbHid,则每次程序都正常;如果有返回null的时候,则程序会停止运行。
测试结果印证了 猜测,一次成功接一次失败,即确实usbHid在前一次进程结束后并未彻底释放,而直到出错被停止运行后才被释放掉。
另外一个测试也验证了以上想法:将手机重启,之后运行以上程序,由于是第一此运行,因此完全正常,而后每次即使不插入usb设备,也不报错。这就说明了usbHid这个变量并没有被释放干净。
至于为何进程结束后,仍不能是放干净,则有待进一步深入,应该是与Java的回收机制有关了……