已获Root权限仍无法访问Android /data/user/0目录问题求助
问题分析
你的核心问题在于:Flutter的Dart IO API运行在应用自身的沙箱进程中,即使通过RootAccess.requestRootAccess获取了root权限,Dart的文件操作依然使用应用的普通UID,而非root权限。这就是为什么直接用File/Directory类访问/data/user/0会报权限拒绝,而通过su执行bash命令能成功——因为su是切换到root上下文执行的。
解决方案
方案1:封装Root权限的文件操作工具类
既然Process.run('su')能工作,可以把常用的文件操作封装成可复用的函数,避免重复编写su命令逻辑,让代码更易维护:
import 'dart:io'; class RootFileUtils { // 检查文件是否存在(root权限) static Future<bool> exists(String path) async { final result = await Process.run('su', ['-c', 'test -e "$path" && echo "exists"']); return result.exitCode == 0 && result.stdout.toString().trim() == 'exists'; } // 列出目录内容(root权限) static Future<List<String>> listDir(String path) async { final result = await Process.run('su', ['-c', 'ls -1 "$path"']); if (result.exitCode != 0) { throw Exception('Failed to list directory: ${result.stderr}'); } return result.stdout.toString().split('\n').where((item) => item.isNotEmpty).toList(); } // 读取文件内容(root权限) static Future<String> readFile(String path) async { final result = await Process.run('su', ['-c', 'cat "$path"']); if (result.exitCode != 0) { throw Exception('Failed to read file: ${result.stderr}'); } return result.stdout.toString(); } }
使用示例:
// 检查文件是否存在 bool exists = await RootFileUtils.exists('/data/user/0/com.bla.bla.bla/afile.txt'); print(exists); // 会正确返回true // 列出目录 List<String> dirContent = await RootFileUtils.listDir('/data/user/0'); for (String item in dirContent) { print(item); }
方案2:通过Android Native层实现Root文件操作
如果觉得封装su命令不够优雅,可以直接在Android原生代码中实现root权限的文件操作,再通过MethodChannel和Flutter通信:
步骤1:创建Android原生方法
在MainActivity.kt中添加:
import android.os.Bundle import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel import java.io.File class MainActivity : FlutterActivity() { private val CHANNEL = "com.example.root_file_ops" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "fileExists" -> { val path = call.argument<String>("path") ?: "" val exists = runAsRoot { File(path).exists() } result.success(exists) } "listDirectory" -> { val path = call.argument<String>("path") ?: "" val files = runAsRoot { File(path).listFiles()?.map { it.name } ?: emptyList() } result.success(files) } else -> result.notImplemented() } } } private fun <T> runAsRoot(block: () -> T): T? { return try { val process = Runtime.getRuntime().exec("su") val outputStream = process.outputStream val cmd = """ ${block.toString()} exit """.trimIndent() outputStream.write(cmd.toByteArray()) outputStream.flush() process.waitFor() block() } catch (e: Exception) { null } } }
步骤2:Flutter端调用Native方法
import 'package:flutter/services.dart'; class RootFileOps { static const MethodChannel _channel = MethodChannel('com.example.root_file_ops'); static Future<bool?> fileExists(String path) async { return await _channel.invokeMethod('fileExists', {'path': path}); } static Future<List<String>?> listDirectory(String path) async { return await _channel.invokeMethod('listDirectory', {'path': path}); } }
使用示例:
bool? exists = await RootFileOps.fileExists('/data/user/0/com.bla.bla.bla/afile.txt'); print(exists); List<String>? dirList = await RootFileOps.listDirectory('/data/user/0'); dirList?.forEach(print);
关键说明
- 不要期望Dart的原生IO API能直接使用root权限,因为Dart进程的权限上下文不会因为
su请求而改变。 - 封装su命令是轻量级方案,适合快速实现;Native层实现更稳定,适合复杂的文件操作场景。
内容的提问来源于stack exchange,提问作者Xeway
相关产品推荐
相关产品推荐

