最近幾個月一直在做IM的項目,遇到很多技術(shù)難點,查閱很多資料,請教了很多大神級別的開發(fā)者,入了很多坑,終于搞定了一個IM的項目,也算是在IM開發(fā)中積累了一些小經(jīng)驗.基本實現(xiàn)了消息(文本,語音,圖像,視頻,位置)收發(fā),未發(fā)的消息草稿功能,消息的轉(zhuǎn)發(fā),消息的復(fù)制,消息列表的置頂?shù)裙δ堋?br>
IM項目在服務(wù)端不具備消息漫游功能,這樣需要在移動端,將大量信息保存到本地,iOS選擇了使用FMDB來實現(xiàn)洒擦。
FMDB是對sqlite3.0的封裝的數(shù)據(jù)庫框架,用OC方式封裝了sqlite C的API,FMDB相比于iOS平臺的sqlite和Core Data來講優(yōu)勢明顯:
(1) 使用起來更加面向?qū)ο笾袈龋∪チ撕芏嗦闊┱歉俊⑷哂嗟腃語言代碼漂坏。
(2) 對比蘋果自帶的Core Data框架蔓腐,更加輕量級和靈活宙地。
(3) 提供了多線程安全的數(shù)據(jù)庫操作方法用爪,有效地防止數(shù)據(jù)混亂活鹰。
在此之前,我也在項目使用到了數(shù)據(jù)庫的持久化,比如像接口json文件的持久化,歷史搜索,推送數(shù)據(jù)的緩存等,而這些數(shù)據(jù)庫創(chuàng)建,往往顯得簡答而單一;只需創(chuàng)建一個靜態(tài)數(shù)據(jù)庫(DB),在添加表1,表2,表3漏策。對于大多數(shù)App而言,一個數(shù)據(jù)庫不超過5張表,這些表之間沒有任何關(guān)聯(lián),平常也就注意版本升級就可以了;似乎客戶端數(shù)據(jù)庫持久化盡在掌握中派哲。 ...
我們知道IM會產(chǎn)生大量的數(shù)據(jù),這些數(shù)據(jù)不是一兩張表就能完成數(shù)據(jù)存儲,而且表與表之間相互關(guān)聯(lián),操作了其中一張表,可能需要引起另外一張表數(shù)據(jù)的改變; IM的UI界面搭建和數(shù)據(jù)庫的關(guān)聯(lián)保持一致,這樣才能有效去實現(xiàn)需求的開發(fā)。顯然簡單掌握數(shù)據(jù)庫表的增刪改查操作是無法滿足IM數(shù)據(jù)持久化的掺喻。
上圖是項目中數(shù)據(jù)庫框架,結(jié)構(gòu)相比一般項目中數(shù)據(jù)庫明顯復(fù)雜了很多,遇到的技術(shù)難點歸納如下:
1 創(chuàng)建消息列表界面;需要將conversationalist
(單聊列表) groupChatslist
(群聊列表) rmsdetail
(聊天詳情)的數(shù)據(jù),通過字段update_date
進行排序處理并用UITabView顯示在界面上,如果對3張單獨處理,很難到達預(yù)期的效果,而且數(shù)據(jù)查詢速度慢,處理邏輯復(fù)雜,可讀性和可維護性非常差,為了解決這些問題這里引入了數(shù)據(jù)庫視圖
芭届。
數(shù)據(jù)庫視圖
是由一張表或兩張以上的表導(dǎo)出的表,視圖和表不同;視圖是個虛表
,不能存儲數(shù)據(jù)
。
數(shù)據(jù)庫視圖根據(jù)表多少可以分為單表視圖
和多表視圖
,單表視圖一般用于查詢和修改會改變基本表的數(shù)據(jù); 多表視圖一般用于查詢不會改變基本表的數(shù)據(jù)感耙。
視圖的優(yōu)點:
(1) 簡化操作,把經(jīng)常用的表定義成視圖褂乍。
(2) 安全可靠,用戶只能查詢和修改能看到的數(shù)據(jù)。
(3) 邏輯上的獨立性即硼,屏蔽了真實表的結(jié)構(gòu)帶來的影響逃片。
視圖的缺陷:
(1) 在視圖上多表查詢,將數(shù)據(jù)庫變成一個復(fù)雜的結(jié)合體,需要花費一定的時間。
(2) 當用戶試圖修改視圖的某些信息時只酥,數(shù)據(jù)庫必須把它轉(zhuǎn)化為對基本表的某些信息的修改褥实,對于簡單的視圖來說,這是很方便的;但是對于比較復(fù)雜的試圖,可能是不可修改的裂允。
FMDB 創(chuàng)建視圖:(view_conversations)
@"CREATE VIEW IF NOT EXISTS view_conversations AS SELECT \
conversationalist._id AS conv_id, \
create_date, \
update_date, \
type, \
is_stick, \
stick_time, \
conversationalist.ower_iphone_number AS ower_iphone_number, \
userlogin_number, \
recipient_numbers, \
recipient_number_ids, \
unread_message_count, \
priority, \
is_notification, \
conversationalist.is_black AS is_black, \
is_delete, \
groupChatslist._id AS group_id, \
name, \
groupChatslist.status AS status, \
organizer_number, \
chairman_number, \
owner_number, \
session_identity, \
groupChatslist_id, \
is_show_in_contact, \
group_type, \
group_version, \
rmsdetail._id AS rms_id, \
// AS 視圖查詢按照該字段,需要AS 聲明
sender_number, \
peer_numbers, \
date, \
timestamp, \
is_read, \
rmsdetail.is_black AS rms_is_black, \
rmsdetail.status AS rms_status, \
box_type, \
content, \
message_type, \
error_code, \
file_name, \
file_type, \
file_path, \
file_expire_date, \
file_thumb_path, \
file_trans_id, \
file_media_duration, \
file_size, \
file_trans_size, \
file_download_url, \
geo_latitude, \
geo_longitude, \
geo_radius, \
geo_free_text, \
imdn_msg_id, \
imdn_type, \
is_burn_after_reading, \
is_silence, \
is_direct, \
is_carbon_copy, \
is_at_msg \
FROM conversationalist LEFT OUTER JOIN groupChatslist ON conversationalist._id = groupChatslist \
LEFT OUTER JOIN rmsdetail ON conversationalist.latest_ rmsdetail _id = rmsdetail._id;"
view_conversations的數(shù)據(jù)查詢( 如查詢當前用戶的所有聊天信息)
+ (NSArray *)loadAllMessage
{
// 查詢sql
NSString* where = [NSString stringWithFormat:@"(owner_number is null or compare_number('%@',owner_number)==1) and is_delete!=1",@"當前用戶手機號136........."];
// 查詢結(jié)果 FMDBMessage 封裝類 sharedInstance單例
NSArray* array = [[FMDBMessage sharedInstance] query:@"view_conversations"
columns:@"*"
where:where
orderBy:@" update_date desc "];
return array;
}
刪除view_conversations視圖
DROP VIEW view_conversations
2 在做消息列表刪除等操作時,需要觸發(fā)2張表,對于操作需要修改2張以上表的我們使用了數(shù)據(jù)庫觸發(fā)器
,sql觸發(fā)器是一種特殊類型的存儲過程,不由用戶直接調(diào)用,它是在指定的表中發(fā)送改變自動生效损离。
FMDB生成觸發(fā)器:
+ (NSString *)sqlWithRmsTigger
{
return @"CREATE TRIGGER IF NOT EXISTS rms_update_conversation_read_on_update AFTER UPDATE OF is_read ON rms \
BEGIN \
UPDATE conversationalist SET unread_message_count = (SELECT count(*) FROM rmsdetail WHERE is_read = 0 AND rmsdetail.conv_id = NEW.conv_id) \
WHERE conversationalist._id = NEW.conv_id; \
END;";
}
結(jié)語:
客戶端選擇使用FMDB本地數(shù)據(jù)持久化,實現(xiàn)簡單的IM數(shù)據(jù)存儲還是比較方便的,但隨著項目的深入,FMDB在使用起來還是有很多缺陷,如數(shù)據(jù)庫遷移非常麻煩,大量的sql語句,給后期維護代碼增加了難度,我們正在考慮使用Realm
,在客戶端數(shù)據(jù)庫持久化道路上還需要很長路要走,因本人技術(shù)水平有限,可能有錯誤的地方,希望多多指教,大家一起學(xué)習(xí)進步!