在上一篇文章中,我們對content provider基礎進行了詳細的介紹。該文中介紹的content provider都是基於sqlite的,但實際上content provider是可以基於其他儲存格式的。本文將開始介紹基於xml的content provider。
第一步,我們先介紹一個重要的部分Cursor。基於xml的content provider所使用的Cursor與基於sqlite的是不同的。基於xml的Cursor需要繼承MatrixCursor類。本文先介紹MatrixCursor類。
MatrixCursor類位於android.database包中。其中定義了四個變數,用來儲存相關的值,其介紹如下:
private final String[] columnNames; 存放列名
private Object[] data;
存放資料值。這裡,我通常認為應該用一個表(二維數組來存放資料值),但實際上卻是用一個一維數組來類比二維數組。所以對於一個二維數組:
其存放在data中的格式為
private int rowCount = 0;
MatrixCursor中存放的資料行數。因此很容易理解data數組的長度必須大於等於(columnCount*rowCount );
private final int columnCount;
列數=columnNames長度
其建構函式為:
/** * 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); }
其中的initialCapacity,就是指存放資料值的數組的容量。
在前面的介紹中,我們提到資料的儲存格式,這一點,我們可以從MatrixCursor如何獲得某一列的值看出來:
/** * 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]; }
mPos為當前行,其擷取第column 列的值為mPos * columnCount + column 。
另外,在MatrixCursor中,定義了一個行構造器類。該類定義了某一行在data中的存放位置,由改行第一列所在位置和最後一列所在位置來定義。其中有定義了獲得某一列的值的函數getString(int column),getShort(int column)等函數,大家是否會覺得這些函數名很熟悉,在基於sqlite的content provider是否經常用到?
/** * 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 boolean isNull(int column) { return get(column) == null; }}
MatrixCursor類中還定義了如何增加行的函數:
/** * 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); } }
這些函數都比較簡單,所以就不詳細介紹了。但是需要注意一點,其中ensureCapacity函數,用於確認,當前data的容量是否已經用完了,如果用完,需要將其容量擴充為原來的兩倍,這樣就能存放新加入行的資料了。