Android | 判断App处于前台还是后台的方案

article/2025/10/7 14:19:38

很多场景下,都需要判断某个App处于前台还是后台。本文集网上编写的前台判断方案于一体。

目前,有6种方案:

 法

判断原理需要权限可以判断其他应用位于前台特点
RunningTaskAndorid4.0系列可以,5.0以上机器不行Android5.0此方法被废弃
RunningProcess当App存在后台常驻的Service时失效
ActivityLifecycleCallbacks简单有效,代码最少
UsageStatsManager需要用户手动授权
AccessibilityService需要用户手动授权
自解析/process当/proc目录下的文件过多时,过多的IO操作会引起耗时

接下来,就对以上6种方法展开详细说明:

目录

1. RunningTask

1.1 原理

1.2 代码实现

1.3 方案缺点

2. RunningProcess

2.1原理

2.2  代码实现

2.3 方案缺点

3.ActivityLifecycleCallbacks

3.1 原理

3.2 代码实现

3.3 方案特点

4. UsageStatsManager

4.1 原理

4.2 代码实现

4.3 方案特点

5.  AccessibilityService

5.1 原理

5.2 代码实现

5.3 方案特点

6. 自解析/process

6.1 原理

6.2 优点

6.3 用法

6.4 方案特点

6.5 方案缺点

6.6 能耗问题解决

7.作为系统进程的获取方式

7.1 技术方案


1. RunningTask

1.1 原理

当一个App处于前台时,会处于RunningTask这个栈的栈顶,所以可以取出RunningTask栈顶的任务进程,与需要判断的App的包名进行比较,来达到目的。

1.2 代码实现

这种方法不仅能获取到前台进程的包名还能获取到activity名称。

public String getForegroundActivity() {  ActivityManager mActivityManager =  (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);  if (mActivityManager.getRunningTasks(1) == null) {  Log.e(TAG, "running task is null, ams is abnormal!!!");  return null;  }  ActivityManager.RunningTaskInfo mRunningTask =  mActivityManager.getRunningTasks(1).get(0);  if (mRunningTask == null) {  Log.e(TAG, "failed to get RunningTaskInfo");  return null;  }  String pkgName = mRunningTask.topActivity.getPackageName();  //String activityName =  mRunningTask.topActivity.getClassName();  return pkgName;  
}  

1.3 方案缺点

getRunningTask方法在5.0以上已经被废弃,只会返回自己和系统的一些不敏感的task,不再返回其他应用的task,用CI方法来判断自身App是否处于后台仍然有效,但是无法判断其他应用是否位于前台,因为不能再获取信息。

2. RunningProcess

2.1原理

通过runningProcess获取到一个当前正在运行的进程的List,我们遍历这个List中的每一个进程,判断这个进程的一个importance 属性是否是前台进程,并且包名是否与我们判断的APP的包名一样,如果这两个条件都符合,那么这个App就处于前台。

2.2  代码实现

  以下code是判断当前应用是否在前台:

private static boolean isAppForeground(Context context) {ActivityManager activityManager =     (ActivityManager)context.getSystemService(Service.ACTIVITY_SERVICE);List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfoList =           		    	     activityManager.getRunningAppProcesses();if (runningAppProcessInfoList == null) {Log.d(TAG,"runningAppProcessInfoList is null!");return false;}for(ActivityManager.RunningAppProcessInfo processInfo : runningAppProcessInfoList) {if (processInfo.processName.equals(context.getPackageName())&&(processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)) { return true;}} return false;
}

以下code是判断在前台的是哪个应用:

