通过内容提供程序传递二进制blob

我有一个内容提供程序,它是我的Android应用程序集自定义的,它需要公开的一个东西是一个小的(20-30 KiB)字节数组。 这些blob的URI如下所示:

content://my.authority/blob/# 

其中#是行号; 生成的游标具有标准的_id列和数据列。 我在提供程序的query()方法中使用MatrixCursor

 byte[] byteData = getMyByteData(); MatrixCursor mc = new MatrixCursor(COLUMNS); mc.addRow(new Object[] { id, byteData }); 

后来,在使用数据的应用程序中,我这样做:

 Cursor c = managedQuery(uri, null, null, null, null); c.moveToFirst(); byte[] data = c.getBlob(c.getColumnIndexOrThrow("data")); 

但是,数据不包含原始字节数组的内容; 相反,它包含类似[B@435cc518东西,它看起来更像是数组的地址而不是内容。 我尝试在java.sql.Blob的实现中包装字节数组,认为它可能正在寻找,因为内容提供者子系统被编写为易于与SQLite一起使用,但它没有帮助。

有没有人得到这个工作? 如果数据在文件系统中,我可以使用ContentProvider中的方法向客户端提供编组的InputStream ,但是我尝试发送的数据作为内容提供商的APK中的资源存在。

    您将无法使用MatrixCursor发送字节数组。 这是因为它依赖于AbstractCursor#fillWindow方法,该CursorWindow使用Object#toString填充CursorWindow 。 所以正在发生的是调用字节数组toString方法,它正在存储它而不是你想要的字节数组的内容。 我能看到的唯一方法是实现自己的Cursor,它将适当地填充CursorWindow用于字节数组。

    从Ice Cream Sandwich来源获取MatrixCursor。 它正确实现了getBlob

    如果您想使用它,修改后的源代码如下。 您可以在Android 1.6+项目中使用它。

     /* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package yuku.androidsdk.android.database.icsmatrixcursor; import android.database.*; import java.util.*; /** * A mutable cursor implementation backed by an array of {@code Object}s. Use * {@link #newRow()} to add rows. Automatically expands internal capacity * as needed. */ public class MatrixCursor extends AbstractCursor { private final String[] columnNames; private Object[] data; private int rowCount = 0; private final int columnCount; /** * Constructs a new cursor with the given initial capacity. * * @param columnNames names of the columns, the ordering of which * determines column ordering elsewhere in this cursor * @param initialCapacity in rows */ public MatrixCursor(String[] columnNames, int initialCapacity) { this.columnNames = columnNames; this.columnCount = columnNames.length; if (initialCapacity < 1) { initialCapacity = 1; } this.data = new Object[columnCount * initialCapacity]; } /** * Constructs a new cursor. * * @param columnNames names of the columns, the ordering of which * determines column ordering elsewhere in this cursor */ public MatrixCursor(String[] columnNames) { this(columnNames, 16); } /** * Gets value at the given column for the current row. */ private Object get(int column) { if (column < 0 || column >= columnCount) { throw new CursorIndexOutOfBoundsException("Requested column: " + column + ", # of columns: " + columnCount); } if (mPos < 0) { throw new CursorIndexOutOfBoundsException("Before first row."); } if (mPos >= rowCount) { throw new CursorIndexOutOfBoundsException("After last row."); } return data[mPos * columnCount + column]; } /** * Adds a new row to the end and returns a builder for that row. Not safe * for concurrent use. * * @return builder which can be used to set the column values for the new * row */ public RowBuilder newRow() { rowCount++; int endIndex = rowCount * columnCount; ensureCapacity(endIndex); int start = endIndex - columnCount; return new RowBuilder(start, endIndex); } /** * Adds a new row to the end with the given column values. Not safe * for concurrent use. * * @throws IllegalArgumentException if {@code columnValues.length != * columnNames.length} * @param columnValues in the same order as the the column names specified * at cursor construction time */ public void addRow(Object[] columnValues) { if (columnValues.length != columnCount) { throw new IllegalArgumentException("columnNames.length = " + columnCount + ", columnValues.length = " + columnValues.length); } int start = rowCount++ * columnCount; ensureCapacity(start + columnCount); System.arraycopy(columnValues, 0, data, start, columnCount); } /** * Adds a new row to the end with the given column values. Not safe * for concurrent use. * * @throws IllegalArgumentException if {@code columnValues.size() != * columnNames.length} * @param columnValues in the same order as the the column names specified * at cursor construction time */ public void addRow(Iterable< ?> columnValues) { int start = rowCount * columnCount; int end = start + columnCount; ensureCapacity(end); if (columnValues instanceof ArrayList< ?>) { addRow((ArrayList< ?>) columnValues, start); return; } int current = start; Object[] localData = data; for (Object columnValue : columnValues) { if (current == end) { // TODO: null out row? throw new IllegalArgumentException( "columnValues.size() > columnNames.length"); } localData[current++] = columnValue; } if (current != end) { // TODO: null out row? throw new IllegalArgumentException( "columnValues.size() < columnNames.length"); } // Increase row count here in case we encounter an exception. rowCount++; } /** Optimization for {@link ArrayList}. */ private void addRow(ArrayList columnValues, int start) { int size = columnValues.size(); if (size != columnCount) { throw new IllegalArgumentException("columnNames.length = " + columnCount + ", columnValues.size() = " + size); } rowCount++; Object[] localData = data; for (int i = 0; i < size; i++) { localData[start + i] = columnValues.get(i); } } /** Ensures that this cursor has enough capacity. */ private void ensureCapacity(int size) { if (size > data.length) { Object[] oldData = this.data; int newSize = data.length * 2; if (newSize < size) { newSize = size; } this.data = new Object[newSize]; System.arraycopy(oldData, 0, this.data, 0, oldData.length); } } /** * Builds a row, starting from the left-most column and adding one column * value at a time. Follows the same ordering as the column names specified * at cursor construction time. */ public class RowBuilder { private int index; private final int endIndex; RowBuilder(int index, int endIndex) { this.index = index; this.endIndex = endIndex; } /** * Sets the next column value in this row. * * @throws CursorIndexOutOfBoundsException if you try to add too many * values * @return this builder to support chaining */ public RowBuilder add(Object columnValue) { if (index == endIndex) { throw new CursorIndexOutOfBoundsException( "No more columns left."); } data[index++] = columnValue; return this; } } // AbstractCursor implementation. @Override public int getCount() { return rowCount; } @Override public String[] getColumnNames() { return columnNames; } @Override public String getString(int column) { Object value = get(column); if (value == null) return null; return value.toString(); } @Override public short getShort(int column) { Object value = get(column); if (value == null) return 0; if (value instanceof Number) return ((Number) value).shortValue(); return Short.parseShort(value.toString()); } @Override public int getInt(int column) { Object value = get(column); if (value == null) return 0; if (value instanceof Number) return ((Number) value).intValue(); return Integer.parseInt(value.toString()); } @Override public long getLong(int column) { Object value = get(column); if (value == null) return 0; if (value instanceof Number) return ((Number) value).longValue(); return Long.parseLong(value.toString()); } @Override public float getFloat(int column) { Object value = get(column); if (value == null) return 0.0f; if (value instanceof Number) return ((Number) value).floatValue(); return Float.parseFloat(value.toString()); } @Override public double getDouble(int column) { Object value = get(column); if (value == null) return 0.0d; if (value instanceof Number) return ((Number) value).doubleValue(); return Double.parseDouble(value.toString()); } @Override public byte[] getBlob(int column) { Object value = get(column); return (byte[]) value; } @Override public boolean isNull(int column) { return get(column) == null; } } 

    您需要在AbstractCursor实现中覆盖fillWindow。 这是一个适用于JUST BLOBS的内容提供商

      public void fillWindow(int position, CursorWindow window) { if (position < 0 || position >= getCount()) { return; } window.acquireReference(); try { int oldpos = mPos; mPos = position - 1; window.clear(); window.setStartPosition(position); int columnNum = getColumnCount(); window.setNumColumns(columnNum); while (moveToNext() && window.allocRow()) { for (int i = 0; i < columnNum; i++) { byte [] field = getBlob(i); if (field != null) { if (!window.putBlob(field, mPos, i)) { window.freeLastRow(); break; } } else { if (!window.putNull(mPos, i)) { window.freeLastRow(); break; } } } } mPos = oldpos; } catch (IllegalStateException e){ // simply ignore it } finally { window.releaseReference(); } }