[Android] Android获取当前顶部Activity名方法历史版本汇总

在做一个显示当前顶部activity名和包名的ToolApp时遇到的问题。

在Android5.0之前,获取top Activity方法非常简单。直接使用getRunningTasks方法即可。

        //getRunningTasks() is deprecated since API Level 21 (Android 5.0)  
        List localList = manager.getRunningTasks(1);  
        ActivityManager.RunningTaskInfo localRunningTaskInfo = (ActivityManager.RunningTaskInfo)localList.get(0);  
        info.packageName = localRunningTaskInfo.topActivity.getPackageName();  
        info.topActivityName = localRunningTaskInfo.topActivity.getClassName();  

但是这个方法到了5.0就被Android因安全原因ban掉了。这之后使用该方法只能获取到Laucher的信息了。

后来有人找出了ActivityManger的一个方法:

private String getLollipopRecentTask() { 
    final int PROCESS_STATE_TOP = 2; 
    try { 
        Field processStateField = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState"); 
        List processes = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE)).getRunningAppProcesses(); 
        for (ActivityManager.RunningAppProcessInfo process : processes) { 
            if (process.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && process.importanceReasonCode == 0) { 
                int state = processStateField.getInt(process); 
                if (state == PROCESS_STATE_TOP) { 
                String[] packname = process.pkgList; 
                return packname[0]; 
                } 
            } 
        } 
    } catch (Exception e) { 
        throw new RuntimeException(e); 
    } 
    return ""; 
}
然而仅仅过了一个Android OS版本,这个也无法生效了。

所以有人找出了这样的方法:

    public static void getInfo() {
        if (Build.VERSION.SDK_INT >= 21) {
            if (mUsageStatsManager != null) {
                long now = System.currentTimeMillis();
                // get app data during 60s
                List stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, now - 60 * 1000, now);
                // get present app
                if ((stats != null) && (!stats.isEmpty())) {
                    int j = 0;
                    for (int i = 0; i < stats.size(); i++) {
                        if (stats.get(i).getLastTimeUsed() > stats.get(j).getLastTimeUsed()) {
                            j = i;
                        }
                    }
                    topPackageName = stats.get(j).getPackageName();
                    topActivityName = "";
                }
            }
        }

通过UsageStatsManager获取最近一段时间的使用数据,找到最后一次被使用的信息。但是这个方法只能获取到Package名,得不到Activity的名字。

后来在StackOverFlow上看到了这样的做法:(https://stackoverflow.com/questions/31980976/get-top-activity-name-in-android-l)

while (true) {
    final long INTERVAL = 1000;
    final long end = System.currentTimeMillis();
    final long begin = end - INTERVAL;
    final UsageEvents usageEvents = manager.queryEvents(begin, end);
    while (usageEvents.hasNextEvent()) {
        UsageEvents.Event event = new UsageEvents.Event();
        usageEvents.getNextEvent(event);
        if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
            Log.d("event", "timestamp : " + event.getTimeStamp());
            Log.d("event", "package name : " + event.getPackageName());
            Log.d("event", "class name : " + event.getClassName());
        }
    }
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

似乎可以生效没有试过。但是需要死循环不停地读,没有办法动态地监听,吃相有些难看,不够优雅。

最后通过翻阅资料,找到了一个极佳的方法,就是借助Accessibility辅助功能来实现。

public class WindowChangeDetectingService extends AccessibilityService {
    private static final String TAG = "WCDService";

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
    }

    @Override
    protected void onServiceConnected() {
        Log.d(TAG, "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;
        // Just in case this helps
        config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
        setServiceInfo(config);
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.d(TAG, "onAccessibilityEvent");
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            if (event.getPackageName() != null && event.getClassName() != null) {
                ComponentName componentName = new ComponentName(
                        event.getPackageName().toString(),
                        event.getClassName().toString()
                );
                ActivityInfo activityInfo = tryGetActivity(componentName);
                boolean isActivity = activityInfo != null;
                if (isActivity) {
                    Log.d(TAG, "CurentActivity " + componentName.flattenToShortString());
                    ActivityManagerUtils.topActivityName = componentName.flattenToShortString();
                    ActivityManagerUtils.topPackageName = event.getPackageName().toString();
                }
            }
        }
    }

    private ActivityInfo tryGetActivity(ComponentName componentName) {
        try {
            return getPackageManager().getActivityInfo(componentName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }
    }

    @Override
    public void onInterrupt() {}
}

需要一个继承Accessbility的Service,在它的onAccessbilityEvent回调方法里监听到窗口状态改变的事件,从中获取Activity的信息。

需要在AndroidManifest里面配置:

        
            
                
            
            
        


这个Service不需要自己手动启动,只要在手机的Setiings Accessbility里面打开注册了这个Service的APP对应的Accessbility的功能,这个Service就会自动启动。


附:我自己做的显示当前页面Activity信息的APP,CurrentActivity。

GitHub:   https://github.com/Haocxx/CurrentActivity


你可能感兴趣的:(Android)