Android ContentProvider游标问题

时间:2014-11-12 08:16:29

标签: android android-contentprovider

我正在尝试为我的应用程序编写一个内容提供程序,允许访问我的应用程序存储在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方法。

1 个答案:

答案 0 :(得分:1)

如果您在ContentProvider中公开媒体文件,则其query()方法应返回MediaStore.MediaColumns