返回介绍

4 query

发布于 2024-12-23 21:41:50 字数 4483 浏览 0 评论 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

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