mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 23:10:33 +02:00
registerCallbacks took a new global ref to the activity on every onCreate without releasing the previous one, and unregisterCallbacks only runs on a real shutdown. Since rotation and resizing recreate the activity, each of those leaked the old one, view hierarchy and all. Releasing it means other threads can no longer use the raw global ref, which had been copied into AndroidStorage and TextDrawerAndroid and was read from the emu thread and IO threads. They now take a local ref under a mutex (Android_GetActivity, and the equivalent inside AndroidStorage for the shortcut activity's sake), so a call in flight keeps its activity alive while the UI thread swaps in the new one. The "no activity" bail is a null check on that local ref rather than a separate HasActivity() test beforehand. Between such a test and the call the UI thread can swap the activity out, and we'd hand null to a Java method that wants a Context. The one remaining HasActivity() caller never passes the activity on, so it has nothing to race with. The ContentUri class ref and method IDs are now only looked up once, instead of leaking a class ref per activity. Headless and the unit test executable link Common but not app-android.cpp, so they get a stub for this the same way they already do for getEnv and findClass. Without it the Android CI build fails at link time, where a normal gradle build is perfectly happy. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
60 lines
1.6 KiB
C++
60 lines
1.6 KiB
C++
#pragma once
|
|
|
|
#include "ppsspp_config.h"
|
|
|
|
#include <string>
|
|
#include <cstdint>
|
|
|
|
#include "Common/Log/LogManager.h"
|
|
#include "Common/File/DirListing.h"
|
|
#include "Common/File/Path.h"
|
|
#include "Common/File/AndroidStorage.h"
|
|
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
|
|
std::string Android_GetInputDeviceDebugString();
|
|
std::vector<std::string> Android_GetNativeCrashHistory(int maxEntries);
|
|
|
|
#if !defined(__LIBRETRO__)
|
|
|
|
#include <jni.h>
|
|
|
|
jclass findClass(const char* name);
|
|
JNIEnv* getEnv();
|
|
|
|
// Returns a new local reference to the current activity, or null if there is none. The activity gets
|
|
// replaced whenever Android recreates it (rotation, resize), so don't hang on to anything longer-lived.
|
|
jobject Android_GetActivity(JNIEnv *env);
|
|
|
|
// Frees the JNI local references created in its scope. Java only does that for us when a native
|
|
// method returns to it, which never happens on the threads we attach ourselves (EmuThread, IO and
|
|
// worker threads), so there every local ref leaks until the thread detaches. Before Android 8 there's
|
|
// only room for 512 of them, and running out aborts the process.
|
|
class JNILocalFrame {
|
|
public:
|
|
explicit JNILocalFrame(JNIEnv *env, int capacity = 16) : env_(env) {
|
|
pushed_ = env_->PushLocalFrame(capacity) == 0;
|
|
}
|
|
~JNILocalFrame() {
|
|
if (pushed_) {
|
|
env_->PopLocalFrame(nullptr);
|
|
}
|
|
}
|
|
JNILocalFrame(const JNILocalFrame &) = delete;
|
|
JNILocalFrame &operator=(const JNILocalFrame &) = delete;
|
|
|
|
private:
|
|
JNIEnv *env_;
|
|
bool pushed_;
|
|
};
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
inline std::string Android_GetInputDeviceDebugString() { return ""; }
|
|
inline std::vector<std::string> Android_GetNativeCrashHistory(int maxEntries) { return {}; }
|
|
|
|
#endif
|
|
|