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

在Android开发中,获取当前顶部Activity名受到版本限制。早期可通过ActivityManager的getRunningTasks方法,但在5.0后被限制。接着尝试使用UsageStatsManager获取Package名,但无法得到Activity名。通过StackOverflow上的建议,利用AccessibilityService监听窗口状态变化,成为一种可行方案,需要在AndroidManifest配置,并在 Accessibility 设置中启用服务。作者提供了实现此功能的开源项目CurrentActivity,GitHub链接为:https://github.com/Haocxx/CurrentActivity。

在做一个显示当前顶部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<ActivityManager.RunningAppProcessInfo> 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<UsageStats> 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
            android:name=".WindowChangeDetectingService"
            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/accessibilityservice"/>
        </service>
<?xml version="1.0" encoding="utf-8"?>
<!-- These options MUST be specified here in order for the events to be received on first
 start in Android 4.1.1 -->
<accessibility-service
    xmlns:tools="http://schemas.android.com/tools"
    android:accessibilityEventTypes="typeWindowStateChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagIncludeNotImportantViews"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_service_description"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:ignore="UnusedAttribute"/>

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


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

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值