Android 9开发的BLE扫描APP适配Android 11文件存储方案咨询
适配Android 11文件存储修改方案
核心崩溃原因
- Android 11(API 30)强制启用分区存储,原有直接读写公共存储根路径
/My Files/的方式被禁止,Environment.getExternalStorageDirectory()接口已废弃 - 原代码将文件流初始化直接写在类成员变量位置,此时Activity还未完成创建、权限未申请、目录未判断是否存在,直接触发IO异常崩溃
- 原代码的文件初始化逻辑存在笔误,且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());
可选扩展:让文件在系统文件管理器可见
如果需要保存的文件可以被用户直接在系统文件管理器查看,不需要连接电脑找应用专属目录,可采用以下方案:
- Manifest恢复添加
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>权限 - 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); } }
- 替换目录初始化代码为公共文档目录:
File publicDocDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS); dir = new File(publicDocDir, "My Files"); if (!dir.exists()) dir.mkdirs();
内容的提问来源于stack exchange,提问作者GAZZA
相关产品推荐
相关产品推荐

