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

Android技术问询:如何列出所有已安装应用并获取及展示各应用总运行时长

Android 实现列出所有已安装应用并展示运行时长

一、声明必要权限

首先在AndroidManifest.xml中添加特殊权限:

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

该权限无法通过常规动态申请流程获取,需引导用户跳转到系统设置开启:

private fun checkUsageStatsPermission() {
    val appOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
    val mode = appOpsManager.checkOpNoThrow(
        AppOpsManager.OPSTR_GET_USAGE_STATS,
        android.os.Process.myUid(),
        packageName
    )
    val hasPermission = mode == AppOpsManager.MODE_ALLOWED
    if (!hasPermission) {
        startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
    }
}

二、获取已安装应用列表

通过PackageManager获取所有已安装应用的基础信息,可按需过滤系统应用或禁用应用:

private fun getInstalledApps(): List<AppInfo> {
    val appList = mutableListOf<AppInfo>()
    val packageManager = packageManager
    val packages = packageManager.getInstalledPackages(PackageManager.GET_META_DATA)
    
    for (packageInfo in packages) {
        // 可选:跳过系统应用
        val isSystemApp = packageInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0
        if (isSystemApp) continue
        
        // 可选:跳过已禁用应用
        val isEnabled = packageManager.getApplicationEnabledSetting(packageInfo.packageName).let {
            it == PackageManager.COMPONENT_ENABLED_STATE_ENABLED ||
            it == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
        }
        if (!isEnabled) continue
        
        val appName = packageManager.getApplicationLabel(packageInfo.applicationInfo).toString()
        val appIcon = packageManager.getApplicationIcon(packageInfo.applicationInfo)
        val packageName = packageInfo.packageName
        
        appList.add(AppInfo(appName, packageName, appIcon, 0))
    }
    return appList
}

// 数据类存储应用信息
data class AppInfo(
    val appName: String,
    val packageName: String,
    val appIcon: Drawable,
    var totalUsageTime: Long // 运行时长(毫秒)
)

三、获取应用运行时长

使用UsageStatsManager(API 21+)查询应用使用统计,计算总运行时长:

private fun getAppUsageTime(appList: List<AppInfo>): List<AppInfo> {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        // 低版本设备无法获取时长,直接返回原列表
        return appList
    }
    
    val usageStatsManager = getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
    val endTime = System.currentTimeMillis()
    val startTime = endTime - 7 * 24 * 60 * 60 * 1000 // 查询过去7天的数据,可自定义时间范围
    
    val usageStatsList = usageStatsManager.queryUsageStats(
        UsageStatsManager.INTERVAL_DAILY,
        startTime,
        endTime
    )
    
    val usageMap = mutableMapOf<String, Long>()
    usageStatsList.forEach { stats ->
        // 累加前台+可见时长,可按需选择单一维度
        val totalTime = stats.totalTimeInForeground + stats.totalTimeVisible
        usageMap[stats.packageName] = usageMap.getOrDefault(stats.packageName, 0) + totalTime
    }
    
    return appList.map { appInfo ->
        appInfo.copy(totalUsageTime = usageMap.getOrDefault(appInfo.packageName, 0))
    }
}

四、展示应用信息

用RecyclerView实现列表展示,先创建item布局item_app_info.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:orientation="horizontal"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/iv_app_icon"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:scaleType="centerCrop"/>

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:layout_marginStart="16dp">

        <TextView
            android:id="@+id/tv_app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:textStyle="bold"/>

        <TextView
            android:id="@+id/tv_package_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:textColor="@android:color/darker_gray"
            android:layout_marginTop="4dp"/>
    </LinearLayout>

    <TextView
        android:id="@+id/tv_usage_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textColor="@android:color/holo_blue_dark"/>
</LinearLayout>

再实现适配器AppInfoAdapter:

class AppInfoAdapter(private val appList: List<AppInfo>) :
    RecyclerView.Adapter<AppInfoAdapter.ViewHolder>() {

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val ivAppIcon: ImageView = itemView.findViewById(R.id.iv_app_icon)
        val tvAppName: TextView = itemView.findViewById(R.id.tv_app_name)
        val tvPackageName: TextView = itemView.findViewById(R.id.tv_package_name)
        val tvUsageTime: TextView = itemView.findViewById(R.id.tv_usage_time)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_app_info, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val appInfo = appList[position]
        holder.ivAppIcon.setImageDrawable(appInfo.appIcon)
        holder.tvAppName.text = appInfo.appName
        holder.tvPackageName.text = appInfo.packageName
        holder.tvUsageTime.text = formatUsageTime(appInfo.totalUsageTime)
    }

    override fun getItemCount(): Int = appList.size

    // 将毫秒转换为友好的时分秒格式
    private fun formatUsageTime(millis: Long): String {
        val seconds = (millis / 1000) % 60
        val minutes = (millis / (1000 * 60)) % 60
        val hours = (millis / (1000 * 60 * 60)) % 24
        val days = millis / (1000 * 60 * 60 * 24)
        return when {
            days > 0 -> "${days}天${hours}时"
            hours > 0 -> "${hours}时${minutes}分"
            minutes > 0 -> "${minutes}分${seconds}秒"
            else -> "${seconds}秒"
        }
    }
}

最后在Activity中初始化并加载数据:

class MainActivity : AppCompatActivity() {
    private lateinit var recyclerView: RecyclerView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)

        checkUsageStatsPermission()

        // 延迟执行,确保用户完成权限授权
        Handler(Looper.getMainLooper()).postDelayed({
            loadAppData()
        }, 2000)
    }

    private fun loadAppData() {
        // 建议放在子线程执行,避免阻塞主线程
        CoroutineScope(Dispatchers.IO).launch {
            val appList = getInstalledApps()
            val appListWithUsage = getAppUsageTime(appList)
            withContext(Dispatchers.Main) {
                recyclerView.adapter = AppInfoAdapter(appListWithUsage)
            }
        }
    }

    // 上述checkUsageStatsPermission、getInstalledApps、getAppUsageTime方法放在此处
}

五、注意事项

  • 版本兼容:UsageStatsManager仅支持API 21及以上设备,低版本需做降级处理。
  • 权限校验:必须确保用户开启使用统计权限,否则无法获取运行时长数据。
  • 性能优化:获取应用列表和查询统计数据属于耗时操作,建议放在子线程执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 22:25:33