从后台任务或服务确定当前前台应用程序

dha*_*wat 107 android android-service android-background

我希望有一个在后台运行的应用程序,它知道任何内置应用程序(消息,联系人等)何时运行.

所以我的问题是:

  1. 我应该如何在后台运行我的应用程序.

  2. 我的后台应用程序如何知道前台运行的应用程序是什么.

有经验的人的回应将不胜感激.

Oli*_*ain 103

关于"2.我的后台应用程序如何知道当前在前台运行的应用程序是什么."

不要使用这个getRunningAppProcesses()方法,因为这会从我的经验中返回各种系统垃圾,你会得到多个结果RunningAppProcessInfo.IMPORTANCE_FOREGROUND.请getRunningTasks()改用

这是我在服务中用来识别当前前台应用程序的代码,它非常简单:

ActivityManager am = (ActivityManager) AppService.this.getSystemService(ACTIVITY_SERVICE);
// The first in the list of RunningTasks is always the foreground task.
RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
Run Code Online (Sandbox Code Playgroud)

多数民众赞成,然后您可以轻松访问前台应用/活动的详细信息:

String foregroundTaskPackageName = foregroundTaskInfo .topActivity.getPackageName();
PackageManager pm = AppService.this.getPackageManager();
PackageInfo foregroundAppPackageInfo = pm.getPackageInfo(foregroundTaskPackageName, 0);
String foregroundTaskAppName = foregroundAppPackageInfo.applicationInfo.loadLabel(pm).toString();
Run Code Online (Sandbox Code Playgroud)

这需要在活动清单中获得额外的许可,并且完美地运行.

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

  • 注意:在API 21(Lollipop)中不推荐使用`getRunningTasks()` - http://developer.android.com/reference/android/app/ActivityManager.html#getRunningTasks(int) (43认同)
  • 编辑到上面:注意:此方法仅用于调试和呈现任务管理用户界面.< - 刚发现在Java doc中.因此,此方法可能会在未经通知的情况下更改. (3认同)
  • 有一种方法可以使用21中介绍的“使用情况统计api” (2认同)

Sve*_*itz 38

我必须以艰难的方式找出正确的解决方案.下面的代码是cyanogenmod7(平板电脑调整)的一部分,并在android 2.3.3/gingerbread上测试.

方法:

  • getForegroundApp - 返回前台应用程序.
  • getActivityForApp - 返回找到的应用程序的活动.
  • isStillActive - 确定先前找到的应用程序是否仍然是活动应用程序.
  • isRunningService - getForegroundApp的辅助函数

希望能在所有方面解决这个问题(:

private RunningAppProcessInfo getForegroundApp() {
    RunningAppProcessInfo result=null, info=null;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <RunningAppProcessInfo> l = mActivityManager.getRunningAppProcesses();
    Iterator <RunningAppProcessInfo> i = l.iterator();
    while(i.hasNext()){
        info = i.next();
        if(info.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                && !isRunningService(info.processName)){
            result=info;
            break;
        }
    }
    return result;
}

private ComponentName getActivityForApp(RunningAppProcessInfo target){
    ComponentName result=null;
    ActivityManager.RunningTaskInfo info;

    if(target==null)
        return null;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <ActivityManager.RunningTaskInfo> l = mActivityManager.getRunningTasks(9999);
    Iterator <ActivityManager.RunningTaskInfo> i = l.iterator();

    while(i.hasNext()){
        info=i.next();
        if(info.baseActivity.getPackageName().equals(target.processName)){
            result=info.topActivity;
            break;
        }
    }

    return result;
}

private boolean isStillActive(RunningAppProcessInfo process, ComponentName activity)
{
    // activity can be null in cases, where one app starts another. for example, astro
    // starting rock player when a move file was clicked. we dont have an activity then,
    // but the package exits as soon as back is hit. so we can ignore the activity
    // in this case
    if(process==null)
        return false;

    RunningAppProcessInfo currentFg=getForegroundApp();
    ComponentName currentActivity=getActivityForApp(currentFg);

    if(currentFg!=null && currentFg.processName.equals(process.processName) &&
            (activity==null || currentActivity.compareTo(activity)==0))
        return true;

    Slog.i(TAG, "isStillActive returns false - CallerProcess: " + process.processName + " CurrentProcess: "
            + (currentFg==null ? "null" : currentFg.processName) + " CallerActivity:" + (activity==null ? "null" : activity.toString())
            + " CurrentActivity: " + (currentActivity==null ? "null" : currentActivity.toString()));
    return false;
}

private boolean isRunningService(String processname){
    if(processname==null || processname.isEmpty())
        return false;

    RunningServiceInfo service;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <RunningServiceInfo> l = mActivityManager.getRunningServices(9999);
    Iterator <RunningServiceInfo> i = l.iterator();
    while(i.hasNext()){
        service = i.next();
        if(service.process.equals(processname))
            return true;
    }

    return false;
}
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,自Android L(API 20)以来,getRunningTasks()已被弃用.从L开始,此方法不再适用于第三方应用程序:引入以文档为中心的最新版本意味着它可以将人员信息泄露给调用者.为了向后兼容,它仍将返回其数据的一小部分:至少是调用者自己的任务,以及可能已知不敏感的其他一些任务,如home. (14认同)
  • 对不起,但它不起作用.有些应用程序是在没有任何理由的情况下进行过滤的,我认为这是在他们运行某些服 所以isRunningService正在将它们踢出去. (2认同)

小智 35

请尝试以下代码:

ActivityManager activityManager = (ActivityManager) newContext.getSystemService( Context.ACTIVITY_SERVICE );
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
for(RunningAppProcessInfo appProcess : appProcesses){
    if(appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
        Log.i("Foreground App", appProcess.processName);
    }
}
Run Code Online (Sandbox Code Playgroud)

进程名称是在前台运行的应用程序的程序包名称.将其与应用程序的包名称进行比较.如果它是相同的,那么您的应用程序正在前台运行.

我希望这回答了你的问题.

  • 从Android版本L开始,这是唯一可行的解​​决方案,因为其他解决方案中使用的方法已被弃用. (6认同)
  • 但是,这不适用于应用程序位于前台且屏幕已锁定的情况 (4认同)
  • 这不太正确。进程的名称并不总是与相应的应用程序包名称相同。 (2认同)

Sum*_*man 24

从棒棒糖开始,这变了.请找到下面的代码,然后用户必须进入设置 - >安全 - >(向下滚动到最后)具有使用权限的应用程序 - >授予我们的应用程序权限

private void printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        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();
            }
        }
    } else {
        ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
        currentApp = tasks.get(0).processName;
    }

    Log.e(TAG, "Current App in foreground is: " + currentApp);
}
Run Code Online (Sandbox Code Playgroud)


