編輯:關於Android編程
SQLiteDatabase db = mOpenHelper.getWritableDatabase(); Cursor cr; cr = db.rawQuery("select * from person where age=20", null); if (cr.moveToFirst()) { for (int i = 0; i < cr.getCount(); i++) { cr.getString(); cr.moveToNext(); } }
//SQLiteDatabase.java public Cursor rawQuery(String sql, String[] selectionArgs) { return rawQueryWithFactory(null, sql, selectionArgs, null, null); } public Cursor rawQueryWithFactory( CursorFactory cursorFactory, String sql, String[] selectionArgs, String editTable, CancellationSignal cancellationSignal) { acquireReference(); try { SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable, cancellationSignal); // ① return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory, selectionArgs); // ② } finally { releaseReference(); } }
// SQLiteDirectCursorDriver.java public final class SQLiteDirectCursorDriver implements SQLiteCursorDriver { private final SQLiteDatabase mDatabase; private final String mEditTable; private final String mSql; private final CancellationSignal mCancellationSignal; private SQLiteQuery mQuery; public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable, CancellationSignal cancellationSignal) { mDatabase = db; mEditTable = editTable; mSql = sql; mCancellationSignal = cancellationSignal; } public Cursor query(CursorFactory factory, String[] selectionArgs) { final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal); //③ final Cursor cursor; try { query.bindAllArgsAsStrings(selectionArgs); if (factory == null) { cursor = new SQLiteCursor(this, mEditTable, query); // ④ } else { cursor = factory.newCursor(mDatabase, this, mEditTable, query); } } catch (RuntimeException ex) { query.close(); throw ex; } mQuery = query; return cursor; }
首先看下SQLiteQuery:
// SQLiteQuery.java public final class SQLiteQuery extends SQLiteProgram { private static final String TAG = "SQLiteQuery"; private final CancellationSignal mCancellationSignal; SQLiteQuery(SQLiteDatabase db, String query, CancellationSignal cancellationSignal) { super(db, query, null, cancellationSignal); mCancellationSignal = cancellationSignal; }正如文章開頭所說,SQLiteQuery繼承自SQLiteProgram,和SQLiteStatement相同。由(Android SQLiteStatement 編譯、執行 分析)可以知道,在其構造函數中,經歷了sql語句的prepare過程,在某個連接池的某個connection中已經含有了相應的stmt。
// SQLiteCursor.java public class SQLiteCursor extends AbstractWindowedCursor { static final String TAG = "SQLiteCursor"; static final int NO_COUNT = -1; private final String mEditTable; private final String[] mColumns; private final SQLiteQuery mQuery; private final SQLiteCursorDriver mDriver; private int mCount = NO_COUNT; private int mCursorWindowCapacity; private MapmColumnNameMap; private final Throwable mStackTrace; public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) { …… mDriver = driver; mEditTable = editTable; mColumnNameMap = null; mQuery = query; mColumns = query.getColumnNames(); mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns); }
// AbstractWindowedCursor.java public abstract class AbstractWindowedCursor extends AbstractCursor { protected CursorWindow mWindow; } // AbstractCursor.java public abstract class AbstractCursor implements CrossProcessCursor { protected int mPos; ...... }在AbstractWindowedCursor中,我們看到了CursorWindow,在數據庫中cursor window是很重要的概念。
// CursorWindow.java public class CursorWindow extends SQLiteClosable implements Parcelable { public int mWindowPtr; // !!! private int mStartPos; private final String mName; private final CloseGuard mCloseGuard = CloseGuard.get(); private static native int nativeCreate(String name, int cursorWindowSize); private static native void nativeClear(int windowPtr); private static native int nativeGetNumRows(int windowPtr); private static native double nativeGetDouble(int windowPtr, int row, int column); …… }mWindowPtr 目測是指向native層sqlite相應window的指針。並且該類含有不少native方法,部分對sqlite中window的操作,應該是通過這個類實現的。
//AbstractCursor.java public final boolean moveToFirst() { return moveToPosition(0); } public final boolean moveToNext() { return moveToPosition(mPos + 1); } public final boolean moveToPosition(int position) { final int count = getCount(); // ⑤ if (position >= count) { mPos = count; return false; } if (position < 0) { mPos = -1; return false; } if (position == mPos) { return true; } boolean result = onMove(mPos, position); /// ⑨ if (result == false) { mPos = -1; } else { mPos = position; if (mRowIdColumnIndex != -1) { mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex)); } } return result; } // SQLiteCursor.java @Override public int getCount() { if (mCount == NO_COUNT) { fillWindow(0); } return mCount; } @Override public boolean onMove(int oldPosition, int newPosition) { if (mWindow == null || newPosition < mWindow.getStartPosition() || newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) { fillWindow(newPosition); } return true; }
// SQLiteCursor.java private void fillWindow(int requiredPos) { clearOrCreateWindow(getDatabase().getPath()); // ⑥ if (mCount == NO_COUNT) { int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0); // ⑦ mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true); // ⑧ mCursorWindowCapacity = mWindow.getNumRows(); if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "received count(*) from native_fill_window: " + mCount); } } else { int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity); mQuery.fillWindow(mWindow, startPos, requiredPos, false); } } protected void clearOrCreateWindow(String name) { if (mWindow == null) { // 建立CursorWindow mWindow = new CursorWindow(name); } else { mWindow.clear(); } }在第⑥中,new出CursorWindow,將其賦值給mWindow,此時,由SQLiteCursor掌管。如下,new CursorWindow的過程,調用了nativeCreate,並使mWindowPtr指向native層的window。
// CursorWindow.java public CursorWindow(String name) { mStartPos = 0; mName = name != null && name.length() != 0 ? name : ""; mWindowPtr = nativeCreate(mName, sCursorWindowSize); // !!! if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window allocation of " + (sCursorWindowSize / 1024) + " kb failed. " + printStats()); } mCloseGuard.open("close"); recordNewWindow(Binder.getCallingPid(), mWindowPtr); }
// SQLiteQuery.java int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) { .... int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(), window, startPos, requiredPos, countAllRows, getConnectionFlags(), mCancellationSignal); return numRows; } // SQLiteSeesion.java public int executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, int connectionFlags, CancellationSignal cancellationSignal) { acquireConnection(sql, connectionFlags, cancellationSignal); try { return mConnection.executeForCursorWindow(sql, bindArgs, window, startPos, requiredPos, countAllRows, cancellationSignal); } finally { releaseConnection(); } } // SQLiteConnection.java public int executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, CancellationSignal cancellationSignal) { final PreparedStatement statement = acquirePreparedStatement(sql); final long result = nativeExecuteForCursorWindow( // !!! mConnectionPtr, statement.mStatementPtr, window.mWindowPtr, startPos, requiredPos, countAllRows); actualPos = (int)(result >> 32); countedRows = (int)result; filledRows = window.getNumRows(); window.setStartPosition(actualPos); return countedRows; ..... }
剩下的getString就比較簡單了,一直會調用到到mWindow的getString
public String getString(int row, int column) { acquireReference(); try { return nativeGetString(mWindowPtr, row - mStartPos, column); } finally { releaseReference(); } }
最後看下第⑦,即window fill 的控制。
這裡有涉及fill策略,一般無需考慮。如果結果集大於window怎麼辦?如果所需某個元素不在window中怎麼辦?尚未詳細分析了,貼下代碼。
若是第一次fill,required row 為0,即從第一條記錄開始fill滿window。
window將會包含所需的row及其周圍的一些row。例如,想要結果集的第120個元素,window大小為90,則將結果集第90-180的元素填充至window,120之前30個,之後60個。 如果window中沒有,將其放置在window的第10個位置。
// DatabaseUtils.java public static int cursorPickFillWindowStartPosition( int cursorPosition, int cursorWindowCapacity) { return Math.max(cursorPosition - cursorWindowCapacity / 3, 0); }
static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz, jint connectionPtr, jint statementPtr, jint windowPtr, jint startPos, jint requiredPos, jboolean countAllRows) { ...... int retryCount = 0; int totalRows = 0; int addedRows = 0; bool windowFull = false; bool gotException = false; while (!gotException && (!windowFull || countAllRows)) { int err = sqlite3_step(statement); if (err == SQLITE_ROW) { LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows); retryCount = 0; totalRows += 1; // Skip the row if the window is full or we haven't reached the start position yet. if (startPos >= totalRows || windowFull) { continue; } CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) { // We filled the window before we got to the one row that we really wanted. // Clear the window and start filling it again from here. // TODO: Would be nicer if we could progressively replace earlier rows. window->clear(); window->setNumColumns(numColumns); startPos += addedRows; addedRows = 0; cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); } if (cpr == CPR_OK) { addedRows += 1; } else if (cpr == CPR_FULL) { windowFull = true; } else { gotException = true; } } else if (err == SQLITE_DONE) { // All rows processed, bail LOG_WINDOW("Processed all rows"); break; } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { // The table is locked, retry LOG_WINDOW("Database locked, retrying"); if (retryCount > 50) { ALOGE("Bailing on database busy retry"); throw_sqlite3_exception(env, connection->db, "retrycount exceeded"); gotException = true; } else { // Sleep to give the thread holding the lock a chance to finish usleep(1000); retryCount++; } } else { throw_sqlite3_exception(env, connection->db); gotException = true; } } LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows" "to the window in %d bytes", statement, totalRows, addedRows, window->size() - window->freeSpace()); sqlite3_reset(statement); // Report the total number of rows on request. if (startPos > totalRows) { ALOGE("startPos %d > actual rows %d", startPos, totalRows); } jlong result = jlong(startPos) << 32 | jlong(totalRows); return result; }
① query的執行同普通sql語句相同,都需經過sql語句的編譯及執行。
② 編譯後為SQLiteQuery,執行後返回SQLiteCursor,SQLiteCursor的mWindow指向native層的cursor window。
③ 通過SQLiteCursor對返回結果進行控制。
④ 執行的過程,是構建SQLiteCursor的過程,並未將結果集寫入相應window。
⑤ 結果集寫入window,發生在第一次類似cursor.moveToFirst()操作中。這是android中處處體現的惰性策略。
⑥ sqlite本身對結果集與window的關系做了優化,android在此基礎上再次優化,以應對結果集過大、跳躍式讀取結果等問題。尚未分析。
RelativeLayout是相對布局控件:以控件之間相對位置或相對父容器位置進行排列。相對布局常用屬性:子類控件相對子類控件:值是另外一個控件的idandroid:la
下面是效果展示:復制代碼 代碼如下:<?xml version=1.0 encoding=utf-8?><LinearLayout xmlns:andr
關於使用MarkDown編輯器的原因 其實前段時間就支持使用MarkDown編輯器來寫博客了,只是由於當時接觸過MarkDown,所以之前的博客都是使用默認的HTML編輯
概述作為一名Android開發人員,每天使用最多的類中,恐怕Context可以排的上一號了。因為Context對象在我們的項目中實在是太常見了,我們在加載資源,啟動Act