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

如何在React Native中实现UsageStats模块获取用户各应用使用时长

实现方案

1 基础概念说明

安卓中的Context是应用运行的上下文对象,是访问系统服务、应用资源、调用系统API的必要入口,你不需要在React Native侧主动传递这个参数,在原生模块开发时可以直接获取到当前应用的Context。

2 安卓端原生模块封装

2.1 先添加权限声明

在android/app/src/main/AndroidManifest.xml中添加权限:

<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
    android:maxSdkVersion="33" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
    android:maxSdkVersion="32" />

2.2 创建原生模块类

在android/app/src/main/java/[你的包名]/下新建UsageStatsModule.java:

import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.HashMap;
import java.util.Map;

public class UsageStatsModule extends ReactContextBaseJavaModule {
    private static final String TAG = "UsageStatsModule";
    private final ReactApplicationContext reactContext;

    public UsageStatsModule(ReactApplicationContext context) {
        super(context);
        reactContext = context;
    }

    @Override
    public String getName() {
        return "UsageStats";
    }

    // 检查是否有使用统计权限
    private boolean checkPermission() {
        AppOpsManager appOps = (AppOpsManager) reactContext.getSystemService(Context.APP_OPS_SERVICE);
        int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
                android.os.Process.myUid(), reactContext.getPackageName());
        return mode == AppOpsManager.MODE_ALLOWED;
    }

    // 暴露给RN的权限检查方法
    @ReactMethod
    public void hasPermission(Promise promise) {
        promise.resolve(checkPermission());
    }

    // 暴露给RN的跳转权限设置页方法
    @ReactMethod
    public void openPermissionSetting() {
        Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        reactContext.startActivity(intent);
    }

    // 暴露给RN的获取使用时长方法
    @ReactMethod
    public void getUsageStats(long startTime, long endTime, Promise promise) {
        if (!checkPermission()) {
            promise.reject("PERMISSION_DENIED", "请先授予应用使用统计权限");
            return;
        }
        UsageStatsManager manager = (UsageStatsManager) reactContext.getSystemService(Context.USAGE_STATS_SERVICE);
        Map<String, UsageStats> usageStatsMap = manager.queryAndAggregateUsageStats(startTime, endTime);
        HashMap<String, Double> usageMap = new HashMap<>();

        // 只保留你需要的社交应用,不需要全量返回可以加过滤逻辑
        HashMap<String, String> targetApps = new HashMap<>();
        targetApps.put("com.facebook.katana", "facebook");
        targetApps.put("com.instagram.android", "instagram");
        targetApps.put("com.ss.android.ugc.aweme", "tiktok国内版");
        targetApps.put("com.zhiliaoapp.musically", "tiktok国际版");

        for (String packageName : usageStatsMap.keySet()) {
            if(targetApps.containsKey(packageName)) {
                UsageStats us = usageStatsMap.get(packageName);
                try {
                    long timeMs = us.getTotalTimeInForeground();
                    Double timeSeconds = (double) (timeMs / 1000);
                    usageMap.put(targetApps.get(packageName), timeSeconds);
                } catch (Exception e) {
                    Log.d(TAG, "获取应用时长出错: " + e.getMessage());
                }
            }
        }
        promise.resolve(usageMap);
    }
}

2.3 注册原生模块

同目录下新建UsageStatsPackage.java:

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class UsageStatsPackage implements ReactPackage {
    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new UsageStatsModule(reactContext));
        return modules;
    }
}

然后打开MainApplication.java,找到getPackages方法,把你的Package加进去:

@Override
protected List<ReactPackage> getPackages() {
    List<ReactPackage> packages = new PackageList(this).getPackages();
    packages.add(new UsageStatsPackage()); // 加这一行
    return packages;
}

3 React Native侧调用

修改你的组件代码如下:

import React, {useState, useEffect} from "react";
import { View, Text, StyleSheet, NativeModules, Alert } from "react-native";
import { Button } from "react-native-paper";

const { UsageStats } = NativeModules;

const Symptom4 = ({navigation}) => {
  const [usageList, setUsageList] = useState({});

  // 组件挂载时先检查权限
  useEffect(() => {
    checkPermission();
  }, []);

  const checkPermission = async () => {
    const hasPermission = await UsageStats.hasPermission();
    if(!hasPermission) {
      Alert.alert(
        "需要权限",
        "请在设置中开启应用使用统计权限以获取社交应用使用时长",
        [
          {text: "去开启", onPress: () => UsageStats.openPermissionSetting()},
          {text: "取消"}
        ]
      )
    }
  }

  const getUsageData = async () => {
    try {
      // 取过去24小时的时长,单位是毫秒
      const endTime = Date.now();
      const startTime = endTime - 24 * 60 * 60 * 1000;
      const data = await UsageStats.getUsageStats(startTime, endTime);
      setUsageList(data);
      console.log("应用使用时长:", data);
    } catch (e) {
      Alert.alert("获取失败", e.message);
      checkPermission();
    }
  }

  return (
    <View style={styles.container}>
      <Text>过去24小时社交应用使用时长(秒):</Text>
      {Object.entries(usageList).map(([appName, seconds]) => (
        <Text key={appName}>{appName}: {seconds}秒</Text>
      ))}
      <Button mode="contained" onPress={getUsageData} style={{marginVertical: 20}}>
        获取使用时长
      </Button>
      <Button mode="outlined" onPress={() => navigation.goBack()}>
        返回
      </Button>
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: 'white' }, 
});

export default Symptom4;

4 注意事项

  • 该功能仅支持安卓5.0(API 21)及以上系统,低于该版本的设备无法使用
  • 应用使用统计权限是特殊权限,无法通过普通的权限申请弹窗获取,必须引导用户跳转系统设置页手动开启
  • 不同地区的tiktok包名可能有差异,如果需要适配更多地区可以自行补充包名映射

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 07:45:06