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

Geo*_*rge 172 service android android-activity

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

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

Nel*_*rez 139

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

这里的例子

有一种从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();
Run Code Online (Sandbox Code Playgroud)

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

<uses-permission android:name="android.permission.GET_TASKS"/>
Run Code Online (Sandbox Code Playgroud)

  • 就这样每个人都知道,文档说明了getRunningTasks()方法.--------注意:此方法仅用于调试和呈现任务管理用户界面.这绝不应该用于应用程序中的核心逻辑,例如根据此处的信息在不同的行为之间进行决策.不支持此类用途,并且将来可能会破坏.例如,如果多个应用程序可以同时主动运行,则出于控制流程的目的而对此处的数据含义进行的假设将是不正确的.------------ (43认同)
  • 关于api 21的半决赛`从LOLLIPOP开始,这种方法不再适用于第三方应用程序:引入以文档为中心的最新版本意味着它可以将人员信息泄露给调用者.为了向后兼容,它仍将返回其数据的一小部分:至少是调用者自己的任务,以及可能已知不敏感的其他一些任务,例如home. (29认同)
  • 你的答案100%正确.Thanxx 4 it.Its更好,你在这里张贴相同的答案 (3认同)
  • 如果我更频繁地需要当前活动,我必须非常频繁地进行轮询,对吗?有没有办法在前台活动发生变化时获得回调事件? (2认同)
  • 有什么办法可以让我们在 Lollipop 中获得最热门的活动吗? (2认同)

Sam*_*Sam 103

警告:Google Play违规行为

Google已威胁要从Play商店中移除应用,如果他们使用辅助功能服务用于非无障碍目的.但据报道,这一情况正在重新考虑之中.


用一个 AccessibilityService

优点

  • 通过Android 7.1(API 25)在Android 2.2(API 8)中进行测试和工作.
  • 不需要投票.
  • 不需要GET_TASKS许可.

缺点

  • 每个用户都必须在Android的辅助功能设置中启用该服务.
  • 这不是100%可靠.有时候事件是无序的.
  • 该服务始终在运行.
  • 当用户尝试启用时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() {}
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

服务信息

把它放进去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"/>
Run Code Online (Sandbox Code Playgroud)

启用服务

应用程序的每个用户都需要明确启用AccessibilityService它才能使用它.有关如何执行此操作,请参阅此StackOverflow答案.

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

  • 谢谢你的代码.我为你创建了[这个应用程序](https://github.com/seguri/GetForegroundActivity):) (2认同)

Com*_*are 85

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

您可能不拥有"当前正在运行的活动".

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

  1. Intent向活动发送广播- 这是展示此模式的示例项目
  2. 让活动提供服务调用的PendingIntent(例如,via createPendingResult())
  3. 让活动通过服务注册回调或监听器对象bindService(),并让服务调用该回调/监听器对象上的事件方法
  4. 将有序广播发送Intent到活动,具有低优先级BroadcastReceiver作为备份(Notification如果活动不在屏幕上则提高a ) - 这是一篇关于此模式的更多博客帖子

  • 谢谢,但由于我有一些活动,并且不想更新它们,我一直在寻找一种获取foregroundActivity的android方法.如你所说,我不应该这样做.意味着我必须绕过这个. (3认同)
  • @George:注意内存泄漏.尽可能在Java中避免使用可变的静态数据成员. (3认同)
  • 谢谢,但我有一个更好的解决方案.我的所有活动都扩展了自定义的BaseActivity类.我已经设置了一个ContextRegister,它将活动注册为当前活动的当前活动,在我的BaseActivity类中有3行.不过,谢谢你们的支持. (2认同)

Vit*_*res 18

它可以通过以下方式完成:

  1. 实现您自己的应用程序类,注册ActivityLifecycleCallbacks - 这样您就可以看到我们的应用程序正在发生什么.在每次恢复时,回调会在屏幕上分配当前可见活动,暂停时会删除分配.它使用registerActivityLifecycleCallbacks()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;
    }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在您的服务调用中getApplication(),将其强制转换为您的应用程序类名称(在本例中为App).比你可以调用app.getActiveActivity()- 这会给你一个当前可见的Activity(当没有活动可见时为null).您可以通过调用获取活动的名称activeActivity.getClass().getSimpleName()

  • @beginner,在将“activeActivity”分配给“null”之前,必须检查“activity”和“activeActivity”是否相同,以避免由于各种活动的生命周期方法的调用顺序混合而导致错误。 (2认同)

Sam*_*Sam 13

使用 ActivityManager

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

优点

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

缺点

  • 在Android 5.1+中不起作用(它只返回您自己的应用程序)
  • 这些API的文档说它们仅用于调试和管理用户界面.
  • 如果要进行实时更新,则需要使用轮询.
  • 依赖于隐藏的API: ActivityManager.RunningAppProcessInfo.processState
  • 此实现不会获取应用切换器活动.

示例(基于KNaito的代码)

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

}
Run Code Online (Sandbox Code Playgroud)

表现

GET_TASKS权限添加到AndroidManifest.xml:

<!--suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.GET_TASKS" />
Run Code Online (Sandbox Code Playgroud)

  • 这不再适用于Android M.getRunningAppProcesses()现在只返回应用程序的包 (10认同)

Mux*_*uxa 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
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class ApplicationModule extends AbstractModule {    
    @Provides
    ContextProvider provideMainActivity() {
        return MyApplication.getCurrent();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 在为`current`分配`currentActivity`之前必须检查`activity`和`currentActivity`是否相同,以避免由于各种活动的生命周期方法的混合调用顺序而导致的错误. (3认同)

esp*_*chi 9

我正在使用它进行测试.它的API> 19,但适用于您应用的活动.

@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");
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

258119 次

最近记录:

6 年,8 月 前