ContentProvider 实战

Posted 程序员七哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ContentProvider 实战相关的知识,希望对你有一定的参考价值。

近期项目中需要从一个应用中拷贝一份文件(该文件无法直接拿到),并且文件放置的目录是data下,外部应用无法直接访问,所以考虑使用ContentProvider来实现。

ContentProvider,android四大组件;ContentProvider 的作用是为不同的应用之间数据共享,提供统一的接口。本文就主要来说下实现样例。

一. 新建项目。相信大家都会了。

新建了两个项目,test01和test02。实例如下:

最终实现从test01的data目录下拷贝文件(data.xml)至test02中。

二.自定义实现ContentProvider。下面是代码展示:

/**
 * 文件共享
 */
public class FileContentProvider extends ContentProvider 

    final private static String TAG = "FileContentProvider";
    //文件名称
    private static String FILENAME = "data.xml";


    @Override
    public boolean onCreate() 
        Log.d(TAG, "========= onCreate is called =========");
        return true;
    

 
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) 
        Log.d(TAG, "========= query is called selection =========> " + selection);

        return null;
    


    /**
     * 获取文件的文件名
     *
     * @param uri
     * @return
     */
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) 
        //判断文件是否存在
        String fileName = "";
        String path = MyApp.getContext().getFilesDir().getAbsolutePath();
        if (TextUtils.isEmpty(path)) 
            Log.i(TAG, "Data path must not be null!");
            ToastUtils.s(MyApp.getContext(), "Data path must not be null! ");
            fileName = "";
            return fileName;
        
        File datapathFile = new File(path);
        if (!datapathFile.exists()) 
            Log.i(TAG, "Data path does not exist!");
            ToastUtils.s(MyApp.getContext(), "Data path does not exist! ");
            fileName = "";
            return fileName;
        
        File datafile = new File(path + File.separator +
                FILENAME);
        if (!datafile.exists()) 
            Log.i(TAG, "Data file not found at " + datafile);
            ToastUtils.s(MyApp.getContext(), "Data file not found at " + datafile);
            fileName = "";
            return fileName;
        
        fileName = FILENAME;
        Log.i(TAG, "fileName:" + FILENAME);
        Log.i(TAG, "uri.getPath():" + uri.getPath());
        return fileName;
    

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) 
        return null;
    

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) 
        return 0;
    

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) 
        return 0;
    


    /**
     * 需要实现该方法,
     *
     * @param uri
     * @param mode
     * @return
     * @throws FileNotFoundException
     */
    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode)
            throws FileNotFoundException 
        String fileName = FILENAME;
        String filePath = MyApp.getContext().getFilesDir().getAbsolutePath() + File.separator + fileName;
        //如果存在文件,则获取文件相关信息,并返回
        File datafile = new File(filePath);
        if (datafile.exists()) 
            Log.i(TAG, " fileName: " + fileName);
            Log.i(TAG, "filePath: " + filePath);
            //计算文件的md5
            Log.i(TAG, "文件 file2MD5 " + Md5CaculateUtil.file2MD5(datafile));
            return ParcelFileDescriptor.open(datafile,
                    ParcelFileDescriptor.MODE_READ_ONLY);
         else 
            Log.d(TAG, "file not found at" + filePath);
            return null;
        
    



 说明:

1.重写了两个方法;

2.getType()方法 返回文件名称,openFile()方法返回文件流;

