Content Provider 代码生成器


A tool to generate Android ContentProviders.
It takes a set of entity (a.k.a "table") definitions as the input, and generates:

  • a ContentProvider class
  • an SQLiteOpenHelper class
  • one Columns class per entity
  • one Cursor class per entity
  • one ContentValues class per entity
  • one Selection class per entity
  • one Model interface per entity
  • one Bean class per entity (optionally)


There are two possible ways to generate the code:

  1. as part of the build script (with a Gradle plugin)
  2. as a one-time step (using a command line tool)

The Gradle plugin is perhaps the 'cleaner' way in the sense that the generated
code won't be part of the source (not checked into VCS). The configuration is declared inside
the Gradle script which allows to update it easily.

Alternatively, a one-time generation can be done (typically at the beginning of the project.)
The generated code is part of the source and checked into VCS: this allows you
to modify it if you need to.

You can decide which option is the best for your project :)

Option 1: Gradle plugin

Add this to your app's build.gradle:

buildscript {
    dependencies {
        classpath 'org.jraf:acpg-gradle-plugin:1.13.1'

apply plugin: 'org.jraf.acpg.gradleplugin'


// This is where you declare a few parameters used to generate the code
acpg {
    // Where to find the entity files (see 'Entity files' below)
    // Optional - default value: 'etc/acpg' in the root project
    entitiesDir file('etc/acpg-entities')

    // Java package in which all the code will be generated
    providerJavaPackage ''

    // ContentProvider authority
    // "${applicationId}" will be substituted by BuildConfig.APPLICATION_ID in the generated code
    authority '${applicationId}.provider'

    // Name of the provider class
    providerClassName 'ExampleProvider'

    // Name of the db file
    databaseFileName 'example.db'

    // Version of the db
    databaseVersion 1

    // Name of the SQLiteOpenHelper class
    // Optional - default value: providerClassName + "SQLiteOpenHelper"
    sqliteOpenHelperClassName 'ExampleSQLiteOpenHelper'

    // Name of a subclass of BaseSQLiteOpenHelperCallbacks
    // Optional - this allows you to get called when the db is opened/created/upgraded
    sqliteOpenHelperCallbacksClassName 'ExampleSQLiteOpenHelperCallbacks'

    // Whether to enable foreign keys support (see 'Advanced usage' below)
    // Optional - default value: false
    enableForeignKeys true

    // Whether @Nullable/@NonNull annotations will be used in the generated code
    // Optional - default value: false
    useAnnotations true

    // Whether support library classes are used or the Android SDK ones (e.g. CursorLoader)
    // Optional - default value: false
    useSupportLibrary true

    // Whether to generate a 'Beans' class for each entity
    // Optional - default value: true
    generateBeans true

    // Name of a boolean field in BuildConfig to enable/disable debug logging in the generated code
    // Optional - default value: "DEBUG"
    debugLogsFieldName 'LOG_DEBUG_PROVIDER'

    // Version of the tool syntax (must be 4)
    // The allows to break the build immediately if an incompatible version of the tool is used. Safety first!
    // Optional - default value: 4
    syntaxVersion 4

Option 2: Command line tool

The configuration is the same, except you declare it in a file named _config.json
in the same folder as the entity files.

Here is an example:

    "syntaxVersion": 4,
    "packageName": "",
    "providerJavaPackage": "",
    "authority": "${applicationId}.provider",
    "providerClassName": "ExampleProvider",
    "databaseFileName": "example.db",
    "databaseVersion": 1,
    "sqliteOpenHelperClassName": "ExampleSQLiteOpenHelper",
    "sqliteOpenHelperCallbacksClassName": "ExampleSQLiteOpenHelperCallbacks",
    "enableForeignKeys": true,
    "useAnnotations": true,
    "useSupportLibrary": true,
    "generateBeans": true,
    "debugLogsFieldName": "LOG_DEBUG_PROVIDER"

About packageName: this must be the same as the value of the package attribute in your manifest.
Not to be confused with the applicationId (see

Get and run the tool

Download the acpg-cli-1.13.1.jar file here:

java -jar acpg-cli-1.13.1.jar -i <input folder> -o <output folder>

  • Input folder: where to find _config.json and your entity json files
  • Output folder: where the resulting files will be generated

Entity files

Create one file per entity, naming it <entity_name>.json.
Inside each file, declare your fields (a.k.a "columns") with a name and a type.
You can also optionally declare a default value, an index flag, a documentation and a nullable flag.

Currently the type can be:

  • String (SQLite type: TEXT)
  • Integer (INTEGER)
  • Long (INTEGER)
  • Float (REAL)
  • Double (REAL)
  • Boolean (INTEGER)
  • Date (INTEGER)
  • byte[] (BLOB)
  • enum (INTEGER).

You can also optionally declare table constraints.

Here is a person.json file as an example:

    "documentation": "A human being which is part of a team.",
    "fields": [
            "documentation": "First name of this person. For instance, John.",
            "name": "first_name",
            "type": "String",
            "defaultValue": "John"
            "documentation": "Last name (a.k.a. Given name) of this person. For instance, Smith.",
            "name": "last_name",
            "type": "String",
            "nullable": true,
            "defaultValue": "Doe"
            "name": "age",
            "type": "Integer",
            "index": true
            "name": "gender",
            "type": "enum",
            "enumName": "Gender",
            "enumValues": [
                {"OTHER": "Value to use when neither male nor female"}
            "nullable": false

    "constraints": [
            "name": "unique_name",
            "definition": "UNIQUE (first_name, last_name) ON CONFLICT REPLACE"

    "defaultOrder": "first_name, last_name, age DESC"


  • An _id primary key field is automatically (implicitly) declared for all entities. It must not be declared in the json file.
  • nullable is optional (true by default).
  • if documentation is present the value will be copied in Javadoc blocks in the generated code.
  • the constraints and defaultOrder sections are optional

A more comprehensive sample is available in the sample-app/etc/acpg folder.

You can have a look at the corresponding generated code in the etc/sample-generated-code folder.

By convention, you should name your entities and fields in lower case with words separated by '_', like in the example above.

The header.txt file (optional)

If a header.txt file is present, its contents will be inserted at the top of every generated file.

Use the generated files

  • When querying a table, use the corresponding Selection class as shown in this example:
PersonSelection where = new PersonSelection();
Cursor c = context.getContentResolver().query(where.uri(), projection,
        where.sel(), where.args(), null);
  • When using the results of a query, wrap the resulting Cursor in the corresponding wrapper class. You can then use
    the generated getters directly as shown in this example:
PersonCursor person = new PersonCursor(c);
String lastName = person.getLastName();
Long age = person.getAge();
  • You can also conveniently combine these two facilities by using the query (or delete) method:
PersonSelection where = new PersonSelection();
PersonCursor person = where.query(context);
String lastName = person.getLastName();
Long age = person.getAge();

or, use a CursorLoader:

  • When updating or inserting into a table, use the corresponding ContentValues class as shown in this example:
PersonContentValues values = new PersonContentValues();
context.getContentResolver().update(values.uri(), values.values(), null, null);



Advanced usage

Foreign key / joins

There is limited support for foreign keys and joins.
Here is an example of the syntax:

    "fields": [
            "name": "main_team_id",
            "type": "Long",
            "nullable": false,
            "foreignKey": {
                "table": "team",
                "onDelete": "CASCADE"
            "name": "first_name",
            "type": "String",
            "nullable": false


In this example, the field main_team_id is a foreign key referencing the primary key of the team table.

  • The appropriate FOREIGN KEY SQL constraint is generated (if enableForeignKeys is set to true in _config.json).
  • The team table will be automatically joined when querying the person table [1].
  • Getters for team columns are generated in the PersonCursor wrapper.
  • Of course if team has foreign keys they will also be handled (and recursively).

[1] A table is automatically joined if at least one of its columns is included in the projection.
If the projection is null (i.e. all columns), all the tables are joined. Caution: you should be extra careful when using a null projection
with joins because you will get several columns named _id in the results!


  • Foreign keys always reference the _id column (the implicit primary key of all tables) and thus must always be of type Long - by design.
  • Only one foreign key to a particular table is allowed per table. In the example above only one column in person can point to team.
  • Loops (i.e. A has a foreign key to B and B has a foreign key to A) aren't detected. The generator will infinitely loop if they exist.
  • Cases such as "A has a FK to B, B has a FK to C, A has a FK to C" generate ambiguities in the queries, because C columns appear twice. In the sample app you can see an example of how to deal with this case, using prefixes and aliases (SQL's AS keyword).


A sample is available in the sample-app folder, with the entities in sample-app/etc/acpg.

You can have a look at the corresponding generated code in the etc/sample-generated-code folder.

Here is the table shema of the sample:
Table shema of the sample


This is a Gradle project.

./gradlew install to 'install' the Gradle plugin to your local maven repo

./gradlew shadowJar to build the cli tool

Similar tools

Here is a list of other tools that try to tackle the same problem.

I did not have the chance to try them out.

  • (a fork of this project that generates more code)
  • (based on sql statements, not json)


This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see

Just to be absolutely clear, this license applies to this program itself,
not to the source it will generate!



发布于:4天以前  |  51次阅读  |  详细内容 »


Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。

发布于:1月以前  |  208次阅读  |  详细内容 »


很多App在首次启动时一通弹窗,申请各式各样的权限。后来苹果为改善用户体验,在App Store审核时要求App必须在使用前一刻才能申请权限,有效改善了此类问题。比如一款直播App,当你启动App时并不需要相机、录音权限,等到你开播时才需要申请这两个权限。这一场景,其实就类似今天要提到的Android动态授权。

发布于:1月以前  |  228次阅读  |  详细内容 »



发布于:1月以前  |  242次阅读  |  详细内容 »



发布于:1月以前  |  230次阅读  |  详细内容 »



发布于:1月以前  |  263次阅读  |  详细内容 »


简化Android的UI开发 6月以前  |  166442次阅读
Google Enjarify:可代替dex2jar的dex反编译 1年以前  |  2148次阅读
Android设计与开发工作流 6月以前  |  2106次阅读
Android多渠道打包工具:apptools 1年以前  |  1847次阅读
Android权限 - 第一篇 1年以前  |  1820次阅读
Google Java编程风格规范(中文版) 1年以前  |  1787次阅读
Stetho 1年以前  |  1730次阅读
Android UI基本技术点 1年以前  |  1716次阅读
30分钟搭建一个android的私有Maven仓库 1年以前  |  1631次阅读
2015 Google IO带来的新 Android 开发工具 1年以前  |  1624次阅读
你应该知道的布局和属性 1年以前  |  1574次阅读
Gradle小知识#3:任务的顺序 1年以前  |  1564次阅读
听FackBook工程师讲*Custom ViewGroups* 1年以前  |  1564次阅读
MVP在Android平台上的应用 1年以前  |  1557次阅读