我正在尝试为我的应用程序编写一个内容提供程序,允许访问我的应用程序存储在SD卡上的某些文件。文件名称是其内容URI的哈希值,因此例如content://my.app.files/test.pdf
位于/sdcard/data/Android/my.app/cache/MD5(my.app.files/test.pdf)
,其中MD5()
是MD5哈希值。
我的内容提供商看起来像这样:
public class CacheContentProvider extends ContentProvider {
private static final String[] COLUMNS= { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE };
public static final Uri CONTENT_URI = Uri.parse("content://my.app.files/");
private DiskCache mCache;
@Override
public boolean onCreate() {
PotDroidApplication.initImageLoader(getContext());
final ImageLoader il = ImageLoader.getInstance();
mCache = il.getDiskCache();
return true;
}
@Override
public String getType(Uri uri) {
final File file = getFileForUri(uri);
final int lastDot = file.getName().lastIndexOf('.');
if (lastDot >= 0) {
final String extension = file.getName().substring(lastDot + 1);
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mime != null) {
return mime;
}
}
return "application/octet-stream";
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
final File f = getFileForUri(getContentUriFromUrlOrUri(uri.toString()));
if (f.exists()) {
return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
}
throw new FileNotFoundException(uri.getPath());
}
private File getFileForUri(Uri uri) {
return DiskCacheUtils.findInCache(uri.toString(), mCache);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
final File file = getFileForUri(uri);
if (projection == null) {
projection = COLUMNS;
}
String[] cols = new String[projection.length];
Object[] values = new Object[projection.length];
int i = 0;
for (String col : projection) {
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
cols[i] = OpenableColumns.DISPLAY_NAME;
values[i++] = uri.getLastPathSegment();
} else if (OpenableColumns.SIZE.equals(col)) {
cols[i] = OpenableColumns.SIZE;
values[i++] = file.length();
}
}
cols = copyOf(cols, i);
values = copyOf(values, i);
final MatrixCursor cursor = new MatrixCursor(cols, 1);
cursor.addRow(values);
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
throw new RuntimeException("Operation not supported");
}
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
throw new RuntimeException("Operation not supported");
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
throw new RuntimeException("Operation not supported");
}
public static Uri getContentUriFromUrlOrUri(String rawUrl) {
if(rawUrl.startsWith(CONTENT_URI.toString()))
return Uri.parse(rawUrl);
try {
URL url = new URL(rawUrl.replace("%20","+"));
return Uri.parse(CONTENT_URI + url.getHost() + url.getPath());
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}
private static String[] copyOf(String[] original, int newLength) {
final String[] result = new String[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
private static Object[] copyOf(Object[] original, int newLength) {
final Object[] result = new Object[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
public static class HashFileNameGenerator extends Md5FileNameGenerator {
@Override
public String generate(String url) {
Uri uri = getContentUriFromUrlOrUri(url);
if(uri == null)
return super.generate(url);
return super.generate(uri.toString());
}
}
}
提供商似乎可以很好地解析content://
URI并与某些应用共享,例如Dropbox,也能正常工作。但是,当我尝试与他们共享文件时,某些应用程序会抛出异常,例如Acrobat PDF转换器。例外情况如下:
Caused by: java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
at android.database.CursorWindow.nativeGetString(Native Method)
at android.database.CursorWindow.getString(CursorWindow.java:434)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
...
所以,显然,应用程序正在寻找通过query()
方法返回的Cursor中不存在的一些列。我究竟做错了什么?我基本上从Android的原生query()
复制了FileProvider
方法。
答案 0 :(得分:1)
如果您在ContentProvider中公开媒体文件,则其query()
方法应返回MediaStore.MediaColumns