• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
Practices of Programmer
Cheerful company shortens the miles.
博客园    首页    新随笔    联系   管理    订阅  订阅

Android不同应用间文件共享方式的实现

本博客依据官方提供的文档,为园友们提供实现的代码。建议园友们对照官方文档阅读本博客:http://developer.android.com/training/secure-file-sharing/index.html


为保障不同应用间文件共享的安全性,Android平台提供了FileProvider组件来保障共享的安全。FileProvider组件根据程序的配置,为其他应用(我们简称为接受应用)提供一个对外的Content URI,同时为接受应用分配临时的访问权限,该权限在接受应用退出时自动过期。

FileProvider是Android支持库中提供的方法,在使用之前,需要得到支持库的支持。为了使用FileProvider,需要在工程的AndroidManifest.xml文件中声明provider,格式类似于:

 

 <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.fileshare.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true" >
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepath" />
</provider>

 

其中,android:authorities属性用生成URIs的authority,一般由工程的包名+fileprovider组成。<meta-data>节点声明应用要共享的文件目录,目录的配置文件位于res/xml文件夹下。共享目录的配置如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<paths>
    <files-path path="imagedir/" name="image" />
</paths>

 

其中path是应用的files目录的子目录,也即应用要共享的文件的目录,name是为了告诉FileProvider,将其值添加到Content URIs中,具体的配置说明请参看这里:http://developer.android.com/training/secure-file-sharing/setup-sharing.html#DefineMetaData

假设在工程的files的imagedir目录下有个名为default_image.jpg的文件,则根据之前的配置,其URI为content:/com.example.fileshare.fileprovider/imagedir/default_image.jpg。

配置好了FileProvider后,还需要为接收应用提供一个选择界面,该界面主要是列出共享的文件供用户选择。界面根据用户的选择将对应的文件返回给接受应用。其实现如下:

public class FileSelector extends ListActivity {

    private File privateRootDirs;         //共享文件的父目录
    private File imageDirs;                //共享文件目录
    private File[] imageFiles;            //共享的文件
    private String[] imageFileName;        //共享的文件名
    
    private ListView lvFileList;        //显示共享文件的列表
    private Uri fileUri;                //FileProvider生成的Content URI
    private Intent resultIntent;        //将用户选择的结果返回给接受应用的Intent
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        privateRootDirs = getFilesDir();
        imageDirs = new File(privateRootDirs, "imagedir");
        imageFiles = imageDirs.listFiles();
        imageFileName = new String[imageFiles.length];
        for(int i=0; i<imageFiles.length; i++) {
            imageFileName[i] = imageFiles[i].getName();
        }
        lvFileList = getListView();
        setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, imageFileName));
        
        lvFileList.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                //根据官网提供的代码片段,这里在生成File实例时,需要传入“共享文件目录”参数
                //否则为报“java.lang.IllegalArgumentException: Failed to find configured root that contains ...“异常

                File requestFile = new File(imageDirs, imageFileName[position]);    
                
                fileUri = FileProvider.getUriForFile(FileSelector.this, "com.example.fileshare.fileprovider", requestFile);
                
                if(fileUri != null) {
                    resultIntent = new Intent();
                    resultIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    resultIntent.setDataAndType(fileUri, getContentResolver().getType(fileUri));
                    FileSelector.this.setResult(Activity.RESULT_OK, resultIntent);
                    finish();
                }
            }
        });
    }
}

 

在声明给Activity时,需要添加如下的filter:

<activity android:name=".FileSelector" >
            <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="image/*" />
                <data android:mimeType="text/plain" />
            </intent-filter>
</activity>

 

为验证程序的正确性,需要重新创建一个新的工程,在工程代码中,调用FileSelector:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        requestFileIntent = new Intent();
        requestFileIntent.setAction(Intent.ACTION_PICK);
        requestFileIntent.setType("image/jpg");
        startActivityForResult(requestFileIntent, 0);
    }

 

为了显示用户在FileSelector选择的文件信息,复写如下方法:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        System.out.println("Stop here");
        if(resultCode == Activity.RESULT_OK) {
            Uri resultUri = data.getData();
            try {
                ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(resultUri, "r");
                FileDescriptor fd = pfd.getFileDescriptor();
                String mimeType = getContentResolver().getType(resultUri);
                Cursor returnCursor = getContentResolver().query(resultUri, null, null, null, null);
                int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
                returnCursor.moveToFirst();
                
                System.out.println("MIMEType: " + mimeType + " nameIndex: " + nameIndex + " sizeIndex: " + sizeIndex);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

 

参考代码可以从这里下载:http://pan.baidu.com/s/11RO0D

PS:由于个人组织能力太烂,这是本人首次写博客,今天先写到这里吧,请大家容我慢慢整理思路。。。

 

代码界的苦行僧,开源界的扫地僧。
posted @ 2013-12-02 23:01  CodeDiving  阅读(1320)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3