我在Android上使用ARToolkit库,通过Android NDK使用本机代码(在本例中为C ++代码)。我在我的ARMovieActivity中声明了C ++方法,它们在不同的情况下被调用。该应用程序的目标是检测NFT标记,图像,并在屏幕上显示一些按钮。在ARMovie.cpp中,有一个方法可以在摄像机的每个帧上调用,验证是否有任何标记可见,如果找到任何标记,它会从Java类调用一个方法。我的问题是,有时(我不知道如何)ID和ARMovie.cpp中对Java类的引用只是null,我打开Activity,大多数时候引用和ID都没问题,并且然后该方法被正确调用,但即使没有关闭或放置Activity在后台,它们只是转为null并且方法不会被调用。我只是不知道该怎么做。
在我的情况下,我正在从Firebase下载NFT标记,当完成后,我初始化相机,并调用加载标记的C ++方法,在此方法中:
public void baixaMarkers(final Activity tela){
pathMarkers = new File(Environment.getExternalStorageDirectory(), "ARMarkers");
if(!pathMarkers.exists()) pathMarkers.mkdirs();
limpaPasta(pathMarkers);
firebaseDatabaseReference
.child("ar")
.orderByChild("idEmpresa")
.equalTo(ControleIds.getEmpresaId())
.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue() == null){
ARMovieActivity.loadingDialog.dismiss();
return;
}
try {
FileOutputStream outputStream = new FileOutputStream(new File(pathMarkers, "markers.dat"));
outputStream.write((Integer.toString((int)dataSnapshot.getChildrenCount()) + "\n\n").getBytes());
for(DataSnapshot child : dataSnapshot.getChildren()){
String fset, fset3, iset;
long idEmpresa, idProduto;
idEmpresa = (long)child.child("idEmpresa").getValue();
idProduto = (long)child.child("idProduto").getValue();
fset = String.valueOf(child.child("fset").getValue());
fset3 = String.valueOf(child.child("fset3").getValue());
iset = String.valueOf(child.child("iset").getValue());
byte[] arquivoBytes = Base64.decode(fset, 0);
FileOutputStream os = new FileOutputStream(pathMarkers + "/" + idEmpresa + "_" + idProduto + ".fset", true);
os.write(arquivoBytes);
os.flush();
os.close();
arquivoBytes = Base64.decode(fset3, 0);
os = new FileOutputStream(pathMarkers + "/" + idEmpresa + "_" + idProduto + ".fset3", true);
os.write(arquivoBytes);
os.flush();
os.close();
arquivoBytes = Base64.decode(iset, 0);
os = new FileOutputStream(pathMarkers + "/" + idEmpresa + "_" + idProduto + ".iset", true);
os.write(arquivoBytes);
os.flush();
os.close();
outputStream.write((idEmpresa + "_" + idProduto + "\n").getBytes());
outputStream.write("NFT\n\n".getBytes());
}
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
ARMovieActivity.loadingDialog.dismiss();
((ARMovieActivity)tela).carregouMarkers();
ARMovieActivity.nativeCreate(tela, pathMarkers.getPath() + "/markers.dat");
((ARMovieActivity)(tela)).configuraGL();
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
ARMovieActivity.nativeCreate()是加载标记的C ++方法。 configuraGL()是初始化摄像机并将引用传递给稍后将用于显示按钮的类的Java方法,它看起来像这样:
public void configuraGL(){
ConnectivityManager cm = (ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
nativeSetInternetState(isConnected ? 1 : 0);
camSurface = new CameraSurface(this);
glView = new GLSurfaceView(this);
Renderer r = new Renderer();
r.setBotoesController(botoesController);
glView.setRenderer(r);
glView.setZOrderMediaOverlay(true);
mainLayout.addView(camSurface, new LayoutParams(128, 128));
mainLayout.addView(glView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
nativeMovieInit(botoesController, new WeakReference<>(botoesController));
if (glView != null) glView.onResume();
}
nativeMovieInit()是在ARMovie.cpp中创建BotoesController的全局引用的C ++方法,它看起来像这样:
JNIEXPORT void JNICALL JNIFUNCTION_NATIVE(nativeMovieInit(JNIEnv* env, jobject obj, jobject movieControllerThis, jobject movieControllerWeakThis)) {
#ifdef DEBUG
LOGI("nativeMovieInit()\n");
#endif
if (!movieControllerThis || !movieControllerWeakThis) {
LOGE("Error: natitveMovieInit called with null reference.\n");
return;
}
//jclass classOfCaller = (jclass)obj; // Static native method gets class as second arg.
jclass movieJClass = env->GetObjectClass(movieControllerThis);
if (!movieJClass) {
LOGE("Error: nativeMovieInit() couldn't get class.");
return;
}
mMovieJClass = (jclass)env->NewGlobalRef(movieJClass);
env->DeleteLocalRef(movieJClass);
if (!mMovieJClass) {
LOGE("Error: nativeMovieInit() couldn't create global ref.");
return;
}
mMoviePlayJMethodID = env->GetStaticMethodID(mMovieJClass, "playFromNative", "(Ljava/lang/Object;ILjava/lang/String;)V");
escondeBotaoMethodID = env->GetStaticMethodID(mMovieJClass, "escondeBotoesNativo", "(Ljava/lang/Object;)V");
if(!mMoviePlayJMethodID || !escondeBotaoMethodID){
return;
}
if (!mMoviePlayJMethodID) {
LOGE("Error: nativeMovieInit() couldn't get method IDs.");
return;
}
mMovieJObjectWeak = env->NewGlobalRef(movieControllerWeakThis);
if (!mMovieJObjectWeak) {
LOGE("Error: nativeMovieInit() couldn't create global ref.");
return;
}
}
我尝试调试这个nativeMovieInit,当它完成这个方法时,引用就在那里,但由于某些原因,并不总是它们在需要那些引用时工作。 有一个叫做nativeVideoFrame的C ++方法,可以在摄像机的每一帧上调用,但是当它对一个标记进行调整时,它就无法调用我需要的方法。 发生这种情况的部分代码在这里:
for (i = 0; i < markersNFTCount; i++) {
markersNFT[i].validPrev = markersNFT[i].valid;
if (markersNFT[i].pageNo >= 0 && markersNFT[i].pageNo == detectedPage) {
markersNFT[i].valid = TRUE;
for (j = 0; j < 3; j++) for (k = 0; k < 4; k++) markersNFT[i].trans[j][k] = trackingTrans[j][k];
}
else markersNFT[i].valid = FALSE;
if (markersNFT[i].valid) {
// Filter the pose estimate.
if (markersNFT[i].ftmi) {
if (arFilterTransMat(markersNFT[i].ftmi, markersNFT[i].trans, !markersNFT[i].validPrev) < 0) {
LOGE("arFilterTransMat error with marker %d.\n", i);
}
}
if (!markersNFT[i].validPrev) {
// Marker has become visible, tell any dependent objects.
//ARMarkerAppearedNotification
if (env && mMovieJClass && mMoviePlayJMethodID && mMovieJObjectWeak) {
env->CallStaticVoidMethod(mMovieJClass, mMoviePlayJMethodID, mMovieJObjectWeak, markersNFT[i].pageNo, env->NewStringUTF(markersNFT[i].datasetPathname)); // play().
}
}
// We have a new pose, so set that.
arglCameraViewRHf(markersNFT[i].trans, markersNFT[i].pose.T, 1.0f /*VIEW_SCALEFACTOR*/);
// Tell any dependent objects about the update.
//ARMarkerUpdatedPoseNotification
}
更具体地说,在这个if:
if (env && mMovieJClass && mMoviePlayJMethodID && mMovieJObjectWeak) {
env->CallStaticVoidMethod(mMovieJClass, mMoviePlayJMethodID, mMovieJObjectWeak, markersNFT[i].pageNo, env->NewStringUTF(markersNFT[i].datasetPathname)); // play().
}
这个if应该总是返回true,因为引用在找到有效标记之前已经设置了,但是这不是这里发生的事情,我只是无法理解这有时会如何工作而其他人却没有。当if失败时,mMovieJClass和mMoviePlayJMethodID的大多数时间都为空。 我初始化传递给onStart上的C ++的引用,如下所示:
@Override
public void onStart() {
super.onStart();
mainLayout = (PercentRelativeLayout)this.findViewById(R.id.mainLayout);
cacheDir = getCacheDir() + "/";
botoesController = new BotoesController(this);
botoesController.baixaMarkers(this);
ARMovieActivity.nativeStart();
}
baixaMarkers()是上面提到的下载标记的方法,最后调用上面提到的configuraGL(),然后调用nativeMovieInit(),它创建显示按钮所需的全局引用,所以botoesController引用无法为null,因为baixaMarkers()是从那里调用的。
哦,我忘记提及有时,如果我关闭此活动,然后重新打开它,它会再次起作用。
我已经尝试了几乎所有的东西,而且我无法找出我做错了什么,有人可以帮我解决这个问题吗?