Android打开系统文件管理器

龚奇逸
2023-12-01

一、前言

Android 4.4(API级别19)引入了存储访问框架(SAF)。
通过SAF,用户可以轻松地浏览和打开所有首选文档存储提供商中的文档,图像和其他文件。
也就是说,接下来介绍的方式适用于android4.4+的操作系统。
参考链接:https://developer.android.com/guide/topics/providers/document-provider

二、详细实现

1、调出系统文件管理器界面
private static final int READ_REQUEST_CODE = 42;
...
/**
 * Fires an intent to spin up the "file chooser" UI and select an image.
 */
public void performFileSearch() {

    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
    // browser.
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

    // Filter to only show results that can be "opened", such as a
    // file (as opposed to a list of contacts or timezones)
    intent.addCategory(Intent.CATEGORY_OPENABLE);

    // Filter to show only images, using the image MIME data type.
    // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
    // To search for all documents available via installed storage providers,
    // it would be "*/*".
    intent.setType("image/*");

    startActivityForResult(intent, READ_REQUEST_CODE);
}
1)Intent除了可以设置为ACTION_OPEN_DOCUMEN,还可以替换为ACTION_GET_CONTENT ,两者的区别如下:
  • 如果您希望您的应用仅读取/导入数据,请使用ACTION_GET_CONTENT。 通过这种方法,应用程序可以导入数据的副本,例如图像文件。
  • 如果希望您的应用对文件提供者拥有的文件具有长期,持久的访问权限,请使用ACTION_OPEN_DOCUMENT
2)Category设置为CATEGORY_OPENABLE,过滤出可以打开的文件
3)Type设置的是MIMETYPE,比如:
  • 过滤所有图片,则设置为:"image/*"
  • 只过滤png图片,则设置为:"image/png"
  • 过滤apk,那么可设置为:"application/vnd.android.package-archive"