三. test01其他实现代码。

  private void goToTest02() 
        if (AppUtil.checkAppInstalled(MainActivity.this, SLF_PACKAGE_NAME)) 
            getFile();
         else 
            ToastUtils.s(MainActivity.this, "应用未安装");
        
    

    /**
     * 获取文件
     */
    private void getFile() 
        Intent intent = new Intent();
        ComponentName componeneName = new ComponentName(SLF_PACKAGE_NAME, SLF_ACTIVITY_NAME);
        intent.setComponent(componeneName);
        String filePath = MyApp.getContext().getFilesDir().getAbsolutePath() + File.separator + FILENAME;
        //如果存在文件,则获取文件的相关信息,并返回
        File datafile = new File(filePath);
        if (datafile.exists()) 
            Log.d(TAG, "文件存在");
            Log.i(TAG, " fileName: " + FILENAME);
            Log.i(TAG, "filePath: " + filePath);
            //计算文件的md5
            Log.i(TAG, "文件 file2MD5 " + Md5CaculateUtil.file2MD5(datafile));
            String fileSize = FileSizeUtil.getAutoFileOrFilesSize(filePath);
            Log.d(TAG, "文件的大小- fileSize is " + fileSize);
            intent.putExtra("fileName", FILENAME);
            startActivity(intent);
            return;
        
        Log.d(TAG, "文件不存在");
        ToastUtils.s(MyApp.getContext(), "file not found at " + FILENAME);
    

说明:

1. 先判断是否安装了test02应用;

2.判断是否存在需要共享的文件;

四. test02 部分实现代码,

   /**
     *
     */
    private void getFileFromOutside() 
        Intent data = getIntent();
        if (data != null) 
            String fileName = data.getStringExtra("fileName");
            if (!TextUtils.isEmpty(fileName)) 
                Log.i(TAG, " fileName: " + fileName);
                readFile(fileName);
             else 
                Log.i(TAG, "文件名未传递,请先核查是否有文件");
            
         else 
            Log.i(TAG, "getIntent() is null");
        
    

    /**
     * 通过getContentResolver 读取文件
     *
     * @param fileName
     */
    private void readFile(String fileName) 
        String filePath = getFilesDir().getAbsolutePath() + File.separator + fileName;
        File datafile = new File(filePath);
        //文件是否已经存在;如果存在,则不用再获取
        if (datafile.exists()) 
            Log.d(TAG, "file is exists");
            String fileSize = FileSizeUtil.getAutoFileOrFilesSize(filePath);
            Log.d(TAG, "文件的大小- fileSize is " + fileSize);
            //计算文件的md5
            Log.i(TAG, " 文件 file2MD5 " + Md5CaculateUtil.file2MD5(new File(filePath)));
            ToastUtils.s(MainActivity.this, "file is exists");
            return;
        
        try 
            //通过contentprovide 获取文件名
            fileName = getContentResolver().getType(mUrl);
            if (TextUtils.isEmpty(fileName)) 
                Log.i(TAG, "fileName is null");
                return;
            
            //通过contentprovide 获取文件
            InputStream is = getContentResolver().openInputStream(mUrl);
            // 获取文件路径
            filePath = getFilesDir().getAbsolutePath() + File.separator + fileName;
            OutputStream os = new FileOutputStream(new File(filePath));
            byte[] b = new byte[1024];
            int len;
            while ((len = is.read(b)) != -1) 
                os.write(b, 0, len);
            
            os.flush();
            is.close();
            os.close();
            // 计算拷贝文件大小
            String fileSize = FileSizeUtil.getAutoFileOrFilesSize(filePath);
            Log.d(TAG, "-导入的 文件的大小- fileSize is " + fileSize);
            //计算文件的md5
            Log.i(TAG, "文件  file2MD5 " + Md5CaculateUtil.file2MD5(new File(filePath)));
         catch (FileNotFoundException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
        
    

说明:

1.先判断文件是否存在;

2.调用ContentProvider提供的方法,从文件流中获取需要共享的文件,写入test02应用中。

3.其他代码详见源码。

4.上传文件“data.xml”至目录“/data/data/com.demo.test01/files”下。

5.运行后,日志,

test01和test02 源码和apk 下载地址

五. 总结。

使用ContentProvider来达到应用见数据共享,需要自定义ContentProvider,重写相关方法;然后在应用中调用ContentProvider提供的外部调用方法。

以上是关于ContentProvider 实战的主要内容,如果未能解决你的问题,请参考以下文章

ContentProvider 实战

ContentProvider的理解与使用

R语言实战回归

ContentProvider原理分析

Android 存储(本地存储 SD卡存储 SharedPreference SQLite ContentProvider)

Android中的ContentProvider源码解析