[TIMOB-24275] Hyperloop: Android - Class not found/Cannot read property 'newInstance' of null (Regression)
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | Critical |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2017-03-09T12:59:43.000+0000 |
Affected Version/s | Hyperloop 2.0.0 |
Fix Version/s | Hyperloop 2.0.1 |
Components | Hyperloop |
Labels | android, hyperloop, qe-hyperloop, regression |
Reporter | Reymundo López |
Assignee | Christopher Williams |
Created | 2017-01-04T23:10:55.000+0000 |
Updated | 2017-03-17T21:38:22.000+0000 |
Description
Same aar is not working in hyperloop 2.0.0 while is working fine in 1.2.8.
I have added an aar and setup everything like I did in my native app, this is how the hyperloop file looks like:
var Configuration = require('io.ridetap.Configuration');
var builder = new Configuration.Builder();
Using hyperloop 1.2.8, the code above works without any problem, but starting in hyperloop 2.0.0 it crash saying Class 'io.ridetap.Configuration$Builder' not found
.
The only differences I see in the generated files for each class is that the path is different:
In hyperloop 1.2.8 the call is this: var parentPackage = require('io.ridetap');
while in hyperloop 2.0.0 is this var parentPackage = require('./io.ridetap');
The full log of the error is this:
ERROR] HyperloopUtil: (main) [5188,5188] Class 'io.ridetap.Configuration$Builder' not found
[ERROR] HyperloopUtil: java.lang.ClassNotFoundException: io.ridetap.Configuration$Builder
[ERROR] HyperloopUtil: at java.lang.Class.classForName(Native Method)
[ERROR] HyperloopUtil: at java.lang.Class.forName(Class.java:400)
[ERROR] HyperloopUtil: at java.lang.Class.forName(Class.java:326)
[ERROR] HyperloopUtil: at hyperloop.HyperloopModule.getJavaClass(HyperloopModule.java:247)
[ERROR] HyperloopUtil: at hyperloop.ProxyFactory.newClass(ProxyFactory.java:81)
[ERROR] HyperloopUtil: at hyperloop.HyperloopModule.getClass(HyperloopModule.java:137)
[ERROR] HyperloopUtil: at org.appcelerator.kroll.runtime.v8.V8Object.nativeFireEvent(Native Method)
[ERROR] HyperloopUtil: at org.appcelerator.kroll.runtime.v8.V8Object.fireEvent(V8Object.java:62)
[ERROR] HyperloopUtil: at org.appcelerator.kroll.KrollProxy.doFireEvent(KrollProxy.java:872)
[ERROR] HyperloopUtil: at org.appcelerator.kroll.KrollProxy.handleMessage(KrollProxy.java:1095)
[ERROR] HyperloopUtil: at org.appcelerator.titanium.proxy.TiViewProxy.handleMessage(TiViewProxy.java:354)
[ERROR] HyperloopUtil: at android.os.Handler.dispatchMessage(Handler.java:98)
[ERROR] HyperloopUtil: at android.os.Looper.loop(Looper.java:154)
[ERROR] HyperloopUtil: at android.app.ActivityThread.main(ActivityThread.java:6119)
[ERROR] HyperloopUtil: at java.lang.reflect.Method.invoke(Native Method)
[ERROR] HyperloopUtil: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
[ERROR] HyperloopUtil: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
[ERROR] HyperloopUtil: Caused by: java.lang.ClassNotFoundException: Didn't find class "io.ridetap.Configuration$Builder" on path: DexPathList[[zip file "/data/app/com.unosquare.p.moovel.rideTapTest-2/base.apk"],nativeLibraryDirectories=[/data/app/com.unosquare.p.moovel.rideTapTest-2/lib/x86, /system/fake-libs, /data/app/com.unosquare.p.moovel.rideTapTest-2/base.apk!/lib/x86, /system/lib, /vendor/lib]]
[ERROR] HyperloopUtil: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
[ERROR] HyperloopUtil: at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
[ERROR] HyperloopUtil: at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
[ERROR] HyperloopUtil: ... 17 more
[ERROR] TiExceptionHandler: (main) [11,5199] ----- Titanium Javascript Runtime Error -----
[ERROR] TiExceptionHandler: (main) [0,5199] - In /hyperloop/io.ridetap.Configuration$Builder.js:1,373
[ERROR] TiExceptionHandler: (main) [0,5199] - Message: Uncaught TypeError: Cannot read property 'newInstance' of null
[ERROR] TiExceptionHandler: (main) [0,5199] - Source: (function (exports, require, module, __filename, __dirname, Titanium, Ti, global, kroll) {var Hyperloop=require("hyperloop"),EnclosingClass=require("./io.ridetap.Configuration"),Builder=function(){var e;e=1==arguments.length&&arguments[0].isNativeProxy&&arguments[0].isInstanceProxy&&arguments[0].isInstanceOf("io.ridetap.Configuration$Builder")?arguments[0]:Builder.class.newInstance(arguments),this.$native=e,this._hasPointer=null!=e,this._private={}},SuperClass=require("./java.lang.Object");Builder.prototype=Object.create(SuperClass.prototype),Builder.prototype.constructor=Builder,Object.defineProperty(Builder.prototype,"super",{get:function(){return this._hasPointer?new Builder(this.$native.super):null},enumerable:!0}),Builder.className="io.ridetap.Configuration$Builder",Builder.prototype.className="io.ridetap.Configuration$Builder",Object.defineProperty(Builder,"class",{get:function(){return Hyperloop.getClass("io.ridetap.Configuration$Builder")},enumerable:!0,configurable:!1}),Builder.extend=function(e){var t=Hyperloop.extend("io.ridetap.Configuration$Builder"),n=function(){function n(e){if(e.apiName&&e.isNativeProxy&&e.isInstanceProxy){var t=require("./"+e.apiName);return new t(e)}return e}function r(){for(var e=[],t=0;t<arguments.length;t++)e[t]=n(arguments[t]);return e}var i=t.newInstance(arguments),a=this,o=e,u={};Object.keys(o).forEach(function(e){a[e]=function(){return o[e].apply(a,arguments)},u[e]=function(){return a[e].apply(a,r.apply(this,arguments))}}),i.setOverrides(u),this.$native=i,this._hasPointer=null!=i,this._private={}};return n.prototype=Object.create(Builder.prototype),n.prototype.constructor=n,n},Builder.cast=function(e){return e.$native&&e.$native.isInstanceProxy?new Builder(Hyperloop.cast("io.ridetap.Configuration$Builder",e.$native)):e},Builder.prototype.with=function(){if(!this._hasPointer)return null;var e=this.$native.callNativeFunction({func:"with",instanceMethod:!0,args:Array.prototype.slice.call(arguments)});if(null==e)return null;if(e.apiName){if("io.ridetap.Configuration$Builder"===e.apiName)return new Builder(e);var t=require("./"+e.apiName);return new t(e)}return e},Builder.prototype.setRequiresID=function(){if(!this._hasPointer)return null;var e=this.$native.callNativeFunction({func:"setRequiresID",instanceMethod:!0,args:Array.prototype.slice.call(arguments)});if(null==e)return null;if(e.apiName){if("io.ridetap.Configuration$Builder"===e.apiName)return new Builder(e);var t=require("./"+e.apiName);return new t(e)}return e},Builder.prototype.apiKey=function(){if(!this._hasPointer)return null;var e=this.$native.callNativeFunction({func:"apiKey",instanceMethod:!0,args:Array.prototype.slice.call(arguments)});if(null==e)return null;if(e.apiName){if("io.ridetap.Configuration$Builder"===e.apiName)return new Builder(e);var t=require("./"+e.apiName);return new t(e)}return e},Builder.prototype.setRequiresPayment=function(){if(!this._hasPointer)return null;var e=this.$native.callNativeFunction({func:"setRequiresPayment",instanceMethod:!0,args:Array.prototype.slice.call(arguments)});if(null==e)return null;if(e.apiName){if("io.ridetap.Configuration$Builder"===e.apiName)return new Builder(e);var t=require("./"+e.apiName);return new t(e)}return e},Builder.prototype.setLogLevel=function(){if(!this._hasPointer)return null;var e=this.$native.callNativeFunction({func:"setLogLevel",instanceMethod:!0,args:Array.prototype.slice.call(arguments)});if(null==e)return null;if(e.apiName){if("io.ridetap.Configuration$Builder"===e.apiName)return new Builder(e);var t=require("./"+e.apiName);return new t(e)}return e},Builder.prototype.build=function(){if(!this._hasPointer)return null;var e=this.$native.callNativeFunction({func:"build",instanceMethod:!0,args:Array.prototype.slice.call(arguments)});if(null==e)return null;if(e.apiName){if("io.ridetap.Configuration$Builder"===e.apiName)return new Builder(e);var t=require("./"+e.apiName);return new t(e)}return e},Builder.prototype.setMapMode=function(){if(!this._hasPointer)return null;var e=this.$na
[ERROR] V8Exception: Exception occurred at /hyperloop/io.ridetap.Configuration$Builder.js:1: Uncaught TypeError: Cannot read property 'newInstance' of null
[ERROR] EGL_emulation: tid 7039: eglSurfaceAttrib(1174): error 0x3009 (EGL_BAD_MATCH)
Hmm, the underlying issue is a ClassNotFoundException suddenly, which means the JVM can't find the class io.ridetap.Configuration$Builder at runtime. Did the AAR get updated recently and maybe is using a new API? Or perhaps we broke grabbing the jar inside jars somehow? [~reymundolopez] It'd be useful to have a copy of the AAR in question for us to test and reproduce this issue with.
Hey @Chris Williams, is there a way that I can send you the aar in a non public way?.
[~reymundolopez] You can email it to me at cwilliams@axway.com
[~reymundolopez] Actually, apparently the corporate mail server decided to filter it out. Please try again at chris.a.williams@gmail.com, thanks!
@Chris Williams no problem, let me send it to you again.
It's been 2 months since Hyperloop 2.0.0 was released and no one can use it in Android because of this major bug. Have you ever thought of posting a public note telling people that they are paying 99$ a month for something that doesn't work? Because that would make a lot of sense.
I'm starting to dive deeper into this, but it's likely not caused by the PR Hans mentioned. Once you decompile the apk's classes you find that the io.ridetap.Configuration and io.ridetap.Configuration$Builder classes aren't in the APK's classes.dex. So the underlying error is right - it can't load a class which isn't there. The question is why this is happening. Maybe multidex is somehow causing it? Maybe the inconspicuous change here is the cause: https://github.com/appcelerator/hyperloop.next/commit/c704f700041b3ea665188c86cf681774320e67d7 ? Not sure, I need to do some more testing...
OK, narrowed this down. Basically Android will complain if you have identical JARs in the class path/APK. We added a partial implementation of filtering out duplicates. We only check the jar base name and extension. The issue is that if you use multiple AARs, they all just hold a "classes.jar" inside, so there will be clashes using that simple criteria. I had a placeholder TODO to add sha1/size checking as well, but never got around to it. That's the cause of the issue here - the RideTap's classes.jar is getting filtered out as a duplicate because of the classes.jar name clash with another AAR. I guess I need to actually add SHA1/size checks now.
All right, here's a PR for 2_0_X branch: https://github.com/appcelerator/hyperloop.next/pull/129 I modified the code to basically skip duplicate JAR checks on 'classes.jar' files (since the name is always a clash, due to AARs needing to have this specific filename inside). For other JARs I now check the base name and SHA1 to remove duplicates and/or error out with details on conflicting JARs with the same name and different hash (asking the user to delete or rename one to resolve it manually).
https://github.com/appcelerator/hyperloop.next/pull/129
PR (master): https://github.com/appcelerator/hyperloop.next/pull/130
Also having this same issue. Worked fine with Hyperloop <2.0 now java.lang.ClassNotFoundException . Mine is for var Intercom = require("io.intercom.android.sdk.Intercom"); How do we install Hyperloop 2.0.1 manually to test your resolution?
[~jerodfritz] Hyperloop 2.0.1 will be pushed together with SDK 6.0.3 next week on Friday.
[~hansknoechel] Samir and I tried this using Hyperloop 2.0.0. and 2.0.1 and saw the same behavior. We saw the splash screen, but no errors or crash. Can you please let us know whether we ran the test correctly. We were expecting to reproduce the bug using 2.0.0, but didn't. We added 2 files to platform/android and used the two-line demo code in app.js.
Verified as fixed, Classes are now found and
./
before a module is no longer required. Tested on: {noformat} macOS Sierra 10.12.3 Pixel Xl (7.1.1) android emulator (4.4.2, 6.0) Ti SDK: 6.0.3.v20170317093820 Hyperloop: 2.0.1 Appc CLI: 6.1.0 Appc NPM: 4.2.9-1 Node v4.6.0 {noformat} *Closing Ticket.*