Android 5.1.1 and above - getRunningAppProcesses()

2019-01-01 06:21发布

It seems Google finally closed all doors for getting the current foreground application package.

After the Lollipop update, which killed getRunningTasks(int maxNum) and thanks to this answer, I used this code to get the foreground application package since Lollipop:

final int PROCESS_STATE_TOP = 2;
RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
    field = RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) { 
}
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (RunningAppProcessInfo app : appList) {
    if (app.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
        app.importanceReasonCode == 0 ) {
        Integer state = null;
        try {
            state = field.getInt( app );
        } catch (Exception ignored) {
        }
        if (state != null && state == PROCESS_STATE_TOP) {
            currentInfo = app;
            break;
        }
    }
}
return currentInfo;

Android 5.1.1 and above (6.0 Marshmallow), it seems, killed getRunningAppProcesses() as well. It now returns a list of your own application package.


UsageStatsManager

We can use the new UsageStatsManager API as described here but it doesn't work for all applications. Some system applications will return the same package

com.google.android.googlequicksearchbox

AccessibilityService (December 2017: Going to be banned for use by Google)

Some applications use AccessibilityService (as seen here) but it has some disadvantages.


Is there another way of getting the current running application package?

7条回答
裙下三千臣
2楼-- · 2019-01-01 06:45

Please try to use getRunningServices() instead of getRunningAppProcesses() method.

 ActivityManager mActivityManager = (ActivityManager) getSy stemService(Context.ACTIVITY_SERVICE);

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);
查看更多
情到深处是孤独
3楼-- · 2019-01-01 06:46

Just throwing out a potential optimization to what I imagine is a heavily copy-pasted bit of code for detecting the top-most application on Android M.

This

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
    long time = System.currentTimeMillis();
    List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
    if (appList != null && appList.size() > 0) {
        SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
        for (UsageStats usageStats : appList) {
            mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
        }
        if (mySortedMap != null && !mySortedMap.isEmpty()) {
            currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
        }
    }
}

Can be simplified to this

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager) context.getSystemService(
        Context.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> appStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
            time - 1000 * 1000, time);
    if (appStatsList != null && !appStatsList.isEmpty()) {
        currentApp = Collections.max(appStatsList, (o1, o2) ->
            Long.compare(o1.getLastTimeUsed(), o2.getLastTimeUsed())).getPackageName();
    }
}

I found myself using this code in a 2 second loop, and wondered why I was using a complex solution that was O(n*log(n)) when a more simple solution was available in Collections.max() which is O(n).

查看更多
看淡一切
4楼-- · 2019-01-01 06:48

Google limited this functionality for system apps only. As been reported in a bug ticket, you will need the REAL_GET_TASKS permission to access there.

Applications must now have ...permission.REAL_GET_TASKS to be able to get process information for all applications. Only the process information for the calling application will be returned if the app doesn't have the permission. Privileges apps will temporarily be able to get process information for all applications if they don't have the new permission, but have deprecated ...permission.GET_TASKS Also,only system apps can acquire the REAL_GET_TASKS permission.

查看更多
笑指拈花
5楼-- · 2019-01-01 06:51

To get a list of running processes on Android 1.6 - Android 6.0 you can use this library I wrote: https://github.com/jaredrummler/AndroidProcesses The library reads /proc to get process info.

Google has significantly restricted access to /proc in Android Nougat. To get a list of running processes on Android Nougat you will need to use UsageStatsManager or have root access.

Click the edit history for previous alternative solutions.

查看更多
琉璃瓶的回忆
6楼-- · 2019-01-01 06:54

Take a look at https://github.com/ricvalerio/foregroundappchecker, it might be what you need. Provides sample code, and takes away the pain of having to implement cross version foreground detector.

Here are two samples:

AppChecker appChecker = new AppChecker();
String packageName = appChecker.getForegroundApp();

Or regularly check:

AppChecker appChecker = new AppChecker();
appChecker
    .when("com.other.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .when("com.my.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .other(new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .timeout(1000)
    .start(this);
查看更多
君临天下
7楼-- · 2019-01-01 07:05
public class AccessibilityDetectingService extends AccessibilityService {

@Override
protected void onServiceConnected() {
    super.onServiceConnected();

    //Configure these here for compatibility with API 13 and below.

    AccessibilityServiceInfo config = new AccessibilityServiceInfo();
    config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
    config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;

    if (Build.VERSION.SDK_INT >= 16)
        //Just in case this helps
        config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;

    setServiceInfo(config);
}

@Override
public void onAccessibilityEvent(final AccessibilityEvent event) {
        if (event == null ) {
            return;
        } else if(event.getPackageName() == null && event.getClassName() == null){
            return;
        }

            if (activityInfo != null){

                Log.d("CurrentActivity", componentName.flattenToShortString());
        }

}

private ActivityInfo tryGetActivity(ComponentName componentName) {
    try {
        return getPackageManager().getActivityInfo(componentName, 0);
    } catch (PackageManager.NameNotFoundException e) {
        return null;
    }
}
@Override
public void onInterrupt() {
}                
}
}//`enter code here`uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.GET_TASKS" />

Then start the service and app accessibility on in your device setting->accessibility->App on that service.

查看更多
登录 后发表回答