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

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

所以我的问题是:

  1. 如何在后台运行应用程序。

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

我们将非常感谢有经验人士的回应。

114268 次浏览

ActivityManager类是查看正在运行的进程的适当工具。

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

做这样的事情:

int showLimit = 20;


/* Get all Tasks available (with limit set). */
ActivityManager mgr = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> allTasks = mgr.getRunningTasks(showLimit);
/* Loop through all tasks returned. */
for (ActivityManager.RunningTaskInfo aTask : allTasks)
{
Log.i("MyApp", "Task: " + aTask.baseActivity.getClassName());
if (aTask.baseActivity.getClassName().equals("com.android.email.activity.MessageList"))
running=true;
}

尝试以下代码:

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

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

我希望这能回答你的问题。

i had to figure out the right solution the hard way. the below code is part of cyanogenmod7 (the tablet tweaks) and is tested on android 2.3.3 / gingerbread.

方法:

  • GetForeground App-返回前台应用程序。
  • GetActivityForApp-返回找到的应用程序的活动。
  • IsStillActive-确定早期发现的应用程序是否仍然是活动应用程序。
  • IsRunningService-getForeground App 的一个助手函数

这有望全面解决这个问题(:

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

关于“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);

Thats it, then you can easily access details of the foreground app/activity:

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

这需要一个额外的许可在活动清单和工作完美。

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

这招对我很管用。但它只给出了主菜单名。也就是说,如果用户已经打开设置—— > 蓝牙—— > 设备名屏幕,RunningAppProcessInfo 将其称为“设置”。无法深入挖掘

ActivityManager activityManager = (ActivityManager) context.getSystemService( Context.ACTIVITY_SERVICE );
PackageManager pm = context.getPackageManager();
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
for(RunningAppProcessInfo appProcess : appProcesses) {
if(appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(appProcess.processName, PackageManager.GET_META_DATA));
Log.i("Foreground App", "package: " + appProcess.processName + " App: " + c.toString());
}
}

从棒棒糖开始,这一切都变了。请找到以下代码,在该用户必须去设置-> 安全-> (滚动到最后)应用程序的使用权限-> 给予权限,我们的应用程序

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

在棒棒糖和以上:

Add to mainfest:

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

然后这样做:

if( mTaskId < 0 )
{
List<AppTask> tasks = mActivityManager.getAppTasks();
if( tasks.size() > 0 )
mTaskId = tasks.get( 0 ).getTaskInfo().id;
}

This is how I am checking if my app is in foreground. Note I am using AsyncTask as suggested by official 仿生人 documentation.`

`

    private class CheckIfForeground extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {


ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
Log.i("Foreground App", appProcess.processName);


if (mContext.getPackageName().equalsIgnoreCase(appProcess.processName)) {
Log.i(Constants.TAG, "foreground true:" + appProcess.processName);
foreground = true;
// close_app();
}
}
}
Log.d(Constants.TAG, "foreground value:" + foreground);
if (foreground) {
foreground = false;
close_app();
Log.i(Constants.TAG, "Close App and start Activity:");


} else {
//if not foreground
close_app();
foreground = false;
Log.i(Constants.TAG, "Close App");


}


return null;
}
}

and execute AsyncTask like this. New CheckIfForeground () . execute () ;

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

至于服务,回购也提供了您需要的代码。本质上,让 android 工作室为您创建服务,然后 onCreate 添加使用 appChecker 的代码片段。但是你需要申请许可。

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

我在一个方法中结合了两种解决方案,它适用于 API 24和 API 21。其他我没测试过的。

Kotlin 守则:

private fun isAppInForeground(context: Context): Boolean {
val appProcessInfo = ActivityManager.RunningAppProcessInfo()
ActivityManager.getMyMemoryState(appProcessInfo)
if (appProcessInfo.importance == IMPORTANCE_FOREGROUND ||
appProcessInfo.importance == IMPORTANCE_VISIBLE) {
return true
} else if (appProcessInfo.importance == IMPORTANCE_TOP_SLEEPING ||
appProcessInfo.importance == IMPORTANCE_BACKGROUND) {
return false
}


val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val foregroundTaskInfo = am.getRunningTasks(1)[0]
val foregroundTaskPackageName = foregroundTaskInfo.topActivity.packageName
return foregroundTaskPackageName.toLowerCase() == context.packageName.toLowerCase()
}

而且是在清单上

<!-- Check whether app in background or foreground -->
<uses-permission android:name="android.permission.GET_TASKS" />

如果我们需要从我们自己的服务/后台线程检查我们的应用程序是否在前台。我是这样实现它的,它对我来说很好用:

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
}

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

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

Update (as pointed out by ):

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

@Override
public void onCreate() {
...
registerActivityLifecycleCallbacks(this);
}

一个简单的解决方案是使用 LiveData。 创建一个单例 LiveData 变量(可能在一个普通的 Kotlin 文件中)。

val foregroundHelper = MutableLiveData<Unit>()

从活动或碎片观察:

foregroundHelper.observe(this, Observer {}) // for Activity
foregroundHelper.observe(viewLifecycleOwner, Observer {}) // for Fragments

现在从您的后台服务,广播接收器等:

val appIsVisibleToTheUser = foregroundHelper.hasActiveObservers()
// Now your logic goes.
if (!appIsVisibleToUser) {
// App is in background
// So In my case:
// I'm showing Notification to the user for the error happened in Background Service.
}

谢谢。