public String getForegroundApp(Context context) {  ActivityManager am =  (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);  List<RunningAppProcesInfo> lr = am.getRunningAppProcesses();  if (lr == null) {  return null;  }  for (RunningAppProcessInfo ra : lr) {  if (ra.importance == RunningAppProcessInfo.IMPORTANCE_VISIBLE  || ra.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {  return ra.processName;  }  }  return null;  
}  

getRunningAppProcess方法只能获取前台包名。

2.3 方案缺点

Android5.0之后已经被废弃。

例如,在聊天类型的App中,常常需要常驻后台来不间断地获取服务器的消息,就需要把Service设置成START_STICKY,kill后会被重启(等待5s左右)来保证Service常驻后台。如果Service设置了这个属性,这个App的进程就会被判断为前台。代码表现为

appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND

  上述code永远成立,这样就永远无法判断到底那个是前台了。

3.ActivityLifecycleCallbacks

3.1 原理

       AndroidSDK14Application类里增加了ActivityLifecycleCallbacks,我们可以通过这个Callback拿到App所有Activity的生命周期回调。

 public interface ActivityLifecycleCallbacks {void onActivityCreated(Activity activity, Bundle savedInstanceState);void onActivityStarted(Activity activity);void onActivityResumed(Activity activity);void onActivityPaused(Activity activity);void onActivityStopped(Activity activity);void onActivitySaveInstanceState(Activity activity, Bundle outState);void onActivityDestroyed(Activity activity);}

       知道这些信息,我们就可以用更官方的办法来解决问题,只需要在ApplicationonCreate()里去注册上述接口,然后由Activity回调回来运行状态即可。

       Android应用开发中一般认为back键是可以捕获的,而Home键是不能捕获的(除非修改framework,但是上述方法从Activity生命周期着手解决问题,虽然这两种方式的Activity生命周期并不相同,但是二者都会执行onStop();所以并不关心到底是触发了哪个键切入后台的。另外,Application是否被销毁,都不会影响判断的正确性

3.2 代码实现

    (1)AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="mytest.example.com.broadcaststudy"><applicationandroid:name=".TestActivityLifecycleApplcation"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".SendBroadcastActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name=".SecondActivity"></activity></application></manifest>

(2) TestActivityLifecycleApplication.java

package mytest.example.com.broadcaststudy;import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;/*** Created by Maureen on 2018/1/18.*/
public class TestActivityLifecycleApplcation extends Application {private final String TAG = "TestActivityLifecycleApplcation";private static TestActivityLifecycleApplcation mTestActivityLifecycleApplcation;private int mActivityCount = 0;@Overridepublic void onCreate() {super.onCreate();mTestActivityLifecycleApplcation = new TestActivityLifecycleApplcation();registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {@Overridepublic void onActivityCreated(Activity activity, Bundle savedInstanceState) {Log.d(TAG,"onActivityCreated");}@Overridepublic void onActivityStarted(Activity activity) {Log.d(TAG,"onActivityStarted");mActivityCount++;}@Overridepublic void onActivityResumed(Activity activity) {Log.d(TAG,"onActivityResumed");}@Overridepublic void onActivityPaused(Activity activity) {Log.d(TAG,"onActivityPaused");}@Overridepublic void onActivityStopped(Activity activity) {Log.d(TAG,"onActivityStopped");mActivityCount--;}@Overridepublic void onActivitySaveInstanceState(Activity activity, Bundle outState) {Log.d(TAG,"onActivitySaveInstanceState");}@Overridepublic void onActivityDestroyed(Activity activity) {Log.d(TAG,"onActivityDestroyed");}});}public static TestActivityLifecycleApplcation getInstance( ) {if (null == mTestActivityLifecycleApplcation)mTestActivityLifecycleApplcation = new TestActivityLifecycleApplcation();return mTestActivityLifecycleApplcation;}public int getActivityCount( ) {return mActivityCount;}
}

(3) SendActivity.java

package mytest.example.com.broadcaststudy;import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;/*** Created by ATC6111 on 2018/1/18.*/
public class SecondActivity extends Activity {private final String TAG = "SecondActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d(TAG, "onCreate");}@Overrideprotected void onStart() {super.onStart();Log.d(TAG, "onStart");}@Overrideprotected void onRestart() {super.onRestart();Log.d(TAG, "onRestart");}@Overrideprotected void onResume() {super.onResume();Log.d(TAG, "onResume");}@Overrideprotected void onPause() {super.onPause();Log.d(TAG, "onPause");}@Overrideprotected void onStop() {super.onStop();Log.d(TAG, "onStop");}@Overrideprotected void onDestroy() {super.onDestroy();Log.d(TAG, "onDestroy");}
}

(4) SendBroadcastActivity.java

package mytest.example.com.broadcaststudy;import android.app.Activity;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;import java.util.List;public class SendBroadcastActivity extends Activity {private static final String TAG = "SendBroadcastActivity";private static final String ACTION_MAUREEN_TEST = "com.maureen.test";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d(TAG,"onCreate");setContentView(R.layout.activity_main);Intent intent = new Intent();intent.setAction(ACTION_MAUREEN_TEST);//intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);Log.d(TAG,"Begin to sendBroadcast:");sendBroadcast(intent);Log.d(TAG,"Send broadcast end!");}@Overrideprotected void onStart() {super.onStart();Log.d(TAG,"onStart");}@Overrideprotected void onRestart() {super.onRestart();Log.d(TAG,"onRestart");}@Overrideprotected void onResume() {super.onResume();Log.d(TAG,"onResume");startActivity(new Intent(this, SecondActivity.class));}@Overrideprotected void onPause() {super.onPause();Log.d(TAG,"onPause");}@Overrideprotected void onStop() {super.onStop();Log.d(TAG,"onStop");}@Overrideprotected void onDestroy() {super.onDestroy();Log.d(TAG,"onDestroy");}
}

在讲解为什么不在onActivityResumed( )与onActivityPaused( )中对activity进行计数,而是在onActivityStarted()和onActivityStopped( )中对activity进行计数之前。

先看以下几种情况的activity生命周期:

A、启动App,进入SendBroadcastActivity:

01-01 05:33:56.401 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityCreated
01-01 05:33:56.401 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onCreate
01-01 05:33:56.427 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: Begin to sendBroadcast:
01-01 05:33:56.430 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: Send broadcast end!
01-01 05:33:56.431 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityStarted
01-01 05:33:56.431 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onStart
01-01 05:33:56.432 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityResumed
01-01 05:33:56.432 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onResume

B、点击back键退出SendBroadcastActivity:

点击back键退出App:
01-01 05:35:37.983 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityPaused
01-01 05:35:37.983 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onPause
01-01 05:35:38.035 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityStopped
01-01 05:35:38.036 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onStop
01-01 05:35:38.036 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityDestroyed
01-01 05:35:38.036 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onDestroy

C、在A情况下点击home键:

01-01 05:37:34.690 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityPaused
01-01 05:37:34.690 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onPause
01-01 05:37:34.708 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivitySaveInstanceState
01-01 05:37:34.708 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityStopped
01-01 05:37:34.708 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onStop

D、在A情况下点击recent键kill进程:

从recent中kill进程:
01-01 05:38:17.867 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityPaused
01-01 05:38:17.867 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onPause
01-01 05:38:17.914 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivitySaveInstanceState
01-01 05:38:17.914 11816-11816/mytest.example.com.broadcaststudy D/TestActivityLifecycleApplcation: onActivityStopped
01-01 05:38:17.914 11816-11816/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onStop

E、新增code在SendBroadcastActivity的onResume( )中启动SecondActivity:

在SendBroadcastActivity的onResume函数中启动SecondActivity:
01-01 05:57:05.262 16836-16836/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onCreate
01-01 05:57:05.286 16836-16836/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onStart
01-01 05:57:05.287 16836-16836/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onResume
01-01 05:57:05.324 16836-16836/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onPause
01-01 05:57:05.367 16836-16836/mytest.example.com.broadcaststudy D/SecondActivity: onCreate
01-01 05:57:05.370 16836-16836/mytest.example.com.broadcaststudy D/SecondActivity: onStart
01-01 05:57:05.370 16836-16836/mytest.example.com.broadcaststudy D/SecondActivity: onResume
01-01 05:57:05.605 16836-16836/mytest.example.com.broadcaststudy D/SendBroadcastActivity: onStop

其中尤其是E,可以看到activity切换时的生命周期。这里暂且称SendBroadcastActivity为A,SecondActivity为B。

如果在A.onResume( )时mActivityCount = 1; A.onPause()时mActivityCount--,

在B.onResume( )时mActivityCount++,此处就会有个短暂的延时,在跳转过程中就会出现mActivityCount = 0,

即判断App在后台,就不正确了。

所以要在onActivityStart( )和onActivityStopped( )中 对activity进行计数。

即在A.onStart()时mActivity = 1; B.onStart( )时mActivity ++; A.onStop()时,mActivity--。

所以,当activity的计数为0时表示应用在后台,否则就在前台。

3.3 方案特点

(1)Android应用开发中,一般认为back键是可以捕获的,而Home键不能捕获(除非修改Framework),虽然这两种方式的Activity生命周期并不相同,但是二者都会执行onStop( );所以并不关心到底是哪个键切入后台的。另外,Application是否销毁,都不会影响判断的正确性;

(2)该方案除了用于判断当前应用内的哪个activity位于前台外,还可用于作为实现“进程完全退出”的一种很好的计数方案;

(3)该方案需要在Application中进行注册相关Activity生命周期的回调,上述code所示。只需要对mActivityCount计数进行判断即可知道是否在前台。

4. UsageStatsManager

4.1 原理

通过使用UsageStatsManager获取,此方法是Android5.0之后提供的新API,可以获取一个时间段内的应用统计信息,但是

必须满足以下要求。

使用前提

  1. 此方法只在android5.0以上有效
  2. AndroidManifest中加入此权限
    <uses-permission  android:name="android.permission.PACKAGE_USAGE_STATS" />
  1. 打开手机设置,点击安全-高级,在有权查看使用情况的应用中,为这个App打上勾

4.2 代码实现

UsageStatsManager mUsageStatsManager = (UsageStatsManager)context.getApplicationContext().getSystemService(Context.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
List<UsageStats> stats ;
if (isFirst){stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - TWENTYSECOND, time);
}else {stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - THIRTYSECOND, time);
}
// Sort the stats by the last time used
if(stats != null) {TreeMap<Long,UsageStats> mySortedMap = new TreeMap<Long,UsageStats>();start=System.currentTimeMillis();for (UsageStats usageStats : stats) {mySortedMap.put(usageStats.getLastTimeUsed(),usageStats);}LogUtil.e(TAG,"isFirst="+isFirst+",mySortedMap cost:"+ (System.currentTimeMillis()-start));if(mySortedMap != null && !mySortedMap.isEmpty()) {                    topPackageName =  mySortedMap.get(mySortedMap.lastKey()).getPackageName();        runningTopActivity=new ComponentName(topPackageName,"");if (LogUtil.isDebug())LogUtil.d(TAG,topPackageName);}
}

