一手遮天 Android - 存储: Android 11 通过 Storage Access Framework 管理文件

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

一手遮天 Android - 存储: Android 11 通过 Storage Access Framework 管理文件

示例如下:

/storage/Android11Demo3.java

/**
 * 本例用于演示 Android 11 通过 Storage Access Framework 管理文件
 *
 * SAF(Storage Access Framework)不需要申请权限,但是需要弹出对框框,让用户选择地址
 */

package com.webabcd.androiddemo.storage;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import com.webabcd.androiddemo.R;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileDescriptor;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

public class Android11Demo3 extends AppCompatActivity {

    private final String LOG_TAG = "Android11Demo3";

    private Button _button1;
    private Button _button2;
    private Button _button3;
    private Button _button4;
    private ImageView _imageView1;

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

        _button1 = findViewById(R.id.button1);
        _button2 = findViewById(R.id.button2);
        _button3 = findViewById(R.id.button3);
        _button4 = findViewById(R.id.button4);
        _imageView1 = findViewById(R.id.imageView1);

        sample();
    }

    private void sample() {
        // 通过 SAF 保存一个文本文件
        _button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createFileText();
            }
        });

        // 通过 SAF 保存一个图片文件
        _button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createFileImage();
            }
        });

        // 通过 SAF 读取一个文本文件
        _button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                chooseFileText();
            }
        });

        // 通过 SAF 读取一个图片文件
        _button4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                chooseFileImage();
            }
        });
    }


    // 弹出创建文件对框框,由用户创建文本文件
    private void createFileText() {
        Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("text/*");
        intent.putExtra(Intent.EXTRA_TITLE, "123.txt"); // 默认文件名
        // 弹出对话框,之后可以在 onActivityResult() 中获取用户的操作结果
        startActivityForResult(intent, 1);
    }
    // 写入文本数据(根据用户选择的 uri 地址)
    private void writeFileText(Uri uri) {
        OutputStream outputStream = null;
        BufferedWriter bufferedWriter = null;
        try {
            outputStream = this.getContentResolver().openOutputStream(uri);
            bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
            bufferedWriter.write("i am webabcd");
        } catch (Exception ex) {
            Log.d(LOG_TAG, "写入数据失败:" + ex.toString());
        } finally {
            try {
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (Exception ex) {

            }
        }
    }


    // 弹出创建文件对框框,由用户创建图片文件
    private void createFileImage() {
        Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        intent.putExtra(Intent.EXTRA_TITLE, "123.jpg"); // 默认文件名
        // 弹出对话框,之后可以在 onActivityResult() 中获取用户的操作结果
        startActivityForResult(intent, 2);
    }
    // 写入图片数据(根据用户选择的 uri 地址)
    private void writeFileImage(Uri uri) {
        // 需要写入的图片数据
        BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.son01, null);
        Bitmap bitmap = bitmapDrawable.getBitmap();

        OutputStream outputStream = null;
        try {
            outputStream = this.getContentResolver().openOutputStream(uri);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 85, outputStream);
        } catch (Exception ex) {
            Log.d(LOG_TAG, "写入数据失败:" + ex.toString());
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.flush();
                    outputStream.close();
                }
            } catch (Exception ex) {

            }
        }
    }


    // 弹出选择文件对话框,由用户选择文本文件
    private void chooseFileText() {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("text/*");
        // 弹出对话框,之后可以在 onActivityResult() 中获取用户的操作结果
        startActivityForResult(intent, 3);
    }
    // 读取文本数据(根据用户选择的 uri 地址)
    private void readFileText(Uri uri) {
        // 获取用户选择的文件的文件名
        Cursor cursor = this.getContentResolver().query(uri, null, null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            Log.d(LOG_TAG, "file name:" + displayName);
            cursor.close();
        }

        // 读取文本数据
        try {
            InputStream inputStream = getContentResolver().openInputStream(uri);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line);
            }
            bufferedReader.close();
            inputStream.close();

            Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();
        } catch (Exception ex) {
            Log.d(LOG_TAG, "读取数据失败:" + ex.toString());
        }

    }


    // 弹出选择文件对话框,由用户选择图片文件
    private void chooseFileImage() {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        startActivityForResult(intent, 4);
    }
    // 读取图片数据(根据用户选择的 uri 地址)
    private void readFileImage(Uri uri) {
        // 获取用户选择的文件的文件名
        Cursor cursor = this.getContentResolver().query(uri, null, null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            Log.d(LOG_TAG, "file name:" + displayName);
            cursor.close();
        }

        // 读取图片数据并显示
        try {
            ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r");
            FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
            Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
            parcelFileDescriptor.close();

            _imageView1.setImageBitmap(bitmap);
        } catch (Exception ex) {
            Log.d(LOG_TAG, "读取数据失败:" + ex.toString());
        }
    }


    // 获取用户在弹出的文件操作对话框中的操作结果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // Activity.RESULT_OK - 相关操作完成
        // Activity.DONT_FINISH_TASK_WITH_ACTIVITY - 没有任何操作
        if (resultCode != Activity.RESULT_OK) {
            Log.d(LOG_TAG, "用户没有任何操作");
            return;
        }

        // 获取用户选择的文件或保存的文件的 uri 地址(类似 content://com.android.providers.downloads.documents/document/3)
        Uri uri = null;
        if (data != null) {
            uri = data.getData();
            Log.d(LOG_TAG, "uri: " + uri.toString());
        }
        if (uri == null) {
            Log.d(LOG_TAG, "没有 uri");
            return;
        }

        if (requestCode == 1) {
            // 写入文本数据
            writeFileText(uri);
        } else if (requestCode == 2) {
            // 写入图片数据
            writeFileImage(uri);
        } else if (requestCode == 3) {
            // 读取文本数据
            readFileText(uri);
        } else if (requestCode == 4) {
            // 读取图片数据
            readFileImage(uri);
        }
    }
}

/layout/activity_storage_android11demo3.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="通过 SAF 保存一个文本文件" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="通过 SAF 保存一个图片文件" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="通过 SAF 读取一个文本文件" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="通过 SAF 读取一个图片文件" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp" />

</LinearLayout>

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

上一篇:HHTP协议——简单的 HTTP 协议


下一篇:判断URL字符串是否合法