返回介绍

4 query

发布于 2024-12-23 21:41:50 字数 4483 浏览 0 评论 0 收藏

现在重点分析一下 SQLiteDatabase 的查询操作:从源码可以看出查询操作最终会调用 rawQueryWithFactory():

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,再调用其 query 操作:

// SQLiteDirectCursorDriver::query():
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,在构造出 SQLiteCursor,并返回 SQLiteCursor 对象。

所以得到的 Cursor 的原型是 SQLiteCursor 类,你会发现没有其他操作,那么查询数据是在哪里呢?

SQLiteCursor 分析:

public final boolean moveToFirst() {
  return moveToPosition(0);
}

public final boolean moveToPosition(int position) {
  // Make sure position isn't past the end of the cursor
  final int count = getCount();
  if (position >= count) {
    mPos = count;
    return false;
  }

  // Make sure position isn't before the beginning of the cursor
  if (position < 0) {
    mPos = -1;
    return false;
  }

  // Check for no-op moves, and skip the rest of the work for them
  if (position == mPos) {
    return true;
  }

  boolean result = onMove(mPos, position);
  if (result == false) {
    mPos = -1;
  } else {
    mPos = position;
  }

  return result;
}

public int getCount() {
  if (mCount == NO_COUNT) {
    fillWindow(0);
  }
  return mCount;
}

private void fillWindow(int requiredPos) {
  clearOrCreateWindow(getDatabase().getPath());

  try {
    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);
    }
  } catch (RuntimeException ex) {
    // Close the cursor window if the query failed and therefore will
    // not produce any results.  This helps to avoid accidentally leaking
    // the cursor window if the client does not correctly handle exceptions
    // and fails to close the cursor.
    closeWindow();
    throw ex;
  }
}

protected void clearOrCreateWindow(String name) {
  if (mWindow == null) {
    mWindow = new CursorWindow(name);
  } else {
    mWindow.clear();
  }
}

到这里你会发现 CursorWindow,那这个对象是干嘛的呢?从文档上看可以发现其保存查询数据库的缓存,那么数据是缓存在哪的呢?先看器构造器:

public CursorWindow(String name) {
  // ...
  mWindowPtr = nativeCreate(mName, sCursorWindowSize);
  
  // .. 
}

nativeCreate 通过 JNI 调用 CursorWindow.cpp 的 create():

status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
  String8 ashmemName("CursorWindow: ");
  ashmemName.append(name);

  status_t result;
  // 创建共享内存
  int ashmemFd = ashmem_create_region(ashmemName.string(), size);
  if (ashmemFd < 0) {
    result = -errno;
  } else {
    result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
    if (result >= 0) {
      // 内存映射
      void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
      // ...
  }
  *outCursorWindow = NULL;
  return result;
}

可以看到查询数据是通过创建共享内存来保存的,但是数据在哪里被保存了呢?继续分析上面 SQLiteCursor:: fillWindow() 函数:

mQuery.fillWindow(mWindow, startPos, requiredPos, true);

其最终会调用 SQLiteConnection::executeForCursorWindow,也是通过 JNI 调用 cpp 文件将查询数据保存到共享内存中。

至于共享内存的知识点,可以参考 Android 系统匿名共享内存 Ashmem

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。