Android: use WeakReference for static activity/service objects

Wrap the activity and service static objects with
a WeakReference to fix a potential memory leak warning.

Task-number: QTBUG-118077
Change-Id: Ifafd137cc49ec5ea23d8425b6bd58b43573970b9
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
This commit is contained in:
Assam Boudjelthia 2023-11-14 11:47:30 +02:00
parent f6e5e52c84
commit 9d3b55b2a8
2 changed files with 43 additions and 20 deletions

View File

@ -6,6 +6,7 @@ package org.qtproject.qt.android;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.util.Log;
import android.view.WindowMetrics; import android.view.WindowMetrics;
import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CompletionInfo;
@ -61,6 +62,8 @@ public class QtInputConnection extends BaseInputConnection
private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod; private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod;
private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary; private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary;
private static final String QtTAG = "QtInputConnection";
private final QtInputConnectionListener m_qtInputConnectionListener; private final QtInputConnectionListener m_qtInputConnectionListener;
class HideKeyboardRunnable implements Runnable { class HideKeyboardRunnable implements Runnable {
@ -68,6 +71,11 @@ public class QtInputConnection extends BaseInputConnection
public void run() { public void run() {
// Check that the keyboard is really no longer there. // Check that the keyboard is really no longer there.
Activity activity = QtNative.activity(); Activity activity = QtNative.activity();
if (activity == null) {
Log.w(QtTAG, "HideKeyboardRunnable: The activity reference is null");
return;
}
Rect r = new Rect(); Rect r = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r); activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);

View File

@ -19,6 +19,7 @@ import android.view.ContextMenu;
import android.view.Menu; import android.view.Menu;
import android.view.View; import android.view.View;
import java.lang.ref.WeakReference;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
@ -31,9 +32,9 @@ import javax.net.ssl.X509TrustManager;
public class QtNative public class QtNative
{ {
private static Activity m_activity = null; private static WeakReference<Activity> m_activity = null;
private static boolean m_activityPaused = false; private static boolean m_activityPaused = false;
private static Service m_service = null; private static WeakReference<Service> m_service = null;
public static final Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations public static final Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations
public static final String QtTAG = "Qt JAVA"; public static final String QtTAG = "Qt JAVA";
@ -54,9 +55,7 @@ public class QtNative
public static boolean isStarted() public static boolean isStarted()
{ {
boolean hasActivity = m_activity != null; return m_started && (isActivityValid() || isServiceValid());
boolean hasService = m_service != null;
return m_started && (hasActivity || hasService);
} }
@UsedFromNativeCode @UsedFromNativeCode
@ -73,14 +72,14 @@ public class QtNative
public static void setActivity(Activity qtMainActivity) public static void setActivity(Activity qtMainActivity)
{ {
synchronized (m_mainActivityMutex) { synchronized (m_mainActivityMutex) {
m_activity = qtMainActivity; m_activity = new WeakReference<>(qtMainActivity);
} }
} }
public static void setService(Service qtMainService) public static void setService(Service qtMainService)
{ {
synchronized (m_mainActivityMutex) { synchronized (m_mainActivityMutex) {
m_service = qtMainService; m_service = new WeakReference<>(qtMainService);
} }
} }
@ -88,23 +87,33 @@ public class QtNative
public static Activity activity() public static Activity activity()
{ {
synchronized (m_mainActivityMutex) { synchronized (m_mainActivityMutex) {
return m_activity; return m_activity != null ? m_activity.get() : null;
} }
} }
public static boolean isActivityValid()
{
return m_activity != null && m_activity.get() != null;
}
@UsedFromNativeCode @UsedFromNativeCode
public static Service service() public static Service service()
{ {
synchronized (m_mainActivityMutex) { synchronized (m_mainActivityMutex) {
return m_service; return m_service != null ? m_service.get() : null;
} }
} }
public static boolean isServiceValid()
{
return m_service != null && m_service.get() != null;
}
@UsedFromNativeCode @UsedFromNativeCode
public static Context getContext() { public static Context getContext() {
if (m_activity != null) if (isActivityValid())
return m_activity; return m_activity.get();
return m_service; return service();
} }
@UsedFromNativeCode @UsedFromNativeCode
@ -174,7 +183,13 @@ public class QtNative
if (!mime.isEmpty()) if (!mime.isEmpty())
intent.setDataAndType(uri, mime); intent.setDataAndType(uri, mime);
activity().startActivity(intent); Activity activity = activity();
if (activity == null) {
Log.w(QtTAG, "openURL(): The activity reference is null");
return false;
}
activity.startActivity(intent);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
@ -209,7 +224,7 @@ public class QtNative
synchronized (m_mainActivityMutex) { synchronized (m_mainActivityMutex) {
final Looper mainLooper = Looper.getMainLooper(); final Looper mainLooper = Looper.getMainLooper();
final Handler handler = new Handler(mainLooper); final Handler handler = new Handler(mainLooper);
final boolean active = (m_activity != null && !m_activityPaused) || m_service != null; final boolean active = (isActivityValid() && !m_activityPaused) || isServiceValid();
if (!active || !handler.post(action)) if (!active || !handler.post(action))
m_lostActions.add(action); m_lostActions.add(action);
} }
@ -219,9 +234,9 @@ public class QtNative
private static void runPendingCppRunnablesOnAndroidThread() private static void runPendingCppRunnablesOnAndroidThread()
{ {
synchronized (m_mainActivityMutex) { synchronized (m_mainActivityMutex) {
if (m_activity != null) { if (isActivityValid()) {
if (!m_activityPaused) if (!m_activityPaused)
m_activity.runOnUiThread(runPendingCppRunnablesRunnable); m_activity.get().runOnUiThread(runPendingCppRunnablesRunnable);
else else
runAction(runPendingCppRunnablesRunnable); runAction(runPendingCppRunnablesRunnable);
} else { } else {
@ -278,10 +293,10 @@ public class QtNative
@Override @Override
public void run() { public void run() {
quitQtAndroidPlugin(); quitQtAndroidPlugin();
if (m_activity != null) if (isActivityValid())
m_activity.finish(); m_activity.get().finish();
if (m_service != null) if (isServiceValid())
m_service.stopSelf(); m_service.get().stopSelf();
m_started = false; m_started = false;
} }