7

J'ai un fournisseur de contenu qui est personnalisé pour mon ensemble d'applications Android, et l'une des choses qu'il doit exposer est un petit tableau d'octets (20-30 KiB). L'URI de ces blobs ressemble:Passage d'un blob binaire via un fournisseur de contenu

content://my.authority/blob/# 

# est le numéro de ligne; le curseur résultant a la colonne standard _id et une colonne de données. J'utilise un MatrixCursor dans query() méthode du fournisseur:

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

Plus tard, dans l'application de consommer les données, je fais:

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

Toutefois, les données ne contient pas le contenu de mon octet d'origine tableau; plutôt, il contient quelque chose comme [[email protected], qui ressemble plus à l'adresse du tableau que le contenu. J'ai essayé d'encapsuler le tableau d'octets dans une implémentation de java.sql.Blob, en pensant que cela pourrait être utile puisque le sous-système de fournisseur de contenu a été écrit pour être facile à utiliser avec SQLite, mais cela n'a pas aidé.

Quelqu'un at-il obtenu ce travail? Si les données se trouvaient dans le système de fichiers, il existe des méthodes dans ContentProvider que je pourrais utiliser pour fournir un InputStream marshalled au client, mais les données que j'essaie de renvoyer en tant que ressource dans le fichier APK du fournisseur de contenu.

+0

BTW Je cible une version assez ancienne (android-3) si c'est important. – user308405

+0

Je comprends que vous avez résolu ce problème. Avez-vous une chance de le partager avec nous? –

Répondre

4

Vous ne pourrez pas utiliser un MatrixCursor pour envoyer le tableau d'octets. C'est parce que cela dépend de AbstractCursor#fillWindow méthode qui remplit le CursorWindow en utilisant Object#toString. Donc, ce qui se passe, c'est que la méthode toString byte array est appelée et qu'elle stocke à la place du contenu du tableau d'octets ce que vous voulez. La seule façon de contourner ce problème est d'implémenter votre propre curseur qui remplira correctement le CursorWindow pour un tableau d'octets.

+0

Merci, j'avais peur de String était ce qui se passait à un certain niveau. J'espérais ne pas devoir implémenter mon propre Cursor, mais c'est le chemin que je vais suivre. – user308405

+0

Avez-vous implémenté votre propre curseur? Je serais très intéressé par votre travail! –

1

Vous devez remplacer fillWindow dans votre implémentation de AbstractCursor. Voici un qui fonctionne sur un fournisseur de contenu pour Blobs JUSTE

 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(); 
      } 
     } 
7

Obtenez le MatrixCursor de crème glacée sources Sandwich. Il implémente correctement getBlob.

Le code source modifié est ci-dessous, si vous souhaitez l'utiliser. Vous pouvez l'utiliser dans les projets 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; 
    } 
} 
Questions connexes