Android: How can I get the current foreground activity (from a service)?

AndroidServiceAndroid Activity

Android Problem Overview


Is there a native android way to get a reference to the currently running Activity from a service?

I have a service running on the background, and I would like to update my current Activity when an event occurs (in the service). Is there a easy way to do that (like the one I suggested above)?

Android Solutions


Solution 1 - Android

Update: this no longer works with other apps' activities as of Android 5.0


Here's a good way to do it using the activity manager. You basically get the runningTasks from the activity manager. It will always return the currently active task first. From there you can get the topActivity.

Example here

There's an easy way of getting a list of running tasks from the ActivityManager service. You can request a maximum number of tasks running on the phone, and by default, the currently active task is returned first.

Once you have that you can get a ComponentName object by requesting the topActivity from your list.

Here's an example.

	ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
	List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
	Log.d("topActivity", "CURRENT Activity ::" + taskInfo.get(0).topActivity.getClassName());
	ComponentName componentInfo = taskInfo.get(0).topActivity;
	componentInfo.getPackageName();

You will need the following permission on your manifest:

<uses-permission android:name="android.permission.GET_TASKS"/>

Solution 2 - Android

Warning: Google Play violation

Google has threatened to remove apps from the Play Store if they use accessibility services for non-accessibility purposes. However, this is reportedly being reconsidered.


##Use an AccessibilityService##

##Benefits##

  • Tested and working in Android 2.2 (API 8) through Android 7.1 (API 25).
  • Doesn't require polling.
  • Doesn't require the GET_TASKS permission.

##Disadvantages##

  • Each user must enable the service in Android's accessibility settings.
  • This isn't 100% reliable. Occasionally the events come in out-of-order.
  • The service is always running.
  • When a user tries to enable the AccessibilityService, they can't press the OK button if an app has placed an overlay on the screen. Some apps that do this are Velis Auto Brightness and Lux. This can be confusing because the user might not know why they can't press the button or how to work around it.
  • The AccessibilityService won't know the current activity until the first change of activity.

##Example##

###Service###

public class WindowChangeDetectingService extends AccessibilityService {

	@Override
	protected void onServiceConnected() {
		super.onServiceConnected();

		//Configure these here for compatibility with API 13 and below.
		AccessibilityServiceInfo config = new AccessibilityServiceInfo();
		config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
		config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;

		if (Build.VERSION.SDK_INT >= 16)
			//Just in case this helps
			config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;

		setServiceInfo(config);
	}

	@Override
	public void onAccessibilityEvent(AccessibilityEvent event) {
		if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
			if (event.getPackageName() != null && event.getClassName() != null) {
				ComponentName componentName = new ComponentName(
					event.getPackageName().toString(),
					event.getClassName().toString()
				);

				ActivityInfo activityInfo = tryGetActivity(componentName);
				boolean isActivity = activityInfo != null;
				if (isActivity)
					Log.i("CurrentActivity", componentName.flattenToShortString());
			}
		}
	}

	private ActivityInfo tryGetActivity(ComponentName componentName) {
		try {
			return getPackageManager().getActivityInfo(componentName, 0);
		} catch (PackageManager.NameNotFoundException e) {
			return null;
		}
	}

	@Override
	public void onInterrupt() {}
}

###AndroidManifest.xml###

Merge this into your manifest:

<application>
	<service
		android:label="@string/accessibility_service_name"
		android:name=".WindowChangeDetectingService"
		android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
		<intent-filter>
			<action android:name="android.accessibilityservice.AccessibilityService"/>
		</intent-filter>
		<meta-data
			android:name="android.accessibilityservice"
			android:resource="@xml/accessibilityservice"/>
	</service>
</application>

###Service Info###

Put this in res/xml/accessibilityservice.xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- These options MUST be specified here in order for the events to be received on first
 start in Android 4.1.1 -->
<accessibility-service
	xmlns:tools="http://schemas.android.com/tools"
	android:accessibilityEventTypes="typeWindowStateChanged"
	android:accessibilityFeedbackType="feedbackGeneric"
	android:accessibilityFlags="flagIncludeNotImportantViews"
	android:description="@string/accessibility_service_description"
	xmlns:android="http://schemas.android.com/apk/res/android"
	tools:ignore="UnusedAttribute"/>

