Fix: Android client stops service when server sends BYE (delete client)

ConnectionThread now calls CaptureService.onNativeExit() via JNI on exit
when g_bExit == S_CLIENT_EXIT (server-initiated). The Java side posts
stopSelf() on the service handler. Guard: instance is null in the
user-initiated teardown path (onDestroy nulls it before nativeStop),
so onNativeExit is a no-op in that case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
yuanyuanxiang
2026-06-25 17:30:23 +02:00
parent 5a1430e904
commit 5296e534ed
2 changed files with 25 additions and 0 deletions

View File

@@ -202,6 +202,7 @@ static jclass g_captureClass = nullptr;
static jmethodID g_requestIdrMid = nullptr; static jmethodID g_requestIdrMid = nullptr;
static jmethodID g_forceFirstFrameMid = nullptr; static jmethodID g_forceFirstFrameMid = nullptr;
static jmethodID g_statusMid = nullptr; // CaptureService.onNativeStatus(String) static jmethodID g_statusMid = nullptr; // CaptureService.onNativeStatus(String)
static jmethodID g_nativeExitMid = nullptr; // CaptureService.onNativeExit() — 服务端主动断开时停止服务
// 心跳日志限流:每 60 秒最多打一次 // 心跳日志限流:每 60 秒最多打一次
static std::atomic<uint64_t> g_lastHbLogMs{0}; static std::atomic<uint64_t> g_lastHbLogMs{0};
@@ -674,6 +675,20 @@ static void ConnectionThread()
g_running.store(false); g_running.store(false);
LOGI("ConnectionThread exit"); LOGI("ConnectionThread exit");
if (g_bExit == S_CLIENT_EXIT && g_jvm && g_captureClass && g_nativeExitMid) {
JNIEnv* jenv = nullptr;
bool attached = false;
if (g_jvm->GetEnv((void**)&jenv, JNI_VERSION_1_6) == JNI_EDETACHED) {
g_jvm->AttachCurrentThread(&jenv, nullptr);
attached = true;
}
if (jenv) {
jenv->CallStaticVoidMethod(g_captureClass, g_nativeExitMid);
if (jenv->ExceptionCheck()) jenv->ExceptionClear();
}
if (attached) g_jvm->DetachCurrentThread();
}
} }
// ------------------------------------------------------------------ JNI // ------------------------------------------------------------------ JNI
@@ -743,6 +758,9 @@ Java_com_yama_client_YamaBridge_nativeInit(
g_statusMid = env->GetStaticMethodID(g_captureClass, "onNativeStatus", "(Ljava/lang/String;)V"); g_statusMid = env->GetStaticMethodID(g_captureClass, "onNativeStatus", "(Ljava/lang/String;)V");
if (env->ExceptionCheck()) { env->ExceptionClear(); g_statusMid = nullptr; } if (env->ExceptionCheck()) { env->ExceptionClear(); g_statusMid = nullptr; }
if (!g_statusMid) LOGE("CaptureService.onNativeStatus not found"); if (!g_statusMid) LOGE("CaptureService.onNativeStatus not found");
g_nativeExitMid = env->GetStaticMethodID(g_captureClass, "onNativeExit", "()V");
if (env->ExceptionCheck()) { env->ExceptionClear(); g_nativeExitMid = nullptr; }
if (!g_nativeExitMid) LOGE("CaptureService.onNativeExit not found");
UseAndroidLog(PostStatus); UseAndroidLog(PostStatus);
} else { } else {
LOGE("CaptureService class not found"); LOGE("CaptureService class not found");

View File

@@ -48,6 +48,13 @@ class CaptureService : Service() {
} }
} }
@JvmStatic
fun onNativeExit() {
val svc = instance ?: return
Log.i(TAG, "onNativeExit: server requested disconnect, stopping service")
svc.idrHandler.post { svc.stopSelf() }
}
@JvmStatic @JvmStatic
fun requestIdr() { fun requestIdr() {
val svc = instance ?: return val svc = instance ?: return