一、Android 存储方式的演变
1、随着Android版本的升级,私有目录(/data/data/package/)的存储方式没有发生改变,当应用卸载之后,这部分的内容也随之删除,变化的是外置卡的存储方式,下面我们讲的内容都是针对外置卡的存储方式而言。
2、Android 9 外置卡存储方式,每个应用都可以访问,包括增删改查(当然这个APP已经申请了读写外置卡的权限)。
3、Android 10 对分区存储做了一个过渡,你可以选择分区存储也可以不选,如果在Application的节点中设置这个属性为true,requestLegacyExternalStorage=“true” 则代表不使用分区存储,就和Android 9 一样。
4、Android 11 强制进行分区存储,也是就是对每个文件夹存储那些文件内容都做了规范,比如Download目录下可以存放任何的文件,Movies(存放 MP4视频类型的文件)、Music(存放mp3音乐类型的)、Picture(存放 png、jpg图片类型的)、Documents(存放文档 txt word 等)
二、Android 存储权限的讲解
媒体文件集指的是图片、音频、视频文件
1、Android 10 中,自己的 App 无需任何权限(这里说的权限也包括 WRITE_EXTERNAL_STORAGE 和 READ_EXTERNAL_STORAGE)就能向媒体集添加文件,也可以编辑和删除自己添加的媒体文件。
如果你要读取并操作非自己应用创建的媒体文件,就需要读写外部存储权限,如果用户没有同意,你将无法编辑和删除并非自己创建的媒体文件。
2、下载文件集
用于和其他应用分享非媒体文件
你无需添加任何权限,就可以编辑和删除自己添加的非媒体文件。
与媒体不同的是,即使有读取外部存储权限,也不允许访问由其他应用创建的非媒体文件。想要获此权限,必须通过调用 Storage Access Framerwork API,启动系统文件选择器,让用户进行选择访问那些文件和目录。
三、具体的实例代码演示
Android 9 之前创建文件方式
public void createOther(View view) {
// 这个就是一个文件目录
File file = new File("/sdcard/lgj.txt");
if (!file.exists()) {
try {
// 创建文件
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Android 11 创建文件的方式
// Android R /data/data/com.android.providers.media/databases/external.db 专门管理各种文件数据库的
// 有一张表,files 专门管理所有文件的数据表(只有当你选择分区存储的时候,才这么玩,包含 文件,图片,视频
// Android R 操作文件的方式,这种方式在Android 10 想要兼容,必须设置 requestLegacyExternalStorage="false"
public void createOn11(View view) {
// MediaStore.Files 这个是放置,所有的文件夹(包括Documents、Movies、Picture等)
// Android 11 操作文件就是操作数据库的,获取到一个路径
Uri uri = MediaStore.Files.getContentUri("external");
// 创建一个ContentValue对象,用来插入数据库(给存储文件的数据)
ContentValues contentValues = new ContentValues();
// 直接创建 lgj.txt 要存储的路径,要创建的文件的上一级存储目录
String path = Environment.DIRECTORY_DOWNLOADS + "lgj";// 这个是文件夹
// 给路径的字段设置键值对
contentValues.put(MediaStore.Downloads.RELATIVE_PATH,path);
// 设置文件的名字,如果数据库已经有了这个文件,有可能存储不成功
contentValues.put(MediaStore.Downloads.DISPLAY_NAME,"lgj.txt");
// 可以不用设置
contentValues.put(MediaStore.Downloads.TITLE,"lgj");
// 第一个参数是,操作那个数据库,因为我们操作的是 external 数据库,所以直接传递上面的那个uri就行
// 插入一条数据,然后把生成的这个文件的路径返回回来
Uri insert = getContentResolver().insert(uri, contentValues);
// 在Android 11 上,所有的文件和文件夹从名字上是无法区分的,除非你这个文件里面是有内容,才能真正成为文件
String content = "我还是我,不一样的烟火";
OutputStream outputStream = null;
try {
// 这块是往这个文件中写入数据,那么他才会真正的成为一个文件,否则就是文件夹
outputStream = getContentResolver().openOutputStream(insert);
BufferedOutputStream bos = new BufferedOutputStream(outputStream);
bos.write(content.getBytes());
bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
创建图片以及对图片的增删改查
public void createImg(View view){
String imgName = "mv.jpg";
ContentResolver contentResolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.ImageColumns.RELATIVE_PATH,Environment.DIRECTORY_PICTURES);
contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME,imgName);
//图片需要多传一个参数,声明图片的类型,
contentValues.put(MediaStore.Images.ImageColumns.MIME_TYPE,"image/jpg");
contentValues.put(MediaStore.Downloads.TITLE,imgName);
// 插入外置卡路径
Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
// 所有文件的创建都需要写数据,如果不写数据那么就生成不了文件
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_background);
try {
OutputStream outputStream = contentResolver.openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Uri queryUri;
public void queryImg(){
// 获取外置卡专门存放图片的URI
Uri externalContentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String selection = MediaStore.Images.Media.DISPLAY_NAME +"=?";
String[] arg = new String[]{"mv.jpg"};
// 第二个参数是指定查询的列,如果为null,那么就查询所有
Cursor cursor = getContentResolver().query(externalContentUri, null, selection, arg, null);
if (cursor != null && cursor.moveToFirst()){
// 这是获取图片存储的绝对路径,
// int columnIndexOrThrow = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
// String data = cursor.getString(columnIndexOrThrow);
// 我要获取这张图片的URI,我先拿到这个图片的id
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
// 将我们查询的 id 转化成对应的URI,
queryUri = ContentUris.withAppendedId(externalContentUri, id);
cursor.close();
}
}
public void deleteImg(){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
int delete = getContentResolver().delete(queryUri, null);
if (delete > 0){
Toast.makeText(this,"删除成功",Toast.LENGTH_SHORT).show();
}
}
}
public void updateImg(){
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME,"美女.jpg");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
int update = getContentResolver().update(queryUri, null, null);
if (update > 0){
Toast.makeText(this,"修改成功",Toast.LENGTH_SHORT).show();
}
}
}
// 动态检查权限
public static boolean checkPermission(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
},1);
}
return false;
}
后续代码继续更新,应该把这些繁琐的操作写成框架