###Enabling the Service###

Each user of the app will need to explicitly enable the AccessibilityService in order for it to be used. See this StackOverflow answer for how to do this.

Note that the user won't be able to press the OK button when trying to enable the accessibility service if an app has placed an overlay on the screen, such as Velis Auto Brightness or Lux.

Solution 3 - Android

> Is there a native android way to get a reference to the currently running Activity from a service?

You may not own the "currently running Activity".

> I have a service running on the background, and I would like to update my current Activity when an event occurs (in the service). Is there a easy way to do that (like the one I suggested above)?

  1. Send a broadcast Intent to the activity -- here is a sample project demonstrating this pattern
  2. Have the activity supply a PendingIntent (e.g., via createPendingResult()) that the service invokes
  3. Have the activity register a callback or listener object with the service via bindService(), and have the service call an event method on that callback/listener object
  4. Send an ordered broadcast Intent to the activity, with a low-priority BroadcastReceiver as backup (to raise a Notification if the activity is not on-screen) -- here is a blog post with more on this pattern

Solution 4 - Android

It can be done by:

  1. Implement your own application class, register for ActivityLifecycleCallbacks - this way you can see what is going on with our app. On every on resume the callback assigns the current visible activity on the screen and on pause it removes the assignment. It uses method registerActivityLifecycleCallbacks() which was added in API 14.

    public class App extends Application {
    
    private Activity activeActivity;
    
    @Override
    public void onCreate() {
        super.onCreate();
        setupActivityListener();
    }
    
    private void setupActivityListener() {
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            }
            @Override
            public void onActivityStarted(Activity activity) {
            }
            @Override
            public void onActivityResumed(Activity activity) {
                activeActivity = activity;
            }
            @Override
            public void onActivityPaused(Activity activity) {
                activeActivity = null;
            }
            @Override
            public void onActivityStopped(Activity activity) {
            }
            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }
            @Override
            public void onActivityDestroyed(Activity activity) {
            }
        });
    }
    
    public Activity getActiveActivity(){
        return activeActivity;
    }
    
    }
    
  2. In your service call getApplication() and cast it to your app class name (App in this case). Than you can call app.getActiveActivity() - that will give you a current visible Activity (or null when no activity is visible). You can get the name of the Activity by calling activeActivity.getClass().getSimpleName()

Solution 5 - Android

I could not find a solution that our team would be happy with so we rolled our own. We use ActivityLifecycleCallbacks to keep track of current activity and then expose it through a service:

public interface ContextProvider {
    Context getActivityContext();
}

public class MyApplication extends Application implements ContextProvider {
    private Activity currentActivity;

    @Override
    public Context getActivityContext() {
         return currentActivity;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityStarted(Activity activity) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityResumed(Activity activity) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityPaused(Activity activity) {
                MyApplication.this.currentActivity = null;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                // don't clear current activity because activity may get stopped after
                // the new activity is resumed
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {
                // don't clear current activity because activity may get destroyed after
                // the new activity is resumed
            }
        });
    }
}

Then configure your DI container to return instance of MyApplication for ContextProvider, e.g.

public class ApplicationModule extends AbstractModule {    
    @Provides
    ContextProvider provideMainActivity() {
        return MyApplication.getCurrent();
    }
}

(Note that implementation of getCurrent() is omitted from the code above. It's just a static variable that's set from the application constructor)

Solution 6 - Android

##Use ActivityManager##

If you only want to know the application containing the current activity, you can do so using ActivityManager. The technique you can use depends on the version of Android:

##Benefits##

  • Should work in all Android versions to-date.

##Disadvantages##

  • Doesn't work in Android 5.1+ (it only returns your own app)
  • The documentation for these APIs says they're only intended for debugging and management user interfaces.
  • If you want real-time updates, you need to use polling.
  • Relies on a hidden API: ActivityManager.RunningAppProcessInfo.processState
  • This implementation doesn't pick up the app switcher activity.

##Example (based on KNaito's code)##

public class CurrentApplicationPackageRetriever {
	
	private final Context context;
	