跳转到“查看应用使用权限”界面的跳转代码如下:

Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);

还要声明权限:

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

4.3 方案特点

1、该方案最大的缺点是需要用户手动授权,因此在使用时要结合场景做适当引导;

2、该方案为Android5.0以后Google官方比较推荐的获取进程信息的方式,是最符合Google意图的方式,不过在使用时会有一些延时需要小心处理。

3、使用时可能会遇到通知栏也被计入,具体可参考:4 种获取前台应用的方法(肯定有你不知道的) - 掘金

5.  AccessibilityService

5.1 原理

Android 辅助功能(AccessibilityService) 为我们提供了一系列的事件回调,帮助我们指示一些用户界面的状态变化。

我们可以派生辅助功能类,进而对不同的 AccessibilityEvent 进行处理。同样的,这个服务就可以用来判断当前的前台应用

优势

  • AccessibilityService 有非常广泛的 ROM 覆盖,特别是非国产手机,从 Android API Level 18(Android 2.2) 到 Android Api Level 23(Android 6.0)
  • AccessibilityService 不再需要轮询的判断当前的应用是不是在前台,系统会在窗口状态发生变化的时候主动回调,耗时和资源消耗都极小
  • 不需要权限请求
  • 它是一个稳定的方法,与 “方法5”读取 /proc 目录不同,它并非利用 Android 一些设计上的漏洞,可以长期使用的可能很大
  • 可以用来判断任意应用甚至 Activity, PopupWindow, Dialog 对象是否处于前台

