zoukankan      html  css  js  c++  java
  • 【Android Developers Training】 37. 共享一个文件

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

    原文链接:http://developer.android.com/training/secure-file-sharing/share-file.html


    一旦你配置了你的应用来使用URI共享文件,你可以响应其他应用关于这些文件的需求。一种响应的方法是在服务应用端提供一个文件选择接口,它可以由其他应用激活。这种方法可以允许客户应用端让用户从服务应用端选择一个文件,然后接收这个文件的URI。

    这节课将会向你展示如何在你的应用中创建一个用来选择文件的Activity,来响应这些索取文件的需求。


    一). 接收文件需求

    为了从客户应用端接收一个文件索取需求,然后以URI形式进行响应,你的应用应该提供一个选择文件的Activity。客户应用端通过调用startActivityForResult()来启动这个Activity。该方法包含了一个Intent,它具有ACTION_PICK行为。当客户应用端调用了startActivityForResult(),你的应用可以向客户应用端返回一个结果,该结果即用户所选文件对应的URI。

    学习如何在客户应用端实现文件索取需求,阅读:Requesting a Shared File


    二). 创建一个文件选择Activity

    为了配置文件选择Activity,我们从在清单文件定义你的Activity开始,在其intent过滤器中,匹配ACTION_PICK行为,以及CATEGORY_DEFAULTCATEGORY_OPENABLE类型。另外,为你的应用向其他应用所提供的文件设置MIME类型过滤器。下面的这段代码展示了如何在清单文件中定义新的Activity和intent过滤器:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
        ...
            <application>
            ...
                <activity
                    android:name=".FileSelectActivity"
                    android:label="@"File Selector" >
                    <intent-filter>
                        <action
                            android:name="android.intent.action.PICK"/>
                        <category
                            android:name="android.intent.category.DEFAULT"/>
                        <category
                            android:name="android.intent.category.OPENABLE"/>
                        <data android:mimeType="text/plain"/>
                        <data android:mimeType="image/*"/>
                    </intent-filter>
                </activity>

    在代码中定义文件选择Activity

    下面,定义一个Activity子类它显示在你内部存储的“files/images/”目录下可以获得的文件,然后允许用户选择期望的文件。下面的代码显示了如何定义这个Activity。并且响应用户的选择:

    public class MainActivity extends Activity {
        // The path to the root of this app's internal storage
        private File mPrivateRootDir;
        // The path to the "images" subdirectory
        private File mImagesDir;
        // Array of files in the images subdirectory
        File[] mImageFiles;
        // Array of filenames corresponding to mImageFiles
        String[] mImageFilenames;
        // Initialize the Activity
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...
            // Set up an Intent to send back to apps that request a file
            mResultIntent =
                    new Intent("com.example.myapp.ACTION_RETURN_FILE");
            // Get the files/ subdirectory of internal storage
            mPrivateRootDir = getFilesDir();
            // Get the files/images subdirectory;
            mImagesDir = new File(mPrivateRootDir, "images");
            // Get the files in the images subdirectory
            mImageFiles = mImagesDir.listFiles();
            // Set the Activity's result to null to begin with
            setResult(Activity.RESULT_CANCELED, null);
            /*
             * Display the file names in the ListView mFileListView.
             * Back the ListView with the array mImageFilenames, which
             * you can create by iterating through mImageFiles and
             * calling File.getAbsolutePath() for each File
             */
             ...
        }
        ...
    }

    三). 响应一个文件选择

    一旦一个用户选择了一个共享的文件,你的应用必须明确哪个文件被选择了,然后为这个文件生成一个对应的URI。若ActivityListView中显示了可获得文件的清单,当用户点击了一个文件名时,系统调用了方法onItemClick(),在该方法中你可以获取被选择的文件。

    onItemClick()中,为选择的文件文件名获取一个File对象,然后将它作为参数传递给getUriForFile(),另外还需传入的参数是你为FileProvider所指定的<provider>标签值。这个结果URI包含了相应的被访问权限,一个对应于文件目录的路径标记(如在XML meta-date中定义的),以及包含扩展名的文件名。有关FileProvider如何了解基于XML meta-data的目录路径的信息,可以阅读:Specify Sharable Directories

    下面的例子展示了你如何检测选中的文件并且获得一个URI: 

        protected void onCreate(Bundle savedInstanceState) {
            ...
            // Define a listener that responds to clicks on a file in the ListView
            mFileListView.setOnItemClickListener(
                    new AdapterView.OnItemClickListener() {
                @Override
                /*
                 * When a filename in the ListView is clicked, get its
                 * content URI and send it to the requesting app
                 */
                public void onItemClick(AdapterView<?> adapterView,
                        View view,
                        int position,
                        long rowId) {
                    /*
                     * Get a File for the selected file name.
                     * Assume that the file names are in the
                     * mImageFilename array.
                     */
                    File requestFile = new File(mImageFilename[position]);
                    /*
                     * Most file-related method calls need to be in
                     * try-catch blocks.
                     */
                    // Use the FileProvider to get a content URI
                    try {
                        fileUri = FileProvider.getUriForFile(
                                MainActivity.this,
                                "com.example.myapp.fileprovider",
                                requestFile);
                    } catch (IllegalArgumentException e) {
                        Log.e("File Selector",
                              "The selected file can't be shared: " +
                              clickedFilename);
                    }
                    ...
                }
            });
            ...
        } 

    记住,你能生成的那些URI所对应的文件,是那些在meta-data文件中包含<paths>标签的(即你定义的)目录内的文件,这方面知识在Specify Sharable Directories(博客链接:http://www.cnblogs.com/jdneo/p/3480405.html)中已经讨论过。如果你为一个在你没有指定的目录内的文件调用了getUriForFile()方法,你会收到一个IllegalArgumentException


    三). 为文件授权 

    现在你有了你想要共享给其他应用的文件URI,你需要允许客户应用端访问这个文件。为了允许访问,可以通过将URI添加至一个Intent,然后为该Intent设置权限标记。你所授予的权限是临时的,并且当接收应用的任务栈被完成后,会自动过期。

    下面的例子展示了如何为文件设置读权限:

       protected void onCreate(Bundle savedInstanceState) {
            ...
            // Define a listener that responds to clicks in the ListView
            mFileListView.setOnItemClickListener(
                    new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView,
                        View view,
                        int position,
                        long rowId) {
                    ...
                    if (fileUri != null) {
                        // Grant temporary read permission to the content URI
                        mResultIntent.addFlags(
                            Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    }
                    ...
                 }
                 ...
            });
        ...
        }

    Caution:

    调用setFlags()是唯一安全的方法,为你的文件授予临时的被访问权限。避免对文件URI调用Context.grantUriPermission(),因为通过该方法授予的权限,你只能通过调用Context.revokeUriPermission()来撤销。


    四). 与需求应用共享文件

    为了与需求应用共享其需要的文件,将包含了URI和响应权限的Intent传递给setResult()。当你定义的Activity被结束后,系统会把这个包含了URI的Intent传递给客户端应用。下面的例子展示了你应该如何做:

    protected void onCreate(Bundle savedInstanceState) {
            ...
            // Define a listener that responds to clicks on a file in the ListView
            mFileListView.setOnItemClickListener(
                    new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView,
                        View view,
                        int position,
                        long rowId) {
                    ...
                    if (fileUri != null) {
                        ...
                        // Put the Uri and MIME type in the result Intent
                        mResultIntent.setDataAndType(
                                fileUri,
                                getContentResolver().getType(fileUri));
                        // Set the result
                        MainActivity.this.setResult(Activity.RESULT_OK,
                                mResultIntent);
                        } else {
                            mResultIntent.setDataAndType(null, "");
                            MainActivity.this.setResult(RESULT_CANCELED,
                                    mResultIntent);
                        }
                    }
            }); 

    向用户提供一个一旦他们选择了文件就能立即回到客户应用的方法。一种实现的方法是提供一个勾选框或者一个完成按钮。使用按钮的android:onClick属性字段为它关联一个方法。在该方法中,调用finish()。例如:

    public void onDoneClick(View v) {
            // Associate a method with the Done button
            finish();
        }
  • 相关阅读:
    禁止google浏览器强制跳转为https
    遍历打印文件目录结构
    添加忽略文件
    部署git服务器
    Location, History, Screen, Navigator对象
    Window 对象
    回调函数,setTimeout,Promise
    闭包
    this
    函数内部运作机制—上下文
  • 原文地址:https://www.cnblogs.com/jdneo/p/3480677.html
Copyright © 2011-2022 走看看