10.自定义的contentProvider:
直接extends android原生的ContentProvider,
自定义的contentProvider被使用时必须添加至App自己的manifest文件中,
(1)内部维护了一个自用的URI_MATCHER,以及一个当前Model衍生类的Class Type的 spareArray,
authority字段和一个作为cache的MiMe sparseArray.
(2)Override 了 onCreate, 会对ActivieAndroid进行初始化,初始化用的configuration
直接是configuration的builder的生成,Authority字段直接取自App的package name,
使用Cahce中的ModelInfo的tableInfos列表,按照每个table的index 依次顺序从1分配tableKey
和ItemKey,分别利用tablekey和ItemKey结合相应table的tableName和之前的authrity字段,
加入到UriMacther中,并且使用tablekey/ItemKey作为匹配码,同时将Table的type也结合tableKey/ItemKey加入到Model type的
sparseArray中,itemkey的区别在于,tableName后面会加#, #在Uri中的意义是位置的标识符<这个其实算是内部定制,
代表具体的某一row>,代表着某一个item,itemkey在这里一直是奇数,在后面的getType时会根据返回的匹配码的奇偶性来区分是dir还是item。
UriMatcher的一些基础知识:
UriMatcher:用于匹配Uri,它的用法如下:
1.首先把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回匹配码为1
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配 content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,
匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,
返回的匹配码为1。
(3)Override的getType(Uri uri):被用来获取Uri资源的MIME类型,对与单个记录,应该以
vnd.android.cursor.item开头,多条的则以vnd.android.cursor.dir开头,注意此方法可能被
多线程调用。这样之前的URIMatcher就起到作用了,利用其对Uri进行match,得到的index是
之前MIMECache中的位置,不过也可能Cache已经将此MIME类型清除,如果被清除,就要跟据uri,
来取得对应的Model的class type,如果match得到的index是偶数,那么就是一个dir<非item>,
否则就是一个item,拼接出一个MIME 字串,将其加入到MIME cache中并返回。
附一些Android MIMEtype的知识:
当一个intent对象被一个Intent Filter进行匹配测试时,有三个方面会被参考到:动作、data
(URI以及数据类型)和类别。其中的data就和MIME Type有关。在data里面定义了该过滤器的数据类型
,一般情况下是指匹配该过滤器的intent对象所包含的URI必须是该类型的。
而一个URI返回什么样的类型是在相关Provider类的getType(Uri uri)方法中决定的。Content providers
能以字符串的形式返回标准的MIME Type或者自定义的MIME Type。在自定义的ContentProvider类中,
一般会重写getType()方法,用于返回一个与URI对应的MIMEType,一个MIME Type由媒体类型(type)与子类型(subtype)组成,
它们之间使用反斜杠/分割,形式是:type/subtype.
自定义MIME type的Type值总是如下两种形式:
1.vnd.android.cursor.dir代表返回结果为多列数据
2.vnd.android.cursor.item 代表返回结果为单列数据.
定义MIME type的subtype值
自定义媒体类型的子类型subtype叫做provider-specific,android内置的provider中出现的subtype一般都比较简单。
比如联系人应用中为一个电话号码创建一个新的联系人,URI对应的MIME type如下:
vnd.android.cursor.item/phone_v2.注意这里子类型subtype的值只是简单的phone_v2。
而对于一般开发者的应用而言,subtypes的值要复杂一些,具体形式可能是根据provider的authority和要查询的表名来的。
例如一个包含火车时刻信息的provider,其中provider的authority为com.example.trains,provider创建的表格中有表Line1,Line2和Line3。
那么查询表Line1记录的URI一般写为content://com.example.trains/Line1,则provider为这个URI的返回的MIME type为
vnd.android.cursor.dir/vnd.example.line1.
查询表Line2中第五条记录的URI为:content://com.example.trains/Line2/5.
provider为这个URI的返回的MIME type为vnd.android.cursor.item/vnd.example.line2.
(4) insert(Uri uri, ContentValues values),将values插入到Uri代表的DB位置中,
这里会得到Cache提供的SqliteDataBase,基于Uri得到对应的Model的ClassType,然后使用该Type得到Table
的Name,调用 SqliteDataBase的insert将values插入,并根据返回的id来决定是否需要notifyChange<如果是有效Id的话>。
并根据返回的Id和Class type构造一个Uri返回。
(5) createUri(Class<? extends Model> type, Long id): 根据提供的Model的类型和id 构造一个指向DB中该条记录的Uri,
"content://" + Authority字段/ + Tablename/ + id<如果有的话>,最后将拼接的字符串作为参数进入Uri.parse得到。
(6)getModelType(Uri uri), 利用UriMatcher得到匹配码,然后从之前的Model sparseArray中获取Model type,找不到就返回null,
以为初始化的时候已经加了一遍了,如果找不到,那就应该是不支持的uri 或 class。
(7)notifyChange(Uri),只是对ContentResolver的notifyChange的转发罢了,以后还要把这一块系统逻辑也看看。
(8)update/delete也没什么特殊的,得到tablename和SqlliteDataBase,进行操作,然后notify,query 则是在得到了Cursor以后指定
此Cursor的watch Uri,Specifies a content URI to watch for changes.<这一块的系统逻辑也要看, mark>
12. Sqlable接口: 很简单,只有一个函数: String toSql(),说白了implements Sqlable的类对于DB,最后还是化为一句
Sql语句.
13. Delete implements Sqlable, toSql函数只返回一个"DELETE ",因为Delete 在Sql中要搭配condition, 因此Delete还有一个
方法,from(Class<? extends Model> table),返回一个From对象,自己也作为此From对象的构造参数。
14. class Select implements Sqlable, 本身有2个Flag: mDistinct 和 mAll,对应Sql中的Distinct和All<两者互斥>.有对应的
setter,要得到的Column则通过一个String数组承载,在构造时可以作为可变列表参数传入。既可以接受纯String,也可以接受
专门的Column类,后者多一个alias<Sql中概念>,toSql()其实就是根据上面的信息构造一个Select语句,如果没有指定Column,
那么使用 "*",同样会有一个from方法,得到一个from对象。
15. Set implements Sqlable,
set用于更新数据记录:
sql="update 数据表 set字段名=字段值 where 条件表达式"
sql="update 数据表 set 字段1=值1,字段2=值2 …… 字段n=值n where 条件表达式",
那么显然构造时需要一个Update对象作为构造参数,也需要一个字符串来制定修改哪些Field和新的修改值。
还有一个mWhere来代表条件表达式,会有一个where()方法来提供此条件,也是字符串,
toSql()也很直白,就是纯粹的SQL语句拼接。
因为Set()后面不会在跟别的SQL修饰,因此可以直接execute了,该execute方法就是把toSql的值和Arugments传给
SQLiteUtils来execute,后者其实也是调用SqlLiteDataBase的execSQL()<为啥不直接调用update?>。
16. Update implements Sqlable, 很简单,就是为上面的Set服务的前缀,在构造时就指定了Class<? extends Model>,
及Table, 会有一个set(String set)方法来拼接构造出上面的Set对象以真正的执行操作。
17. Join不再赘述。
18. From implements Sqlable,另外一个可以execute的结尾SQL操作类,Sql中的From后面接了很多的修饰限定,
看样子作者是懒了,不想再把后面的修饰限定进行独立成类,因此代价就是From类会有很多的方法来表示
这些修饰限定,From的构造参数是 Class<? extends Model>(要进行操作的Table)以及Sqlable queryBase(前置的操作类型),
为了指定filter,会有where方法来供使用者填入filter<两种形式: 纯string 和 参数列表都支持>,
而where用的filter为了支持And/Or等逻辑操作,From也提供了这些方法,每次where函数调用都会拼接出最新的filter字符串。
groupBy/having/orderBy/limit/offset 都是对应的Sql修饰,也是内部有flag/String保存这些值,最后toSql()时拼接。
对了还有join和Join的一堆兄弟姐妹<最晕的操作>。
纵观这些Sql操作类,其实用的都是类Builder模式,只不过不是很规范的builder生成对象的模式,而是自己build<配置>自己。
execute/executeSingleLine方法都是直接调用了SQLiteUtils的对应方法。
exists/count则是会对当前的Sql语句在进行处理<"SELECT EXISTS(SELECT 1 "/"SELECT COUNT(*) ">,然后交给SQLiteUtils进行Query.
直接extends android原生的ContentProvider,
自定义的contentProvider被使用时必须添加至App自己的manifest文件中,
(1)内部维护了一个自用的URI_MATCHER,以及一个当前Model衍生类的Class Type的 spareArray,
authority字段和一个作为cache的MiMe sparseArray.
(2)Override 了 onCreate, 会对ActivieAndroid进行初始化,初始化用的configuration
直接是configuration的builder的生成,Authority字段直接取自App的package name,
使用Cahce中的ModelInfo的tableInfos列表,按照每个table的index 依次顺序从1分配tableKey
和ItemKey,分别利用tablekey和ItemKey结合相应table的tableName和之前的authrity字段,
加入到UriMacther中,并且使用tablekey/ItemKey作为匹配码,同时将Table的type也结合tableKey/ItemKey加入到Model type的
sparseArray中,itemkey的区别在于,tableName后面会加#, #在Uri中的意义是位置的标识符<这个其实算是内部定制,
代表具体的某一row>,代表着某一个item,itemkey在这里一直是奇数,在后面的getType时会根据返回的匹配码的奇偶性来区分是dir还是item。
UriMatcher的一些基础知识:
UriMatcher:用于匹配Uri,它的用法如下:
1.首先把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回匹配码为1
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配 content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,
匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,
返回的匹配码为1。
(3)Override的getType(Uri uri):被用来获取Uri资源的MIME类型,对与单个记录,应该以
vnd.android.cursor.item开头,多条的则以vnd.android.cursor.dir开头,注意此方法可能被
多线程调用。这样之前的URIMatcher就起到作用了,利用其对Uri进行match,得到的index是
之前MIMECache中的位置,不过也可能Cache已经将此MIME类型清除,如果被清除,就要跟据uri,
来取得对应的Model的class type,如果match得到的index是偶数,那么就是一个dir<非item>,
否则就是一个item,拼接出一个MIME 字串,将其加入到MIME cache中并返回。
附一些Android MIMEtype的知识:
当一个intent对象被一个Intent Filter进行匹配测试时,有三个方面会被参考到:动作、data
(URI以及数据类型)和类别。其中的data就和MIME Type有关。在data里面定义了该过滤器的数据类型
,一般情况下是指匹配该过滤器的intent对象所包含的URI必须是该类型的。
而一个URI返回什么样的类型是在相关Provider类的getType(Uri uri)方法中决定的。Content providers
能以字符串的形式返回标准的MIME Type或者自定义的MIME Type。在自定义的ContentProvider类中,
一般会重写getType()方法,用于返回一个与URI对应的MIMEType,一个MIME Type由媒体类型(type)与子类型(subtype)组成,
它们之间使用反斜杠/分割,形式是:type/subtype.
自定义MIME type的Type值总是如下两种形式:
1.vnd.android.cursor.dir代表返回结果为多列数据
2.vnd.android.cursor.item 代表返回结果为单列数据.
定义MIME type的subtype值
自定义媒体类型的子类型subtype叫做provider-specific,android内置的provider中出现的subtype一般都比较简单。
比如联系人应用中为一个电话号码创建一个新的联系人,URI对应的MIME type如下:
vnd.android.cursor.item/phone_v2.注意这里子类型subtype的值只是简单的phone_v2。
而对于一般开发者的应用而言,subtypes的值要复杂一些,具体形式可能是根据provider的authority和要查询的表名来的。
例如一个包含火车时刻信息的provider,其中provider的authority为com.example.trains,provider创建的表格中有表Line1,Line2和Line3。
那么查询表Line1记录的URI一般写为content://com.example.trains/Line1,则provider为这个URI的返回的MIME type为
vnd.android.cursor.dir/vnd.example.line1.
查询表Line2中第五条记录的URI为:content://com.example.trains/Line2/5.
provider为这个URI的返回的MIME type为vnd.android.cursor.item/vnd.example.line2.
(4) insert(Uri uri, ContentValues values),将values插入到Uri代表的DB位置中,
这里会得到Cache提供的SqliteDataBase,基于Uri得到对应的Model的ClassType,然后使用该Type得到Table
的Name,调用 SqliteDataBase的insert将values插入,并根据返回的id来决定是否需要notifyChange<如果是有效Id的话>。
并根据返回的Id和Class type构造一个Uri返回。
(5) createUri(Class<? extends Model> type, Long id): 根据提供的Model的类型和id 构造一个指向DB中该条记录的Uri,
"content://" + Authority字段/ + Tablename/ + id<如果有的话>,最后将拼接的字符串作为参数进入Uri.parse得到。
(6)getModelType(Uri uri), 利用UriMatcher得到匹配码,然后从之前的Model sparseArray中获取Model type,找不到就返回null,
以为初始化的时候已经加了一遍了,如果找不到,那就应该是不支持的uri 或 class。
(7)notifyChange(Uri),只是对ContentResolver的notifyChange的转发罢了,以后还要把这一块系统逻辑也看看。
(8)update/delete也没什么特殊的,得到tablename和SqlliteDataBase,进行操作,然后notify,query 则是在得到了Cursor以后指定
此Cursor的watch Uri,Specifies a content URI to watch for changes.<这一块的系统逻辑也要看, mark>
12. Sqlable接口: 很简单,只有一个函数: String toSql(),说白了implements Sqlable的类对于DB,最后还是化为一句
Sql语句.
13. Delete implements Sqlable, toSql函数只返回一个"DELETE ",因为Delete 在Sql中要搭配condition, 因此Delete还有一个
方法,from(Class<? extends Model> table),返回一个From对象,自己也作为此From对象的构造参数。
14. class Select implements Sqlable, 本身有2个Flag: mDistinct 和 mAll,对应Sql中的Distinct和All<两者互斥>.有对应的
setter,要得到的Column则通过一个String数组承载,在构造时可以作为可变列表参数传入。既可以接受纯String,也可以接受
专门的Column类,后者多一个alias<Sql中概念>,toSql()其实就是根据上面的信息构造一个Select语句,如果没有指定Column,
那么使用 "*",同样会有一个from方法,得到一个from对象。
15. Set implements Sqlable,
set用于更新数据记录:
sql="update 数据表 set字段名=字段值 where 条件表达式"
sql="update 数据表 set 字段1=值1,字段2=值2 …… 字段n=值n where 条件表达式",
那么显然构造时需要一个Update对象作为构造参数,也需要一个字符串来制定修改哪些Field和新的修改值。
还有一个mWhere来代表条件表达式,会有一个where()方法来提供此条件,也是字符串,
toSql()也很直白,就是纯粹的SQL语句拼接。
因为Set()后面不会在跟别的SQL修饰,因此可以直接execute了,该execute方法就是把toSql的值和Arugments传给
SQLiteUtils来execute,后者其实也是调用SqlLiteDataBase的execSQL()<为啥不直接调用update?>。
16. Update implements Sqlable, 很简单,就是为上面的Set服务的前缀,在构造时就指定了Class<? extends Model>,
及Table, 会有一个set(String set)方法来拼接构造出上面的Set对象以真正的执行操作。
17. Join不再赘述。
18. From implements Sqlable,另外一个可以execute的结尾SQL操作类,Sql中的From后面接了很多的修饰限定,
看样子作者是懒了,不想再把后面的修饰限定进行独立成类,因此代价就是From类会有很多的方法来表示
这些修饰限定,From的构造参数是 Class<? extends Model>(要进行操作的Table)以及Sqlable queryBase(前置的操作类型),
为了指定filter,会有where方法来供使用者填入filter<两种形式: 纯string 和 参数列表都支持>,
而where用的filter为了支持And/Or等逻辑操作,From也提供了这些方法,每次where函数调用都会拼接出最新的filter字符串。
groupBy/having/orderBy/limit/offset 都是对应的Sql修饰,也是内部有flag/String保存这些值,最后toSql()时拼接。
对了还有join和Join的一堆兄弟姐妹<最晕的操作>。
纵观这些Sql操作类,其实用的都是类Builder模式,只不过不是很规范的builder生成对象的模式,而是自己build<配置>自己。
execute/executeSingleLine方法都是直接调用了SQLiteUtils的对应方法。
exists/count则是会对当前的Sql语句在进行处理<"SELECT EXISTS(SELECT 1 "/"SELECT COUNT(*) ">,然后交给SQLiteUtils进行Query.