劣势

  • 需要要用户开启辅助功能
  • 辅助功能会伴随应用被“强行停止”而剥夺

5.2 代码实现

参考博客:http://effmx.com/articles/tong-guo-android-fu-zhu-gong-neng-accessibility-service-jian-ce-ren-yi-qian-tai-jie-mian/

步骤:

(1)派生AccessibilityService,创建窗口状态探测服务

创建DetectionService.java

public class DetectionService extends AccessibilityService {final static String TAG = "DetectionService";static String foregroundPackageName;@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return 0; // 根据需要返回不同的语义值}/*** 重载辅助功能事件回调函数,对窗口状态变化事件进行处理* @param event*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {/** 如果 与 DetectionService 相同进程,直接比较 foregroundPackageName 的值即可* 如果在不同进程,可以利用 Intent 或 bind service 进行通信*/foregroundPackageName = event.getPackageName().toString();/** 基于以下还可以做很多事情,比如判断当前界面是否是 Activity,是否系统应用等,* 与主题无关就不再展开。*/ComponentName cName = new ComponentName(event.getPackageName().toString(),event.getClassName().toString());}}@Overridepublic void onInterrupt() {}@Overrideprotected  void onServiceConnected() {super.onServiceConnected();}
}

(2)创建Accessibility Service Info属性文件