	public CurrentApplicationPackageRetriever(Context context) {
		this.context = context;
	}
	
	public String get() {
		if (Build.VERSION.SDK_INT < 21)
			return getPreLollipop();
		else
			return getLollipop();
	}

	private String getPreLollipop() {
		@SuppressWarnings("deprecation")
		List<ActivityManager.RunningTaskInfo> tasks =
			activityManager().getRunningTasks(1);
		ActivityManager.RunningTaskInfo currentTask = tasks.get(0);
		ComponentName currentActivity = currentTask.topActivity;
		return currentActivity.getPackageName();
	}
	
	private String getLollipop() {
		final int PROCESS_STATE_TOP = 2;

		try {
			Field processStateField = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");

			List<ActivityManager.RunningAppProcessInfo> processes =
				activityManager().getRunningAppProcesses();
			for (ActivityManager.RunningAppProcessInfo process : processes) {
				if (
					// Filters out most non-activity processes
					process.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
					&&
					// Filters out processes that are just being
					// _used_ by the process with the activity
					process.importanceReasonCode == 0
				) {
					int state = processStateField.getInt(process);

					if (state == PROCESS_STATE_TOP) {
						String[] processNameParts = process.processName.split(":");
						String packageName = processNameParts[0];
						
						/*
						 If multiple candidate processes can get here,
						 it's most likely that apps are being switched.
						 The first one provided by the OS seems to be
						 the one being switched to, so we stop here.
						 */
						return packageName;
					}
				}
			}
		} catch (NoSuchFieldException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}

		return null;
	}

	private ActivityManager activityManager() {
		return (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
	}
	
}

##Manifest##

Add the GET_TASKS permission to AndroidManifest.xml:

<!--suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.GET_TASKS" />

Solution 7 - Android

I'm using this for my tests. It's API > 19, and only for activities of your app, though.

@TargetApi(Build.VERSION_CODES.KITKAT)
public static Activity getRunningActivity() {
    try {
        Class activityThreadClass = Class.forName("android.app.ActivityThread");
        Object activityThread = activityThreadClass.getMethod("currentActivityThread")
                .invoke(null);
        Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
        activitiesField.setAccessible(true);
        ArrayMap activities = (ArrayMap) activitiesField.get(activityThread);
        for (Object activityRecord : activities.values()) {
            Class activityRecordClass = activityRecord.getClass();
            Field pausedField = activityRecordClass.getDeclaredField("paused");
            pausedField.setAccessible(true);
            if (!pausedField.getBoolean(activityRecord)) {
                Field activityField = activityRecordClass.getDeclaredField("activity");
                activityField.setAccessible(true);
                return (Activity) activityField.get(activityRecord);
            }
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    throw new RuntimeException("Didn't find the running activity");
}

Solution 8 - Android

Here's what I suggest and what has worked for me. In your application class, implement an Application.ActivityLifeCycleCallbacks listener and set a variable in your application class. Then query the variable as needed.

class YourApplication: Application.ActivityLifeCycleCallbacks {
    
    var currentActivity: Activity? = null

    fun onCreate() {
        registerActivityLifecycleCallbacks(this)
    }

    ...
    
    override fun onActivityResumed(activity: Activity) {
        currentActivity = activity
    }
}

Solution 9 - Android

Use this code for API 21 or above. This works and gives better result compared to the other answers, it detects perfectly the foreground process.

if (Build.VERSION.SDK_INT >= 21) {
	String currentApp = null;
	UsageStatsManager usm = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);
	long time = System.currentTimeMillis();
	List<UsageStats> applist = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1000, time);
	if (applist != null && applist.size() > 0) {
		SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
		for (UsageStats usageStats : applist) {
			mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);

		}
		if (mySortedMap != null && !mySortedMap.isEmpty()) {
			currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
		}
	}

Solution 10 - Android

I like the idea of the Application.ActivityLifecycleCallbacks. But it can be a bit tricky getting the top activity

  • Your app might have multiple activities
  • Some activities might get destroyed
  • Some activities might go to background

To handle all those cases, you need to track each activity life cycle. This is exactly what I did with my below solution.

It all consolidates into a single call of getTopForegroundActivity() that returns the top foreground activity or null if no activities in the stack or non of them are in the foreground.

Usage

public class MyApp extends Application {