4)startActivityForResult 启动Inetent,以便在onActivityResult接收文件选择结果
2、在onActivityResult中处理返回结果
@Override
public void onActivityResult(int requestCode, int resultCode,
        Intent resultData) {

    // The ACTION_OPEN_DOCUMENT intent was sent with the request code
    // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
    // response to some other intent, and the code below shouldn't run at all.

    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        // The document selected by the user won't be returned in the intent.
        // Instead, a URI to that document will be contained in the return intent
        // provided to this method as a parameter.
        // Pull that URI using resultData.getData().
        Uri uri = null;
        if (resultData != null) {
            uri = resultData.getData();
            Log.i(TAG, "Uri: " + uri.toString());
            showImage(uri);
        }
    }
}
1)从Intent中getData()获取到返回的Uri对象,旧版本是file://开头,新版本是content://开头
3、从Uri获取所需的信息
1)从Uri中获取文件元数据,一般只能获取到文件名与大小
public void dumpImageMetaData(Uri uri) {

    // The query, since it only applies to a single document, will only return
    // one row. There's no need to filter, sort, or select fields, since we want
    // all fields for one document.
    Cursor cursor = getActivity().getContentResolver()
            .query(uri, null, null, null, null, null);

    try {
    // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
    // "if there's anything to look at, look at it" conditionals.
        if (cursor != null && cursor.moveToFirst()) {

            // Note it's called "Display Name".  This is
            // provider-specific, and might not necessarily be the file name.
            String displayName = cursor.getString(
                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            Log.i(TAG, "Display Name: " + displayName);

            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
            // If the size is unknown, the value stored is null.  But since an
            // int can't be null in Java, the behavior is implementation-specific,
            // which is just a fancy term for "unpredictable".  So as
            // a rule, check if it's null before assigning to an int.  This will
            // happen often:  The storage API allows for remote files, whose
            // size might not be locally known.
            String size = null;
            if (!cursor.isNull(sizeIndex)) {
                // Technically the column stores an int, but cursor.getString()
                // will do the conversion automatically.
                size = cursor.getString(sizeIndex);
            } else {
                size = "Unknown";
            }
            Log.i(TAG, "Size: " + size);
        }
    } finally {
        cursor.close();
    }
}
2)将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;
}
3)将Uri转换为InputStream
private String readTextFromUri(Uri uri) throws IOException {
    StringBuilder stringBuilder = new StringBuilder();
    try (InputStream inputStream =
            getContentResolver().openInputStream(uri);
            BufferedReader reader = new BufferedReader(
            new InputStreamReader(Objects.requireNonNull(inputStream)))) {
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }
    }
    return stringBuilder.toString();
}
MIMETYPE 常见的用法:
文件类型mime名称文件类型mime名称
3gpvideo/3gpppdbchemical/x-pdb
aabapplication/x-authoware-binpdfapplication/pdf
aamapplication/x-authoware-mappfrapplication/font-tdpfr
aasapplication/x-authoware-segpgmimage/x-portable-graymap
aiapplication/postscriptpictimage/x-pict
aifaudio/x-aiffpmapplication/x-perl
aifcaudio/x-aiffpmdapplication/x-pmd
aiffaudio/x-aiffpngimage/png
alsaudio/X-Alpha5pnmimage/x-portable-anymap
amcapplication/x-mpegpnzimage/png
aniapplication/octet-streampotapplication/vnd.ms-powerpoint
apkapplication/vnd.android.package-archiveppmimage/x-portable-pixmap
asctext/plainppsapplication/vnd.ms-powerpoint
asdapplication/astoundpptapplication/vnd.ms-powerpoint
asfvideo/x-ms-asfpqfapplication/x-cprplayer
asnapplication/astoundpqiapplication/cprplayer
aspapplication/x-asapprcapplication/x-prc
asxvideo/x-ms-asfproxyapplication/x-ns-proxy-autoconfig
auaudio/basicpsapplication/postscript
avbapplication/octet-streamptlkapplication/listenup
avivideo/x-msvideopubapplication/x-mspublisher
awbaudio/amr-wbpvxvideo/x-pv-pvx
bcpioapplication/x-bcpioqcpaudio/vnd.qcelp
binapplication/octet-streamqtvideo/quicktime
bldapplication/bldqtiimage/x-quicktime
bld2application/bld2qtifimage/x-quicktime
bmpimage/bmpr3ttext/vnd.rn-realtext3d
bpkapplication/octet-streamraaudio/x-pn-realaudio
bz2application/x-bzip2ramaudio/x-pn-realaudio
calimage/x-calsrarapplication/x-rar-compressed
ccnapplication/x-cncrasimage/x-cmu-raster
ccoapplication/x-cocoardfapplication/rdf+xml
cdfapplication/x-netcdfrfimage/vnd.rn-realflash
cgimagnus-internal/cgirgbimage/x-rgb
chatapplication/x-chatrlfapplication/x-richlink
classapplication/octet-streamrmaudio/x-pn-realaudio
clpapplication/x-mscliprmfaudio/x-rmf
cmxapplication/x-cmxrmmaudio/x-pn-realaudio
coapplication/x-cult3d-objectrmvbaudio/x-pn-realaudio
codimage/cis-codrnxapplication/vnd.rn-realplayer
cpioapplication/x-cpioroffapplication/x-troff
cptapplication/mac-compactprorpimage/vnd.rn-realpix
crdapplication/x-mscardfilerpmaudio/x-pn-realaudio-plugin
cshapplication/x-cshrttext/vnd.rn-realtext
csmchemical/x-csmlrtex-lml/x-gps
csmlchemical/x-csmlrtfapplication/rtf
csstext/cssrtgapplication/metastream
curapplication/octet-streamrtxtext/richtext
dcmx-lml/x-evmrvvideo/vnd.rn-realvideo
dcrapplication/x-directorrwcapplication/x-rogerwilco
dcximage/x-dcxs3maudio/x-mod
dhtmltext/htmls3zaudio/x-mod
dirapplication/x-directorscaapplication/x-supercard
dllapplication/octet-streamscdapplication/x-msschedule
dmgapplication/octet-streamsdfapplication/e-score
dmsapplication/octet-streamseaapplication/x-stuffit
docapplication/mswordsgmtext/x-sgml
dotapplication/x-dotsgmltext/x-sgml
dviapplication/x-dvishapplication/x-sh
dwfdrawing/x-dwfsharapplication/x-shar
dwgapplication/x-autocadshtmlmagnus-internal/parsed-html
dxfapplication/x-autocadshwapplication/presentations
dxrapplication/x-directorsi6image/si6
ebkapplication/x-expandedbooksi7image/vnd.stiwap.sis
embchemical/x-embl-dl-nucleotidesi9image/vnd.lgtwap.sis
emblchemical/x-embl-dl-nucleotidesisapplication/vnd.symbian.install
epsapplication/postscriptsitapplication/x-stuffit
eriimage/x-eriskdapplication/x-Koan
esaudio/echospeechskmapplication/x-Koan
eslaudio/echospeechskpapplication/x-Koan
etcapplication/x-earthtimesktapplication/x-Koan
etxtext/x-setextslcapplication/x-salsa
evmx-lml/x-evmsmdaudio/x-smd
evyapplication/x-envoysmiapplication/smil
exeapplication/octet-streamsmilapplication/smil
fh4image/x-freehandsmpapplication/studiom
fh5image/x-freehandsmzaudio/x-smd
fhcimage/x-freehandsndaudio/basic
fifimage/fifspctext/x-speech
fmapplication/x-makersplapplication/futuresplash
fpximage/x-fpxsprapplication/x-sprite
fvivideo/isivideospriteapplication/x-sprite
gauchemical/x-gaussian-inputsptapplication/x-spt
gcaapplication/x-gca-compressedsrcapplication/x-wais-source
gdbx-lml/x-gdbstkapplication/hyperstudio
gifimage/gifstmaudio/x-mod
gpsapplication/x-gpssv4cpioapplication/x-sv4cpio
gtarapplication/x-gtarsv4crcapplication/x-sv4crc
gzapplication/x-gzipsvfimage/vnd
hdfapplication/x-hdfsvgimage/svg-xml
hdmtext/x-hdmlsvhimage/svh
hdmltext/x-hdmlsvrx-world/x-svr
hlpapplication/winhlpswfapplication/x-shockwave-flash
hqxapplication/mac-binhex40swflapplication/x-shockwave-flash
htmtext/htmltapplication/x-troff
htmltext/htmltadapplication/octet-stream
htstext/htmltalktext/x-speech
icex-conference/x-cooltalktarapplication/x-tar
icoapplication/octet-streamtazapplication/x-tar
iefimage/ieftbpapplication/x-timbuktu
ifmimage/giftbtapplication/x-timbuktu
ifsimage/ifstclapplication/x-tcl
imyaudio/melodytexapplication/x-tex
insapplication/x-NET-Installtexiapplication/x-texinfo
ipsapplication/x-ipscripttexinfoapplication/x-texinfo
ipxapplication/x-ipixtgzapplication/x-tar
itaudio/x-modthmapplication/vnd.eri.thm
itzaudio/x-modtifimage/tiff
ivri-world/i-vrmltiffimage/tiff
j2kimage/j2ktkiapplication/x-tkined
jadtext/vnd.sun.j2me.app-descriptortkinedapplication/x-tkined
jamapplication/x-jamtocapplication/toc
jarapplication/java-archivetoyimage/toy
jnlpapplication/x-java-jnlp-filetrapplication/x-troff
jpeimage/jpegtrkx-lml/x-gps
jpegimage/jpegtrmapplication/x-msterminal
jpgimage/jpegtsiaudio/tsplayer
jpzimage/jpegtspapplication/dsptype
jsapplication/x-javascripttsvtext/tab-separated-values
jwcapplication/jwctsvtext/tab-separated-values
kjxapplication/x-kjxttfapplication/octet-stream
lakx-lml/x-lakttzapplication/t-time
latexapplication/x-latextxttext/plain
lccapplication/fastmanultaudio/x-mod
lclapplication/x-digitallocaustarapplication/x-ustar
lcrapplication/x-digitallocauuapplication/x-uuencode
lghapplication/lghuueapplication/x-uuencode
lhaapplication/octet-streamvcdapplication/x-cdlink
lmlx-lml/x-lmlvcftext/x-vcard
lmlpackx-lml/x-lmlpackvdovideo/vdo
lsfvideo/x-ms-asfvibaudio/vib
lsxvideo/x-ms-asfvivvideo/vivo
lzhapplication/x-lzhvivovideo/vivo
m13application/x-msmediaviewvmdapplication/vocaltec-media-desc
m14application/x-msmediaviewvmfapplication/vocaltec-media-file
m15audio/x-modvmiapplication/x-dreamcast-vms-info
m3uaudio/x-mpegurlvmsapplication/x-dreamcast-vms
m3urlaudio/x-mpegurlvoxaudio/voxware
ma1audio/ma1vqeaudio/x-twinvq-plugin
ma2audio/ma2vqfaudio/x-twinvq
ma3audio/ma3vqlaudio/x-twinvq
ma5audio/ma5vrex-world/x-vream
manapplication/x-troff-manvrmlx-world/x-vrml
mapmagnus-internal/imagemapvrtx-world/x-vrt
mbdapplication/mbedletvrwx-world/x-vream
mctapplication/x-mascotvtsworkbook/formulaone
mdbapplication/x-msaccesswavaudio/x-wav
mdzaudio/x-modwaxaudio/x-ms-wax
meapplication/x-troff-mewbmpimage/vnd.wap.wbmp
meltext/x-vmelwebapplication/vnd.xara
miapplication/x-mifwiimage/wavelet
midaudio/midiwisapplication/x-InstallShield
midiaudio/midiwmvideo/x-ms-wm
mifapplication/x-mifwmaaudio/x-ms-wma
milimage/x-calswmdapplication/x-ms-wmd
mioaudio/x-miowmfapplication/x-msmetafile
mmfapplication/x-skt-lbswmltext/vnd.wap.wml
mngvideo/x-mngwmlcapplication/vnd.wap.wmlc
mnyapplication/x-msmoneywmlstext/vnd.wap.wmlscript
mocapplication/x-mochawmlscapplication/vnd.wap.wmlscriptc
mochaapplication/x-mochawmlscripttext/vnd.wap.wmlscript
modaudio/x-modwmvaudio/x-ms-wmv
mofapplication/x-yumekarawmxvideo/x-ms-wmx
molchemical/x-mdl-molfilewmzapplication/x-ms-wmz
mopchemical/x-mopac-inputwpngimage/x-up-wpng
movvideo/quicktimewptx-lml/x-gps
movievideo/x-sgi-moviewriapplication/x-mswrite
mp2audio/x-mpegwrlx-world/x-vrml
mp3audio/x-mpegwrzx-world/x-vrml
mp4video/mp4wstext/vnd.wap.wmlscript
mpcapplication/vnd.mpohun.certificatewscapplication/vnd.wap.wmlscriptc
mpevideo/mpegwvvideo/wavelet
mpegvideo/mpegwvxvideo/x-ms-wvx
mpgvideo/mpegwxlapplication/x-wxl
mpg4video/mp4x-gzipapplication/x-gzip
mpgaaudio/mpegxarapplication/vnd.xara
mpnapplication/vnd.mophun.applicationxbmimage/x-xbitmap
mppapplication/vnd.ms-projectxdmapplication/x-xdma
mpsapplication/x-mapserverxdmaapplication/x-xdma
mrltext/x-mrmlxdwapplication/vnd.fujixerox.docuworks
mrmapplication/x-mrmxhtapplication/xhtml+xml
msapplication/x-troff-msxhtmapplication/xhtml+xml
mtsapplication/metastreamxhtmlapplication/xhtml+xml
mtxapplication/metastreamxlaapplication/vnd.ms-excel
mtzapplication/metastreamxlcapplication/vnd.ms-excel
mzvapplication/metastreamxllapplication/x-excel
narapplication/zipxlmapplication/vnd.ms-excel
nbmpimage/nbmpxlsapplication/vnd.ms-excel
ncapplication/x-netcdfxltapplication/vnd.ms-excel
ndbx-lml/x-ndbxlwapplication/vnd.ms-excel
ndwnapplication/ndwnxmaudio/x-mod
nifapplication/x-nifxmltext/xml
nmzapplication/x-screamxmzaudio/x-mod
nokia-op-logoimage/vnd.nok-oplogo-colorxpiapplication/x-xpinstall
npxapplication/x-netfpxxpmimage/x-xpixmap
nsndaudio/nsndxsittext/xml
nvaapplication/x-neva1xsltext/xml
odaapplication/odaxultext/xul
oomapplication/x-AtlasMate-Pluginxwdimage/x-xwindowdump
pacaudio/x-pacxyzchemical/x-pdb
paeaudio/x-epacyz1application/x-yz1
panapplication/x-panzapplication/x-compress
pbmimage/x-portable-bitmapzacapplication/x-zaurus-zac
pcximage/x-pcxzipapplication/zip

