我正试图在 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 上。
最佳答案
我们在 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/