创建res/xml/detection_service_config.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- 根据的 Service 的不同功能需要,你可能需要不同的配置 -->
<accessibility-servicexmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeWindowStateChanged"android:accessibilityFeedbackType="feedbackGeneric"android:accessibilityFlags="flagIncludeNotImportantViews" />

(3)注册Detection service到AndroidManifest.xml

在AndroidManifest.xml中添加

<serviceandroid:name="your_package.DetectionService"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService"/></intent-filter><meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/detection_service_config"/></service>

(4)使用detection service判断应用是否在前台

创建isForegroundPkgViaDetectionService( )函数

  /*** 方法6:使用 Android AccessibilityService 探测窗口变化,跟据系统回传的参数获取 前台对象 的包名与类名** @param packageName 需要检查是否位于栈顶的App的包名*/public static boolean isForegroundPkgViaDetectionService(String packageName) {return packageName.equals(DetectingService.foregroundPackageName);}

去设置里开启辅助功能,就可以通过isForegroundPkgDetectService( )判断应用是否在前台了,只需要传入相应应用的包为参数即可。

当然,也可以参照以下方式引导用户开启辅助功能:

(1)引导用户开启辅助功能

 final static String TAG = "AccessibilityUtil";// 此方法用来判断当前应用的辅助功能服务是否开启public static boolean isAccessibilitySettingsOn(Context context) {int accessibilityEnabled = 0;try {accessibilityEnabled = Settings.Secure.getInt(context.getContentResolver(),android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);} catch (Settings.SettingNotFoundException e) {Log.i(TAG, e.getMessage());}if (accessibilityEnabled == 1) {String services = Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);if (services != null) {return services.toLowerCase().contains(context.getPackageName().toLowerCase());}}return false;}private void anyMethod() {// 判断辅助功能是否开启if (!isAccessibilitySettingsOn(getContext())) {// 引导至辅助功能设置页面startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));} else {// 执行辅助功能服务相关操作}}

效果如下:

5.3 方案特点

1. AccessibilityService有非常广泛的ROM覆盖,特别是非国产手机,从API Level 8 (Android2.2)到API Level 23(Android6.0)

2. AccessibilityService不再需要轮询地判断当前的应用是不是在前台,系统会在窗口状态发生变化的时候主动回调,耗时和资源消耗都极小;

3.不需要权限请求;

4.它是一个稳定的方法,并非利用Android一些设计上的 漏洞,可以长期使用的可能很大;

5.可以用来判断任意应用甚至Activity,PopupWindow,Dialog对象是否处于前台。

5.4 方案缺点

1、需要用户手动开启辅助功能;

2、辅助功能会伴随应用被“强行停止”或第三方管理工具通过Root而剥夺,而且进程重启需要对用户进行重新引导开启;

3、部分厂商可能对辅助功能进行限制,如已知的vivo部分机型。

6. 自解析/process

6.1 原理

无意中看到乌云上有人提的一个漏洞,Linux系统内核会把process进程信息保存在/proc目录下,Shell命令去获取的他,再根据进程的属性判断是否为前台

