use weak reference to activity when using threads

Daniel Gultsch created

fixes #4366

Change summary

src/main/java/eu/siacs/conversations/ui/RecordingActivity.java  | 60 +-
src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java | 42 +
2 files changed, 66 insertions(+), 36 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/RecordingActivity.java 🔗

@@ -18,6 +18,7 @@ import android.widget.Toast;
 import androidx.databinding.DataBindingUtil;
 
 import java.io.File;
+import java.lang.ref.WeakReference;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Locale;
@@ -136,30 +137,41 @@ public class RecordingActivity extends Activity implements View.OnClickListener
             }
         }
         if (saveFile) {
-            new Thread(
-                            () -> {
-                                try {
-                                    if (!outputFileWrittenLatch.await(2, TimeUnit.SECONDS)) {
-                                        Log.d(
-                                                Config.LOGTAG,
-                                                "time out waiting for output file to be written");
-                                    }
-                                } catch (InterruptedException e) {
-                                    Log.d(
-                                            Config.LOGTAG,
-                                            "interrupted while waiting for output file to be written",
-                                            e);
-                                }
-                                runOnUiThread(
-                                        () -> {
-                                            setResult(
-                                                    Activity.RESULT_OK,
-                                                    new Intent()
-                                                            .setData(Uri.fromFile(mOutputFile)));
-                                            finish();
-                                        });
-                            })
-                    .start();
+            new Thread(new Finisher(outputFileWrittenLatch, mOutputFile, this)).start();
+        }
+    }
+
+    private static class Finisher implements Runnable {
+
+        private final CountDownLatch latch;
+        private final File outputFile;
+        private final WeakReference<Activity> activityReference;
+
+        private Finisher(CountDownLatch latch, File outputFile, Activity activity) {
+            this.latch = latch;
+            this.outputFile = outputFile;
+            this.activityReference = new WeakReference<>(activity);
+        }
+
+        @Override
+        public void run() {
+            try {
+                if (!latch.await(5, TimeUnit.SECONDS)) {
+                    Log.d(Config.LOGTAG, "time out waiting for output file to be written");
+                }
+            } catch (final InterruptedException e) {
+                Log.d(Config.LOGTAG, "interrupted while waiting for output file to be written", e);
+            }
+            final Activity activity = activityReference.get();
+            if (activity == null) {
+                return;
+            }
+            activity.runOnUiThread(
+                    () -> {
+                        activity.setResult(
+                                Activity.RESULT_OK, new Intent().setData(Uri.fromFile(outputFile)));
+                        activity.finish();
+                    });
         }
     }
 

src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java 🔗

@@ -5,6 +5,7 @@ import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
 
 import android.Manifest;
 import android.annotation.SuppressLint;
+import android.app.Activity;
 import android.app.PictureInPictureParams;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -297,21 +298,38 @@ public class RtpSessionActivity extends XmppActivity
     }
 
     private void checkMicrophoneAvailabilityAsync() {
-        new Thread(this::checkMicrophoneAvailability).start();
+        new Thread(new MicrophoneAvailabilityCheck(this)).start();
     }
 
-    private void checkMicrophoneAvailability() {
-        final long start = SystemClock.elapsedRealtime();
-        final boolean isMicrophoneAvailable = AppRTCAudioManager.isMicrophoneAvailable();
-        final long stop = SystemClock.elapsedRealtime();
-        Log.d(Config.LOGTAG, "checking microphone availability took " + (stop - start) + "ms");
-        if (isMicrophoneAvailable) {
-            return;
+    private static class MicrophoneAvailabilityCheck implements Runnable {
+
+        private final WeakReference<Activity> activityReference;
+
+        private MicrophoneAvailabilityCheck(final Activity activity) {
+            this.activityReference = new WeakReference<>(activity);
+        }
+
+        @Override
+        public void run() {
+            final long start = SystemClock.elapsedRealtime();
+            final boolean isMicrophoneAvailable = AppRTCAudioManager.isMicrophoneAvailable();
+            final long stop = SystemClock.elapsedRealtime();
+            Log.d(Config.LOGTAG, "checking microphone availability took " + (stop - start) + "ms");
+            if (isMicrophoneAvailable) {
+                return;
+            }
+            final Activity activity = activityReference.get();
+            if (activity == null) {
+                return;
+            }
+            activity.runOnUiThread(
+                    () ->
+                            Toast.makeText(
+                                            activity,
+                                            R.string.microphone_unavailable,
+                                            Toast.LENGTH_LONG)
+                                    .show());
         }
-        runOnUiThread(
-                () ->
-                        Toast.makeText(this, R.string.microphone_unavailable, Toast.LENGTH_LONG)
-                                .show());
     }
 
     private void putScreenInCallMode() {