Android | A solution for judging whether the App is in the foreground or in the background

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

  1. This method is only valid on Android 5.0 and above

  2. Add this permission to AndroidManifest

    <uses-permission  android:name="android.permission.PACKAGE_USAGE_STATS" />
  1. Open the phone settings, click Security-Advanced, and tick the app in the apps that have the right to view usageimg

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:

img

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

  1. does not require any permissions

  2. 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

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

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

Guess you like

Origin blog.csdn.net/fromVillageCoolBoy/article/details/130523341
Recommended