三、文件创建、编辑与删除

1、创建文件,可以在onActivity中接收到结果

// Here are some examples of how you might call this method.
// The first parameter is the MIME type, and the second parameter is the name
// of the file you are creating:
//
// createFile("text/plain", "foobar.txt");
// createFile("image/png", "mypicture.png");

// Unique request code.
private static final int WRITE_REQUEST_CODE = 43;
...
private void createFile(String mimeType, String fileName) {
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);

    // Filter to only show results that can be "opened", such as
    // a file (as opposed to a list of contacts or timezones).
    intent.addCategory(Intent.CATEGORY_OPENABLE);

    // Create a file with the requested MIME type.
    intent.setType(mimeType);
    intent.putExtra(Intent.EXTRA_TITLE, fileName);
    startActivityForResult(intent, WRITE_REQUEST_CODE);
}

2、获取Uri之后,要是文件的Document.COLUMN_FLAGS 包含SUPPORTS_DELETE,则可以删除该文件:

DocumentsContract.deleteDocument(getContentResolver(), uri);

3、编辑文件

private static final int EDIT_REQUEST_CODE = 44;
/**
 * Open a file for writing and append some text to it.
 */
 private void editDocument() {
    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
    // file browser.
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

    // Filter to only show results that can be "opened", such as a
    // file (as opposed to a list of contacts or timezones).
    intent.addCategory(Intent.CATEGORY_OPENABLE);

    // Filter to show only text files.
    intent.setType("text/plain");

    startActivityForResult(intent, EDIT_REQUEST_CODE);
}

