編輯:關於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 Map mColumnNameMap;
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在此基礎上再次優化,以應對結果集過大、跳躍式讀取結果等問題。尚未分析。
Android布局之RelativeLayout相對布局
RelativeLayout是相對布局控件:以控件之間相對位置或相對父容器位置進行排列。相對布局常用屬性:子類控件相對子類控件:值是另外一個控件的idandroid:la
從零開始學android實現計算器功能示例分享(計算器源碼)
下面是效果展示:復制代碼 代碼如下:<?xml version=1.0 encoding=utf-8?><LinearLayout xmlns:andr
我的Android進階之旅------)關於使用CSDN-markdown編輯器來編寫博客
關於使用MarkDown編輯器的原因 其實前段時間就支持使用MarkDown編輯器來寫博客了,只是由於當時接觸過MarkDown,所以之前的博客都是使用默認的HTML編輯
android中Context的應用總結
概述作為一名Android開發人員,每天使用最多的類中,恐怕Context可以排的上一號了。因為Context對象在我們的項目中實在是太常見了,我們在加載資源,啟動Act