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

已获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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:05:29