6.2 优点

  1. 不需要任何权限
  2. 可以判断任意一个应用是否在前台,而不局限在自身应用

6.3 用法


(1)获取一系列正在运行的App的进程

List<AndroidAppProcess> processes = ProcessManager.getRunningAppProcesses();

(2)获取任一正在运行的App进程的详细信息

AndroidAppProcess process = processes.get(location);
String processName = process.name;Stat stat = process.stat();
int pid = stat.getPid();
int parentProcessId = stat.ppid();
long startTime = stat.stime();
int policy = stat.policy();
char state = stat.state();Statm statm = process.statm();
long totalSizeOfProcess = statm.getSize();
long residentSetSize = statm.getResidentSetSize();PackageInfo packageInfo = process.getPackageInfo(context, 0);
String appName = packageInfo.applicationInfo.loadLabel(pm).toString();

(3)判断是否在前台

if (ProcessManager.isMyProcessInTheForeground()) {// do stuff
}

(4)获取一系列正在运行的App进程的详细信息

List<ActivityManager.RunningAppProcessInfo> processes = ProcessManager.getRunningAppProcessInfo(ctx);

6.4 方案特点

1、不需要任何权限;

2、可以判断任意一个应用是否在前台,而不局限在自身应用;

3、当/proc下文件夹过多时,此方法是耗时操作;

4、该方案存在能耗问题;

5、在Android6.0.1以上版本或部分厂商版本受限于SEAndroid,只能获取到第3方进程的信息。

6.5 方案缺点

1. 当/proc下文件夹过多时,此方法是耗时操作

2. 该方案再6.0手机适配运行ok,但在最新的小米、华为6.0.1手机中发现受限于SELinux,无法读取系统应用的设备节点进行解析,只能解析第三方应用设备节点。

6.6 能耗问题解决

1、Java层对象缓存:对调用比较频繁的Java层对象在JNI中建立全局缓存,这就避免了每次调用时都需要通过JNI接口获取;

对一些判断是需要的场景在初始化时由Java层传入Jni层,并建立全局缓存。

2、过来的为Android进程:将pid小于1000的Native进程过滤掉;

3、只解析发生变化的进程:在每次轮询解析/proc节点时先判断进程的pid在缓存中是否存在,如果存在只需要更新进程的优先

级信息,其他信息不会发生变化;如果进程之前不存在则需要全新解析:

(1)命中缓存时的解析代码如下

//Code,待补充

(2)未命中缓存时,则进行全新解析

//Code,待补充

4、在解析进程时,过来父进程为zygote的进程:Android中所有应用进程的父进程都是Zygote;

5、在Java层对调用做缓存处理:对于调用比较频繁的情况,如果当次Native调用没有完成,则返回之前的值,不需要阻塞等待;

6、对于只关心前台进程的场景进行特殊处理:

//code,待补充

通过优化,适配方案的能耗与系统接口基本保持一致。

7.作为系统进程的获取方式

7.1 技术方案

虽然getRunningTask从Android5.0开始被系统废弃,但是作为系统应用时,该接口依然是可用的。在用户取得Root权限,或者应用跟厂商合作时,应用本身可能会被内置在系统目录,即:/system/app/或system/private-app/等目录,因此对于这种情况,使用getRunningTask获取依然是一种方便的实现。

1、需要判断应用是否为系统应用:

//Code,待补充。

2、在AndroidManifest.xml中需要声明如下权限:

//Code,待补充。

参考链接:

1.GitHub - wenmingvs/AndroidProcess: 判断App位于前台或者后台的6种方法

2.http://effmx.com/articles/tong-guo-android-fu-zhu-gong-neng-accessibility-service-jian-ce-ren-yi-qian-tai-jie-mian/

3.4 种获取前台应用的方法(肯定有你不知道的) - 掘金

4.http://www.voidcn.com/article/p-aaftfysq-bny.html


http://chatgpt.dhexx.cn/article/kPEZUhX4.shtml

相关文章

fastapi_No.18_后台应用

