In many scenarios, it is necessary to determine whether an App is in the foreground or background.
Currently, there are 6 options:
method | judgment principle | permission required | It can be judged that other applications are in the foreground | features |
---|---|---|---|---|
① | RunningTask | no | Andorid4.0 series can, but machines above 5.0 can't | This method is deprecated in Android5.0 |
② | RunningProcess | no | Invalid when the App has a Service that is resident in the background | none |
③ | ActivityLifecycleCallbacks | no | no | Simple and effective with minimal code |
④ | UsageStatsManager | yes | yes | User manual authorization required |
⑤ | AccessibilityService | no | yes | User manual authorization required |
⑥ | Self-parsing/process | no | yes | When there are too many files in the /proc directory, too many IO operations will cause time-consuming |
1. RunningTask
1.1 Principle
When an App is in the foreground, it will be at the top of the RunningTask stack, so you can take out the task process at the top of the RunningTask stack and compare it with the package name of the App that needs to be judged to achieve the goal.
1.2 Code implementation
This method can not only get the package name of the foreground process but also get the activity name and taskid.
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(); //int taskid = mRunningTask.id; //Once you get the taskid, you can use activityManager.moveTaskToFront(taskInfo.id, 0 ); Back to the foreground return pkgName; }
1.3 Disadvantages of the solution
The getRunningTask method has been deprecated above 5.0. It will only return some insensitive tasks of itself and the system, and no longer return the tasks of other applications. It is still valid to use the CI method to determine whether the own App is in the background, but it cannot determine whether other applications are in the background. Front desk, because the information can no longer be obtained.
1.4 Others
getRunningServices() gets the background services running in the system.
getRecentTasks() Get the recently opened tasks, which can be used to view the recently opened applications on the mobile phone.
...
2. RunningProcess
2.1 Principle
Obtain a list of currently running processes through runningProcess, we traverse each process in this list, and judge whether an importance attribute of this process is a foreground process, and whether the package name is the same as the package name of the app we judged, if If these two conditions are met, then the App is in the foreground.
2.2 Code implementation
The following code is to determine whether the current application is in the foreground:
private static boolean isAppForeground(Context context) { ActivityManager activityManager = (ActivityManager)context.getSystemService(Service.ACTIVITY_SERVICE); //Get the process collection List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfoList = activityManager.get RunningAppProcesses(); if (runningAppProcessInfoList == null) { Log.d(TAG,"runningAppProcessInfoList is null!"); return false; } //processInfo.processName is the process name for(ActivityManager.RunningAppProcessInfo processInfo : runningAppProcessInfoList) { //Judging the importance level of the process and the comparison between the process name and the package name //ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND =100 //importance attribute == 100 means that the process is in the foreground, and other numbers are in the background, so the application is judged by importance and processName Is it in the foreground if (processInfo.processName.equals(context.getPackageName()) &&(processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)) { return true; } } return false; }
The following code is to determine which application is in the foreground:
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; }
The getRunningAppProcess method can only get the foreground package name. Through the package name, the method of using intent can also return to the foreground
Intent intent = context.getPackageManager().getLaunchIntentForPackage(appProcessInfo.processName); context.startActivity(intent);
2.3 Disadvantages of the scheme
It has been abandoned after Android5.0.
For example, in a chat-type app, it is often necessary to stay in the background to continuously obtain messages from the server. It is necessary to set the Service to START_STICKY, and it will be restarted after being killed (wait for about 5 seconds) to ensure that the Service stays in the background. If Service sets this property, the process of this App will be judged as the foreground. The code behaves as
appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
The above code is always established, so it will never be possible to judge which one is the foreground.
3.ActivityLifecycleCallbacks
3.1 Principle
AndroidSDK14 adds ActivityLifecycleCallbacks to the Application class, through which we can get the lifecycle callbacks of all activities in the App.
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); }
Knowing this information, we can use a more official method to solve the problem. We only need to register the above interface in Application's onCreate(), and then the Activity will call back to the running state.
In Android application development, it is generally believed that the back key can be captured, but the Home key cannot be captured (unless the framework is modified), but the above method starts from the Activity life cycle to solve the problem, although the Activity life cycle of the two methods is not the same, But both will execute onStop(); so it doesn't matter which key is triggered to cut into the background. In addition, whether the Application is destroyed or not will not affect the correctness of the judgment.
3.2 Code implementation
(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"> <application android: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; public class TestActivityLifecycleApplcation extends Application { private final String TAG = "TestActivityLifecycleApplcation"; private static TestActivityLifecycleApplcation mTestActivityLifecycleApplcation; private int mActivityCount = 0; @Override public void onCreate() { super.onCreate(); mTestActivityLifecycleApplcation = new TestActivityLifecycleApplcation(); registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { Log.d(TAG,"onActivityCreated"); } @Override public void onActivityStarted(Activity activity) { Log.d(TAG,"onActivityStarted"); mActivityCount++; } @Override public void onActivityResumed(Activity activity) { Log.d(TAG,"onActivityResumed"); } @Override public void onActivityPaused(Activity activity) { Log.d(TAG,"onActivityPaused"); } @Override public void onActivityStopped(Activity activity) { Log.d(TAG,"onActivityStopped"); mActivityCount--; } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { Log.d(TAG,"onActivitySaveInstanceState"); } @Override public 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; public class SecondActivity extends Activity { private final String TAG = "SecondActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate"); } @Override protected void onStart() { super.onStart(); Log.d(TAG, "onStart"); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "onRestart"); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume"); } @Override protected void onPause() { super.onPause(); Log.d(TAG, "onPause"); } @Override protected void onStop() { super.onStop(); Log.d(TAG, "onStop"); } @Override protected 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"; @Override protected 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!"); } @Override protected void onStart() { super.onStart(); Log.d(TAG,"onStart"); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG,"onRestart"); } @Override protected void onResume() { super.onResume(); Log.d(TAG,"onResume"); startActivity(new Intent(this, SecondActivity.class)); } @Override protected void onPause() { super.onPause(); Log.d(TAG,"onPause"); } @Override protected void onStop() { super.onStop(); Log.d(TAG,"onStop"); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestroy"); } }
Before explaining why the activity is not counted in onActivityResumed( ) and onActivityPaused( ), but in onActivityStarted() and onActivityStopped( ).
First look at the activity life cycle of the following situations:
A. Start the App and enter 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. Click the back button to exit 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. Click the home button in case A:
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. In the case of A, click the recent key to kill the process:
从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. Add code to start SecondActivity in onResume( ) of SendBroadcastActivity:
在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
Especially E, you can see the life cycle when the activity is switched. Here we call SendBroadcastActivity A and SecondActivity B for the time being.
If mAactivityCount = 1 at A.onResume(); mAactivityCount-- at A.onPause(),
When B.onResume ( ), mAactivityCount++, there will be a short delay here, and mAactivityCount = 0 will appear during the jump,
That is, it is incorrect to judge that the App is in the background.
So count the activity in onActivityStart( ) and onActivityStopped( ).
That is, when A.onStart(), mActivity = 1; when B.onStart(), mActivity ++; when A.onStop(), mActivity--.
Therefore, when the activity count is 0, it means that the application is in the background, otherwise it is in the foreground.
3.3 Program Features
(1) In Android application development, it is generally believed that the back key can be captured, but the Home key cannot be captured (unless the Framework is modified). Although the Activity life cycles of the two methods are not the same, both will execute onStop( ); So it doesn't matter which key cuts into the background. In addition, whether the Application is destroyed or not will not affect the correctness of the judgment;
(2) In addition to judging which activity in the current application is in the foreground, this scheme can also be used as a good counting scheme to realize "complete process exit";
(3) This solution needs to register the callback of the relevant Activity life cycle in the Application, as shown in the above code. You only need to judge the mActivityCount count to know whether it is in the foreground.
3.4 Recommended use*
You can determine whether the application needs to be updated in onStart. or fulfill other needs
You can directly call getRunningAppProcess or getRunningTask in onStop to determine whether the application is running in the background, and you can do some things at this time. Then call onCreate from the background back to the foreground, and you can do some things.
the code
1. Start a foreground service when the application enters the background
2. Count the number of activities
3. Record the instance of the current activity
4. Use variables to record whether it is in the background state
public class MyApp extends Application { public static Context INSTANCE; private Application.ActivityLifecycleCallbacks mCallback; private int mActivityCount = 0; //是否进入后台 private boolean isBackground = false; private Activity curActivity;//当前activity private Intent mService; @Override public void onCreate() { super.onCreate(); INSTANCE = this; mCallback = new Application.ActivityLifecycleCallbacks() { @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { Log.i("zxd", "onActivityCreated: "); } @Override public void onActivityStarted(@NonNull Activity activity) { mActivityCount++; curActivity = activity; if (mService != null) stopService(mService); if (mIsOnBackground) { //Back to the foreground from the background //Set interface processing //mOnBackgroundCallback.handleOnForeground(activity); //Reset the background logo mIsOnBackground = false; } } @Override public void onActivityResumed(@NonNull Activity activity) { Log.i("zxd", "onActivityResumed: "); } @Override public void onActivityPaused(@NonNull Activity activity) { Log.i("zxd", "onActivityPaused: "); } @Override public void onActivityStopped(@NonNull Activity activity) { Log.i("zxd", "onActivityStopped: "); mActivityCount--; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mService = new Intent(getApplicationContext(), ForeService.class); startForegroundService(mService); } if (liveActivityCount == 0) {//处于后台 mOnBackgroundCallback.handleOnBackground(activity); mIsOnBackground = true; } } @Override public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { Log.i("zxd", "onActivitySaveInstanceState: "); } @Override public void onActivityDestroyed(@NonNull Activity activity) { Log.i("zxd", "onActivityDestroyed: "); if (mService != null) stopService(mService); } }; registerActivityLifecycleCallbacks(mCallback); } //Press background callback private OnBackgroundCallback mOnBackgroundCallback = null; / ** * Push background callback processing */ interface OnBackgroundCallback { // Trigger void handleOnBackground(Activity activity) when pressing the background; //Trigger void handleOnForeground(Activity activity) when returning to the foreground from the background ; } public void setOnBackgroundCallback(OnBackgroundCallback onBackgroundCallback) { mOnBackgroundCallback = onBackgroundCallback; } }
4. UsageStatsManager
4.1 Principle
Obtained by using UsageStatsManager, this method is a new API provided after Android 5.0, which can obtain application statistics within a period of time, but
The following requirements must be met.
Prerequisites
-
This method is only valid on Android 5.0 and above
-
Add this permission to AndroidManifest
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
-
Open the phone settings, click Security-Advanced, and tick the app in the apps that have the right to view usage
4.2 Code implementation
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); } }
The jump code to jump to the "View App Usage Permissions" interface is as follows:
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); startActivity(intent);
Also declare permissions:
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
4.3 Program Features
1. The biggest disadvantage of this solution is that it requires manual authorization by the user, so proper guidance should be given in combination with the scene when using it;
2. This solution is the way Google officially recommends to obtain process information after Android 5.0. It is the way that is most in line with Google's intentions, but there will be some delays when using it that needs to be handled carefully.
3. When using it, you may encounter that the notification bar is also counted. For details, please refer to: 4 ways to obtain the foreground application (there must be some you don’t know) - Nuggets
5. AccessibilityService
5.1 Principle
The Android Accessibility Service (AccessibilityService) provides us with a series of event callbacks to help us indicate some user interface state changes.
We can derive the accessibility class to process different AccessibilityEvents. Similarly, this service can be used to determine the current foreground application
Advantage
-
AccessibilityService has a very wide ROM coverage, especially for non-domestic mobile phones, from Android API Level 18 (Android 2.2) to Android Api Level 23 (Android 6.0)
-
AccessibilityService no longer needs to poll to determine whether the current application is in the foreground, the system will actively call back when the window state changes, and the time-consuming and resource consumption are extremely small
-
No permission request required
-
It is a stable method, unlike "method 5" to read the /proc directory, it does not use some design loopholes in Android, and it may be used for a long time
-
Can be used to determine whether any application or even Activity, PopupWindow, Dialog object is in the foreground
disadvantage
-
Requires the user to enable accessibility features
-
Accessibility features are taken away with the app being "force stopped"
5.2 Code implementation
Reference blog: http://effmx.com/articles/tong-guo-android-fu-zhu-gong-neng-accessibility-service-jian-ce-ren-yi-qian-tai-jie-mian/
step:
(1) Deriving AccessibilityService to create a window status detection service
Create DetectionService.java
public class DetectionService extends AccessibilityService { final static String TAG = "DetectionService"; static String foregroundPackageName; @Override public int onStartCommand(Intent intent, int flags, int startId) { return 0; // return different semantic values as needed } / ** * Overload the accessibility event callback function to handle window state change events * @param event */ @Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { /* * If it is the same process as DetectionService, just compare the value of foregroundPackageName directly * If it is in a different process, you can use Intent or bind service to communicate */ foregroundPackageName = event.getPackageName().toString(); / * * It is also possible based on the following Do many things, such as judging whether the current interface is an Activity, whether it is a system application, etc. * If it has nothing to do with the theme, it will not be expanded. */ ComponentName cName = new ComponentName(event.getPackageName().toString(), event.getClassName().toString()); } } @Override public void onInterrupt() { } @Override protected void onServiceConnected() { super.onServiceConnected(); } }
(2) Create an Accessibility Service Info property file
Create res/xml/detection_service_config.xml
<?xml version="1.0" encoding="utf-8"?> <!-- According to the different functional requirements of the Service, you may need different configurations --> <accessibility-service xmlns:android="http:/ /schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeWindowStateChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagIncludeNotImportantViews" />
(3) Register Detection service to AndroidManifest.xml
Add in AndroidManifest.xml
<service android:name="your_package.DetectionService" 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/detection_service_config"/> </service>
(4) Use the detection service to determine whether the application is in the foreground
Create the isForegroundPkgViaDetectionService( ) function
/** * Method 6: Use Android AccessibilityService to detect window changes, and obtain the package name and class name of the foreground object according to the parameters returned by the system * * @param packageName needs to check whether it is the package name of the App on the top of the stack */ public static boolean isForegroundPkgViaDetectionService(String packageName) { return packageName.equals(DetectingService.foregroundPackageName); }
Go to the settings to enable the auxiliary function, you can use isForegroundPkgDetectService( ) to judge whether the application is in the foreground, you only need to pass in the package of the corresponding application as a parameter.
Of course, you can also refer to the following ways to guide users to enable accessibility features:
(1) Guide users to enable auxiliary functions
final static String TAG = "AccessibilityUtil"; // This method is used to determine whether the accessibility service of the current application is enabled 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 ; // Determine whether the accessibility function is enabled if (!isAccessibilitySettingsOn(getContext())) { // Lead to the accessibility setting page startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)); } else { // Perform operations related to accessibility services } }
The effect is as follows:
5.3 Program Features
\1. AccessibilityService has a very wide range of ROM coverage, especially for non-domestic mobile phones, from API Level 8 (Android2.2) to API Level 23 (Android6.0)
\2. AccessibilityService no longer needs to poll to determine whether the current application is in the foreground, the system will actively call back when the window state changes, and the time-consuming and resource consumption are extremely small;
3. No permission request is required;
4. It is a stable method, it does not take advantage of some design loopholes in Android, and it may be used for a long time;
5. It can be used to judge whether any application or even Activity, PopupWindow, and Dialog objects are in the foreground.
5.4 Disadvantages of the scheme
1. The user needs to manually enable the auxiliary function;
2. The auxiliary function will be deprived of the application by "forced stop" or third-party management tools through Root, and the process restart needs to reboot the user to open;
3. Some manufacturers may restrict auxiliary functions, such as some known vivo models.
6. Self-analysis/process
6.1 Principle
I accidentally saw a vulnerability mentioned by someone on Wuyun. The Linux system kernel will save the process process information in the /proc directory, and the Shell command will get it, and then judge whether it is the foreground according to the attributes of the process.
6.2 Advantages
-
does not require any permissions
-
Can determine whether any application is in the foreground, not limited to its own application
6.3 Usage
(1) Get a series of running App processes
List<AndroidAppProcess> processes = ProcessManager.getRunningAppProcesses();
(2) * *Get detailed information of any running App process**
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) * *Judge whether it is in the foreground**
if (ProcessManager.isMyProcessInTheForeground()) { // do stuff }
(4) * Get detailed information of a series of running App processes*
List<ActivityManager.RunningAppProcessInfo> processes = ProcessManager.getRunningAppProcessInfo(ctx);
6.4 Program Features
1. No permission is required;
2. It can judge whether any application is in the foreground, not limited to its own application;
3. When there are too many folders under /proc, this method is a time-consuming operation;
4. The scheme has energy consumption problems;
5. In Android 6.0.1 or later versions or some manufacturers' versions are limited by SEAndroid, only third-party process information can be obtained.
6.5 Disadvantages of the scheme
\1. When there are too many folders under /proc, this method is a time-consuming operation
\2. This solution is ok for 6.0 mobile phones, but it is found in the latest Xiaomi and Huawei 6.0.1 mobile phones that it is limited by SELinux, and cannot read the device nodes of the system application for analysis, and can only analyze the device nodes of third-party applications .
6.6 Solve the problem of energy consumption
1. Java layer object cache : establish a global cache in JNI for frequently called Java layer objects, which avoids the need to obtain through the JNI interface every time it is called;
For some scenarios that are necessary for judgment, the Java layer is passed to the Jni layer during initialization, and a global cache is established.
2. The Android process comes over : filter out the Native process with a pid less than 1000;
3. Only parse the process that has changed : When polling and parsing the /proc node, first determine whether the process pid exists in the cache, and if it exists, only need to update the priority of the process
level information, other information will not change; if the process does not exist before, a new parsing is required:
(1) The parsing code when hitting the cache is as follows:
//Code, to be added
(2) When the cache is not hit, a new parsing is performed:
//Code, to be added
4. When parsing the process, the process whose parent process is zygote: the parent process of all application processes in Android is Zygote;
5. Cache the calls at the Java layer : For frequent calls, if the current Native call is not completed, the previous value will be returned without blocking and waiting;
6. Special processing for scenarios that only care about the foreground process:
//code, to be added
Through optimization, the energy consumption of the adaptation scheme is basically consistent with the system interface.
7. As a system process acquisition method
7.1 Technical solution
Although getRunningTask has been abandoned by the system since Android 5.0, this interface is still available as a system application. When the user obtains Root permission, or the application cooperates with the manufacturer, the application itself may be built into the system directory, namely: /system/app/ or system/private-app/, so in this case, using getRunningTask to obtain is still is a convenience implementation.
1. It is necessary to determine whether the application is a system application:
//Code, to be added.
2. The following permissions need to be declared in AndroidManifest.xml:
//Code, to be added.
Reference link:
1. GitHub - wemingvs/AndroidProcess: 6 ways to judge whether the App is in the foreground or background
3. 4 ways to get the foreground application (there must be some you don't know) - Nuggets
4.http://www.voidcn.com/article/p-aaftfysq-bny.html
Android | A solution for judging whether the App is in the foreground or in the background
Android judges whether the App is running in the foreground or in the background (running status)
How to determine whether an Android application is placed in the foreground or background