1

I'm using the ARToolkit library on Android, with the native code, in this case, the C++ code, through the Android NDK. I have the C++ methods declared in my ARMovieActivity, which get called in different situations. The objective of the application is to detect an NFT marker, a imagem, and display some buttons on the screen. In the ARMovie.cpp, there's a method that gets called on each frame of the camera, that verify if any of the markers are visible, and if it finds any, it calls a method from a Java class. My problem is that sometimes (I don't know how) the ID and the reference to the Java class in the ARMovie.cpp are just null, I open the Activity, and most of the times the reference and the ID are ok, and then the method gets called correctly, but even without closing or puttin the Activity on background, they just turn to null and the method doesn't get called. I just don't know what to do anymore.

In my case, I'm downloading the NFT markers from Firebase, and when that finishes, I initialize the camera, and call the C++ method that loads the markers, in this method:

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) {

                }
            });
}
    }

The ARMovieActivity.nativeCreate() is the C++ method that loads the markers. The configuraGL() is the Java method that initializes the camera and passes the reference to the class that will be used later to display the buttons, it looks like this:

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();
}

That nativeMovieInit() is the C++ method that creates the global reference to the BotoesController in the ARMovie.cpp, it looks like this:

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;
}
}

I tried debugging this nativeMovieInit, and when it finishes this method, the references are there, but for some reason, not always they are working when it needs those references. There's a C++ method called nativeVideoFrame, that gets called on each frame of the camera, but when it fins a marker, it's failing on calling the method that I need. The part of the code that this happens is here:

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

    }

More specifically, in this if:

if (env && mMovieJClass && mMoviePlayJMethodID && mMovieJObjectWeak) {
            env->CallStaticVoidMethod(mMovieJClass, mMoviePlayJMethodID, mMovieJObjectWeak, markersNFT[i].pageNo, env->NewStringUTF(markersNFT[i].datasetPathname)); // play().
        }

This if should always return true, since the reference was set way before it found a valid marker, but that's not what's happening here, I just can't understand how this could work sometimes and others just don't. When the if fails, most of the times the mMovieJClass and the mMoviePlayJMethodID are null. I initialize the reference that gets passed to the C++ on the onStart, like this:

@Override
public void onStart() {
    super.onStart();

    mainLayout = (PercentRelativeLayout)this.findViewById(R.id.mainLayout);

    cacheDir = getCacheDir() + "/";
    botoesController = new BotoesController(this);
    botoesController.baixaMarkers(this);
    ARMovieActivity.nativeStart();
}

The baixaMarkers() is the method mentioned above that downloads the markers, and in the end, calls the configuraGL(), also mentioned above, that then calls the nativeMovieInit(), which creates the global reference needed to display the buttons, so there's no way the botoesController reference is null, since the baixaMarkers() is called from there.

Oh, and I forgot to mention that sometimes, if I close this Activity, and then reopen it, it works again.

I already tried almost everything, and I can't find out what I'm doing wrong, can someone please help me with this?

0 Answers0