You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Service检测未知包名的已启动App并获取其名称?

嘿,我懂你要实现的需求了——后台跑着你的App,用户一打开别的App,不管包名熟不熟,你的Service都能立刻揪出它的名字,弹个Toast出来是吧?我给你俩靠谱的方案,都是Android平台上能用的路子:

方法一:使用官方推荐的UsageStatsManager(Android 5.0+)

这是Google官方认可的获取应用使用情况的API,相比旧的getRunningTasks更合规,不过需要申请特殊权限。

步骤1:配置权限

先在AndroidManifest.xml里加必要的权限:

<!-- 获取应用使用统计的系统保护权限 -->
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
    tools:ignore="ProtectedPermissions" />
<!-- Android 11及以上版本,要获取未知包名需添加此权限 -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
    tools:ignore="QueryAllPackagesPermission" />

步骤2:引导用户开启权限

PACKAGE_USAGE_STATS不能直接动态申请,得跳转到系统设置让用户手动开启:

// 检查是否已获取权限
private boolean hasUsageStatsPermission(Context context) {
    AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
    int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
            android.os.Process.myUid(), context.getPackageName());
    return mode == AppOpsManager.MODE_ALLOWED;
}

// 跳转到设置页面引导用户开启权限
private void requestUsageStatsPermission(Context context) {
    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
}

步骤3:在Service中定时查询最近启动的App

用Handler定时查询最近的应用使用记录,示例如下:

private Handler mHandler = new Handler();
private Runnable mCheckRecentAppRunnable = new Runnable() {
    @Override
    public void run() {
        getRecentLaunchedApp();
        // 每隔2秒查询一次,可根据需求调整频率
        mHandler.postDelayed(this, 2000);
    }
};

private void getRecentLaunchedApp() {
    UsageStatsManager usageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
    long endTime = System.currentTimeMillis();
    long startTime = endTime - 1000 * 10; // 查询最近10秒的使用记录
    List<UsageStats> usageStatsList = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, startTime, endTime);

    if (usageStatsList != null && !usageStatsList.isEmpty()) {
        // 按最后使用时间排序,取最新的应用
        Collections.sort(usageStatsList, (a, b) -> Long.compare(b.getLastTimeUsed(), a.getLastTimeUsed()));
        UsageStats recentStats = usageStatsList.get(0);
        String packageName = recentStats.getPackageName();
        
        // 获取应用名称,找不到则显示包名
        String appName = "";
        try {
            PackageManager pm = getPackageManager();
            ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
            appName = pm.getApplicationLabel(info).toString();
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            appName = packageName;
        }
        
        // 弹出Toast提示
        Toast.makeText(getApplicationContext(), "用户启动了:" + appName, Toast.LENGTH_SHORT).show();
    }
}

// 在Service启动时开启定时任务
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (!hasUsageStatsPermission(this)) {
        requestUsageStatsPermission(this);
    } else {
        mHandler.post(mCheckRecentAppRunnable);
    }
    return START_STICKY;
}

// 销毁时移除回调避免内存泄漏
@Override
public void onDestroy() {
    super.onDestroy();
    mHandler.removeCallbacks(mCheckRecentAppRunnable);
}

注意事项

  • 定时频率别太高,避免增加耗电;
  • Android 12+对后台Service限制严格,建议将Service设为前台Service,防止被系统杀死;
  • QUERY_ALL_PACKAGES权限在Google Play上架时需说明用途,否则可能被拒。

方法二:用AccessibilityService监听界面切换(更实时)

这个方法不需要QUERY_ALL_PACKAGES权限,能实时监听App启动/切换事件,但需要用户手动开启辅助功能,体验上多一步操作。

步骤1:自定义AccessibilityService

创建继承自AccessibilityService的类,监听窗口切换事件:

public class AppMonitorAccessibilityService extends AccessibilityService {
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // 监听窗口状态变化(App启动/切换的核心事件)
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            String packageName = event.getPackageName().toString();
            
            // 获取应用名称
            String appName = "";
            try {
                PackageManager pm = getPackageManager();
                ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
                appName = pm.getApplicationLabel(info).toString();
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
                appName = packageName;
            }
            
            // 弹出Toast提示
            Toast.makeText(getApplicationContext(), "用户启动了:" + appName, Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onInterrupt() {
        // 服务被中断时的提示
        Toast.makeText(this, "应用监听服务被中断", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        // 配置监听的事件类型
        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
        info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
        setServiceInfo(info);
    }
}

步骤2:在Manifest中注册Service

<service
    android:name=".AppMonitorAccessibilityService"
    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/accessibility_service_config" />
</service>

步骤3:创建辅助服务配置文件

在res/xml目录下创建accessibility_service_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeWindowStateChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true" />

步骤4:引导用户开启辅助功能

需要引导用户到系统设置开启你的辅助服务:

// 检查辅助服务是否已开启
private boolean isAccessibilityServiceEnabled(Context context) {
    int accessibilityEnabled = 0;
    final String service = context.getPackageName() + "/" + AppMonitorAccessibilityService.class.getName();
    try {
        accessibilityEnabled = Settings.Secure.getInt(
                context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);
    } catch (Settings.SettingNotFoundException e) {
        e.printStackTrace();
    }
    TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(':');

    if (accessibilityEnabled == 1) {
        String settingValue = Settings.Secure.getString(
                context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
        if (settingValue != null) {
            splitter.setString(settingValue);
            while (splitter.hasNext()) {
                String serviceName = splitter.next();
                if (serviceName.equalsIgnoreCase(service)) {
                    return true;
                }
            }
        }
    }
    return false;
}

// 跳转到辅助功能设置页面
private void requestAccessibilityPermission(Context context) {
    Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
}

注意事项

  • 辅助功能需要用户手动开启,需在App内友好提示操作流程;
  • 该方法监听更实时,无需定时查询,耗电更低;
  • 部分定制ROM可能限制辅助服务后台运行,需测试适配。

内容的提问来源于stack exchange,提问作者user6763460

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:24:39