Step 16. CursorWindow.native_init


      这个函数定义在frameworks/base/core/jni/android_database_CursorWindow.cpp文件中,对应的函数为native_init_memory函数:



    1. static JNINativeMethod sMethods[] =  
    2. {  
    3.     ......  
    4. "native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory},  
    5. };



          函数native_init_memory的实现如下所示:


      1. static void native_init_memory(JNIEnv * env, jobject object, jobject memObj)  
      2. {  
      3.     sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, memObj));  
      4.     ......  
      5.   
      6. new CursorWindow();  
      7.     ......  
      8. if (!window->setMemory(memory)) {  
      9.        ......  
      10.     }  
      11.   
      12.     ......  
      13.     SET_WINDOW(env, object, window);  
      14. }



            函数首先是将前面Step 15中传进来的Binder接口转换为IMemory接口,接着创建一个C++层的CursorWindow对象,再接着用这个IMemory接口来初始化这个C++层的CursorWindow对象,最后像前面的Step 8一样,通过宏SET_WINDOW把这个C++层的CursorWindow对象和前面在Step 15中创建的Java层CursorWindow对象关联起来。



       



            下面我们就重点关注CursorWindow类的setMemory函数的实现,看看它是如何使用这个IMemory接口来初始化其内部的匿名共享内存对象的。



            Step 17. CursorWindow.setMemory



            这个函数定义在frameworks/base/core/jni/CursorWindow.cpp文件中:


        1. bool CursorWindow::setMemory(const sp<IMemory>& memory)  
        2. {  
        3.     mMemory = memory;  
        4.     mData = (uint8_t *) memory->pointer();  
        5.     ......  
        6.     mHeader = (window_header_t *) mData;  
        7.   
        8. // Make the window read-only  
        9.     ssize_t size = memory->size();  
        10.     mSize = size;  
        11.     mMaxSize = size;  
        12.     mFreeOffset = size;  
        13.     ......  
        14. return true;  
        15. }


              从前面一篇文章 Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析中,我们知道,这里得到的IMemory接口,实际上是一个Binder引用,它指向前面在Step 9中创建的MemoryBase对象,当我们第一次调用这个接口的pointer函数时,它便会通过Binder进程间通信机制去请求这个MemoryBase对象把它内部的匿名共享内存文件描述符返回来给它,而Binder驱动程序发现要传输的是一个文件描述符的时候,就会在目标进程中创建另外一个文件描述符,这个新建的文件描述符与要传输的文件描述符指向的是同一个文件,在我们这个情景中,这个文件就是我们前面创建的匿名共享内存文件了。因此,在目标进程中,即在Content Provider进程中,它可以通过这个新建的文件描述符来访问这块匿名共享内存,这也是匿名共享内存在进程间的共享原理,具体可以参考另外一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析



         



              这样,在Content Provider这一侧,就可以把第三方应用程序请求的数据保存在这个匿名共享内存中了,回到前面的Step 14中,下一步要执行的函数便是bulkQuery了,它的作用为请求的数据制定好一个SQL数据库查询计划。这个bulkQuery函数是由一个实现了IContentProvider接口的Binder对象来实现的,具体可以参考前面一篇文章 Android应用程序组件Content Provider的启动过程源代码分析 中,这个Binder对象的实际类型是定义在ContentProivder类内部的Transport类。



              Step 18. Transport.bulkQuery


              这个函数定义在frameworks/base/core/java/android/content/ContentProvider.java文件中:


        1. public abstract class ContentProvider implements ComponentCallbacks {  
        2.     ......  
        3.   
        4. class Transport extends ContentProviderNative {  
        5.         ......  
        6.   
        7. public IBulkCursor bulkQuery(Uri uri, String[] projection,  
        8.                 String selection, String[] selectionArgs, String sortOrder,  
        9.                 IContentObserver observer, CursorWindow window) {  
        10.             ......  
        11.   
        12. this.query(uri, projection,  
        13.                 selection, selectionArgs, sortOrder);  
        14.             ......  
        15.   
        16. return new CursorToBulkCursorAdaptor(cursor, observer,  
        17. this.getClass().getName(),  
        18.                 hasWritePermission(uri), window);  
        19.         }  
        20.   
        21.         ......  
        22.     }  
        23.   
        24.     ......  
        25. }



                这个函数主要做了两件事情,一是调用ContentProvider的子类的query函数构造一个数据库查询计划,注意,从这个函数返回来的时候,还没有真正执行数据库查询的操作,而只是按照查询条件准备好了一个SQL语句,要等到第一次使用的时候才会去执行数据库查询操作;二是使用前面一步得到的Cursor接口以及传下来的参数window来创建一个CursorToBulkCursorAdaptor对象,这个对象实现了IBulkCursor接口,同时它也是一个Binder对象,是用来返回给第三方应用程序使用的,第三方应用程序必须通过这个接口来获取从ContentProvider中查询得到的数据,而这个CursorToBulkCursorAdaptor对象的功能就是利用前面获得的Cursor接口来执行数据库查询操作,然后把查询得到的结果保存在从参数传下来的window对象内部所引用的匿名共享内存中去。我们先来看ContentProvider的子类的query函数的实现,在我们这个情景中,这个子类就是ArticlesProvider了,然后再回过头来看看这个CursorToBulkCursorAdaptor对象是如何把数据库查询计划与匿名共享内存关联起来的。


                Step 19. ArticlesProvider.query


        这个函数定义在前面一篇文章 Android应用程序组件Content Provider应用实例 介绍的应用程序ArtilcesProvider源代码工程目录下,在文件packages/experimental/ArticlesProvider/src/shy/luo/providers/articles/ArticlesProvider.java 中:



        1. public class ArticlesProvider extends ContentProvider {  
        2.     ......  
        3.   
        4. @Override  
        5. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {  
        6.         SQLiteDatabase db = dbHelper.getReadableDatabase();  
        7.   
        8. new SQLiteQueryBuilder();  
        9. null;  
        10.   
        11. switch (uriMatcher.match(uri)) {  
        12.             ......  
        13. case Articles.ITEM_POS: {  
        14. 1);  
        15.                 sqlBuilder.setTables(DB_TABLE);  
        16.                 sqlBuilder.setProjectionMap(articleProjectionMap);  
        17. ", 1";  
        18. break;  
        19.             }  
        20.             ......  
        21.         }  
        22.   
        23. null, null, TextUtils.isEmpty(sortOrder) ? Articles.DEFAULT_SORT_ORDER : sortOrder, limit);  
        24.         ......  
        25.   
        26. return cursor;  
        27.     }  
        28.   
        29.     ......  
        30. }



              从前面的Step 1中可以看到,传进来的参数uri的值为“content://shy.luo.providers.articles/pos”,通过uriMatcher的match函数来匹配这个uri的时候,得到的匹配码为Articles.ITEM_POS,这个知识点可以参考前面这篇文章 Android应用程序组件Content Provider应用实例 。因为我们的数据是保存在SQLite数据库里面的,因此,必须要构造一个SQL语句来将所请求的数据查询出来。这里是通过SQLiteQueryBuilder类来构造这个SQL查询语句的,构造好了以后,就调用它的query函数来准备一个数据库查询计划。 Step 20. SQLiteQueryBuilder.query


              这个函数定义在frameworks/base/core/java/android/database/sqlite/SQLiteQueryBuilder.java文件中:

        1. public class SQLiteQueryBuilder  
        2. {  
        3.     ......  
        4.   
        5. public Cursor query(SQLiteDatabase db, String[] projectionIn,  
        6.             String selection, String[] selectionArgs, String groupBy,  
        7.             String having, String sortOrder, String limit) {  
        8.         ......  
        9.   
        10.         String sql = buildQuery(  
        11.             projectionIn, selection, groupBy, having,  
        12.             sortOrder, limit);  
        13.   
        14.         ......  
        15.   
        16. return db.rawQueryWithFactory(  
        17.             mFactory, sql, selectionArgs,  
        18.             SQLiteDatabase.findEditTable(mTables));  
        19.     }  
        20.   
        21.     ......  
        22. }



              这里首先是调用buildQuery函数来构造一个SQL语句,它无非就是根据从参数传来列名子句、select子句、where子句、group by子句、having子句、order子句以及limit子句来构造一个完整的SQL子句,这些都是SQL语法的基础知识了,这里我们就不关注了。构造好这个SQL查询语句之后,就调用从参数传下来的数据库对象db的rawQueryWithFactory函数来进一步操作了。



         



               Step 21. SQLiteDatabase.rawQueryWithFactory



               这个函数定义在frameworks/base/core/java/android/database/sqlite/SQLiteDatabase.java文件中:


          1. public class SQLiteDatabase extends SQLiteClosable {  
          2.     ......  
          3.   
          4. public Cursor rawQueryWithFactory(  
          5.             CursorFactory cursorFactory, String sql, String[] selectionArgs,  
          6.             String editTable) {  
          7.         ......  
          8.   
          9. new SQLiteDirectCursorDriver(this, sql, editTable);  
          10.   
          11. null;  
          12. try {  
          13.             cursor = driver.query(  
          14. null ? cursorFactory : mFactory,  
          15.                 selectionArgs);  
          16. finally {  
          17.             ......  
          18.         }  
          19.   
          20. return cursor;  
          21.     }  
          22.   
          23.     ......  
          24. }



                这个函数会在内部创建一个SQLiteCursorDriver对象driver,然后调用它的query函数来创建一个Cursor对象,这个Cursor对象的实际类型是SQLiteCursor,下面我们将会看到,前面我们也已经看到,这个SQLiteCursor的内部就包含了一个数据库查询计划。