Bootstrap

Android 保存/读取本地SD卡文件(兼容Android 13)

1.manifeast文件

(1)app权限

<!--存储权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

(2)application配置

<application
	...
    android:requestLegacyExternalStorage="true"
    android:usesCleartextTraffic="true"
    ...>

(3)组件配置

注意:Android 12以上,组件创建会自动生成以下属性

android:exported="true"

表示”是否支持其它应用调用当前组件”

如果不添加改属性,会报错。

2.动态申请文件存储权限

说明,Android的权限根据版本号分为三种

1:Android6.0之前

2:Android6.0-Android 10

3:Android 11以后

其中,6.0之前不需要动态申请权限,只需要在manifest文件中申请即可。从6.0之后,app需要动态申请权限,即弹框询问用户,是否给用户授权。Android 11以后,对权限的控制进一步收紧,很多的权限申请发生改变,例如,此前操作文件,只需要声明读写权限即可,但是现在划分了图片、音频、视频等等,并且操作普通文件的权限也变为MANAGE_EXTERNAL_STORAGE

代码如下:

private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private boolean havePermission = false;
@Override
protected void onResume() {
    super.onResume();
    checkPermission();
}
private AlertDialog dialog;

private void checkPermission() {
    //检查权限(NEED_PERMISSION)是否被授权 PackageManager.PERMISSION_GRANTED表示同意授权
    if (Build.VERSION.SDK_INT >= 30) {
        if (!Environment.isExternalStorageManager()) {
            if (dialog != null) {
                dialog.dismiss();
                dialog = null;
            }
            dialog = new AlertDialog.Builder(this)
                    .setTitle("提示")//设置标题
                    .setMessage("请开启文件访问权限,否则无法正常使用本应用!")
                    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int i) {
                            dialog.dismiss();
                        }
                    })
                    .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                            Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
                            startActivity(intent);
                        }
                    }).create();
            dialog.show();
        } else {
            havePermission = true;
            Log.i("swyLog", "Android 11以上,当前已有权限");
        }
    } else {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                //申请权限
                if (dialog != null) {
                    dialog.dismiss();
                    dialog = null;
                }
                dialog = new AlertDialog.Builder(this)
                        .setTitle("提示")//设置标题
                        .setMessage("请开启文件访问权限,否则无法正常使用本应用!")
                        .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                                ActivityCompat.requestPermissions(BrowserActivity.this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
                            }
                        }).create();
                dialog.show();
            } else {
                havePermission = true;
                Log.i("swyLog", "Android 6.0以上,11以下,当前已有权限");
            }
        } else {
            havePermission = true;
            Log.i("swyLog", "Android 6.0以下,已获取权限");
        }
    }
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);    
switch (requestCode) {
        case REQUEST_EXTERNAL_STORAGE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                havePermission = true;
                Toast.makeText(this, "授权成功!", Toast.LENGTH_SHORT).show();
            } else {
                havePermission = false;
                Toast.makeText(this, "授权被拒绝!", Toast.LENGTH_SHORT).show();
            }
            return;
        }
    }
}

3.文件操作

(1)定义文件保存的路径及文件名

private String FILE_SAVE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MobileReport/";
private String FILE_NAME = "user.txt";

(2)保存文件

private void saveLoginAccount(String json) {
    if (TextUtils.isEmpty(json)) {
        return;
    }
    Log.i("swyLog", "saveLoginAccount called");
    String value = encodeToString(json);
    Log.i("swyLog", "save 明文:" + json);
    Log.i("swyLog", "save 密文:" + value);
    File storage = new File(FILE_SAVE_PATH);
    if (!storage.exists()) {
        storage.mkdirs();
    }
    File tmepfile = new File(storage.getPath());
    if (!tmepfile.exists()) {
        tmepfile.mkdirs();
    }
    File file = new File(tmepfile, FILE_NAME);
    if (file.exists()) {
        Log.i("swyLog", "删除原有文件");
        file.delete();
    }
    if (!file.exists()) {
        Log.i("swyLog", "文件删除成功");
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    FileOutputStream fileOutputStream = null;
    try {
        fileOutputStream = new FileOutputStream(file);
        fileOutputStream.write(value.getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (fileOutputStream != null) {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

(3)读取文件

private String uploadLoginAccount() {
    Log.i("swyLog", "uploadLoginAccount called");
    InputStream inputStream = null;
    Reader reader = null;
    BufferedReader bufferedReader = null;
    try {
        File storage = new File(FILE_SAVE_PATH);
        if (!storage.exists()) {
            return "";
        }
        File file = new File(storage, FILE_NAME);
        if (!file.exists()) {
            return "";
        }
        inputStream = new FileInputStream(file);
        reader = new InputStreamReader(inputStream);
        bufferedReader = new BufferedReader(reader);
        StringBuilder result = new StringBuilder();
        String temp;
        while ((temp = bufferedReader.readLine()) != null) {
            result.append(temp);
        }
        String value = decodeToString(result.toString());
        Log.i("swyLog", "upload 密文:" + result);
        Log.i("swyLog", "upload 明文:" + value);
        return value;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return "";
}

(4)删除文件

private void deleteLoginAccount() {
    Log.i("swyLog", "deleteLoginAccount called");
    File storage = new File(FILE_SAVE_PATH);
    if (!storage.exists()) {
        storage.mkdirs();
    }
    File tmepfile = new File(storage.getPath());
    if (!tmepfile.exists()) {
        tmepfile.mkdirs();
    }
    File file = new File(tmepfile, FILE_NAME);
    if (file.exists()) {
        try {
            Log.i("swyLog", "删除原有文件");
            file.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    if (!file.exists()) {
        Log.i("swyLog", "文件删除成功");
    }
}

(5)base64 加解密方法

/**
 * 字符Base64加密
 *
 * @param str
 * @return
 */
public static String encodeToString(String str) {
    try {
        return Base64.encodeToString(str.getBytes("UTF-8"), Base64.DEFAULT);
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return "";
}

/**
 * 字符Base64解密
 *
 * @param str
 * @return
 */
public static String decodeToString(String str) {
    try {
        return new String(Base64.decode(str.getBytes("UTF-8"), Base64.DEFAULT));
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return "";
}

说明:我这里是从项目中复制的代码,作用是,将字符串加密之后,保存到sd卡种,加密的原因自然不言而喻,因为有些信息是不方便直接展示给用户看的。

;