生成Content Provider

BDFlow甚至可以很简单地生成Content Provider

开始

此功能主要使用了schematic库。

ContentProvider描述类

为了生成一个ContentProvider,我们首先需要定义一个描述类:

@ContentProvider(authority = TestContentProvider.AUTHORITY,
        database = TestDatabase.class,
        baseContentUri = TestContentProvider.BASE_CONTENT_URI)
public class TestContentProvider {

    public static final String AUTHORITY = "com.raizlabs.android.dbflow.test.provider";

    public static final String BASE_CONTENT_URI = "content://";

}

当然你也可以在任何类上通过注解将其定义为ContentProvider的描述类。推荐的一个写法就是将@Database描述类同时作为@ContentProvider的描述类。它们同时注解于同一个类上是完全没问题的:

@ContentProvider(authority = TestDatabase.AUTHORITY,
        database = TestDatabase.class,
        baseContentUri = TestDatabase.BASE_CONTENT_URI)
@Database(name = TestDatabase.NAME, version = TestDatabase.VERSION)
public class TestDatabase {

    public static final String NAME = "TestDatabase";

    public static final int VERSION = "1";

    public static final String AUTHORITY = "com.raizlabs.android.dbflow.test.provider";

    public static final String BASE_CONTENT_URI = "content://";

}

添加到Manifest中

ContentProvider将会被生成为类名$Provider,在使用ContentProvider的其他App或本App中我们需要在AndroidManifest.xml文件中声明:

<provider
    android:authorities="com.raizlabs.android.dbflow.test.provider"
    android:exported="true|false"
    android:name=".provider.TestContentProvider$Provider"/>

android:exported:设定改服务是否可以跨进程(App)使用,为true时其他应用才可以使用此服务。

注意,你必须有至少一个@TableEndpoint以确保它能通过编译检测

对数据添加Endpoints

有两种方式定义@TableEndpoint

  1. @ContentProvider描述类中创建一个内部类。
  2. @Table类中指定content provider的类名

@TableEndpoint:将ContentProvider中的检索、删除和更新的操作映射到本地数据库。

一些建议:

  1. 如果使用@ContentProvider内部类作为@TableEndpoint,其类名最好与被引用的表同名。
  2. 创建一个public static final String ENDPOINT = "{tableName}"属性,使之可以在其他地方被引用。
  3. 创建一个buildUri()方法(如下),方便创建URI

示例:

@TableEndpoint(ContentProviderModel.ENDPOINT)
public static class ContentProviderModel {

    public static final String ENDPOINT = "ContentProviderModel";

    private static Uri buildUri(String... paths) {
        Uri.Builder builder = Uri.parse(BASE_CONTENT_URI + AUTHORITY).buildUpon();
        for (String path : paths) {
            builder.appendPath(path);
        }
        return builder.build();
    }

    @ContentUri(path = ContentProviderModel.ENDPOINT,
            type = ContentUri.ContentType.VND_MULTIPLE + ENDPOINT)
    public static Uri CONTENT_URI = buildUri(ENDPOINT);

}

或者对已有的数据表进行设置:

@TableEndpoint(name = ContentProviderModel.NAME, contentProviderName = "ContentDatabase")
@Table(database = ContentDatabase.class, tableName = ContentProviderModel.NAME)
public class ContentProviderModel extends BaseProviderModel<ContentProviderModel> {

    public static final String NAME = "ContentProviderModel";

    @ContentUri(path = NAME, type = ContentUri.ContentType.VND_MULTIPLE + NAME)
    public static final Uri CONTENT_URI = ContentUtils.buildUri(ContentDatabase.AUTHORITY);

    @Column
    @PrimaryKey(autoincrement = true)
    long id;

    @Column
    String notes;

    @Column
    String title;

    @Override
    public Uri getDeleteUri() {
        return TestContentProvider.ContentProviderModel.CONTENT_URI;
    }

    @Override
    public Uri getInsertUri() {
        return TestContentProvider.ContentProviderModel.CONTENT_URI;
    }

    @Override
    public Uri getUpdateUri() {
        return TestContentProvider.ContentProviderModel.CONTENT_URI;
    }

    @Override
    public Uri getQueryUri() {
        return TestContentProvider.ContentProviderModel.CONTENT_URI;
    }
}

之后的章节会更详细地介绍@ContentUri的使用方法。

更方便地创建ContentProvider

这里还有两种更方便的方式将你的Model改为支持ContentProvider,只需要继承下面两个类即可:

  1. BaseProviderModel: 重写了Model中的所有方法使之可以用于ContentProvider
  2. BaseSyncableProviderModel:与上面相同,但它将会把数据的更新与本地的数据更新及时同步。

使用Content Provider

你可以通过ContentUtils方便地使用ContentProvider

ContentProviderModel contentProviderModel = ...; // some instance

int count = ContentUtils.update(getContentResolver(), ContentProviderModel.CONTENT_URI, contentProviderModel);

Uri uri = ContentUtils.insert(getContentResolver(), ContentProviderModel.CONTENT_URI, contentProviderModel);

int count = ContentUtils.delete(getContentResolver(), someContentUri, contentProviderModel);

推荐:通过继承BaseSyncableProviderModel来使本地数据库保持一致,除非使用BaseProviderModel就足够了。

MyModel model = new MyModel();
model.id = 5;
model.load(); // queries the content provider

model.someProp = "Hello"
model.update(false); // runs an update on the CP

model.insert(false); // inserts the data into the CP

高级用法

通知方法

你可以通过定义@Notify方法监听ContentProvider中指定的操作,并通过返回Uri[]来通知相应的ContentResolver

支持监听:

  1. Update
  2. Insert
  3. Delete

Example:

@Notify(method = Notify.Method.UPDATE,
paths = {}) // specify paths that will call this method when specified.
public static Uri[] onUpdate(Context context, Uri uri) {

  return new Uri[] {
    // return custom uris here
  };
}

高级ContentUri

Path Segments

通过#符号,你可以定义高级path匹配:

path:

path = "Friends/#/#"

segments:

segments = {@PathSegment(segment = 1, column = "id"),
    @PathSegment(segment = 2, column = "name")}

组合起来:

@ContentUri(type = ContentType.VND_MULTIPLE,
path = "Friends/#/#",
segments = {@PathSegment(segment = 1, column = "id"),
    @PathSegment(segment = 2, column = "name")})
public static Uri withIdAndName(int id, String name) {
  return buildUri(id, name);
}