android — Глобальная ссылка на NDK теряется

Я использую библиотеку ARToolkit на Android с собственным кодом, в данном случае кодом C ++, через Android NDK. У меня есть методы C ++, объявленные в моей ARMovieActivity, которые вызываются в разных ситуациях. Целью приложения является обнаружение маркера NFT, изображения и отображение некоторых кнопок на экране. В ARMovie.cpp есть метод, который вызывается в каждом кадре камеры, который проверяет, виден ли какой-либо из маркеров, и, если он обнаруживает, вызывает метод из класса Java. Моя проблема в том, что иногда (я не знаю, как) идентификатор и ссылка на класс Java в ARMovie.cpp просто нулевые, я открываю Activity, и в большинстве случаев ссылка и идентификатор в порядке, и тогда метод вызывается правильно, но даже не закрывая и не помещая Activity в фоновом режиме, они просто обнуляются, а метод не вызывается. Я просто не знаю, что делать дальше.

В моем случае я загружаю маркеры NFT из Firebase, и когда это заканчивается, я инициализирую камеру и вызываю метод 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 () — это метод C ++, который создает глобальную ссылку на BotoesController в ARMovie.cpp, он выглядит следующим образом:

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, и когда он заканчивает этот метод, ссылки есть, но по какой-то причине не всегда они работают, когда нужны эти ссылки.
Есть метод C ++, называемый nativeVideoFrame, который вызывается в каждом кадре камеры, но когда он заканчивает маркер, он не может вызвать нужный мне метод.
Часть кода, в которой это происходит, находится здесь:

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 (env && mMovieJClass && mMoviePlayJMethodID && mMovieJObjectWeak) {
env->CallStaticVoidMethod(mMovieJClass, mMoviePlayJMethodID, mMovieJObjectWeak, markersNFT[i].pageNo, env->NewStringUTF(markersNFT[i].datasetPathname)); // play().
}

Это должно всегда возвращать true, поскольку ссылка была задана задолго до того, как она нашла действительный маркер, но это не то, что здесь происходит, я просто не могу понять, как это иногда может работать, а другие просто нет. При сбое if в большинстве случаев значения mMovieJClass и mMoviePlayJMethodID равны нулю.
Я инициализирую ссылку, которая передается в C ++ в onStart, вот так:

@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 не может быть нулевой, так как оттуда вызывается baixaMarkers ().

О, и я забыл упомянуть, что иногда, если я закрываю это действие, а затем снова открываю его, оно снова работает.

Я уже почти все перепробовал, и я не могу понять, что я делаю неправильно, может кто-нибудь помочь мне с этим?

1

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …