Android:如何获取当前前台活动(来自服务)?

时间:2010-10-06 14:39:10

标签: android service android-activity

是否有一种原生的android方法来获取对服务中当前正在运行的Activity的引用?

我有一个在后台运行的服务,我想在事件发生时(在服务中)更新我当前的Activity。有没有一种简单的方法(就像我上面建议的那样)?

13 个答案:

答案 0 :(得分:137)

这是使用活动管理器完成此操作的好方法。 您基本上从活动管理器获取runningTasks。它将始终首先返回当前活动的任务。从那里你可以获得topActivity。

Example here

有一种从ActivityManager服务获取正在运行的任务列表的简单方法。 您可以请求在手机上运行的最大任务数,默认情况下,首先返回当前活动的任务。

完成后,您可以通过从列表中请求topActivity来获取ComponentName对象。

这是一个例子。

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

您的清单上需要以下权限:

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

答案 1 :(得分:100)

警告:Google Play违规

Google has threatened如果他们使用辅助功能服务用于不可访问性目的,则从Play商店中删除应用。但是,this is reportedly being reconsidered


使用AccessibilityService

优势

  • 通过Android 7.1(API 25)在Android 2.2(API 8)中测试并使用。
  • 不需要投票。
  • 不需要GET_TASKS权限。

缺点

  • 每位用户必须在Android的辅助功能设置中启用该服务。
  • 这不是100%可靠。有时候事件是无序的。
  • The service is always running.
  • 当用户尝试启用AccessibilityService时,如果应用在屏幕上放置了叠加层,则无法按“确定”按钮。一些应用程序是Velis Auto Brightness和Lux。这可能会令人困惑,因为用户可能不知道为什么他们不能按下按钮或如何解决它。
  • AccessibilityService在第一次更改活动之前不会知道当前的活动。

实施例

服务

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

将其合并到您的清单中:

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

服务信息

将其放入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"/>

启用服务

应用的每个用户都需要明确启用AccessibilityService才能使用它。有关如何执行此操作,请参阅this StackOverflow answer

请注意,如果应用在屏幕上放置了叠加层,例如Velis Auto Brightness或Lux,则在尝试启用辅助功能服务时,用户将无法按OK按钮。

答案 2 :(得分:81)

  

是否有一种原生的android方法来获取对服务中当前正在运行的Activity的引用?

您可能不拥有“当前正在运行的活动”。

  

我有一个在后台运行的服务,我想在事件发生时(在服务中)更新我当前的Activity。有没有一种简单的方法(就像我上面建议的那样)?

  1. 向活动发送广播Intent - 展示此模式的here is a sample project
  2. 让活动提供服务调用的PendingIntent(例如,通过createPendingResult()
  3. 让活动通过bindService()向服务注册回调或侦听器对象,并让服务调用该回调/侦听器对象上的事件方法
  4. 向活动发送有序广播Intent,并将低优先级BroadcastReceiver作为备份(如果活动不在屏幕上,则提升Notification) - {{ 3}}关于此模式的更多内容

答案 3 :(得分:18)

可以通过以下方式完成:

  1. 实现您自己的应用程序类,注册ActivityLifecycleCallbacks - 这样您就可以看到我们的应用程序正在发生什么。在每次恢复时,回调会在屏幕上分配当前可见活动,暂停时会删除分配。它使用API​​ 14中添加的方法registerActivityLifecycleCallbacks()

    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. 在您的服务电话getApplication()中,将其投放到您的应用类名称(在这种情况下为应用)。您可以调用app.getActiveActivity() - 这将为您提供当前可见的活动(如果没有活动可见,则为null)。您可以致电activeActivity.getClass().getSimpleName()

  3. 获取活动的名称

答案 4 :(得分:13)

我找不到我们团队满意的解决方案,所以我们推出了自己的解决方案。我们使用ActivityLifecycleCallbacks来跟踪当前活动,然后通过服务公开它:

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

然后配置您的DI容器以返回MyApplication ContextProvider的实例,例如

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

(请注意,上面的代码中省略了getCurrent()的实现。它只是一个从应用程序构造函数设置的静态变量)

答案 5 :(得分:12)

使用ActivityManager

如果您只想知道包含当前活动的应用程序,可以使用ActivityManager执行此操作。您可以使用的技术取决于Android的版本:

优势

  • 应该适用于所有Android版本。

缺点

  • 不适用于Android 5.1 + (它只返回您自己的应用)
  • The documentation for these APIs表示他们只是用于调试和管理用户界面。
  • 如果您想要实时更新,则需要使用轮询。
  • 依赖于隐藏的API:ActivityManager.RunningAppProcessInfo.processState
  • 此实施不会启动应用切换器活动。

示例(基于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);
    }

}

清单

GET_TASKS权限添加到AndroidManifest.xml

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

答案 6 :(得分:9)

我正在使用它进行测试。它的API&gt;但是,只有 用于您应用的活动。

@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");
}

答案 7 :(得分:1)

将此代码用于API 21或更高版本。与其他答案相比,这可以工作并提供更好的结果,它可以完美地检测前景过程。

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

答案 8 :(得分:1)

我喜欢 Application.ActivityLifecycleCallbacks 的想法。但是获得顶级活动可能有点棘手

  • 您的应用可能有多个活动
  • 某些活动可能会被破坏
  • 某些活动可能会进入后台

要处理所有这些情况,您需要跟踪每个活动生命周期。这正是我使用以下解决方案所做的。

所有这些都合并为一次 getTopForegroundActivity() 调用,该调用返回顶部前台活动,或者 null(如果堆栈中没有活动或没有活动在前台)。

用法

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
        }
    }
}

来源

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

答案 9 :(得分:0)

这是我的答案,工作得很好......

你应该能够以这种方式获得当前的活动...... 如果您使用包含许多片段的一些活动构建您的应用程序,并且您想要跟踪当前活动的内容,则需要做很多工作。我的senario是我有一个活动有多个碎片。所以我可以通过Application Object跟踪当前活动,它可以存储全局变量的所有当前状态。

这是一种方式。当您启动Activity时,您将存储该Activity Application.setCurrentActivity(getIntent()); 此应用程序将存储它。 在您的服务类上,您可以简单地这样做 Intent currentIntent = Application.getCurrentActivity();  。getApplication()startActivity(currentIntent);

答案 10 :(得分:0)

我不知道这是不是一个愚蠢的答案,但每次我输入任何活动的onCreate()时都会在共享首选项中存储一个标志来解决这个问题,然后我使用shered偏好中的值来找出它是什么前景活动。

答案 11 :(得分:0)

这是我的建议,对我有用。在您的应用程序类中,实现一个Application.ActivityLifeCycleCallbacks侦听器并在您的应用程序类中设置一个变量。然后根据需要查询变量。

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

    fun onCreate() {
        registerActivityLifecycleCallbacks(this)
    }

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

答案 12 :(得分:-2)

刚刚发现了这个。用apis作为:

  • minSdkVersion 19
  • targetSdkVersion 26

    ActivityManager.getCurrentActivity(上下文)

希望这有用。