<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                本節將分析MediaProvider query函數。此次分析的焦點不是MediaProvider本身的業務邏輯,而是要搞清query函數返回的Cursor到底是什么,其代碼如下,: **MediaProvider.java::query** ~~~ public Cursor query(Uri uri, String[]projectionIn, String selection, String[] selectionArgs, String sort) { int table= URI_MATCHER.match(uri); ...... //根據uri取出對應的DatabaseHelper對象,MediaProvider針對內部存儲中的媒體文件和 //外部存儲(即SD卡)中的媒體文件分別創建了兩個數據庫 DatabaseHelper database = getDatabaseForUri(uri); //我們在7.3.2節分析過getReadableDatabase函數的兄弟getWritableDatabase函數 SQLiteDatabase db = database.getReadableDatabase(); //創建一個SQLiteQueryBuilder對象,用于方便開發人員編寫SQL語句 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); ......//設置qb的參數,例如調用setTables函數為本次查詢設定目標Table //①調用SQLiteQueryBuilder的query函數 Cursor c =qb.query(db, projectionIn, selection, combine(prependArgs, selectionArgs), groupBy, null, sort, limit); if (c !=null) { //2調用該Cursor對象的setNotificationUri函數,這部分內容和ContentObserver //有關,相關內容留到第8章再進行分析 c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } ~~~ 上邊代碼列出的兩個關鍵點分別是: - 調用SQLiteQueryBuilder的query函數得到一個Cursor類型的對象。 - 調用Cursor類型對象的setNotificationUri函數。從名字上看,是為該對象設置通知URI。和ContentObserver有關的內容留到 第8章再進行分析。 來看SQLiteQueryBuilder的query函數。 1. SQLiteQueryBuilder的query函數分析 **SQLiteQueryBuilder.java::query** ~~~ public Cursor query(SQLiteDatabase db, String[]projectionIn, String selection, String[] selectionArgs, String groupBy, String having, String sortOrder, String limit) { ...... //調用buildQuery函數得到對應SQL語句的字符串 String sql= buildQuery( projectionIn, selection, groupBy, having, sortOrder, limit); /* 調用SQLiteDatbase的rawQueryWithFactory函數,mFactory是SQLiteQueryBuilder 的成員變量,初始值為null,本例也沒有設置它,故mFactory為null */ returndb.rawQueryWithFactory( mFactory, sql, selectionArgs, SQLiteDatabase.findEditTable(mTables)); } ~~~ **SQLiteDatabase.java::rawQueryWithFactory** ~~~ public Cursor rawQueryWithFactory( CursorFactory cursorFactory, String sql, String[] selectionArgs, StringeditTable) { verifyDbIsOpen(); BlockGuard.getThreadPolicy().onReadFromDisk(); //數據庫開發中經常碰到連接池的概念,其目的也是緩存重型資源。有興趣的讀者不妨自行 //研究一下面這個getDbConnection函數 SQLiteDatabase db = getDbConnection(sql); //創建一個SQLiteDirectCursorDriver對象 SQLiteCursorDriver driver = new SQLiteDirectCursorDriver( db, sql,editTable); Cursorcursor = null; try { //調用SQLiteCursorDriver的query函數 cursor = driver.query( cursorFactory != null ? cursorFactory : mFactory, selectionArgs); }finally { releaseDbConnection(db); } returncursor; ~~~ 以上代碼中又出現一個新類型,即SQLiteCursorDriver,cursor變量是其query函數的返回值。 (1) SQLiteCursorDriver query函數分析 SQLiteCursorDriverquery函數的代碼如下: **SQLiteCursorDriver.java::query** ~~~ public Cursor query(CursorFactory factory,String[] selectionArgs) { //本例中,factory為空 SQLiteQuery query = null; try { mDatabase.lock(mSql); mDatabase.closePendingStatements(); //①構造一個SQLiteQuery對象 query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs); //原來,最后返回的游標對象其真實類型是SQLiteCursor if(factory == null) {//②構造一個SQLiteCursor對象 mCursor = new SQLiteCursor(this, mEditTable, query); }else { mCursor =factory.newCursor(mDatabase, this, mEditTable, query); } mQuery = query; query = null; return mCursor;//原來返回的cursor對象其真實類型是SQLiteCursor }finally { if (query != null) query.close(); mDatabase.unlock(); } } ~~~ SQLiteCursorDriver的query函數的主要功能就是創建一個SQLiteQuery實例和一個SQLiteCursor實例。至此,我們終于搞清楚了MediaProvider 的query返回的游標對象其真實類型是SQLiteCursor。 * * * * * **注意**:這里的MediaProvider query指的是圖7-5中的編號為8關鍵調用。 * * * * * 下面來看SQLiteQuery和SQLiteCursor為何方神圣。 (2) SQLiteQuery介紹 在圖7-4中曾介紹過SQLiteQuery,它保存了和查詢(即SQL的SELECT命令)命令相關的信息,其構造函數的代碼如下: **SQLiteQuery.java::構造函數** ~~~ SQLiteQuery(SQLiteDatabase db, String query, intoffsetIndex, String[] bindArgs) { //注意,在本例中offsetIndex為0,offsetIndex的意義到后面再解釋 super(db,query);//調用基類構造函數 mOffsetIndex = offsetIndex; bindAllArgsAsStrings(bindArgs); } ~~~ SQLiteQuery的基類是SQLiteProgram,其代碼如下: **SQLiteProgram.java::構造函數** ~~~ SQLiteProgram(SQLiteDatabase db, String sql) { this(db,sql, null, true);//調用另外一個構造函數,注意傳遞的參數 } SQLiteProgram(SQLiteDatabase db, String sql,Object[] bindArgs, boolean compileFlag) { //本例中bindArgs為null,compileFlag為true mSql =sql.trim(); //返回該SQL語句的類型,query語句將返回STATEMENT_SELECT類型 int n =DatabaseUtils.getSqlStatementType(mSql); switch(n) { caseDatabaseUtils.STATEMENT_UPDATE: mStatementType = n | STATEMENT_CACHEABLE; break; caseDatabaseUtils.STATEMENT_SELECT: /* mStatementType成員變量用于標示該SQL語句的類型,如果該SQL語句 是STATEMENT_SELECT類型,則mStatementType將設置STATEMENT_CACHEABLE 標志。該標志表示此對象將被緩存起來,以避免再次執行同樣的SELECT命令時重新構造 一個對象 */ mStatementType = n | STATEMENT_CACHEABLE | STATEMENT_USE_POOLED_CONN; break; ......//其他情況處理 default: mStatementType = n; } db.acquireReference();//增加引用計數 db.addSQLiteClosable(this); mDatabase= db; nHandle =db.mNativeHandle;//此mNativeHandle對應一個SQLite3實例 if(bindArgs != null) ......//綁定參數 //complieAndBindAllArgs將為此對象綁定一個sqlite3_stmt實例,native層對象的指針 //保存在nStatement成員變量中 if(compileFlag) compileAndbindAllArgs(); } ~~~ 來看compileAndbindAllArgs函數,其代碼是: **SQLiteProgram.java::compileAndbindAllArgs** ~~~ void compileAndbindAllArgs() { ...... //如果該對象還未和native層的sqlite3_stmt實例綁定,則調用compileSql函數 if(nStatement == 0) compileSql(); ......//綁定參數 for(int index : mBindArgs.keySet()) { Object value = mBindArgs.get(index); if (value == null) { native_bind_null(index); }......//綁定其他類型的數據 } } ~~~ compileSql函數將綁定Java層SQLiteQuery對象到一個Native的sqlite3_stmt實例。根據前文的分析,這個綁定是通過SQLiteCompileSql對象實施的,其相關代碼如下: **SQLiteProgram.java::compileSql** ~~~ private void compileSql() { //如果mStatementType未設置STATEMENT_CACHEABLE標志,則每次都創建一個 // SQLiteCompiledSql對象。根據7.3.2節小標題3中的分析,該對象會真正和native層的 //sqlite_stmt實例綁定 if((mStatementType & STATEMENT_CACHEABLE) == 0) { mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql); nStatement = mCompiledSql.nStatement; return; } //從SQLiteDatabase對象中查詢是否已經緩存過符合該SQL語句要求的SQLiteCompiledSql //對象 mCompiledSql = mDatabase.getCompiledStatementForSql(mSql); if(mCompiledSql == null) { //創建一個新的SQLiteCompiledSql對象,并把它保存到mDatabase中 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql); mCompiledSql.acquire(); mDatabase.addToCompiledQueries(mSql, mCompiledSql); } ...... //保存Native層sqlite3_stmt實例指針到nStatement成員變量 nStatement= mCompiledSql.nStatement; } ~~~ 通過以上分析可以發現,SQLiteQuery將和一個代表SELECT命令的sqlite3_stmt實例綁定。同時,為了減少創建sqlite3_stmt實例的開銷,SQLiteDatabase框架還會把對應的SQL語句和對應的SQLiteCompiledSql對象緩存起來。如果下次執行同樣的SELECT語句,那么系統將直接取出之前保存的SQLiteCompiledSql對象,這樣就不用重新創建sqlite3_stmt實例了。 * * * * * **思考**:與直接使用SQLite API相比,SQLiteDatabase框架明顯考慮了更多問題。不知讀者自己封裝C++ SQLite庫時是否想到了這些問題? * * * * * (3) SQLiteCursor分析 再來看SQLiteCursor類,其構造函數的代碼如下: **SQLiteCursor.java::構造函數** ~~~ public SQLiteCursor(SQLiteCursorDriver driver,String editTable, SQLiteQuery query) { ...... mDriver =driver; mEditTable = editTable; mColumnNameMap = null; mQuery =query;//保存此SQLiteQuery對象 query.mDatabase.lock(query.mSql); try { //得到此次執行query得到的結果集所包含的列數 intcolumnCount = mQuery.columnCountLocked(); mColumns = new String[columnCount]; for(int i = 0; i < columnCount; i++) { String columnName = mQuery.columnNameLocked(i); //保存列名 mColumns[i] = columnName; if ("_id".equals(columnName)) { mRowIdColumnIndex = i;//保存“_id”列在結果集中的索引位置 } } }finally { query.mDatabase.unlock(); } } ~~~ SQLiteCursor比較簡單,此處不再詳述。 2. Cursor分析 至此,我們已經知道MediaProvider query返回的游標對象的真實類型了。現在,終于可以請Cursor家族登臺亮相了,如圖7-6所示。 :-: ![](http://img.blog.csdn.net/20150803130933944?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖7-6 Cursor家族 圖7-6中元素較多,包含的知識點也較為復雜,因此必須仔細閱讀下文的解釋。 通過7.3.1節中SQLiteTest示例可知,query查詢(即SELECT命令)的結果和一個Native sqlite_stmt實例綁定在一起,開發者可通過遍歷該sqlite_stmt實例得到自己想要的結果(例如,調用sqlite3_step遍歷結果集中的行,然后通過sqlite3_column_xxx取出指定列的值)。查詢結果集可通過數據庫開發技術中一個專用術語——游標(Cursor)來遍歷和獲取。圖7-6的左上部分是和Cursor有關的類,它們包括:接口類Cursor和CrossProcessCursor、抽象類AbstractCursor、AbstractWindowCursor,以及真正的實現類SQliteCursor。根據前面的分析,SQLiteCursor內部保存一個已經綁定了sqlite3_stmt實例的SQLiteQuery對象,故讀者可簡單地把SQLiteCursor看成是一個已經包含了查詢結果集的游標對象,雖然此時還并未真正執行SQL語句。 如上所述,SQLiteCursor是一個已經包含了結果集的游標對象。從進程角度看,query的結果集目前還屬于MediaProvider所在的進程,而本次query請求是由客戶端發起的,所以一定要有一種方法將MediaProvider中的結果集傳遞到客戶端進程。數據傳遞使用的技術很簡單,就是大家耳熟能詳的共享內存技術。SQLite API沒有提供相關的功能,但是SQLiteDatabase框架對跨進程數據傳遞進行了封裝,最終得到了圖7-6左上部分的CursorWindow類。其代碼中的注釋明確表明了CursorWindow的作用:它是“A buffer containing multiplecursor rows”。 認識了CursorWindow,相信讀者也能猜出query中數據傳遞的大致流程了。 MediaProvider將結果集中的數據存儲到CursorWindow的共享內存中,然后客戶端將其從共享內存中取出來即可。 上述流程描述是對的,但實際過程并非如此簡單,因為SQLiteDatabase框架希望客戶端看到的不是共享內存,而是一個代表結果集的游標對象,就好像客戶端查詢的是本進程中的數據庫一樣。由于存在這種要求[^①],Android構造了圖7-6右下角的類家族。 其中,最重要的兩個類是CursorToBulkCursorAdaptor和BulkCursorToCursorAdatpor。從名字上看,它們采用了設計模式中的Adaptor模式;從繼承關系上看,這兩個類將參與跨進程的Binder通信(其中客戶端使用的BulkCursorToCursorAdaptor通過mBulkCursor與位于MediaProvider所在進程的CursorToBulkCursorAdaptor通信)。這兩個類中最重要的是onMove函數,以后我們碰到時再作分析。 另外,圖7-6中右上角部分展示了CursorWrapperInner類的派生關系。CursorWrapperInner類是ContentResolver query函數最終返回給客戶端的游標對象的類型。CursorWrapperInner的目的應該是拓展CursorToBulkCursorAdaptor類的功能。 Cursor家族有些復雜。筆者覺得,目前對Cursor的架構設計有些過度(over-designed)。這不僅會導致我們分析時困難重重,并且也會對實際代碼的運行效率造成一定損失。 下面我們將焦點放到跨進程的數據傳輸上。 [^①]:此處應該還存在其他方面的設計考慮,希望讀者能參與討論。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看