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

Android 9开发的BLE扫描APP适配Android 11文件存储方案咨询

适配Android 11文件存储修改方案

核心崩溃原因

  1. Android 11(API 30)强制启用分区存储,原有直接读写公共存储根路径/My Files/的方式被禁止,Environment.getExternalStorageDirectory()接口已废弃
  2. 原代码将文件流初始化直接写在类成员变量位置,此时Activity还未完成创建、权限未申请、目录未判断是否存在,直接触发IO异常崩溃
  3. 原代码的文件初始化逻辑存在笔误,且BLE扫描回调是异步线程,直接弹出Toast会触发线程异常

一、Manifest配置修改

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.joelwasserman.androidbletutorial">

    <!-- 兼容高低版本蓝牙权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" tools:targetApi="s" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
    <uses-feature android:name="android.hardware.bluetooth_le"  android:required="true" />

    <!-- 移除原有存储权限,用应用专属目录不需要额外权限 -->
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:requestLegacyExternalStorage="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

二、Java代码修改

1. 替换存储相关成员变量声明

删除原类中直接初始化文件流的代码,仅保留变量声明:

public class MainActivity extends AppCompatActivity {
    // 原有蓝牙、控件相关成员变量保留
    File dir;
    BufferedWriter bw1, bw2;
    FileWriter fw1, fw2;
    String fileName1, fileName2;
    File file1, file2;
    int i = 0;
    // 其他原有成员变量不变

删除原空参构造方法public MainActivity() throws IOException {}

2. onCreate中初始化存储目录

使用系统分配的应用专属存储目录,无需额外权限即可读写:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // 初始化存储目录,路径为 /sdcard/Android/data/你的包名/files/Documents/My Files
    File appFilesDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
    dir = new File(appFilesDir, "My Files");
    // 目录不存在则自动创建
    if (!dir.exists()) {
        dir.mkdirs();
    }

    // 原有其他onCreate逻辑不变
}

3. 修正startScanning中的文件初始化逻辑

修复原有笔误,确保文件流正确初始化:

public void startScanning() {
    i++;
    // 初始化第一个设备存储文件
    fileName1 = "DATASTOREBLE1_" + i + ".txt";
    file1 = new File(dir, fileName1);
    try {
        fw1 = new FileWriter(file1.getAbsoluteFile());
    } catch (IOException e) {
        e.printStackTrace();
    }
    bw1 = new BufferedWriter(fw1);
    // 初始化第二个设备存储文件
    fileName2 = "DATASTOREBLE2_" + i + ".txt";
    file2 = new File(dir, fileName2);
    try {
        fw2 = new FileWriter(file2.getAbsoluteFile());
    } catch (IOException e) {
        e.printStackTrace();
    }
    // 修复原有笔误:原代码错误把bw2赋值给bw1
    bw2 = new BufferedWriter(fw2);

    // 原有其他扫描逻辑不变
}

4. 修正扫描回调中的Toast调用

扫描回调运行在异步线程,需切到主线程弹出提示:

// 把两处Toast代码替换为以下逻辑
runOnUiThread(() -> Toast.makeText(MainActivity.this,fileName1 + " 已保存到\n " + dir, Toast.LENGTH_SHORT).show());

可选扩展:让文件在系统文件管理器可见

如果需要保存的文件可以被用户直接在系统文件管理器查看,不需要连接电脑找应用专属目录,可采用以下方案:

  1. Manifest恢复添加<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>权限
  2. onCreate中添加权限申请逻辑:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    if (!Environment.isExternalStorageManager()) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivity(intent);
    }
}
  1. 替换目录初始化代码为公共文档目录:
File publicDocDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
dir = new File(publicDocDir, "My Files");
if (!dir.exists()) dir.mkdirs();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 08:21:03