Aya*_*fov 8

考虑到已getRunningTasks()被弃用getRunningAppProcesses()且不可靠,我决定结合StackOverflow中提到的两种方法:

   private boolean isAppInForeground(Context context)
    {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
        {
            ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
            ActivityManager.RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
            String foregroundTaskPackageName = foregroundTaskInfo.topActivity.getPackageName();

            return foregroundTaskPackageName.toLowerCase().equals(context.getPackageName().toLowerCase());
        }
        else
        {
            ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
            ActivityManager.getMyMemoryState(appProcessInfo);
            if (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE)
            {
                return true;
            }

            KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
            // App is foreground, but screen is locked, so show notification
            return km.inKeyguardRestrictedInputMode();
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 你需要提到添加到清单中的已弃用权限<uses-permission android:name ="android.permission.GET_TASKS"/>否则应用程序将崩溃 (2认同)
  • android.permission.GET_TASKS - 目前在 Play 商店中被禁止 (2认同)

Cha*_*ffy 6

ActivityManager类是适当的工具,看看哪些进程正在运行.

要在后台运行,通常需要使用服务.


rva*_*rio 6

为了确定前台应用程序,可以使用来检测前台应用程序,可以使用https://github.com/ricvalerio/foregroundappchecker。根据设备的android版本,它使用不同的方法。

至于服务,存储库还提供了所需的代码。本质上,让android studio为您创建服务,然后onCreate添加使用appChecker的代码段。但是,您将需要请求许可。


Sar*_*tal 5

对于需要从我们自己的服务/后台线程检查我们的应用程序是否在前台的情况。这是我实施的方式,对我来说很好用:

public class TestApplication extends Application implements Application.ActivityLifecycleCallbacks {

    public static WeakReference<Activity> foregroundActivityRef = null;

    @Override
    public void onActivityStarted(Activity activity) {
        foregroundActivityRef = new WeakReference<>(activity);
    }

    @Override
    public void onActivityStopped(Activity activity) {
        if (foregroundActivityRef != null && foregroundActivityRef.get() == activity) {
            foregroundActivityRef = null;
        }
    }

    // IMPLEMENT OTHER CALLBACK METHODS
}
Run Code Online (Sandbox Code Playgroud)

现在要从其他类中检查应用是否在前台,只需调用:

if(TestApplication.foregroundActivityRef!=null){
    // APP IS IN FOREGROUND!
    // We can also get the activity that is currently visible!
}
Run Code Online (Sandbox Code Playgroud)

更新(如SHS所指出):

不要忘记在Application类的onCreate方法中注册回调。

@Override
public void onCreate() {
    ...
    registerActivityLifecycleCallbacks(this);
}
Run Code Online (Sandbox Code Playgroud)

  • 这是过去两个小时以来我一直在寻找的东西。谢谢!:) (2认同)
  • 我们需要调用“registerActivityLifecycleCallbacks(this);” 内部覆盖应用程序类(TestApplication)的方法“onCreate()”。这将在我们的应用程序类中启动回调。 (2认同)