解剖Android联系人之一,基于2.1 博客分类: 基础 AndroidSQLiteSQL数据结构Gmail
程序员文章站
2024-03-01 10:42:10
...
最近要做个联系人相关的东西,所以需要研究研究
Android的联系人数据结构比较不同,看起来比较混乱,作为新手来讲的我。
但还是得上啊,因为就我一个人
先看了几天的API,References,源码之类的(当然不是时时在看,这会儿翻翻,那会儿看看),比较迷糊,也有点理解,理解最多的就是他不是常理中想像的那样。
说到联系人,有个类不得不说,那就是ContactsContract,我是基于2.1看的,这个类有5000多行,也够多的
说实话,Android里面内部类,回调这些玩意用的太多了。。。
当然说了这么多的时候我还是不清楚联系人的结构
记得某位大神说的,理论不懂就实践,实践不懂就理论,我也比较喜欢这样,但我啥时候才能成为大神呢,至少是个小神吧
于是我开始实践,准备从数据库下手,它里面的数据最终是存在sqlite里面的,于是就要把这个数据库导出来瞧瞧
我开始想把我的Android Phone里的联系人给导出来,里面有些联系人的数据,或许可以用来分析,看下
但因为找不到数据线,就此作罢
于是开始导模拟器中的库,里面也是有数据的,是我测试的时候放进去的
位置位于/data/data/com.android.providers.contacts/databases/contacts2.db
不管是用命令adb pull,还是DDMS中的File Explorer都是可以导出到宿主机的
可能有些人上面这个文件的位置不一样,具体翻翻就知道他在哪了
然后就是sqlite3命令了
看客们不要高兴太早,我连自己都没有把握,接下来会是什么样子,我只是把这一个过程记录下来
看到有.help命令,当然第一个就是输入他了
出来的东西目前也不是很有用,就是一些命令的用法,其中有个命令是.table,这是我们需要的,他这里的命令前面都是带dot symbol的,该死的,网络又断了,这么不给力的网
这下面一共有这么多表
前几天还是看过一些资料的,ContactsContract有很多个内部类,其中有Data,RawContacts,Contacts之类的
这表里面也有类似的这几个表,当然都要打开看看咯
这都是我自己的的信息,真真假假,假假真真
命令行看着不怎么清晰,我前几天还在找和安装sqlite的图形化界面,结果未遂
可能新手看官看不出来啥东西,不过我有点看清楚了,因为我知道这有些数据项是什么意思
当然这所有的表当中可能还有些是视图,感兴趣的也可以都select出来看看
当然这还有些表是要select出来看的,比如mimetypes,
比如看到view_data里面的数据是不是又有点开心了呢?
顺路把其他几个view也查处来看看吧
比较让我失望的是android_metadata,settings这些表当中没有查询出什么数据
因为我总觉得这些mimetypes什么的应该和某个数字或字符相对应(后来发现了mimetypes中的文字就是和该表当中貌似第一列主键的这个东西对应的,在其他的表中也是直接存的这个数字)
恩,接着再往下看吧,该搞那块了?
这些表当中数据的存放不是通常的一个字段对应下面的数据都是这个意思,比如username字段下的数据都是用户名
在data表当中,至少我们现在可以明显的看出来在data表中,每行数据有指定他的类型,说他是什么,他就是什么
用原话来说基本就是这样Generic data column, the meaning is {@link #MIMETYPE} specific
大概可能就是说当每一条数据的MIMETYPE定了,那么它每一列代表的含义也就定了
这种方式大概就是为了扩展的需要吧,当年我们也有这么干的时候
不知道大家对这个查询出来的数据有没有和我们平时不同的感觉
我感觉它缺了个title,虽然他的title含义不是固定的,但我总想看看
于是想把他弄出来看看
还记得我们最开始的时候.help出来有很多命令,我们只用了.table这个最关键的
既然想看表的结构么,当然就是desc或者describe这些命令了,不才我虽然正式开始Rock Android才不久,但是以前还是搞SSH的,Oracle和MySQL还是略懂一二的
不过没有发现类似的命令,倒是发现了个.schema(Show the CREATE statements)命令,既然图形化的看不见,看SQL也是一样的
sqlite> .schema data
好长啊,又是表,又是索引,又是触发器,这么一个小数据库开是挺专业的
好吧,既然没有图形化的,我手动给他画个,刷个牙,回来再继续,晚上10点半了
这就是这个表所有的列了,我们把它变换下
上面这行的“ ...... ”是我自己干的,没有什么意义,只是想让看的更清楚点,他们都省略了前后值之间的下标的一些项,相信你懂的
随便找几条数据来比着看下吧
有个字段中他把电话号码做了个reverse,神马意图?
再来一个变换
再看看创建的两个索引
我们可以猜测是为了加快查询速度吧
那么data1一般保存的是啥数据,看起来应该挺关键的
mimetype_id和raw_contact_id应该比较好理解,前面的就相当于类型分类,后面的可能是个类似于主键之类的东西吧
一般貌似在代码中首先查询个这个东西出来,然后再去查询其他的
再来看看触发器
不知道我这么给他排版对不对,应该是这样的吧
第一个大概意思是对data表中的数据进行DELETE操作的时候,要更新raw_contacts表中的version字段数据,要删除phone_lookup中的数据,要删除status_updates中的数据,要删除name_lookup中的数据
这个OLD是个神马东西,先不管了
第二个更简单,就是说更新data表中的数据的时候,要更新data表的version字段数据,要更新raw_contacts表的version字段数据
这些version字段等的更新都是加一操作
这应该可以说明联系人信息的修改会被记录下来,这正是我要的,因为要做一个同步程序,虽然我也看过别人很多开源的代码,但都还是迷迷糊糊的
data表拆完了,再来拆contacts和raw_contacts表看看,都是体力劳动
高手们就直接会下个或者自己写个图形化的客户端来搞
头脑发晕了,在.schema contacts后面多打了个分号,结果不报错,也啥都不显示。。。
sqlite> .schema contacts
表头
对比数据
如此变换
三条索引
一个触发器
这个就是当最后一次联系时间变了,就把联系次数加一
应该就是给这个人打过一次电话了,这个次数就会增加一
剩下raw_contacts,继续
表头
对比数据
如此变换
两个索引
三个触发器
这就是三个主要表的东西,写了这么多,都是体力活,当然还有很多不是很清楚,但也有点点进步了
接下来该干啥了,该睡觉了,明天或者后天,或者大后天继续
下一步就是修改联系人数据,然后再导份出来看下,结合API看看
UPDATE 2011-06-08
可视化操作数据库工具
我在Ubuntu下用的是SQLITEMAN,Ubuntu最简单的安装办法就是sudo apt-get install sqliteman,还不错,供我们查看数据已经足够了,还有人就直接在Android上装的软件查看的,那个也行,但就屏幕太小了
联系人本身的逻辑理解了还是很简单的,当时做的时候遇到的最大的问题就是效率问题
面对2000+条联系人怎么让读写更快,传输的数据最小,确实比较费事
读写最快的原则就是尽量少开关数据库,将数据放缓存,缓存又会占用内存,这是个很矛盾的问题
另外如果没有获取ROOT权限的真机是没有办法查看到/data/目录下面的内容的,手机厂家是出于安全因素考虑,所以当时即使我找到了数据线也没有办法直接看里面的内容
貌似有些问题到现在都还没有解决的很彻底又被拉去干别的事情去了。。。
Android的联系人数据结构比较不同,看起来比较混乱,作为新手来讲的我。
但还是得上啊,因为就我一个人
先看了几天的API,References,源码之类的(当然不是时时在看,这会儿翻翻,那会儿看看),比较迷糊,也有点理解,理解最多的就是他不是常理中想像的那样。
说到联系人,有个类不得不说,那就是ContactsContract,我是基于2.1看的,这个类有5000多行,也够多的
说实话,Android里面内部类,回调这些玩意用的太多了。。。
当然说了这么多的时候我还是不清楚联系人的结构
记得某位大神说的,理论不懂就实践,实践不懂就理论,我也比较喜欢这样,但我啥时候才能成为大神呢,至少是个小神吧
于是我开始实践,准备从数据库下手,它里面的数据最终是存在sqlite里面的,于是就要把这个数据库导出来瞧瞧
我开始想把我的Android Phone里的联系人给导出来,里面有些联系人的数据,或许可以用来分析,看下
但因为找不到数据线,就此作罢
于是开始导模拟器中的库,里面也是有数据的,是我测试的时候放进去的
位置位于/data/data/com.android.providers.contacts/databases/contacts2.db
不管是用命令adb pull,还是DDMS中的File Explorer都是可以导出到宿主机的
可能有些人上面这个文件的位置不一样,具体翻翻就知道他在哪了
然后就是sqlite3命令了
看客们不要高兴太早,我连自己都没有把握,接下来会是什么样子,我只是把这一个过程记录下来
$ sqlite3 contacts2.db SQLite version 3.6.22 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite>
看到有.help命令,当然第一个就是输入他了
sqlite> .help .backup ?DB? FILE Backup DB (default "main") to FILE .bail ON|OFF Stop after hitting an error. Default OFF .databases List names and files of attached databases .dump ?TABLE? ... Dump the database in an SQL text format If TABLE specified, only dump tables matching LIKE pattern TABLE. .echo ON|OFF Turn command echo on or off .exit Exit this program .explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off. With no args, it turns EXPLAIN on. .genfkey ?OPTIONS? Options are: --no-drop: Do not drop old fkey triggers. --ignore-errors: Ignore tables with fkey errors --exec: Execute generated SQL immediately See file tool/genfkey.README in the source distribution for further information. .header(s) ON|OFF Turn display of headers on or off .help Show this message .import FILE TABLE Import data from FILE into TABLE .indices ?TABLE? Show names of all indices If TABLE specified, only show indices for tables matching LIKE pattern TABLE. .load FILE ?ENTRY? Load an extension library .log FILE|off Turn logging on or off. FILE can be stderr/stdout .mode MODE ?TABLE? Set output mode where MODE is one of: csv Comma-separated values column Left-aligned columns. (See .width) html HTML <table> code insert SQL insert statements for TABLE line One value per line list Values delimited by .separator string tabs Tab-separated values tcl TCL list elements .nullvalue STRING Print STRING in place of NULL values .output FILENAME Send output to FILENAME .output stdout Send output to the screen .prompt MAIN CONTINUE Replace the standard prompts .quit Exit this program .read FILENAME Execute SQL in FILENAME .restore ?DB? FILE Restore content of DB (default "main") from FILE .schema ?TABLE? Show the CREATE statements If TABLE specified, only show tables matching LIKE pattern TABLE. .separator STRING Change separator used by output mode and .import .show Show the current values for various settings .tables ?TABLE? List names of tables If TABLE specified, only list tables matching LIKE pattern TABLE. .timeout MS Try opening locked tables for MS milliseconds .width NUM1 NUM2 ... Set column widths for "column" mode .timer ON|OFF Turn the CPU timer measurement on or off
出来的东西目前也不是很有用,就是一些命令的用法,其中有个命令是.table,这是我们需要的,他这里的命令前面都是带dot symbol的,该死的,网络又断了,这么不给力的网
sqlite> .tables _sync_state status_updates _sync_state_metadata v1_settings activities view_contacts agg_exceptions view_contacts_restricted android_metadata view_data calls view_data_restricted contact_entities_view view_groups contact_entities_view_restricted view_raw_contacts contacts view_raw_contacts_restricted data view_v1_contact_methods groups view_v1_extensions mimetypes view_v1_group_membership name_lookup view_v1_groups nickname_lookup view_v1_organizations packages view_v1_people phone_lookup view_v1_phones raw_contacts view_v1_photos settings
这下面一共有这么多表
前几天还是看过一些资料的,ContactsContract有很多个内部类,其中有Data,RawContacts,Contacts之类的
这表里面也有类似的这几个表,当然都要打开看看咯
sqlite> select * from contacts; 1|海 郭|||0|0|0|0|1|1|0nE044748D16||0
sqlite> select * from raw_contacts; 1|0||||2|1|0|1|0|0||0|0||0|海 郭|40||||
sqlite> select * from data; 1||4|1|0|0|0|坏人|1||||||||||||||||| 2||5|1|0|0|0|http://lucane.iteye.com|7||||||||||||||||| 3||3|1|0|0|0|长板坡宜昌, 湖北 443200|1||长板坡|||宜昌|湖北|443200|||||||||| 4||6|1|0|0|0|非人|||||||||||||||||| 5||7|1|0|0|0|1-504-115-9854|1||45895114051||||||||||||||| 6||7|1|0|0|0|1-226-548-89|2||988456221||||||||||||||| 7||2|1|0|0|0|304683630|3|||4|||||||||||||| 8||8|1|0|0|0|home|1||翻译官||||||||||||||| 9||9|1|0|0|0|海 郭|海|郭|||||||||||||||| 10||1|1|0|0|0|xscript#live.com|2||||||||||||||||| 11||1|1|0|0|0|xseaer#gmail.com|1|||||||||||||||||
这都是我自己的的信息,真真假假,假假真真
命令行看着不怎么清晰,我前几天还在找和安装sqlite的图形化界面,结果未遂
可能新手看官看不出来啥东西,不过我有点看清楚了,因为我知道这有些数据项是什么意思
当然这所有的表当中可能还有些是视图,感兴趣的也可以都select出来看看
当然这还有些表是要select出来看的,比如mimetypes,
sqlite> select * from mimetypes; 1|vnd.android.cursor.item/email_v2 2|vnd.android.cursor.item/im 3|vnd.android.cursor.item/postal-address_v2 4|vnd.android.cursor.item/nickname 5|vnd.android.cursor.item/website 6|vnd.android.cursor.item/note 7|vnd.android.cursor.item/phone_v2 8|vnd.android.cursor.item/organization 9|vnd.android.cursor.item/name 10|vnd.android.cursor.item/photo 11|vnd.android.cursor.item/group_membership
sqlite> select * from view_data; 1|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/nickname|坏人|1|||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 2|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/website|http://lucane.iteye.com|7|||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 3|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/postal-address_v2|长板坡宜昌, 湖北 443200|1||长板坡|||宜昌|湖北|443200||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 4|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/note|非人||||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 5|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/phone_v2|1-504-115-9854|1||45895114051|||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 6|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/phone_v2|1-226-548-89|2||988456221|||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 7|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/im|304683630|3|||4||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 8|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/organization|home|1||翻译官|||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 9|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/name|海 郭|海|郭||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 10|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/email_v2|xscript#live.com|2|||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 11|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/email_v2|xseaer#gmail.com|1|||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1||
比如看到view_data里面的数据是不是又有点开心了呢?
顺路把其他几个view也查处来看看吧
sqlite> select * from view_contacts; 1||海 郭|1|1|0nE044748D16||1|0|0|0|0|
sqlite> select * from view_contacts_restricted; 1||海 郭|1|1|0nE044748D16||1|0|0|0|0|
sqlite> select * from view_data_restricted; 1|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/nickname|坏人|1|||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 2|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/website|http://lucane.iteye.com|7|||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 3|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/postal-address_v2|长板坡宜昌, 湖北 443200|1||长板坡|||宜昌|湖北|443200||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 4|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/note|非人||||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 5|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/phone_v2|1-504-115-9854|1||45895114051|||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 6|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/phone_v2|1-226-548-89|2||988456221|||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 7|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/im|304683630|3|||4||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 8|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/organization|home|1||翻译官|||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 9|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/name|海 郭|海|郭||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 10|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/email_v2|xscript#live.com|2|||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1|| 11|1|1||||2|1|||||0|0|0||vnd.android.cursor.item/email_v2|xseaer@#mail.com|1|||||||||||||||||||0|0|0|0|海 郭|0nE044748D16||1||
比较让我失望的是android_metadata,settings这些表当中没有查询出什么数据
因为我总觉得这些mimetypes什么的应该和某个数字或字符相对应(后来发现了mimetypes中的文字就是和该表当中貌似第一列主键的这个东西对应的,在其他的表中也是直接存的这个数字)
恩,接着再往下看吧,该搞那块了?
这些表当中数据的存放不是通常的一个字段对应下面的数据都是这个意思,比如username字段下的数据都是用户名
在data表当中,至少我们现在可以明显的看出来在data表中,每行数据有指定他的类型,说他是什么,他就是什么
用原话来说基本就是这样Generic data column, the meaning is {@link #MIMETYPE} specific
大概可能就是说当每一条数据的MIMETYPE定了,那么它每一列代表的含义也就定了
这种方式大概就是为了扩展的需要吧,当年我们也有这么干的时候
不知道大家对这个查询出来的数据有没有和我们平时不同的感觉
我感觉它缺了个title,虽然他的title含义不是固定的,但我总想看看
于是想把他弄出来看看
还记得我们最开始的时候.help出来有很多命令,我们只用了.table这个最关键的
既然想看表的结构么,当然就是desc或者describe这些命令了,不才我虽然正式开始Rock Android才不久,但是以前还是搞SSH的,Oracle和MySQL还是略懂一二的
不过没有发现类似的命令,倒是发现了个.schema(Show the CREATE statements)命令,既然图形化的看不见,看SQL也是一样的
sqlite> .schema data
CREATE TABLE data (_id INTEGER PRIMARY KEY AUTOINCREMENT,package_id INTEGER REFERENCES package(_id),mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL,raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL,is_primary INTEGER NOT NULL DEFAULT 0,is_super_primary INTEGER NOT NULL DEFAULT 0,data_version INTEGER NOT NULL DEFAULT 0,data1 TEXT,data2 TEXT,data3 TEXT,data4 TEXT,data5 TEXT,data6 TEXT,data7 TEXT,data8 TEXT,data9 TEXT,data10 TEXT,data11 TEXT,data12 TEXT,data13 TEXT,data14 TEXT,data15 TEXT,data_sync1 TEXT, data_sync2 TEXT, data_sync3 TEXT, data_sync4 TEXT ); CREATE INDEX data_mimetype_data1_index ON data (mimetype_id,data1); CREATE INDEX data_raw_contact_id ON data (raw_contact_id); CREATE TRIGGER data_deleted BEFORE DELETE ON data BEGIN UPDATE raw_contacts SET version=version+1 WHERE _id=OLD.raw_contact_id; DELETE FROM phone_lookup WHERE data_id=OLD._id; DELETE FROM status_updates WHERE status_update_data_id=OLD._id; DELETE FROM name_lookup WHERE data_id=OLD._id; END; CREATE TRIGGER data_updated BEFORE UPDATE ON data BEGIN UPDATE data SET data_version=OLD.data_version+1 WHERE _id=OLD._id; UPDATE raw_contacts SET version=version+1 WHERE _id=OLD.raw_contact_id; END;
好长啊,又是表,又是索引,又是触发器,这么一个小数据库开是挺专业的
好吧,既然没有图形化的,我手动给他画个,刷个牙,回来再继续,晚上10点半了
引用
_id package_id mimetype_id raw_contact_id is_primary is_super_primary data_version data1 data2 data3 data4 data5 data6 data7 data8 data9 data10 data11 data12 data13 data14 data15 data_sync1 data_sync2 data_sync3 data_sync4
这就是这个表所有的列了,我们把它变换下
引用
_id package_id mimetype_id raw_contact_id is_primary is_super_primary data_version data_sync1 ...... data_sync4 data1 ...... data15
上面这行的“ ...... ”是我自己干的,没有什么意义,只是想让看的更清楚点,他们都省略了前后值之间的下标的一些项,相信你懂的
随便找几条数据来比着看下吧
引用
5||7|1|0|0|0|1-504-115-9854|1||45895114051|||||||||||||||
7||2|1|0|0|0|304683630|3|||4||||||||||||||
8||8|1|0|0|0|home|1||翻译官|||||||||||||||
9||9|1|0|0|0|海 郭|海|郭||||||||||||||||
10||1|1|0|0|0|xscript#live.com|2|||||||||||||||||
11||1|1|0|0|0|xseaer#gmail.com|1|||||||||||||||||
7||2|1|0|0|0|304683630|3|||4||||||||||||||
8||8|1|0|0|0|home|1||翻译官|||||||||||||||
9||9|1|0|0|0|海 郭|海|郭||||||||||||||||
10||1|1|0|0|0|xscript#live.com|2|||||||||||||||||
11||1|1|0|0|0|xseaer#gmail.com|1|||||||||||||||||
有个字段中他把电话号码做了个reverse,神马意图?
再来一个变换
引用
_id INTEGER PRIMARY KEY AUTOINCREMENT
package_id INTEGER REFERENCES package(_id)
mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL
raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL
is_primary INTEGER NOT NULL DEFAULT 0
is_super_primary INTEGER NOT NULL DEFAULT 0
data_version INTEGER NOT NULL DEFAULT 0
data1 TEXT
data2 TEXT
data3 TEXT
data4 TEXT
data5 TEXT
data6 TEXT
data7 TEXT
data8 TEXT
data9 TEXT
data10 TEXT
data11 TEXT
data12 TEXT
data13 TEXT
data14 TEXT
data15 TEXT
data_sync1 TEXT
data_sync2 TEXT
data_sync3 TEXT
data_sync4 TEXT
package_id INTEGER REFERENCES package(_id)
mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL
raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL
is_primary INTEGER NOT NULL DEFAULT 0
is_super_primary INTEGER NOT NULL DEFAULT 0
data_version INTEGER NOT NULL DEFAULT 0
data1 TEXT
data2 TEXT
data3 TEXT
data4 TEXT
data5 TEXT
data6 TEXT
data7 TEXT
data8 TEXT
data9 TEXT
data10 TEXT
data11 TEXT
data12 TEXT
data13 TEXT
data14 TEXT
data15 TEXT
data_sync1 TEXT
data_sync2 TEXT
data_sync3 TEXT
data_sync4 TEXT
再看看创建的两个索引
CREATE INDEX data_mimetype_data1_index ON data (mimetype_id,data1); CREATE INDEX data_raw_contact_id ON data (raw_contact_id);
我们可以猜测是为了加快查询速度吧
那么data1一般保存的是啥数据,看起来应该挺关键的
mimetype_id和raw_contact_id应该比较好理解,前面的就相当于类型分类,后面的可能是个类似于主键之类的东西吧
一般貌似在代码中首先查询个这个东西出来,然后再去查询其他的
再来看看触发器
CREATE TRIGGER data_deleted BEFORE DELETE ON data BEGIN UPDATE raw_contacts SET version=version+1 WHERE _id=OLD.raw_contact_id; DELETE FROM phone_lookup WHERE data_id=OLD._id; DELETE FROM status_updates WHERE status_update_data_id=OLD._id; DELETE FROM name_lookup WHERE data_id=OLD._id; END; CREATE TRIGGER data_updated BEFORE UPDATE ON data BEGIN UPDATE data SET data_version=OLD.data_version+1 WHERE _id=OLD._id; UPDATE raw_contacts SET version=version+1 WHERE _id=OLD.raw_contact_id; END;
不知道我这么给他排版对不对,应该是这样的吧
第一个大概意思是对data表中的数据进行DELETE操作的时候,要更新raw_contacts表中的version字段数据,要删除phone_lookup中的数据,要删除status_updates中的数据,要删除name_lookup中的数据
这个OLD是个神马东西,先不管了
第二个更简单,就是说更新data表中的数据的时候,要更新data表的version字段数据,要更新raw_contacts表的version字段数据
这些version字段等的更新都是加一操作
这应该可以说明联系人信息的修改会被记录下来,这正是我要的,因为要做一个同步程序,虽然我也看过别人很多开源的代码,但都还是迷迷糊糊的
data表拆完了,再来拆contacts和raw_contacts表看看,都是体力劳动
高手们就直接会下个或者自己写个图形化的客户端来搞
头脑发晕了,在.schema contacts后面多打了个分号,结果不报错,也啥都不显示。。。
sqlite> .schema contacts
CREATE TABLE contacts (_id INTEGER PRIMARY KEY AUTOINCREMENT,display_name TEXT,photo_id INTEGER REFERENCES data(_id),custom_ringtone TEXT,send_to_voicemail INTEGER NOT NULL DEFAULT 0,times_contacted INTEGER NOT NULL DEFAULT 0,last_time_contacted INTEGER,starred INTEGER NOT NULL DEFAULT 0,in_visible_group INTEGER NOT NULL DEFAULT 1,has_phone_number INTEGER NOT NULL DEFAULT 0,lookup TEXT,status_update_id INTEGER REFERENCES data(_id),single_is_restricted INTEGER NOT NULL DEFAULT 0); CREATE INDEX contacts_has_phone_index ON contacts (has_phone_number); CREATE INDEX contacts_restricted_index ON contacts (single_is_restricted); CREATE INDEX contacts_visible_index ON contacts (in_visible_group,display_name COLLATE LOCALIZED); CREATE TRIGGER contacts_times_contacted UPDATE OF last_time_contacted ON contacts BEGIN UPDATE contacts SET times_contacted = (new.times_contacted + 1) WHERE _id = new._id;END;
表头
引用
_id display_name photo_id custom_ringtone send_to_voicemail times_contacted last_time_contacted starred in_visible_group has_phone_number lookup status_update_id single_is_restricted
对比数据
引用
1 | 海 郭 | | | 0 | 0 | 0 | 0 | 1 | 1 | 0nE044748D16 | | 0
如此变换
引用
_id INTEGER PRIMARY KEY AUTOINCREMENT
display_name TEXT
photo_id INTEGER REFERENCES data(_id)
custom_ringtone TEXT
send_to_voicemail INTEGER NOT NULL DEFAULT 0
times_contacted INTEGER NOT NULL DEFAULT 0
last_time_contacted INTEGER
starred INTEGER NOT NULL DEFAULT 0
in_visible_group INTEGER NOT NULL DEFAULT 1
has_phone_number INTEGER NOT NULL DEFAULT 0
lookup TEXT
status_update_id INTEGER REFERENCES data(_id)
single_is_restricted INTEGER NOT NULL DEFAULT 0
display_name TEXT
photo_id INTEGER REFERENCES data(_id)
custom_ringtone TEXT
send_to_voicemail INTEGER NOT NULL DEFAULT 0
times_contacted INTEGER NOT NULL DEFAULT 0
last_time_contacted INTEGER
starred INTEGER NOT NULL DEFAULT 0
in_visible_group INTEGER NOT NULL DEFAULT 1
has_phone_number INTEGER NOT NULL DEFAULT 0
lookup TEXT
status_update_id INTEGER REFERENCES data(_id)
single_is_restricted INTEGER NOT NULL DEFAULT 0
三条索引
CREATE INDEX contacts_has_phone_index ON contacts (has_phone_number); CREATE INDEX contacts_restricted_index ON contacts (single_is_restricted); CREATE INDEX contacts_visible_index ON contacts (in_visible_group,display_name COLLATE LOCALIZED);
一个触发器
CREATE TRIGGER contacts_times_contacted UPDATE OF last_time_contacted ON contacts BEGIN UPDATE contacts SET times_contacted = (new.times_contacted + 1) WHERE _id = new._id; END;
这个就是当最后一次联系时间变了,就把联系次数加一
应该就是给这个人打过一次电话了,这个次数就会增加一
剩下raw_contacts,继续
sqlite> .schema raw_contacts CREATE TABLE raw_contacts (_id INTEGER PRIMARY KEY AUTOINCREMENT,is_restricted INTEGER DEFAULT 0,account_name STRING DEFAULT NULL, account_type STRING DEFAULT NULL, sourceid TEXT,version INTEGER NOT NULL DEFAULT 1,dirty INTEGER NOT NULL DEFAULT 0,deleted INTEGER NOT NULL DEFAULT 0,contact_id INTEGER REFERENCES contacts(_id),aggregation_mode INTEGER NOT NULL DEFAULT 0,aggregation_needed INTEGER NOT NULL DEFAULT 1,custom_ringtone TEXT,send_to_voicemail INTEGER NOT NULL DEFAULT 0,times_contacted INTEGER NOT NULL DEFAULT 0,last_time_contacted INTEGER,starred INTEGER NOT NULL DEFAULT 0,display_name TEXT,display_name_source INTEGER NOT NULL DEFAULT 0,sync1 TEXT, sync2 TEXT, sync3 TEXT, sync4 TEXT ); CREATE INDEX raw_contacts_contact_id_index ON raw_contacts (contact_id); CREATE INDEX raw_contacts_source_id_index ON raw_contacts (sourceid, account_type, account_name); CREATE TRIGGER raw_contacts_deleted BEFORE DELETE ON raw_contacts BEGIN DELETE FROM data WHERE raw_contact_id=OLD._id; DELETE FROM agg_exceptions WHERE raw_contact_id1=OLD._id OR raw_contact_id2=OLD._id; DELETE FROM contacts WHERE _id=OLD.contact_id AND (SELECT COUNT(*) FROM raw_contacts WHERE contact_id=OLD.contact_id )=1; END; CREATE TRIGGER raw_contacts_marked_deleted BEFORE UPDATE ON raw_contacts BEGIN UPDATE raw_contacts SET version=OLD.version+1 WHERE _id=OLD._id AND NEW.deleted!= OLD.deleted; END; CREATE TRIGGER raw_contacts_times_contacted UPDATE OF last_time_contacted ON raw_contacts BEGIN UPDATE raw_contacts SET times_contacted = (new.times_contacted + 1) WHERE _id = new._id;END;
表头
引用
id is_restricted account_name account_type sourceid version dirty deleted contact_id aggregation_mode aggregation_needed custom_ringtone send_to_voicemail times_contacted last_time_contacted starred display_name display_name_source sync1 sync2 sync3 sync4
对比数据
引用
1 | 0 | | | | 2 | 1 | 0 | 1 | 0 | 0 | | 0 | 0 | | 0 | 海 郭 | 40 | | | |
如此变换
引用
id INTEGER PRIMARY KEY AUTOINCREMENT
is_restricted INTEGER DEFAULT 0
account_name STRING DEFAULT NULL
account_type STRING DEFAULT NULL
sourceid TEXT
version INTEGER NOT NULL DEFAULT 1
dirty INTEGER NOT NULL DEFAULT 0
deleted INTEGER NOT NULL DEFAULT 0
contact_id INTEGER REFERENCES contacts(_id)
aggregation_mode INTEGER NOT NULL DEFAULT 0
aggregation_needed INTEGER NOT NULL DEFAULT 1
custom_ringtone TEXT
send_to_voicemail INTEGER NOT NULL DEFAULT 0
times_contacted INTEGER NOT NULL DEFAULT 0
last_time_contacted INTEGER
starred INTEGER NOT NULL DEFAULT 0
display_name TEXT
display_name_source INTEGER NOT NULL DEFAULT 0
sync1 TEXT
sync2 TEXT
sync3 TEXT
sync4 TEXT
is_restricted INTEGER DEFAULT 0
account_name STRING DEFAULT NULL
account_type STRING DEFAULT NULL
sourceid TEXT
version INTEGER NOT NULL DEFAULT 1
dirty INTEGER NOT NULL DEFAULT 0
deleted INTEGER NOT NULL DEFAULT 0
contact_id INTEGER REFERENCES contacts(_id)
aggregation_mode INTEGER NOT NULL DEFAULT 0
aggregation_needed INTEGER NOT NULL DEFAULT 1
custom_ringtone TEXT
send_to_voicemail INTEGER NOT NULL DEFAULT 0
times_contacted INTEGER NOT NULL DEFAULT 0
last_time_contacted INTEGER
starred INTEGER NOT NULL DEFAULT 0
display_name TEXT
display_name_source INTEGER NOT NULL DEFAULT 0
sync1 TEXT
sync2 TEXT
sync3 TEXT
sync4 TEXT
两个索引
CREATE INDEX raw_contacts_contact_id_index ON raw_contacts (contact_id); CREATE INDEX raw_contacts_source_id_index ON raw_contacts (sourceid, account_type, account_name);
三个触发器
CREATE TRIGGER raw_contacts_deleted BEFORE DELETE ON raw_contacts BEGIN DELETE FROM data WHERE raw_contact_id=OLD._id; DELETE FROM agg_exceptions WHERE raw_contact_id1=OLD._id OR raw_contact_id2=OLD._id; DELETE FROM contacts WHERE _id=OLD.contact_id AND (SELECT COUNT(*) FROM raw_contacts WHERE contact_id=OLD.contact_id )=1; END; CREATE TRIGGER raw_contacts_marked_deleted BEFORE UPDATE ON raw_contacts BEGIN UPDATE raw_contacts SET version=OLD.version+1 WHERE _id=OLD._id AND NEW.deleted!= OLD.deleted; END; CREATE TRIGGER raw_contacts_times_contacted UPDATE OF last_time_contacted ON raw_contacts BEGIN UPDATE raw_contacts SET times_contacted = (new.times_contacted + 1) WHERE _id = new._id; END;
这就是三个主要表的东西,写了这么多,都是体力活,当然还有很多不是很清楚,但也有点点进步了
接下来该干啥了,该睡觉了,明天或者后天,或者大后天继续
下一步就是修改联系人数据,然后再导份出来看下,结合API看看
UPDATE 2011-06-08
可视化操作数据库工具
我在Ubuntu下用的是SQLITEMAN,Ubuntu最简单的安装办法就是sudo apt-get install sqliteman,还不错,供我们查看数据已经足够了,还有人就直接在Android上装的软件查看的,那个也行,但就屏幕太小了
联系人本身的逻辑理解了还是很简单的,当时做的时候遇到的最大的问题就是效率问题
面对2000+条联系人怎么让读写更快,传输的数据最小,确实比较费事
读写最快的原则就是尽量少开关数据库,将数据放缓存,缓存又会占用内存,这是个很矛盾的问题
另外如果没有获取ROOT权限的真机是没有办法查看到/data/目录下面的内容的,手机厂家是出于安全因素考虑,所以当时即使我找到了数据线也没有办法直接看里面的内容
貌似有些问题到现在都还没有解决的很彻底又被拉去干别的事情去了。。。