    private ActivityTracker activityTracker;

    @Override
    public void onCreate() {
        super.onCreate();

        activityTracker = new ActivityTracker();
        registerActivityLifecycleCallbacks(activityTracker);
        
        ...

        Activity activity = activityTracker.getTopForegroundActivity();
        if(activity != null) {
            // Do something
        }
    }
}

Source

public class ActivityTracker implements Application.ActivityLifecycleCallbacks {
    private final Map<Activity, ActivityData> activities = new HashMap<>();


    public Activity getTopForegroundActivity() {
        if (activities.isEmpty()) {
            return null;
        }
        ArrayList<ActivityData> list = new ArrayList<>(activities.values());
        Collections.sort(list, (o1, o2) -> {
            int compare = Long.compare(o2.started, o1.started);
            return compare != 0 ? compare : Long.compare(o2.resumed, o1.resumed);
        });

        ActivityData topActivity = list.get(0);
        return topActivity.started != -1 ? topActivity.activity : null;
    }


    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        activities.put(activity, new ActivityData(activity));
    }

    @Override
    public void onActivityStarted(@NonNull Activity activity) {
        ActivityData activityData = activities.get(activity);
        if (activityData != null) {
            activityData.started = System.currentTimeMillis();
        }
    }

    @Override
    public void onActivityResumed(@NonNull Activity activity) {
        ActivityData activityData = activities.get(activity);
        if (activityData != null) {
            activityData.resumed = System.currentTimeMillis();
        }
    }

    @Override
    public void onActivityPaused(@NonNull Activity activity) {
        ActivityData activityData = activities.get(activity);
        if (activityData != null) {
            activityData.resumed = -1;
        }
    }

    @Override
    public void onActivityStopped(@NonNull Activity activity) {
        ActivityData activityData = activities.get(activity);
        if (activityData != null) {
            activityData.started = -1;
        }
    }

    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(@NonNull Activity activity) {
        activities.remove(activity);
    }

    private static class ActivityData {

        public final Activity activity;
        public long started;
        public long resumed;

        private ActivityData(Activity activity) {
            this.activity = activity;
        }
    }
}

Solution 11 - Android

I don't know if it's a stupid answer, but resolved this problem by storing a flag in shared preferences every time I entered onCreate() of any activity, then I used the value from shered preferences to find out what it's the foreground activity.

Solution 12 - Android

Here is my answer that works just fine...

You should be able to get current Activity in this way... If you structure your app with a few Activities with many fragments and you want to keep track of what is your current Activity, it would take a lot of work though. My senario was I do have one Activity with multiple Fragments. So I can keep track of Current Activity through Application Object, which can store all of the current state of Global variables.

Here is a way. When you start your Activity, you store that Activity by Application.setCurrentActivity(getIntent()); This Application will store it. On your service class, you can simply do like Intent currentIntent = Application.getCurrentActivity(); getApplication().startActivity(currentIntent);

Solution 13 - Android

Just recently found out about this. With apis as:

  • minSdkVersion 19

  • targetSdkVersion 26

    ActivityManager.getCurrentActivity(context)

Hope this is of any use.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGeorgeView Question on Stackoverflow
Solution 1 - AndroidNelson RamirezView Answer on Stackoverflow
Solution 2 - AndroidSamView Answer on Stackoverflow
Solution 3 - AndroidCommonsWareView Answer on Stackoverflow
Solution 4 - AndroidVit VeresView Answer on Stackoverflow
Solution 5 - AndroidMuxaView Answer on Stackoverflow
Solution 6 - AndroidSamView Answer on Stackoverflow
Solution 7 - AndroidespinchiView Answer on Stackoverflow
Solution 8 - AndroidsuperuserdoView Answer on Stackoverflow
Solution 9 - AndroidManish GodhaniView Answer on Stackoverflow
Solution 10 - AndroidIlya GazmanView Answer on Stackoverflow
Solution 11 - AndroidVlad VladescuView Answer on Stackoverflow
Solution 12 - AndroidJohn3328View Answer on Stackoverflow
Solution 13 - Androiduser3709410View Answer on Stackoverflow