欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

解剖Android联系人之一,基于2.1 博客分类: 基础 AndroidSQLiteSQL数据结构Gmail 

程序员文章站 2024-03-01 10:58:52
...
最近要做个联系人相关的东西,所以需要研究研究

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|||||||||||||||||


有个字段中他把电话号码做了个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


再看看创建的两个索引
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

三条索引
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


两个索引
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/目录下面的内容的,手机厂家是出于安全因素考虑,所以当时即使我找到了数据线也没有办法直接看里面的内容

貌似有些问题到现在都还没有解决的很彻底又被拉去干别的事情去了。。。