如何获取并展示UsageStats数据?如何提取应用启停时间?
Hey there! Let's tackle your two main needs: building a clean app usage display, and pulling out individual app session start/end times from the UsageStats data you're already fetching.
1. Implementing the App Usage Display UI
A RecyclerView is perfect for showing a scrollable list of app usage details. We'll add app icons, package names, total foreground time, and last used time to each list item.
Step 1: Add Required Permissions
First, make sure you have the necessary permission in your AndroidManifest.xml (note: users need to manually enable this in Settings > Security > Apps with usage access):
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
Step 2: Layout for the RecyclerView
Create activity_usage_stats.xml to hold the RecyclerView:
<?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="match_parent" android:orientation="vertical"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/usage_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Step 3: Item Layout for Each App Entry
Make item_usage_stats.xml to display individual app details:
<?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/app_icon" android:layout_width="48dp" android:layout_height="48dp" android:marginEnd="16dp"/> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/package_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:textStyle="bold"/> <TextView android:id="@+id/total_foreground_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="14sp" android:textColor="@android:color/darker_gray"/> <TextView android:id="@+id/last_used_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="14sp" android:textColor="@android:color/darker_gray"/> </LinearLayout> </LinearLayout>
Step 4: Build the RecyclerView Adapter
Create an adapter to bind your UsageStats data to the UI:
public class UsageStatsAdapter extends RecyclerView.Adapter<UsageStatsAdapter.UsageViewHolder> { private List<UsageStats> mUsageStatsList; private PackageManager mPackageManager; public UsageStatsAdapter(List<UsageStats> usageStatsList, PackageManager packageManager) { mUsageStatsList = usageStatsList; mPackageManager = packageManager; } @NonNull @Override public UsageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_usage_stats, parent, false); return new UsageViewHolder(view); } @Override public void onBindViewHolder(@NonNull UsageViewHolder holder, int position) { UsageStats stats = mUsageStatsList.get(position); String packageName = stats.getPackageName(); // Set package name holder.packageNameTextView.setText(packageName); // Format total foreground time (ms to hh:mm:ss) String totalTime = formatMillisToTime(stats.getTotalTimeInForeground()); holder.totalForegroundTimeTextView.setText("Total Foreground Time: " + totalTime); // Format last used time String lastUsed = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) .format(new Date(stats.getLastTimeUsed())); holder.lastUsedTimeTextView.setText("Last Used: " + lastUsed); // Load app icon try { Drawable appIcon = mPackageManager.getApplicationIcon(packageName); holder.appIconImageView.setImageDrawable(appIcon); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); holder.appIconImageView.setImageResource(R.drawable.ic_default_app); } } @Override public int getItemCount() { return mUsageStatsList.size(); } public static class UsageViewHolder extends RecyclerView.ViewHolder { ImageView appIconImageView; TextView packageNameTextView; TextView totalForegroundTimeTextView; TextView lastUsedTimeTextView; public UsageViewHolder(@NonNull View itemView) { super(itemView); appIconImageView = itemView.findViewById(R.id.app_icon); packageNameTextView = itemView.findViewById(R.id.package_name); totalForegroundTimeTextView = itemView.findViewById(R.id.total_foreground_time); lastUsedTimeTextView = itemView.findViewById(R.id.last_used_time); } } // Helper to convert milliseconds to readable time private String formatMillisToTime(long millis) { long seconds = millis / 1000; long minutes = seconds / 60; long hours = minutes / 60; seconds = seconds % 60; minutes = minutes % 60; return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds); } }
Step 5: Bind Data to RecyclerView in Your Activity
Update your existing code to connect the stats list to the RecyclerView:
// After fetching the stats list RecyclerView recyclerView = findViewById(R.id.usage_recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); UsageStatsAdapter adapter = new UsageStatsAdapter(stats, getPackageManager()); recyclerView.setAdapter(adapter);
2. Extracting App Start & End Times
Important note: queryUsageStats() returns aggregated data (like total foreground time for the period). To get individual app session start/end times, you need to use UsageEvents—it tracks every foreground/background transition.
Step 1: Query UsageEvents
Add this code to fetch events for your time range:
UsageStatsManager usageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_MONTH, -1); long start = calendar.getTimeInMillis(); long end = System.currentTimeMillis(); // Fetch all usage events in the time range UsageEvents events = usageStatsManager.queryEvents(start, end);
Step 2: Process Events to Capture Sessions
Track when apps move to foreground (start) and background (end):
// Track apps currently in foreground Map<String, Long> currentForegroundApps = new HashMap<>(); // Store all app sessions (create a model class for this) List<AppSession> appSessions = new ArrayList<>(); UsageEvents.Event event = new UsageEvents.Event(); while (events.hasNextEvent()) { events.getNextEvent(event); switch (event.getEventType()) { case UsageEvents.Event.MOVE_TO_FOREGROUND: // App started - record start time currentForegroundApps.put(event.getPackageName(), event.getTimeStamp()); break; case UsageEvents.Event.MOVE_TO_BACKGROUND: // App closed - calculate session duration Long startTime = currentForegroundApps.remove(event.getPackageName()); if (startTime != null) { long endTime = event.getTimeStamp(); appSessions.add(new AppSession(event.getPackageName(), startTime, endTime)); } break; } } // Handle apps still in foreground at the end of the period for (Map.Entry<String, Long> entry : currentForegroundApps.entrySet()) { appSessions.add(new AppSession(entry.getKey(), entry.getValue(), end)); }
Step 3: Create an AppSession Model
Store session data neatly with this class:
public class AppSession { private String packageName; private long startTime; private long endTime; public AppSession(String packageName, long startTime, long endTime) { this.packageName = packageName; this.startTime = startTime; this.endTime = endTime; } // Getters for session data public String getPackageName() { return packageName; } public long getStartTime() { return startTime; } public long getEndTime() { return endTime; } public long getDuration() { return endTime - startTime; } }
Now you can use the appSessions list to display individual start/end times, just like you did with the aggregated stats!
内容的提问来源于stack exchange,提问作者Sanjay Hadiya

