java - 如何将 Mapbox GL Native Android Activity 示例 Java 应用程序翻译成 NativeScript?

标签 java android nativescript mapbox

我正试图在 NativeScript Mapbox plugin 中追踪一个令人讨厌的崩溃错误.在 Android 上,使用该插件构建的任何应用程序都会在 Resume 时崩溃。

为了排除 Mapbox GL Native Android 库中的错误,我按照安装步骤创建了一个 very simple example app in Java that loads and displays a map .

无论我暂停和恢复多少次,该示例应用程序都不会崩溃。

我注意到 NativeScript Mapbox 插件似乎没有调用推荐的 Mapbox 生命周期 Hook ,并且在 Mapbox native 问题列表中报告了许多崩溃,答案是“遵循生命周期 Hook 指南”。

所以我的下一个想法是看看我是否可以按照推荐的生命周期 Hook 将 Java 代码直接翻译成 NativeScript(就像在示例应用程序中所做的那样)。这样我就可以确定崩溃是因为生命周期 Hook 没有被正确调用还是因为一些更深奥的 NativeScript 问题。

工作的 Java Activity 是:

package com.amapboxtest.mapboxtest;

import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;

import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.d( "test","onCreate()");

        Mapbox.getInstance(this, "MAPBOX_ACCESS_TOKEN_HERE");

        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        mapView = findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(@NonNull MapboxMap mapboxMap) {

                Log.d( "test","onMapReady()");

                mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded() {
                    @Override
                    public void onStyleLoaded(@NonNull Style style) {

                        Log.d( "test", "onStyleLoaded()");

                    // Map is set up and the style has loaded. Now you can add data or make other map adjustments


                    }
                });
            }
        });

    }

    @Override
    public void onStart() {

        Log.d( "test", "onStart()");

        super.onStart();
        mapView.onStart();
    }

    @Override
    public void onResume() {

        Log.d( "test", "onResume");

        super.onResume();
        mapView.onResume();
    }

    @Override
    public void onPause() {
        Log.d( "test", "onPause");

        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onStop() {

        Log.d( "test", "onStop");

        super.onStop();
        mapView.onStop();
    }

    @Override
    public void onLowMemory() {

        Log.d( "test", "onLowMemory()");

        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {

        Log.d( "test","onDestroy");

        super.onDestroy();
        mapView.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {

        Log.d( "test", "onSaveInstanceState()");

        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
} 

我对 NativeScript 翻译的初步尝试:

/**
*
* @link https://github.com/NativeScript/android-runtime/issues/981
*/

import {setActivityCallbacks, AndroidActivityCallbacks} from "tns-core-modules/ui/frame";

import * as application from "tns-core-modules/application";

declare const com, java, org;

@JavaProxy("com.amapboxtest.MainActivity")
class Activity extends android.support.v7.app.AppCompatActivity {
    public isNativeScriptActivity;

    private _callbacks: AndroidActivityCallbacks;

    private mapView: any;

    public onCreate(savedInstanceState: android.os.Bundle): void {

      console.log( "Activity::onCreate()" );

      this.isNativeScriptActivity = true;

      if (!this._callbacks) {
        setActivityCallbacks(this);
      }

      this._callbacks.onCreate(this, savedInstanceState, super.onCreate );

      console.log( "Activity::onCreate(): after _callbacks.onCreate()" );

      let layout;
      let resourceId;

      console.log( "Activity::onCreate(): before getting layout" );

      // this fails.

      try {
        layout = this.getResources().getIdentifier( "activity_main", "layout", this.getPackageName() );
      } catch( e ) {
        console.error( "Unable to get layout:", e );
        throw e;
      }

      this.setContentView(layout);

      console.log( "Activity::onCreate(): before getting resourceId" );

      try {
        resourceId = this.getResources().getIdentifier( "mapView", "id", this.getPackageName() );
      } catch( e ) {
        console.error( "Unable to get resourceId:", e );
        throw e;
      }

      this.mapView = this.findViewById( resourceId );

      console.log( "Activity::onCreate(): after findViewById()" );

      this.mapView.onCreate( savedInstanceState ); 

      console.log( "Activity::onCreate(): after this.mapView.onCreate( savedInstanceState" );

      com.mapbox.mapboxsdk.Mapbox.getInstance( application.android.context, 'SET_ACCESS_TOKEN_HERE' );

      console.log( "Activity::onCreate(): after getInstance()" );

      // modelled after mapbox.android.ts in the Nativescript-Mapbox plugin.

      this.mapView.getMapAsync(
        new com.mapbox.mapboxsdk.maps.OnMapReadyCallback({
          onMapReady: mapboxMap => {

            console.log( "onMapReady()");

            this.mapView.addOnDidFinishLoadingStyleListener(
              new com.mapbox.mapboxsdk.maps.MapView.OnDidFinishLoadingStyleListener({
                onDidFinishLoadingStyle : style => {

                  console.log( "style loaded" );

                }
              })
            );

            let builder = new com.mapbox.mapboxsdk.maps.Style.Builder();

            const Style = com.mapbox.mapboxsdk.constants.Style;

            mapboxMap.setStyle( 
              builder.fromUrl( Style.LIGHT )
            );
          } 
        })
      );

    } // end of onCreate()

    // -------------------------------------------------------

    public onSaveInstanceState(outState: android.os.Bundle): void {

        console.log( "Activity::onSaveInstanceState()" );

        this._callbacks.onSaveInstanceState(this, outState, super.onSaveInstanceState);

        this.mapView.onSaveInstanceState( outState );

    }

    // -------------------------------------------------------

    public onStart(): void {

        console.log( "Activity::onStart()" );

        this._callbacks.onStart(this, super.onStart);

        this.mapView.onStart();
    }

    // -------------------------------------------------------

    public onStop(): void {

        console.log( "Activity::onStop()" );

        this._callbacks.onStop(this, super.onStop);

        this.mapView.onStop();
    }

    // -------------------------------------------------------

    public onDestroy(): void {

        console.log( "Activity::onDestroy()" );

        this._callbacks.onDestroy(this, super.onDestroy);

        this.mapView.onDestroy();
    }

    // -------------------------------------------------------

    public onBackPressed(): void {

        console.log( "Activity::onBackPressed()" );

        this._callbacks.onBackPressed(this, super.onBackPressed);
    }

    // -------------------------------------------------------

    public onRequestPermissionsResult(requestCode: number, permissions: Array<string>, grantResults: Array<number>): void {

        console.log( "Activity::onCRequestPermissionResult()" );

        this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined /*TODO: Enable if needed*/);
    }

    // -------------------------------------------------------

    public onActivityResult(requestCode: number, resultCode: number, data: android.content.Intent): void {

        console.log( "Activity::onActivityResult()" );

        this._callbacks.onActivityResult(this, requestCode, resultCode, data, super.onActivityResult);
    }
}

// END

我从 Java 应用复制了 gradle 依赖项并将它们添加到 App_Resources/Android/app.gradle。

我还复制了 app/src/main/res/layout/activity_main.xml 和 content_main.xml 文件。

最初,它会在 content_main.xml 上出错,因为 Nativescript 显然不支持 android.support.constraint.ConstraintLayout。因此,根据我在 NativeScript Mapbox 插件中看到的内容,我将其更改为 android.widget.FrameLayout。我在 Java 应用程序中对此进行了测试,它似乎可以正常工作。

但是,现在我陷入了一个我不理解的运行时异常:

System.err: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.amapboxtest.nsmapboxtest/com.amapboxtest.MainActivity}: com.tns.NativeScriptException: 
System.err: Calling js method onCreate failed
System.err: 
System.err: Error: android.view.InflateException: Binary XML file line #9: Binary XML file line #12: Error inflating class com.mapbox.mapboxsdk.maps.MapView
System.err: Caused by: android.view.InflateException: Binary XML file line #12: Error inflating class com.mapbox.mapboxsdk.maps.MapView
System.err: Caused by: java.lang.reflect.InvocationTargetException
System.err:     java.lang.reflect.Constructor.newInstance0(Native Method)
System.err:     java.lang.reflect.Constructor.newInstance(Constructor.java:334)
System.err:     android.view.LayoutInflater.createView(LayoutInflater.java:647)

它在 Activity 的这一行死亡:

layout = this.getResources().getIdentifier( "activity_main", "layout", this.getPackageName() );

NativeScript 显然不公开 R.id,因此调用 getResources()。

我是 NativeScript 和原生 Android 开发的新手,我不得不深入研究杂草,但我需要解决这个崩溃问题,让这个简单的翻译工作是下一个关键步骤。

任何关于我在将此示例翻译成 NativeScript 时做错了什么的指导将不胜感激。我必须想象我遗漏了一些简单的东西。

我已将有效的 Java 示例和损坏的 NativeScript 示例放在 Github 上。

Java MapboxTest Repository

NativeScript NsMapboxTest Repository

最佳答案

我们在 NativeScript 中没有 activity_main.xml。您的 UI 是使用 NativeScript 组件使用 XML/JavaScript 构建的。要初始化 UI,您必须执行 this与原始实现一样,否则您将失去框架内提供的所有核心导航功能。

 appModule.android.init(this.getApplication());

然后您可以在放置 map 框 View 的根组件中监听 onCreate Activity 事件,使用 getViewById 获取引用,然后访问 nativeView 属性获取实际的原生 mapbox View 。

关于java - 如何将 Mapbox GL Native Android Activity 示例 Java 应用程序翻译成 NativeScript?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55446126/

相关文章:

java - 如何获取 JDBC 中的行数?

扫描仪中的 Java 正则表达式

java - 使用流收集返回相同的列表,对重复项进行切割和求和,抛出非静态引用

javascript - 为什么 Promise 记录数据但返回相同数据的未定义

node.js - npm install -g @vue/cli @vue/cli-init 错误

java - Android Studio 模拟器 - Java 实例不支持 32 位 JVM

android - ListView 内的 Horizo​​ntalScrollView : minor vertical scroll stops horizontal scroll

android - onItemSelected() 嵌套开关...如何避免它?

android - 从 android 中的 wsdl 生成客户端代码

ios - 如何在 iOS 的 Nativescript 中调用 NSLog