Skip to content

Commit

Permalink
Merge pull request #729 from F43nd1r/lastactivities
Browse files Browse the repository at this point in the history
Finish all activities in stack instead of only last one
  • Loading branch information
F43nd1r authored Feb 7, 2019
2 parents 3f8e4a6 + c81e48f commit a09a246
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 31 deletions.
46 changes: 31 additions & 15 deletions acra-core/src/main/java/org/acra/builder/LastActivityManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.acra.ACRA;
import org.acra.collections.WeakStack;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

import static org.acra.ACRA.LOG_TAG;

Expand All @@ -34,7 +36,7 @@
public final class LastActivityManager {

@NonNull
private WeakReference<Activity> lastActivityCreated = new WeakReference<>(null);
private final WeakStack<Activity> activityStack = new WeakStack<>();

/**
* Create and register a new instance
Expand All @@ -46,7 +48,7 @@ public LastActivityManager(@NonNull Application application) {
@Override
public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {
if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "onActivityCreated " + activity.getClass());
lastActivityCreated = new WeakReference<>(activity);
activityStack.add(activity);
}

@Override
Expand All @@ -67,9 +69,6 @@ public void onActivityPaused(@NonNull Activity activity) {
@Override
public void onActivityStopped(@NonNull Activity activity) {
if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "onActivityStopped " + activity.getClass());
synchronized (this) {
notify();
}
}

@Override
Expand All @@ -80,6 +79,10 @@ public void onActivitySaveInstanceState(@NonNull Activity activity, Bundle outSt
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "onActivityDestroyed " + activity.getClass());
synchronized (activityStack) {
activityStack.remove(activity);
activityStack.notify();
}
}
});
}
Expand All @@ -89,26 +92,39 @@ public void onActivityDestroyed(@NonNull Activity activity) {
*/
@Nullable
public Activity getLastActivity() {
return lastActivityCreated.get();
return activityStack.peek();
}

/**
* @return a list of activities in the current process
*/
@NonNull
public List<Activity> getLastActivities() {
return new ArrayList<>(activityStack);
}

/**
* clear saved activity
* clear saved activities
*/
public void clearLastActivity() {
lastActivityCreated.clear();
public void clearLastActivities() {
activityStack.clear();
}

/**
* wait until the last activity is stopped
*
* @param timeOutInMillis timeout for wait
*/
public void waitForActivityStop(int timeOutInMillis) {
synchronized (this) {
try {
wait(timeOutInMillis);
} catch (InterruptedException ignored) {
public void waitForAllActivitiesDestroy(int timeOutInMillis) {
synchronized (activityStack) {
long start = System.currentTimeMillis();
long now = start;
while (!activityStack.isEmpty() && start + timeOutInMillis > now) {
try {
activityStack.wait(start - now + timeOutInMillis);
} catch (InterruptedException ignored) {
}
now = System.currentTimeMillis();
}
}
}
Expand Down
29 changes: 13 additions & 16 deletions acra-core/src/main/java/org/acra/util/ProcessFinisher.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,29 +58,26 @@ public void endApplication() {
}

public void finishLastActivity(@Nullable Thread uncaughtExceptionThread) {
// Trying to solve https://github.com/ACRA/acra/issues/42#issuecomment-12134144
// Determine the current/last Activity that was started and close
// it. Activity#finish (and maybe it's parent too).
final Activity lastActivity = lastActivityManager.getLastActivity();
if (lastActivity != null) {
final boolean isMainThread = uncaughtExceptionThread == lastActivity.getMainLooper().getThread();
if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Finishing the last Activity prior to killing the Process");
if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Finishing activities prior to killing the Process");
boolean wait = false;
for(Activity activity : lastActivityManager.getLastActivities()) {
final boolean isMainThread = uncaughtExceptionThread == activity.getMainLooper().getThread();
final Runnable finisher = () -> {
lastActivity.finish();
if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Finished " + lastActivity.getClass());
activity.finish();
if (ACRA.DEV_LOGGING) ACRA.log.d(LOG_TAG, "Finished " + activity.getClass());
};
if (isMainThread) {
finisher.run();
} else {
lastActivity.runOnUiThread(finisher);
// A crashed activity won't continue its lifecycle. So we only wait if something else crashed
wait = true;
activity.runOnUiThread(finisher);
}

// A crashed activity won't continue its lifecycle. So we only wait if something else crashed
if (!isMainThread) {
lastActivityManager.waitForActivityStop(100);
}
lastActivityManager.clearLastActivity();
}
if (wait) {
lastActivityManager.waitForAllActivitiesDestroy(100);
}
lastActivityManager.clearLastActivities();
}

private void stopServices() {
Expand Down
135 changes: 135 additions & 0 deletions acra-javacore/src/main/java/org/acra/collections/WeakStack.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2019
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.acra.collections;

import android.support.annotation.NonNull;

import java.lang.ref.WeakReference;
import java.util.*;

/**
* A stack which keeps only weak references
*
* @author F43nd1r
* @since 5.3.0
*/
public class WeakStack<T> extends AbstractCollection<T> {
private final List<WeakReference<T>> contents = new ArrayList<>();

private void cleanup() {
for (WeakReference<T> weakReference : contents) {
if (weakReference.get() == null) contents.remove(weakReference);
}
}

@Override
public int size() {
cleanup();
return contents.size();
}

@Override
public boolean contains(Object o) {
if (o != null) {
for (WeakReference<T> weakReference : contents) {
if (o.equals(weakReference.get())) return true;
}
}
return false;
}

@NonNull
@Override
public Iterator<T> iterator() {
return new WeakIterator<>(contents.iterator());
}

@Override
public boolean add(T t) {
return contents.add(new WeakReference<T>(t));
}

@Override
public boolean remove(Object o) {
if (o != null) {
for (int i = 0; i < contents.size(); i++) {
if (o.equals(contents.get(i).get())) {
contents.remove(i);
return true;
}
}
}
return false;
}

public T peek() {
for (int i = contents.size() - 1; i >= 0; i--) {
T result = contents.get(i).get();
if (result != null) return result;
}
throw new EmptyStackException();
}

public T pop() {
T result = peek();
remove(result);
return result;
}

@Override
public void clear() {
contents.clear();
}

private static class WeakIterator<T> implements Iterator<T> {
private final Iterator<WeakReference<T>> iterator;
private T next;

private WeakIterator(Iterator<WeakReference<T>> iterator) {
this.iterator = iterator;
}

@Override
public boolean hasNext() {
if (next != null) return true;
while (iterator.hasNext()) {
T t = iterator.next().get();
if (t != null) {
//to ensure next() can't throw after hasNext() returned true, we need to dereference this
next = t;
return true;
}
}
return false;
}

@Override
public T next() {
T result = next;
next = null;
while (result == null) {
result = iterator.next().get();
}
return result;
}

@Override
public void remove() {
iterator.remove();
}
}
}

0 comments on commit a09a246

Please sign in to comment.