如何在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
相关产品推荐
相关产品推荐

