You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android服务定时检测前台应用异常:调用getRunningTasks返回启动器包名

Hey there, let's break down why your current approach isn't working and fix it with the right tools for the job!

Why getRunningTasks() Fails

Starting with Android 5.0 (API Level 21), ActivityManager.getRunningTasks() was deprecated, and the system added strict permission restrictions. Regular apps can only retrieve task stack info for their own app, or get the launcher's package name when the launcher is in the foreground—that's exactly why you're seeing com.sec.android.app.launcher when switching to other apps. This method is now only useful for debugging or internal app task management, not for monitoring other apps' foreground status.

The Correct Solution: Use UsageStatsManager

To legally get the foreground app from the background, Android recommends using UsageStatsManager, which provides app usage statistics including the current foreground app. Note that this requires the user to manually grant the ACCESS_USAGE_STATS permission.

Step 1: Declare the Permission

Add this to your AndroidManifest.xml:

<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
    tools:ignore="ProtectedPermissions" />

This is a protected permission—you can't request it via the standard requestPermissions() flow. You'll need to guide the user to the system settings to enable it.

Step 2: Guide Users to Enable the Permission

Check if you have the permission before starting your service (or when your app first launches), and redirect to settings if not:

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;
}

// Call this to open the permission settings page
private void requestUsageStatsPermission(Context context) {
    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
}

Step 3: Fetch the Current Foreground App

Once you have the permission, use UsageStatsManager to get the foreground app's package name:

private String getForegroundAppPackageName(Context context) {
    UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
    long currentTime = System.currentTimeMillis();
    // Fetch usage stats from the last 10 seconds to get the latest foreground app
    List<UsageStats> usageStatsList = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
            currentTime - 10000, currentTime);

    if (usageStatsList == null || usageStatsList.isEmpty()) {
        return null;
    }

    String foregroundPackageName = null;
    long lastActiveTime = 0;

    for (UsageStats stats : usageStatsList) {
        if (stats.getLastTimeUsed() > lastActiveTime) {
            lastActiveTime = stats.getLastTimeUsed();
            foregroundPackageName = stats.getPackageName();
        }
    }

    return foregroundPackageName;
}

Step 4: Implement Periodic Detection in Your Service

To check every 5 seconds, use a Handler for the recurring task (for production, consider a static inner class + weak reference to avoid memory leaks—here's a simple working version):

public class ForegroundMonitorService extends Service {
    private Handler mDetectionHandler;
    private Runnable mDetectionTask;

    @Override
    public void onCreate() {
        super.onCreate();
        mDetectionHandler = new Handler();
        mDetectionTask = new Runnable() {
            @Override
            public void run() {
                String foregroundPackage = getForegroundAppPackageName(ForegroundMonitorService.this);
                if (foregroundPackage != null) {
                    // Handle the detection result
                    switch (foregroundPackage) {
                        case "com.android.chrome":
                            Log.d("ForegroundMonitor", "Chrome is active");
                            // Add your logic here (e.g., return the result)
                            break;
                        case "com.google.android.apps.maps":
                            Log.d("ForegroundMonitor", "Google Maps is active");
                            // Add your logic here
                            break;
                        default:
                            // Handle other apps if needed
                            break;
                    }
                }
                // Schedule next check in 5 seconds
                mDetectionHandler.postDelayed(this, 5000);
            }
        };
        // Start the first check immediately
        mDetectionHandler.post(mDetectionTask);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // Stop the recurring task to prevent memory leaks
        if (mDetectionHandler != null && mDetectionTask != null) {
            mDetectionHandler.removeCallbacks(mDetectionTask);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    // Include the hasUsageStatsPermission, requestUsageStatsPermission, and getForegroundAppPackageName methods here
}

Key Notes

  • Android 10+ Restrictions: From Android 10 (API 29), background apps have limited access to foreground app info. UsageStatsManager may have slight delays, or in some cases, return less accurate data. For higher precision (e.g., accessibility tools), consider using AccessibilityService instead.
  • Service Persistence: On Android 8.0+, regular background services are prone to being killed by the system. For long-running detection, use a ForegroundService (which shows a persistent notification) or adjust your logic to use WorkManager (though it's not ideal for 5-second intervals).
  • Package Name Accuracy: Double-check package names: Chrome is com.android.chrome, Google Maps is com.google.android.apps.maps.

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

火山引擎 最新活动