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

前端 未结 12 1801
北恋
北恋 2020-11-22 07:53

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 up

相关标签:
12条回答
  • 2020-11-22 08:28

    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();
            }
        }
    
    0 讨论(0)
  • 2020-11-22 08:29

    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:

    • Pre-Lollipop: ActivityManager.getRunningTasks (example)
    • Lollipop: ActivityManager.getRunningAppProcesses (example)

    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" />
    
    0 讨论(0)
  • 2020-11-22 08:31

    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)

    0 讨论(0)
  • 2020-11-22 08:36

    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);

    0 讨论(0)
  • 2020-11-22 08:41

    Just recently found out about this. With apis as:

    • minSdkVersion 19
    • targetSdkVersion 26

      ActivityManager.getCurrentActivity(context)

    Hope this is of any use.

    0 讨论(0)
  • 2020-11-22 08:43

    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
    0 讨论(0)
提交回复
热议问题