Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-23165] Hyperloop: Android Anonymous interface implementations need to have default implementations of equals/hashCode/toString

GitHub Issuen/a
TypeBug
PriorityCritical
StatusClosed
ResolutionFixed
Resolution Date2016-04-13T16:36:17.000+0000
Affected Version/sn/a
Fix Version/sRelease 5.4.0
ComponentsHyperloop
Labelsandroid, hyperloop
ReporterBrian García
AssigneeChristopher Williams
Created2016-04-07T09:41:28.000+0000
Updated2016-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);

Comments

  1. Christopher Williams 2016-04-07

    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/
  2. Brian García 2016-04-07

    I understand what you explained, but I'm currently overriding the onLeScan method, or at least I think so. thanks for explanation!
  3. Christopher Williams 2016-04-07

    [~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:
       var scanCallback = new LeScanCallback({
           onLeScan : function(device, rssi, data) {
               console.log('startScan');
           },
           hashCode: function () {
               return 123; // any int should work
           }
       });
       
  4. Brian García 2016-04-07

    @Chris Williams thanks I'll try. Something out of the topic, how can I iterate over a java.Util.List?
  5. Wilson Luu 2016-08-01

    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)
  6. Wilson Luu 2016-08-01

    Forgot to mention that I used Hyperloop 1.2.5 along with my above stack.

JSON Source