文章目录 简介使用场景 创建后台应用步骤第一步&#xff1a;定义后台应用函数第二步&#xff1a;使用后台应用函数 在依赖项中的后台应用示例代码示例代码说明 简介 后台应用就是在发送响应后运行的函数。通常用在请求后需要耗费较长时间处理&#xff0c;且客户端不需要在接收…

win11如何禁用后台应用权限 Windows11禁用后台应用权限的设置方法

我在使用Win11系统的电脑时经常会遇到自动打开应用程序情况&#xff0c;很多小伙伴都不知道到如何关闭&#xff0c;那么遇到这种情况应该要如何解决呢?下面就和小编一起来看看Win11系统要如何去禁用软件的后台权限吧。更多windows11系统教程&#xff0c;可以参考小白一键重装网…

提高软件CPU占用率

案例&#xff1a; 独立线程以5ms一包的速率接收指令&#xff08;一共大概70-80种指令类型&#xff09;&#xff0c;并放入一个队列A 主线程中&#xff0c;根据定时器去队列A中取数据&#xff0c;并解析/计算/显示&#xff08;速率&#xff1a;200包/秒&#xff09; 现象&…

CPU使用率100%,如何解决

文章目录 CPU使用率概念CPU使用率的重要指标计算公式 查看CPU使用率CPU 使用率过高总结 CPU使用率 概念 单位时间内CPU使用情况的统计&#xff0c;以百分比方式显示 Linux是一个多任务操作系统&#xff0c;将每个CPU的时间划分为很短的时间片&#xff0c;通过调度器轮流分配…

解决 服务器cpu使用率100%,变成矿鸡之我与病毒crypto斗智斗勇,宝塔面板cpu使用率100%爆红

关于 简单解决宝塔面板显示CPU使用率 100%&#xff0c;但top却没找到相关进程这件事&#xff01;&#xff01;&#xff01; 如图&#xff1a; 作为一个小白&#xff0c;我只是想简单的搭建一个个人网站&#xff0c;结果碰到这个闹心的事。 服务器: 阿里云服务器 面板&#xff…

linux的mysql占用cpu过高_关于在Linux环境下,Mysql占用CPU使用率100%的优化

今天使用 MobaXterml 连接上一台物联网的Linux服务器的使用&#xff0c;发现该服务器的CPU使用率一直在100%左右。 使用top 命令发现Mysql占用了大量的CPU 用数据库工具 Navicat premium 以 root 的身份连接数据库&#xff0c;执行查询语句&#xff1a; show PROCESSLIST; 发现…

CPU使用率到100%,有哪些因素影响?

关注星标公众号&#xff0c;不错过精彩内容 转自 | 涛哥依旧 最近&#xff0c;一位同事急匆匆跑过来跟我说&#xff1a;糟糕了&#xff0c;服务器CPU的使用率达100%了。 我心想不可能啊&#xff0c;CPU有那么多核&#xff0c;怎会跑满&#xff1f;于是看了一眼&#xff0c;结果…

记一次golang cpu 占用100%

最近重新部署了下测试服务器&#xff0c;发现其负载达到了4.*&#xff0c; cpu 使用率达到了100%&#xff0c;吓的瑟瑟发抖&#xff0c;马上起来查看情况。 1&#xff0c; 想到了使用go性能查看工具&#xff1a; pprof&#xff0c;因为项目是用gin开发的&#xff0c;所在直接安…

Mysql CPU占用100%查询

这几天每天都会收到服务器负载报警&#xff0c;抽出时间查看一下原因。 进入到服务器使用top命令查看&#xff1a; 能够看到是mysql常年霸居榜首&#xff0c;导致服务器负载变高。理论上来讲&#xff0c;数据量没有那么大&#xff0c;因为很多数据都是没用的&#xff0c;前一段…

【故障排查】如何排查CPU占用100%

线上后台报警CPU占用100%&#xff0c;CPU占用过高&#xff0c;本文介绍一下如何排查CPU占用过高原因。 步骤1. top 输入top命令&#xff0c;找到占用CPU最高的进程。按ShiftP键排序&#xff1a; 可以看到CPU占用最高的pid是92129。 步骤2. top -Hp pid 查看指定进程内线程信…

