使用ContentProvider和Cursor跨进程传输非数据库数据

1.使用ContentProvider跨进程传输数据,依靠的是ContentProvider$Transport extends ContentProviderNative。ContentProvider是处理业务的核心,但是本身不具备跨进程的能力。

2.而最终需要获得数据库数据(也可以是非数据库数据),还需要其他的Binder。IBulkCursor,实际类型是CursorToBulkCursorAdapter extends BulkCursorNative。

3.查询到的数据最初是保存到Cursor中的,最后会创建一个CursorWindow,通过fillWindow方法,将Cursor中的数据放到window中,window是一块共享内存,最后通过IBulkCursor控制Cursor将数据填充到window中,然后返回window的引用给客户端。所以客户端获得的Cursor不是服务端的Cursor,而是封装了IBulkCursor的BulkCursorToCursorAdapter。

4.Cursor是用于控制获取第几行第几列的数据的存取。CrossProcessCursor的就是利用Cursor获取第几行第几列的数据的方法去填充window的:

    @Override
    public void fillWindow(int position, CursorWindow window) {
        DatabaseUtils.cursorFillWindow(this, position, window);
    }

5.如果需要自定义Cursor,只要继承AbstractCursor就行,AbstractCursor继承了CrossProcessCursor,就是有window了,就可以把数据分享给客户端,那么只需要复写以下方法即可:

    /* -------------------------------------------------------- */
    /* These need to be implemented by subclasses */
    @Override
    abstract public int getCount();

    @Override
    abstract public String[] getColumnNames();

    @Override
    abstract public String getString(int column);
    @Override
    abstract public short getShort(int column);
    @Override
    abstract public int getInt(int column);
    @Override
    abstract public long getLong(int column);
    @Override
    abstract public float getFloat(int column);
    @Override
    abstract public double getDouble(int column);
    @Override
    abstract public boolean isNull(int column);

从Cursor的以上方法获取到某行某列的数据后,怎么填充到window,Cursor中的信息怎么从服务端去到客户端,客户端怎么获取到window的引用,怎么把从服务端返回的数据适配成一个Cursor,这都会被ContentProvider和Cursor框架自动解决。

6.需要取哪一行的数据,是通过Cursor的moveToPosition接口确定的,然后上面需要复写的方法就是在确定了行的基础上,取位于第几列的数据。所以Cursor框架就是存取矩阵数据的,数据位置通过行列确定。在初始化Cursor时就需要通过参数确定有哪些列(String[] columns)。

7.MatrixCursor就是一个继承了AbstractCursor的非抽象类。如果想要分享非数据库信息,可以通过创建MatrixCursor,并addRow加入行数据。而如果时数据库信息的话,这个Cursor就会通过SQLiteOpenHelper创建,类型时SQLiteCursor extends AbstractWindowedCursor,而SQLiteCursor 自己会创建并填充CursorWindow,而没有继承AbstractWindowedCursor,就需要外部创建CursorWindow,再通过Cursor#fillWindow方法传给Cursor并把Cursor代表的数据填充到window中。

8.在客户端获得Cursor后(实际类型时BulkCursorToCursorAdapter extends AbstractWindowedCursor),虽然此时服务端中Cursor代表的数据已经填充进Cursor Window指向的共享内存,但并没有获得CursorWindow的引用。在客户端调用BulkCursorToCursorAdapter#moveToxxx时,就会去获取window,如果本地没有就会通过IBulkCursor去获取,然后服务端中就会填充该window并返回给客户端。如果你没有调用moveToxxx方法就去调用Cursor#getxxx(int Column)方法的话,将会报错,因为每个getxxx方法会检查,检查代码如下:

    @Override
    protected void checkPosition() {
        super.checkPosition();
        
        if (mWindow == null) {
            throw new StaleDataException("Attempting to access a closed CursorWindow." +
                    "Most probable cause: cursor is deactivated prior to calling this method.");
        }
    }

写一个传输非数据库数据的Cursor的步骤:

1.复写Cursor    int getType(int columnIndex);该方法用于确定某一列的数据的类型,然后取该列数据写到window时才知道调用哪个getXXX()方法获得正确类型的数据。

2.复写Cursor 的 XXX getXXX(int columnIndex);这类方法根据当前mPos和参数columnIndex返回一个某一类型的数据

3.继承AbstractCursor,该类继承了CrossProcessCursor且复写了fillWindow。否则如果不继承AbstractCursor,那么可以继承CrossProcessCursor,然后复写Cursor的 void fillWindow(int position, CursorWindow window);该方法用于把从position开始的数据填充到参数window所代表的共享空间中。

 

以下是继承AbstractCursor自定义一个Cursor的例子:使用ContentProvider访问非数据库数据

 

你可能感兴趣的:(使用ContentProvider和Cursor跨进程传输非数据库数据)