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