private void alterDocument(Uri uri) {
    try {
        ParcelFileDescriptor pfd = getActivity().getContentResolver().
                openFileDescriptor(uri, "w");
        FileOutputStream fileOutputStream =
                new FileOutputStream(pfd.getFileDescriptor());
        fileOutputStream.write(("Overwritten by MyCloud at " +
                System.currentTimeMillis() + "\n").getBytes());
        // Let the document provider know you're done by closing the stream.
        fileOutputStream.close();
        pfd.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

4、检查最新的文件数据

final int takeFlags = intent.getFlags()
            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(uri, takeFlags);

四、拓展

1、Android 7.0在存储访问框架中添加了虚拟文件的概念。
2、即使虚拟文件没有二进制表示形式,您的客户端应用程序也可以通过将其强制转换为其他文件类型或使用ACTION_VIEW意图查看这些文件来打开其内容
3、要打开虚拟文件,您的客户端应用程序需要包含特殊的逻辑来处理它们。如果要获取文件的字节表示形式(例如,预览文件),则需要从文档提供者处请求其他MIME类型。
4、要在您的应用程序中获取虚拟文档的URI,首先要创建一个Intent以打开文件选择器UI,就像前面在Seach中为文档显示的代码一样。
5、重要提示:由于应用无法使用openInputStream()方法直接打开虚拟文件,因此,如果您在ACTION_OPEN_DOCUMENT意向中包括CATEGORY_OPENABLE类别,则您的应用将不会收到任何虚拟文件。用户做出选择后,系统将调用onActivityResult()方法,如先前在处理结果中所示。您的应用可以检索文件的URI,然后使用类似于以下代码片段的方法来确定文件是否为虚拟文件。验证文件为虚拟文件后,可以将其强制转换为其他MIME类型,例如图像文件。以下代码段显示了如何检查虚拟文件是否可以表示为映像,如果可以,则从虚拟文件获取输入流。

private boolean isVirtualFile(Uri uri) {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false;
    }

    Cursor cursor = getContentResolver().query(
        uri,
        new String[] { DocumentsContract.Document.COLUMN_FLAGS },
        null, null, null);

    int flags = 0;
    if (cursor.moveToFirst()) {
        flags = cursor.getInt(0);
    }
    cursor.close();

    return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}


private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter)
    throws IOException {

    ContentResolver resolver = getContentResolver();

    String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);

    if (openableMimeTypes == null ||
        openableMimeTypes.length < 1) {
        throw new FileNotFoundException();
    }

    return resolver
        .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
        .createInputStream();
}

 类似资料: