Storage Access Framework(SAF)

Storage Access Framework(SAF) 存储访问框架,Android 4.4+ 以后引入的为用户浏览手机中的存储内容提供了便利,可供访问的内容包括:文档,图片,视频,音频,下载

SAF 框架的组成

  • Document provider

    DocumentsProvider 的子类

    一个特殊的 ContentProvider,让一个存储服务(比如 Google Drive)可以对外展示自己所管理的文件

    Android 系统内置了几个 Document provider,比如关于下载,图片以及视频的 Document provider

  • Client app

    一个普通的客户端软件

    触发 ACTION_OPEN_DOCUMENTACTION_CREATE_DOCUMENT 就可以接收到来自于 Document provider 返回的内容,比如选择一个图片,然后返回一个Uri

  • Picker

    类似于文件管理器的界面,而且是系统级的界面,提供额访问客户端过滤条件的 Document provider 内容的通道

SAF

SAF 的核心是实现了 DocumentsProvider 的子类,还是一个 ContentProvider 在一个 document provider 中是以传统的文件目录树组织起来的

SAF 流程图

document provider data 是基于传统的文件层次结构的,不过那只是对外的表现形式

如何存储你的数据,取决于你自己,只要对外的接口能够通过 DocumentsProvider 的 api 访问就可以

下面的流程图展示了一个 photo 应用使用 SAF 可能的结构

从图中可以看出 Picker 是链接调用者和内容提供者的一个桥梁,它提供并告诉调用者,可以选择 哪些内容提供者,比如这里的 DriveDocProvider,UsbDocProvider,CloundDocProvider

当客户端触发了 ACTION_OPEN_DOCUMENTACTION_CREATE_DOCUMENT 的 Intent,就会发生上述交互。当然我们还可以在 Intent 中增加过滤条件,比如限制 MIME type 的类型为 "image"

客户端调用,并获取返回的 Uri

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final int READ_REQUEST_CODE = 42;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn_show = (Button) findViewById(R.id.btn_show);
        btn_show.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        startActivityForResult(intent, READ_REQUEST_CODE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            Uri uri;
            if (data != null) {
                uri = data.getData();
                Log.e("HeHe", "Uri: " + uri.toString());
            }
        }
    }
}

根据 uri 获取文件参数

public void dumpImageMetaData(Uri uri) {
    Cursor cursor = getContentResolver()
            .query(uri, null, null, null, null, null);
    try {
        if (cursor != null && cursor.moveToFirst()) {
            String displayName = cursor.getString(
                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            Log.e("HeHe", "Display Name: " + displayName);
            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
            String size = null;
            if (!cursor.isNull(sizeIndex)) {
                size = cursor.getString(sizeIndex);
            }else {
                size = "Unknown";
            }
            Log.e("HeHe", "Size: " + size);
        }
    }finally {
        cursor.close();
    }
}

根据 Uri 获得 Bitmap

private Bitmap getBitmapFromUri(Uri uri) throws IOException {
        ParcelFileDescriptor parcelFileDescriptor =
        getContentResolver().openFileDescriptor(uri, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
        parcelFileDescriptor.close();
        return image;
}

根据 Uri 获取输入流

private String readTextFromUri(Uri uri) throws IOException {
    InputStream inputStream = getContentResolver().openInputStream(uri);
    BufferedReader reader = new BufferedReader(new InputStreamReader(
            inputStream));
    StringBuilder stringBuilder = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
        stringBuilder.append(line);
    }
    fileInputStream.close();
    parcelFileDescriptor.close();
    return stringBuilder.toString();
}

创建新文件以及删除文件

创建文件

private void createFile(String mimeType, String fileName) {
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType(mimeType);
    intent.putExtra(Intent.EXTRA_TITLE, fileName);
    startActivityForResult(intent, WRITE_REQUEST_CODE);
}

可在 onActivityResult() 中获取被创建文件的 uri

删除文件

前提是 Document.COLUMN_FLAGS 包含 SUPPORTS_DELETE

DocumentsContract.deleteDocument(getContentResolver(), uri);

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.