【Android Developers Training】 37. 共享一个

系统 1669 0

注:本文翻译自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_DEFAULT CATEGORY_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。若 Activity ListView 中显示了可获得文件的清单,当用户点击了一个文件名时,系统调用了方法 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();

    }
      
    

【Android Developers Training】 37. 共享一个文件


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论