vmware虚拟机WinXp sp3的系统cpu占用100%的解决方案

0x00 问题场景 1. 环境配置 角色版本位数备注虚拟机软件VMware Workstation15\原始版本12&#xff0c;升到15Host OSWindows 7 Professional6.1.7601, Service Pack 164-bit\Guest OSWindows XPservice Pack 332 bit\ 2. 环境说明 VMware在很久以前是version12&#xff0c;…

Win10环境VMware开WinXP虚拟机CPU占用100%

解决办法 网上试了各种方法&#xff0c;还是不太行&#xff0c;最后莫名其妙的好了。而这之前我只做了一件事&#xff0c;在windows的功能里打开hyper-V服务。 后续 某一天&#xff0c;windows系统自动更新了&#xff0c;然后本来运行得好好的win xp虚拟机变得巨卡。我在w…

IDEACPU占用100%_卡顿 解决办法

解决IDEA占用CPU100%的问题 直接上答案为什么会占用CPU如何查找占用CPU的原因 直接上答案 绝大部分的原因是你打开了一个有非常非常多的代码的类&#xff0c;并且你并不编辑这个类&#xff0c;你只是打开了。。。 当然&#xff0c;如果不是这样&#xff0c;你才能去排查是不是…

linux的cpu使用率100%?

1&#xff1a;背景 在linux环境中&#xff0c;我们都会通过top指令获取当前实例的进场占用cpu情况&#xff0c;如下所示 [123456789root ~]$ top top - 11:48:13 up 622 days, 15:51, 2 users, load average: 0.22, 0.19, 0.11 Tasks: 788 total, 1 running, 787 sleepin…

【最实用实战】如何排查CPU占用100%

线上后台报警CPU占用100%&#xff0c;CPU占用过高&#xff0c;本文介绍一下如何排查CPU占用过高原因。 步骤1. top 输入top命令&#xff0c;找到占用CPU最高的进程。按ShiftP键排序&#xff1a; 可以看到CPU占用最高的pid是92129。 步骤2. top -Hp pid 查看指定进程内线程信…

虚拟机CPU占用100%问题记录

项目场景 kswapd0占用CPU过高&#xff0c;严重影响服务器及虚拟机的使用。 原因分析 kswapd0 是 Linux 系统虚拟内存管理中负责换页的进程。 系统物理内存不足时&#xff0c;kswapd0 会频繁的进行换页操作&#xff08;使用swap分区与内存换页操作交换数据&#xff09;&…

linux cpu占用100%排查

步骤一、找到最耗CPU的进程 工具&#xff1a;top 方法&#xff1a; 执行top -c &#xff0c;显示进程运行信息列表键入P (大写p)&#xff0c;进程按照CPU使用率排序图示&#xff1a; 线上服务CPU100%问题快速定位实战 如上图&#xff0c;最耗CPU的进程PID为10765 步骤二&…

揭秘win10系统CPU占用100%的真正原因/找出那些罪魁祸首

经常会有 Win10 用户反应&#xff0c;电脑没有运行太多程序&#xff0c;但是在任务管理器中&#xff0c;经常可以看到电脑CPU占用率却一直居高不下。那么&#xff0c;CPU占用100%的正真原因是什么呢&#xff1f;下面小编收集了一些针对CPU占用过高的原因及解决办法&#xff0…

CPU占用100%的原因及解决办法

电脑关机故障速解 经常出现cPU占用100%的情况&#xff0c;主要问题可能发生在下面的某些方面: cPU占用率高的九种可能 1、防杀毒软件造成故障 由于新版的KV、金山、瑞星都加入了对网页、插件、邮件的随机监控&#xff0c;无疑增大了系统负担。处理方式:基本上没有合理的处理…

前端请求SDK_前端埋点:质量指标验证

背景 随着万物互联、线上线下结合&#xff0c;终端触达的面越来越广&#xff0c;数据采集应该是企业数据团队最基础和重要的工作之一。做数据的同学应该深有体会&#xff0c;收集的数据内容是否全面、准确、实时&#xff0c;直接影响到数据处理和计算的成本、分析和应用的准确。…