[TIMOB-23165] Hyperloop: Android Anonymous interface implementations need to have default implementations of equals/hashCode/toString
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | Critical |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2016-04-13T16:36:17.000+0000 |
Affected Version/s | n/a |
Fix Version/s | Release 5.4.0 |
Components | Hyperloop |
Labels | android, hyperloop |
Reporter | Brian García |
Assignee | Christopher Williams |
Created | 2016-04-07T09:41:28.000+0000 |
Updated | 2016-08-01T22:47:32.000+0000 |
Description
Trying to implement BLE scanner the following error is returned:
[ERROR] : HyperloopProxy: (main) [828,2867] Exception thrown during invocation of method: public boolean android.bluetooth.BluetoothAdapter.startLeScan(android.bluetooth.BluetoothAdapter$LeScanCallback), args: [null]
[ERROR] : HyperloopProxy: java.lang.NullPointerException: Expected to unbox a 'int' primitive type but was returned null
[ERROR] : HyperloopProxy: at $Proxy0.hashCode(Unknown Source)
[ERROR] : HyperloopProxy: at java.util.Collections.secondaryHash(Collections.java:3405)
[ERROR] : HyperloopProxy: at java.util.HashMap.containsKey(HashMap.java:325)
[ERROR] : HyperloopProxy: at android.bluetooth.BluetoothAdapter.startLeScan(BluetoothAdapter.java:2189)
[ERROR] : HyperloopProxy: at android.bluetooth.BluetoothAdapter.startLeScan(BluetoothAdapter.java:2157)
[ERROR] : HyperloopProxy: at java.lang.reflect.Method.invoke(Native Method)
[ERROR] : HyperloopProxy: at java.lang.reflect.Method.invoke(Method.java:372)
[ERROR] : HyperloopProxy: at hyperloop.BaseProxy.invokeMethod(BaseProxy.java:133)
[ERROR] : HyperloopProxy: at hyperloop.BaseProxy.callNativeFunction(BaseProxy.java:115)
[ERROR] : HyperloopProxy: at org.appcelerator.kroll.runtime.v8.V8Object.nativeFireEvent(Native Method)
[ERROR] : HyperloopProxy: at org.appcelerator.kroll.runtime.v8.V8Object.fireEvent(V8Object.java:62)
[ERROR] : HyperloopProxy: at org.appcelerator.kroll.KrollProxy.doFireEvent(KrollProxy.java:918)
[ERROR] : HyperloopProxy: at org.appcelerator.kroll.KrollProxy.handleMessage(KrollProxy.java:1141)
[ERROR] : HyperloopProxy: at org.appcelerator.titanium.proxy.TiViewProxy.handleMessage(TiViewProxy.java:356)
[ERROR] : HyperloopProxy: at ti.modules.titanium.ui.widget.listview.ListViewProxy.handleMessage(ListViewProxy.java:312)
[ERROR] : HyperloopProxy: at android.os.Handler.dispatchMessage(Handler.java:98)
[ERROR] : HyperloopProxy: at android.os.Looper.loop(Looper.java:145)
[ERROR] : HyperloopProxy: at android.app.ActivityThread.main(ActivityThread.java:5951)
[ERROR] : HyperloopProxy: at java.lang.reflect.Method.invoke(Native M
[ERROR] : HyperloopProxy: at java.lang.reflect.Method.invoke(Method.java:372)
[ERROR] : HyperloopProxy: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
[ERROR] : HyperloopProxy: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
and Code to reproduce:
var BluetoothManager = require('android.bluetooth.BluetoothManager'),
BluetoothDevice = require('android.bluetooth.BluetoothDevice'),
Context = require('android.content.Context')
LeScanCallback = require('android.bluetooth.BluetoothAdapter.LeScanCallback'),
Activity = require('android.app.Activity'),
activity = new Activity(Ti.Android.currentActivity);
var bm = BluetoothManager.cast(activity.getSystemService(Context.BLUETOOTH_SERVICE));
var bluetoothAdapter = bm.getAdapter();
console.log(bluetoothAdapter.isEnabled());
var scanCallback = new LeScanCallback({
onLeScan : function(device, rssi, data) {
console.log('startScan');
}
});
bluetoothAdapter.startLeScan(scanCallback);
OK, so after a little thinking, I believe I know what is causing this. I couldn't test on an emulator because there's no bluetooth, but the stack trace jogged a few thoughts. My guess here its that the dynamic proxy class we're generating for the anonymous instance of the LeScanCallback is being asked for it's hashcode. Our dynamic function invoker looks for an override method in the JS code for any method, and if it doesn't exist, it returns a null return value. So in this case, it's looking for a hashcode method on the LeScanCallback, sees none and returns null - but hashcode has to return a primitive int; so Java chokes. So the question here is: what the heck should be do about the base Object methods: hashCode(), equals(), toString() on anonymous implementations of interfaces if the user doesn't provide a JS implementation? This may provide some clues: https://javaclippings.wordpress.com/2009/03/18/dynamic-proxies-equals-hashcode-tostring/
I understand what you explained, but I'm currently overriding the onLeScan method, or at least I think so. thanks for explanation!
[~bgarcia] The note was more to myself than anyone else. But basically, you may be able to workaround this in the meantime by also overriding the hashCode method in your LeScanCallback impl:
@Chris Williams thanks I'll try. Something out of the topic, how can I iterate over a java.Util.List?
Closing ticket as fixed. Verified that if I use the above code, I do not get a null pointer exception. Tested on: Appc CLI NPM: 4.2.7 Appc CLI Core: 5.4.0-37 Arrow: 1.8.2 SDK: 5.4.0.v20160801022303 Node: v4.4.7 OS: Mac OS X (10.11.6) Device: Samsung Galaxy S4 (4.4.4)
Forgot to mention that I used Hyperloop 1.2.5 along with my above stack.