关于NDK丢失的全球参考

时间:2017-06-06 20:38:49

标签: android c++ android-ndk artoolkit

我在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()是从那里调用的。

哦,我忘记提及有时,如果我关闭此活动,然后重新打开它,它会再次起作用。

我已经尝试了几乎所有的东西,而且我无法找出我做错了什么,有人可以帮我解决这个问题吗?

0 个答案:

没有答案