ContentProvider 实战

  • Post author:
  • Post category:其他


近期项目中需要从一个应用中拷贝一份文件(该文件无法直接拿到),并且文件放置的目录是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提供的外部调用方法。



版权声明:本文为zxw136511485原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。