首页 文章

在本地codenameone android build中包含cn1lib?

提问于
浏览
1

我目前正在考虑移植一个应用程序,我开始使用react-native to codenameone开发 . 为此,我仍在检查它的可行性和工作量(因为我必须移植或开发一些本地库绑定从react-native到codenameone,因为codenameone错过了我的一些需求,比如socket.io支持例) . 免费的codenameone构建 Cloud 服务仅限1Mb的应用程序,我必须在本地进行我的测试构建(只有几个测试类和使用谷歌 Map cn1lib,我的测试应用程序已经超过1Mb限制)可悲的是,那里没有关于如何执行本地构建的关于codenameone的免费文档,实际上我在互联网上找不到关于如何执行的任何说明(我在博客文章中找到了关于如何执行本地iOS的一些基本和弃用的说明为Android构建但没有任何东西) . 所以我不得不自己搞清楚......经过一段时间花在挖掘gradle配置参数上后,我终于成功构建了一个基本的codenameone app localy,可以在我的android测试设备上运行 . 但问题是,当我添加外部cn1lib(谷歌 Map 本机cn1lib https://github.com/codenameone/codenameone-google-maps)时,我的应用程序错误,当选择依赖于此lib的屏幕时 . 在android错误日志中,我可以找到此消息:

D/MyApplication(  551): [EDT] 0:0:0,99 - Exception: java.lang.ClassCastException - com.codename1.googlemaps.InternalNativeMapsImpl cannot be cast to com.codename1.system.NativeInterface
W/System.err(  551): java.lang.ClassCastException: com.codename1.googlemaps.InternalNativeMapsImpl cannot be cast to com.codename1.system.NativeInterface
W/System.err(  551):    at com.codename1.system.NativeLookup.create(Unknown Source)
W/System.err(  551):    at com.codename1.googlemaps.MapContainer.<init>(MapContainer.java:171)
W/System.err(  551):    at com.codename1.googlemaps.MapContainer.<init>(MapContainer.java:151)
W/System.err(  551):    at com.tbdlab.testapp.MyApplication.start(MyApplication.java:207)
W/System.err(  551):    at com.tbdlab.testapp.MyApplicationStub.run(MyApplicationStub.java:183)
W/System.err(  551):    at com.codename1.ui.Display.processSerialCalls(Unknown Source)
W/System.err(  551):    at com.codename1.ui.Display.mainEDTLoop(Unknown Source)
W/System.err(  551):    at com.codename1.ui.RunnableWrapper.run(Unknown Source)
W/System.err(  551):    at com.codename1.impl.CodenameOneThread$1.run(Unknown Source)
W/System.err(  551):    at java.lang.Thread.run(Thread.java:818)

我真的不明白为什么当我查看我编译的apk的dex文件时,无法将InternalNativeMapsImpl强制转换为NativeInterface,并且正确包含了来自google maps cn1lib的所有必需类(用于android)(所以我有 com.codenameone.googlemaps.InternalNativeMapscom.codenameone.googlemaps.InternalNativeMapsImplcom.codenameone.googlemaps.MapContainer )以及它们所依赖的codenameone本机接口类( com.codename1.system.NativeInterfacecom.codename1.impl.android.LifecycleListener ...) . 我对它们进行了反编译并且代码是正确的(无论如何我都不使用任何混淆方法,因此没有真正的理由说明编译后的代码与源代码有所不同) . 我可能在这里缺少一些使用cn1lib来构建本地codenameone的东西 .

那么有没有人已经成功使用执行本机绑定的cn1lib进行本地构建?如果是,那么确切的程序是什么?我真的希望有人能够提供帮助,因为,在这一点上,我正在认真考虑坚持使用本地反应(我很满意,但事实上它不是完全原生的)或跳入颤动(或者kotlin native)即使我仍然认为codenameone提供了许多优于其他解决方案的优势(但是在开发阶段不能执行本地构建对我来说完全禁止)

2 回答

  • 0

    如上所述,在我的一些测试中(我使用完整的cn1libs集合,我需要一些自定义库),我已经超过了1Mb限制(服务器因此而拒绝了我的测试版本) . 所以在开发阶段使用免费构建 Cloud 服务器对我来说不是一个选择(无论如何,如果我不确定我是否可以在必要时完全独立,我将不会使用解决方案 . 要进行我的发布版本,我肯定会订阅并且使用 Cloud buid服务器,因为它比推送本地服务器方便得多,而且我没有Mac电脑(我只有一个测试iphone),当我想制作一些iOS版本时需要借一个; ) . 但我想确保如果由于任何原因,您的服务消失,我仍然可以进行构建 . 此外,我没有看到在我的应用程序的开发阶段支付订阅的意义(这可能需要几个月),特别是因为我不确定我会使用codenameone作为最终解决方案(我仍然需要检查的数量)工作它将需要适应我已经拥有的一些libs作为反应原生的codenameone)) . 这就是我尝试进行本地构建的原因 .

    关于socket.io库,我已经开始创建一个将使用本机解决方案的cn1lib(https://github.com/socketio/socket.io-client-java用于android,https://github.com/socketio/socket.io-client-swift用于iOS,原始socket.io lib用于javascript) . 这不是一个真正的问题,如果我想从react-native切换,只是给出一个我必须在codenameone中创建的库的示例 .

    关于cn1lib如何工作,我已经想到了,我已经把我的android项目包含在cn1 google-maps lib的所有必要类中(所以我在main.zip,nativeand.zip和stubs.zip中包含了内容)正如已经说过的那样,在我生成的apk的.dex文件中检查它们实际上是否正确打包在它们中 . 所以我的问题似乎并不是我忘了在我的项目中包含一些cn1lib类而是其他东西 . 错误消息是: Exception: java.lang.ClassCastException - com.codename1.googlemaps.InternalNativeMapsImpl cannot be cast to com.codename1.system.NativeInterface 所以它确实不知道可能导致此问题的原因 . 我从这里拿了codenameone核心类https://github.com/codenameone/CodenameOne/tree/master/CodenameOne/src/ https://github.com/codenameone/CodenameOne/tree/master/Ports/Android http://github.com/codenameone/codenameone-skins

    将它们包含在我的项目中,所以我想我没有错过一个 . 在构建一个不使用cn1lib的项目时(就像一个简单的“hello word”应用程序),它编译并运行就好了android测试设备 . 问题是,当我的应用程序尝试创建googlemap视图时,它会返回强制转换异常(然后默认尝试创建一个html浏览器mapview并在此处失败,因为它缺少一些html文件) . 所以它可能是一个配置问题(编译器使用的java版本可能是一个问题,因为已经在cn1lib main.zip文件中编译的本机类文件?)这是我使用的gradle构建文件:

    buildscript {
            repositories {
                jcenter()
                maven { url "https://plugins.gradle.org/m2/" }
                google()
            }
            dependencies {
                classpath 'com.android.tools.build:gradle:3.0.1'
                classpath 'me.tatarka:gradle-retrolambda:3.2.0'
            }
        }
    
    
        allprojects {
            repositories {
                jcenter()
                google()
            }
        }
    
        apply plugin: 'com.android.application'
        apply plugin: 'me.tatarka.retrolambda'
    
    
        android {
            compileSdkVersion 26
            buildToolsVersion '26.0.2'
            dexOptions {
                // Prevent OutOfMemory with MultiDex during the build phase
                javaMaxHeapSize "4g"
            }
            lintOptions {
                checkReleaseBuilds false
            }
    
            defaultConfig {
                applicationId "com.tbdlab.testapp"
                minSdkVersion 15
                targetSdkVersion 23
                versionCode 1
                versionName "1.0"
                multiDexEnabled true
                testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            }
            buildTypes {
                release {
                    minifyEnabled false
                    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' //my proguard files are actually empty so no obfuscation is performed. I checked it in the generated apk
                }
            }
            compileOptions {
                sourceCompatibility JavaVersion.VERSION_1_7//.VERSION_1_8
                targetCompatibility JavaVersion.VERSION_1_7//.VERSION_1_8
            }
        }
        dependencies {
            compile fileTree(include: ['*.jar'], dir: 'libs')
            androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
                    exclude group: 'com.android.support', module: 'support-annotations'
                })
            compile 'com.google.android.gms:play-services:9.4.0' //compile 'com.google.android.gms:play-services-maps:11.8.0'
            compile 'com.android.support:multidex:1.0.1'
        }
    

    这是我的AndroidManifest.xml文件,其中包含了cn1lib中定义的所有权限:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> 
    <!--- Permissions requiered by the google maps cn1lib -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> 
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
    
    <application android:allowBackup="true"  android:icon="@drawable/icon" android:label="MyApplication" android:name="android.support.multidex.MultiDexApplication">
    <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
    <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="...masked_it_but_put_my_correct_key_here..."/>
    <activity  android:label="MyApplication" android:launchMode="singleTop" android:name="com.tbdlab.testapp.MyApplicationStub" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <receiver android:name="com.codename1.impl.android.LocalNotificationPublisher"/>
    <service android:exported="false" android:name="com.codename1.impl.android.BackgroundFetchHandler"/>
    <activity android:name="com.codename1.impl.android.CodenameOneBackgroundFetchActivity" android:theme="@android:style/Theme.NoDisplay"/>
    <activity android:name="com.codename1.location.CodenameOneBackgroundLocationActivity" android:theme="@android:style/Theme.NoDisplay"/>
    <service android:exported="false" android:name="com.codename1.location.BackgroundLocationHandler"/>
    <service android:exported="false" android:name="com.codename1.location.GeofenceHandler"/>
    <service android:exported="false" android:name="com.codename1.media.AudioService"/>
    <activity android:excludeFromRecents="true" android:exported="false" android:name="com.google.android.gms.auth.api.signin.internal.SignInHubActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
    <provider android:authorities="com.tbdlab.testapp.google_measurement_service" android:exported="false" android:name="com.google.android.gms.measurement.AppMeasurementContentProvider"/>
    <receiver android:enabled="true" android:name="com.google.android.gms.measurement.AppMeasurementReceiver">
        <intent-filter>
            <action android:name="com.google.android.gms.measurement.UPLOAD"/>
        </intent-filter>
    </receiver>
    <service android:enabled="true" android:exported="false" android:name="com.google.android.gms.measurement.AppMeasurementService"/>
    <activity android:name="com.google.android.gms.ads.AdActivity" android:theme="@android:style/Theme.Translucent"/>
    

    我真的没有看到什么会导致强制转换异常,但由于缺乏关于如何创建本地构建的基本教程,我可能会错过一些甚至不知道它...

    对于这个测试我做了一个非常简单的应用程序,而不是只显示一个原生的谷歌 Map ,它在模拟器中正确运行,并在构建 Cloud 服务器上编译,并在我的Android测试设备中运行良好 . 所以问题是我的gradle构建配置(或者可能是AndroidManifest.xml文件,即使我认为它对JVM没有任何影响),也可能是我的android项目中包含的codenameone核心和cn1lib本地构建 .

  • 0

    1mb是巨大的,因为它可以适应完整的谷歌 Map 应用程序和更多 . 它映射到 jar 的编译大小,从6kb开始 . 整个cn1lib(只有一部分被打包)是40kb . 所以我建议使用构建服务器进行测试 .

    几年前,Steve为使用本机接口 Build 了一些支持here . 在我们雇用他之后,由于时间和需求不足(不是因为我们告诉他或类似的东西),他停止了维护 . 我不确定它的状态,但您可以将其用作本机接口如何工作的参考 .

    还有this plugindirect link here)我个人没试过 .

    通常是直接调用本机实现的 native interface generates an intermediate class . 除了Java SE之外的所有平台的本机实现都不是't implement the native interface and shouldn' t . 我想我在文档中的某处解释过,但在谷歌 Map 的情况下再次解释它是非常容易的 .

    这是来自本机界面的方法:

    public PeerComponent createNativeMap(int mapId);
    

    这与Android实现类的方法相同:

    public android.view.View createNativeMap(int mapId);
    

    正如您所看到的,返回值不同,我们需要将其包装在对等组件中以抽象该行为 . 通过避免继承和转换,我们可以灵活地制作更合理的原生API .

    这是我们的构建服务器为 Map 生成的类,因为您可以看到它只是“粘合代码”:

    package com.codename1.googlemaps;
    
    import com.codename1.ui.PeerComponent;
    
    public class InternalNativeMapsStub implements InternalNativeMaps{
        private InternalNativeMapsImpl impl = new InternalNativeMapsImpl();
    
        public void setShowMyLocation(boolean param0) {
            impl.setShowMyLocation(param0);
        }
    
        public void setRotateGestureEnabled(boolean param0) {
            impl.setRotateGestureEnabled(param0);
        }
    
        public void setMapType(int param0) {
            impl.setMapType(param0);
        }
    
        public int getMapType() {
            return impl.getMapType();
        }
    
        public int getMaxZoom() {
            return impl.getMaxZoom();
        }
    
        public int getMinZoom() {
            return impl.getMinZoom();
        }
    
        public long addMarker(byte[] param0, double param1, double param2, String param3, String param4, boolean param5) {
            return impl.addMarker(param0, param1, param2, param3, param4, param5);
        }
    
        public void addToPath(long param0, double param1, double param2) {
            impl.addToPath(param0, param1, param2);
        }
    
        public long finishPath(long param0) {
            return impl.finishPath(param0);
        }
    
        public void removeMapElement(long param0) {
            impl.removeMapElement(param0);
        }
    
        public void removeAllMarkers() {
            impl.removeAllMarkers();
        }
    
        public PeerComponent createNativeMap(int param0) {
            return PeerComponent.create(impl.createNativeMap(param0));
        }
    
        public void setPosition(double param0, double param1) {
            impl.setPosition(param0, param1);
        }
    
        public void calcScreenPosition(double param0, double param1) {
            impl.calcScreenPosition(param0, param1);
        }
    
        public int getScreenX() {
            return impl.getScreenX();
        }
    
        public int getScreenY() {
            return impl.getScreenY();
        }
    
        public void calcLatLongPosition(int param0, int param1) {
            impl.calcLatLongPosition(param0, param1);
        }
    
        public double getScreenLat() {
            return impl.getScreenLat();
        }
    
        public double getScreenLon() {
            return impl.getScreenLon();
        }
    
        public void deinitialize() {
            impl.deinitialize();
        }
    
        public float getZoom() {
            return impl.getZoom();
        }
    
        public void setZoom(double param0, double param1, float param2) {
            impl.setZoom(param0, param1, param2);
        }
    
        public double getLatitude() {
            return impl.getLatitude();
        }
    
        public double getLongitude() {
            return impl.getLongitude();
        }
    
        public long beginPath() {
            return impl.beginPath();
        }
    
        public void initialize() {
            impl.initialize();
        }
    
        public boolean isSupported() {
            return impl.isSupported();
        }
    
    }
    

    关于socket.io,您可以通过调用 BrowserComponent 来包装JavaScript版本,以获取本机JS代码作为开始 . 完整的原生端口可以在以后出现 .

    看起来你有cn1libs想出来,但只是为了完整性这是他们应该如何工作:

    cn1lib只是一个zip文件,包含每个平台的其他zip文件 . 刷新lib解压缩,并将文件排列在lib / impl下的相应目录中 . 因此,您需要打包与您尝试使用您的发行版编译的平台匹配的lib / impl目录 .

    cn1libs还包括两个附加属性文件codenameone_library_appended.propertiescodenameone_library_required.properties . 刷新库将通过将这些值设置为构建提示自动为您处理 . 前面的值附加到现有的构建提示,后者覆盖现有的构建提示 .

    构建提示有效地告诉构建服务器如何编译一些东西,例如如果我们想将东西注入plist,manifest等,那么这个映射到本地构建的方式会有很大差异 . 在某些情况下,例如plistInject,理解它是微不足道的,但其他情况可能很奇怪 . 如果您对特定构建提示如何映射到本地构建有疑问,那么您可以问这个问题 .

相关问题