本發(fā)明屬于數(shù)據(jù)處理
技術(shù)領(lǐng)域:
:,尤其涉及一種數(shù)據(jù)恢復(fù)的方法、裝置及Android設(shè)備。
背景技術(shù):
::自2011年以來,國內(nèi)外手機(jī)市場以安卓Android手機(jī)占有率最高。Android系統(tǒng)中,數(shù)據(jù)存儲(chǔ)都是采用SQLite數(shù)據(jù)庫。大多數(shù)的傳統(tǒng)方法采用直接調(diào)用Android框架層的應(yīng)用程序編程接口(ApplicationProgrammingInterface,API)來提取Android設(shè)備中存儲(chǔ)的信息,該方法簡便易操作,但不能恢復(fù)Android設(shè)備中已刪除的數(shù)據(jù)。而現(xiàn)有方法,可以恢復(fù)誤操作或故意刪除的數(shù)據(jù)信息,但各有其優(yōu)缺點(diǎn)。例如,芯片拆除恢復(fù)法,恢復(fù)率最高,但要求技術(shù)高、實(shí)現(xiàn)難度大;暴力估算法,可很好地恢復(fù)已刪除的數(shù)據(jù),但只可對整個(gè)存儲(chǔ)單元整體解析后恢復(fù)提取,當(dāng)刪除數(shù)據(jù)之前所在頁面部分或者完全被新寫入的數(shù)據(jù)覆蓋時(shí),無法恢復(fù)已刪除的數(shù)據(jù)。技術(shù)實(shí)現(xiàn)要素:本發(fā)明實(shí)施例提供了一種數(shù)據(jù)恢復(fù)的方法、裝置及Android設(shè)備,旨在解決現(xiàn)有技術(shù)提供的數(shù)據(jù)恢復(fù)的方法,實(shí)現(xiàn)難度大,或者當(dāng)刪除數(shù)據(jù)之前所在頁面部分或者完全被新寫入的數(shù)據(jù)覆蓋時(shí),無法恢復(fù)已刪除的數(shù)據(jù)的問題。一方面,提供一種數(shù)據(jù)恢復(fù)的方法,所述方法包括:判斷是否滿足預(yù)設(shè)的檢查點(diǎn)條件,所述的檢查點(diǎn)條件在預(yù)寫日志W(wǎng)AL文件滿足清空條件之前;如果預(yù)設(shè)的檢查點(diǎn)條件成立,則判斷第一數(shù)據(jù)庫是否有刪除記錄,如果是, 則根據(jù)所述WAL文件恢復(fù)已刪除的記錄至第二數(shù)據(jù)庫的表中;在確定需要數(shù)據(jù)恢復(fù)時(shí)候,根據(jù)所述的第二數(shù)據(jù)庫恢復(fù)數(shù)據(jù)至第一數(shù)據(jù)庫中。進(jìn)一步地,在所述的步驟在確定需要數(shù)據(jù)恢復(fù)時(shí)候,根據(jù)所述的第二數(shù)據(jù)庫恢復(fù)數(shù)據(jù)至第一數(shù)據(jù)庫的表中之前還包括:如果預(yù)設(shè)的檢查點(diǎn)條件成立,則判斷第一數(shù)據(jù)庫是否有修改記錄,如果是,則根據(jù)所述WAL文件恢復(fù)已修改的記錄至第二數(shù)據(jù)庫的表中。進(jìn)一步地,所述判斷第一數(shù)據(jù)庫是否有刪除記錄,包括:獲取第一數(shù)據(jù)庫變化前的B+tree葉子頁鍵值對列表大小;監(jiān)聽第一數(shù)據(jù)庫中的數(shù)據(jù)是否發(fā)生變化;當(dāng)監(jiān)聽到第一數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生變化時(shí),獲取第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大小;將第一數(shù)據(jù)庫變化前后獲取到的B+tree葉子頁鍵值對列表大小進(jìn)行比較,如果第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大小小于第一數(shù)據(jù)庫變化前的B+tree葉子頁鍵值對列表大小,則判定第一數(shù)據(jù)庫有刪除記錄。進(jìn)一步地,所述監(jiān)聽第一數(shù)據(jù)庫中的數(shù)據(jù)是否發(fā)生變化,包括:通過函數(shù)ContentResolver.registerContentObserver()注冊需要監(jiān)聽的第一數(shù)據(jù)庫;當(dāng)監(jiān)聽到回調(diào)函數(shù)onChange()被執(zhí)行時(shí),判定所監(jiān)聽的第一數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生變化。進(jìn)一步地,所述獲取第一數(shù)據(jù)庫變化前后的B+tree葉子頁鍵值對列表大小,包括:獲取第一數(shù)據(jù)庫文件,所述第一數(shù)據(jù)庫文件是所述第一數(shù)據(jù)庫的表對應(yīng)的數(shù)據(jù)庫文件;獲取所述第一數(shù)據(jù)庫文件中的系統(tǒng)表;根據(jù)第一數(shù)據(jù)庫表的表名在所述系統(tǒng)表中查詢,得到第一數(shù)據(jù)庫表的根頁 編號;根據(jù)所述根頁編號定位到相應(yīng)的根頁后,分析Btree中所有的頁節(jié)點(diǎn),根據(jù)頁頭中的頁類型標(biāo)志查找到第一數(shù)據(jù)庫表的B+tree葉子頁;獲取所述B+tree葉子頁的鍵值對列表大小。進(jìn)一步地,所述獲取所述B+tree葉子頁的鍵值對列表大小,包括:創(chuàng)建一個(gè)指向所述B+tree葉子頁的游標(biāo);獲取所述游標(biāo)從第一條記錄移動(dòng)到最后一條記錄所迭代的次數(shù),將所述迭代的次數(shù)作為所述B+tree葉子頁的鍵值對列表大小。進(jìn)一步地,所述系統(tǒng)表中包括第一數(shù)據(jù)庫表的表名和建立所述第一數(shù)據(jù)庫表的語句,所述根據(jù)所述WAL文件恢復(fù)已刪除的記錄至第二數(shù)據(jù)庫的表中,包括:根據(jù)所述第一數(shù)據(jù)庫表的表名、所述建立所述第一數(shù)據(jù)庫表的語句以及所述WAL日志文件,構(gòu)建第二數(shù)據(jù)庫文件,所述第二數(shù)據(jù)庫文件是所述第二數(shù)據(jù)庫的表對應(yīng)的數(shù)據(jù)庫文件;對比第一數(shù)據(jù)庫文件和所述第二數(shù)據(jù)庫文件,根據(jù)所述第二數(shù)據(jù)庫文件找到第一數(shù)據(jù)庫文件中已刪除的記錄,并將所述第二數(shù)據(jù)庫的表中在第一數(shù)據(jù)庫文件中存儲(chǔ)在記錄刪除。另一方面,提供一種數(shù)據(jù)恢復(fù)的裝置,所述裝置包括:第一判斷單元,用于判斷是否滿足預(yù)設(shè)的檢查點(diǎn)條件,所述的檢查點(diǎn)條件在預(yù)寫日志W(wǎng)AL文件滿足清空條件之前;第二判斷單元,用于如果預(yù)設(shè)的檢查點(diǎn)條件成立,則判斷第一數(shù)據(jù)庫是否有刪除記錄;第一恢復(fù)單元,用于如果第一數(shù)據(jù)庫有刪除記錄,則根據(jù)所述WAL文件恢復(fù)已刪除的記錄至第二數(shù)據(jù)庫的表中;第二恢復(fù)單元,用于在確定需要數(shù)據(jù)恢復(fù)時(shí)候,根據(jù)所述的第二數(shù)據(jù)庫恢復(fù)數(shù)據(jù)至第一數(shù)據(jù)庫中。進(jìn)一步地,所述裝置,還包括:第三恢復(fù)單元,用于如果預(yù)設(shè)的檢查點(diǎn)條件成立,則判斷第一數(shù)據(jù)庫是否有修改記錄,如果是,則根據(jù)所述WAL文件恢復(fù)已修改的記錄至第二數(shù)據(jù)庫的表中。進(jìn)一步地,所述第二判斷單元,包括:第一獲取模塊,用于獲取第一數(shù)據(jù)庫變化前的B+tree葉子頁鍵值對列表大小;監(jiān)聽模塊,用于監(jiān)聽第一數(shù)據(jù)庫中的數(shù)據(jù)是否發(fā)生變化;第二獲取模塊,用于當(dāng)監(jiān)聽到第一數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生變化時(shí),獲取第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大小;判斷模塊,用于將第一數(shù)據(jù)庫變化前后獲取到的B+tree葉子頁鍵值對列表大小進(jìn)行比較,如果第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大小小于第一數(shù)據(jù)庫變化前的B+tree葉子頁鍵值對列表大小,則判定第一數(shù)據(jù)庫有刪除記錄。進(jìn)一步地,所述監(jiān)聽模塊,包括:注冊子模塊,用于通過函數(shù)ContentResolver.registerContentObserver()注冊需要監(jiān)聽的第一數(shù)據(jù)庫;判斷子模塊,用于當(dāng)監(jiān)聽到回調(diào)函數(shù)onChange()被執(zhí)行時(shí),判定所監(jiān)聽的第一數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生變化。進(jìn)一步地,所述獲取模塊,包括:第一獲取子模塊,用于獲取第一數(shù)據(jù)庫文件,所述第一數(shù)據(jù)庫文件是所述第一數(shù)據(jù)庫的表對應(yīng)的數(shù)據(jù)庫文件;第二獲取子模塊,用于獲取所述第一數(shù)據(jù)庫文件中的系統(tǒng)表;第三獲取子模塊,用于根據(jù)第一數(shù)據(jù)庫表的表名在所述系統(tǒng)表中查詢,得到第一數(shù)據(jù)庫表的根頁編號;第四獲取子模塊,用于根據(jù)所述根頁編號定位到相應(yīng)的根頁后,分析Btree 中所有的頁節(jié)點(diǎn),根據(jù)頁頭中的頁類型標(biāo)志查找到第一數(shù)據(jù)庫表的B+tree葉子頁;第五獲取子模塊,用于獲取所述B+tree葉子頁的鍵值對列表大小。進(jìn)一步地,所述第五獲取子模塊,包括:創(chuàng)建微模塊,用于創(chuàng)建一個(gè)指向所述B+tree葉子頁的游標(biāo);獲取微模塊,用于獲取所述游標(biāo)從第一條記錄移動(dòng)到最后一條記錄所迭代的次數(shù),將所述迭代的次數(shù)作為所述B+tree葉子頁的鍵值對列表大小。進(jìn)一步地,所述系統(tǒng)表中包括第一數(shù)據(jù)庫表的表名和建立所述第一數(shù)據(jù)庫表的語句,所述第一恢復(fù)單元,包括:構(gòu)建模塊,用于根據(jù)所述第一數(shù)據(jù)庫表的表名、所述建立所述第一數(shù)據(jù)庫表的語句以及所述WAL日志文件,構(gòu)建第二數(shù)據(jù)庫文件,所述第二數(shù)據(jù)庫文件是所述第二數(shù)據(jù)庫的表對應(yīng)的數(shù)據(jù)庫文件;恢復(fù)模塊,用于對比第一數(shù)據(jù)庫文件和所述第二數(shù)據(jù)庫文件,根據(jù)所述第二數(shù)據(jù)庫文件找到第一數(shù)據(jù)庫文件中已刪除的記錄,并將所述第二數(shù)據(jù)庫的表中在第一數(shù)據(jù)庫文件中存儲(chǔ)在記錄刪除。再一方面,提供一種Android設(shè)備,所述Android設(shè)備包括如上所述的數(shù)據(jù)恢復(fù)的裝置。在本發(fā)明實(shí)施例,在預(yù)寫日志W(wǎng)AL文件滿足預(yù)設(shè)的檢查點(diǎn)條件之前,也就是在WAL文件清空之前,發(fā)現(xiàn)第一數(shù)據(jù)庫有刪除記錄,則先根據(jù)WAL文件,將WAL文件中關(guān)于刪除記錄的數(shù)據(jù)及時(shí)恢復(fù)至第二數(shù)據(jù)庫的表中,在確定需要數(shù)據(jù)恢復(fù)時(shí)候,再根據(jù)所述的第二數(shù)據(jù)庫恢復(fù)數(shù)據(jù)至第一數(shù)據(jù)庫中??梢韵扔赪AL日志文件清空前將WAL文件中關(guān)于刪除記錄的數(shù)據(jù)恢復(fù),從而避免因WAL文件已清空且有數(shù)據(jù)刪除的情況下,無法恢復(fù)該刪除數(shù)據(jù)的問題。并且,相比現(xiàn)有的數(shù)據(jù)恢復(fù)的方法,實(shí)現(xiàn)簡單,且不會(huì)出現(xiàn)當(dāng)刪除數(shù)據(jù)之前所在頁面部分或者完全被新寫入的數(shù)據(jù)覆蓋時(shí),無法恢復(fù)已刪除的數(shù)據(jù)的問題。附圖說明圖1是本發(fā)明實(shí)施例一提供的數(shù)據(jù)恢復(fù)的方法的實(shí)現(xiàn)流程圖;圖2是本發(fā)明實(shí)施例一中提供的判斷第一數(shù)據(jù)庫是否有刪除記錄的實(shí)現(xiàn)流程圖;圖3是本發(fā)明實(shí)施例一中提供的獲取第一數(shù)據(jù)庫更新前后B+tree葉子葉鍵值對列表大小的實(shí)現(xiàn)流程圖;圖4是本發(fā)明實(shí)施例二提供的數(shù)據(jù)恢復(fù)的裝置的結(jié)構(gòu)框圖。具體實(shí)施方式為了使本發(fā)明的目的、技術(shù)方案及優(yōu)點(diǎn)更加清楚明白,以下結(jié)合附圖及實(shí)施例,對本發(fā)明進(jìn)行進(jìn)一步詳細(xì)說明。應(yīng)當(dāng)理解,此處所描述的具體實(shí)施例僅僅用以解釋本發(fā)明,并不用于限定本發(fā)明。在本發(fā)明實(shí)施例中,判斷預(yù)寫日志W(wǎng)AL文件是否滿足預(yù)設(shè)的檢查點(diǎn)條件;如果否,則判斷第一數(shù)據(jù)庫是否有刪除記錄;如果是,則先根據(jù)所述WAL文件恢復(fù)已刪除的記錄至第二數(shù)據(jù)庫的表中,在確定需要數(shù)據(jù)恢復(fù)時(shí)候,再根據(jù)所述的第二數(shù)據(jù)庫恢復(fù)數(shù)據(jù)至第一數(shù)據(jù)庫中。以下結(jié)合具體實(shí)施例對本發(fā)明的實(shí)現(xiàn)進(jìn)行詳細(xì)描述:實(shí)施例一圖1示出了本發(fā)明實(shí)施例一提供的數(shù)據(jù)恢復(fù)的方法的實(shí)現(xiàn)流程,詳述如下:在步驟S101中,判斷是否滿足預(yù)設(shè)的檢查點(diǎn)條件,所述的檢查點(diǎn)條件在預(yù)寫日志W(wǎng)AL文件滿足清空條件之前,如果預(yù)設(shè)的檢查點(diǎn)條件成立,則執(zhí)行步驟S102,否則,執(zhí)行步驟S105。在本發(fā)明實(shí)施例中,同步預(yù)寫日志(WriteAheadLog,WAL)文件和數(shù)據(jù)庫文件的行為被稱為檢查點(diǎn)checkpoint,它由SQLite自動(dòng)執(zhí)行,默認(rèn)是在WAL文件積累到1000頁修改的時(shí)候;當(dāng)然,在適當(dāng)?shù)臅r(shí)候,也可以手動(dòng)執(zhí)行 checkpoint,SQLite提供了相關(guān)的接口。若預(yù)設(shè)的檢查點(diǎn)條件是WAL文件積累到1000頁,則檢測到WAL文件積累到1000頁時(shí),系統(tǒng)會(huì)將WAL文件中的內(nèi)容修改到數(shù)據(jù)庫文件中。其中,SQLite在3.7.0之前的版本中采用回滾日志文件保證事務(wù)提交的原子性,而SQLite3.7.0版本之后,SQLite提供了WAL,WAL是用于實(shí)現(xiàn)原子事務(wù)的一種機(jī)制。目前,Android平臺集成的SQLite版本都是基于3.7.0以上的,可在shell終端通過sqlite3命令查詢,如下:root@android:/#sqlite3SQLiteversion3.7.112012-03-2011:35:50Enter".help"forinstructionsEnterSQLstatementsterminatedwitha";"。WAL工作原理如下:傳統(tǒng)的操作模式中,SQLite使用回滾日志,從SQL狀態(tài)集中捕獲預(yù)修改數(shù)據(jù),然后修改數(shù)據(jù)庫文件。使用WAL時(shí),將反過來處理。取代寫入原始信息模式,預(yù)修改數(shù)據(jù)放入回滾日志,利用WAL放棄原始未關(guān)聯(lián)數(shù)據(jù)庫文件的數(shù)據(jù)。WAL用來記錄給定事務(wù)導(dǎo)致的數(shù)據(jù)變化。一個(gè)提交操作變?yōu)樘厥獾膶戇M(jìn)WAL的記錄,以表明前面的修改確實(shí)完成,并實(shí)現(xiàn)了原子性(Atomicity,ACID)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)原則。數(shù)據(jù)庫文件和日志文件之間的角色變化會(huì)立刻改變事務(wù)的動(dòng)態(tài)性能。不用在數(shù)據(jù)庫文件中競爭相同的數(shù)據(jù)庫頁,在WAL中,多事務(wù)可以同時(shí)記錄它們數(shù)據(jù)的改變,并能夠持續(xù)從數(shù)據(jù)庫中讀取未改變的數(shù)據(jù)。在引入WAL機(jī)制之前,SQLite使用rollbackjournal機(jī)制實(shí)現(xiàn)原子事務(wù)。rollbackjournal機(jī)制的原理是:在修改數(shù)據(jù)庫文件中的數(shù)據(jù)之前,先將修改所在分頁中的數(shù)據(jù)備份在另外一個(gè)地方,然后才將修改寫入到數(shù)據(jù)庫文件中;如果事務(wù)失敗,則將備份數(shù)據(jù)拷貝回來,撤銷修改;如果事務(wù)成功,則刪除備份數(shù)據(jù),提交修改。WAL機(jī)制的原理是:修改并不直接寫入到數(shù)據(jù)庫文件中,而是寫入到另外一個(gè)稱為WAL的文件中;如果事務(wù)失敗,WAL中的記錄會(huì)被忽略,撤銷修改;如果事務(wù)成功,它將在隨后的某個(gè)時(shí)間被寫回到數(shù)據(jù)庫文件中,提交修改。一個(gè)正在寫入且不斷增長的WAL文件的沒有結(jié)束的數(shù)據(jù)變化流,無法擴(kuò)展或無法承擔(dān)難以預(yù)測的文件系統(tǒng)故障。WAL使用檢查點(diǎn)函數(shù)將修改寫回?cái)?shù)據(jù)庫。這個(gè)處理自動(dòng)進(jìn)行,所以開發(fā)者無須關(guān)注自己如何管理檢查點(diǎn)和數(shù)據(jù)庫寫回處理。默認(rèn)情況下,當(dāng)WAL文件發(fā)現(xiàn)有1000頁被修改時(shí),將調(diào)用檢查點(diǎn)??尚薷臋z查點(diǎn)執(zhí)行標(biāo)準(zhǔn)的參數(shù)以適應(yīng)不同的操作場合。在讀的時(shí)候,SQLite將在WAL文件中搜索,找到最后一個(gè)寫入點(diǎn),記住它,并忽略在此之后的寫入點(diǎn)(這保證了讀寫和讀讀可以并行執(zhí)行);隨后,它確定所要讀的數(shù)據(jù)所在頁是否在WAL文件中,如果在,則讀WAL文件中的數(shù)據(jù),如果不在,則直接讀數(shù)據(jù)庫文件中的數(shù)據(jù)。在寫的時(shí)候,SQLite將數(shù)據(jù)寫入到WAL文件中即可,但是必須保證獨(dú)占寫入,因此不同進(jìn)程的寫與寫之間不能并行執(zhí)行。WAL在實(shí)現(xiàn)的過程中,使用了共享內(nèi)存技術(shù),因此,所有的讀寫進(jìn)程必須在同一個(gè)機(jī)器上,否則,無法保證數(shù)據(jù)一致性。WAL優(yōu)點(diǎn):1.讀和寫可以完全地并發(fā)執(zhí)行,不會(huì)互相阻塞(但是寫之間仍然不能并發(fā))。2.WAL在大多數(shù)情況下,擁有更好的性能(因?yàn)闊o需每次寫入時(shí)都要寫兩個(gè)文件)。3.磁盤I/O行為更容易被預(yù)測。WAL缺點(diǎn):1.訪問數(shù)據(jù)庫的所有程序必須在同一主機(jī)上,且支持共享內(nèi)存技術(shù)。2.每個(gè)數(shù)據(jù)庫現(xiàn)在對應(yīng)3個(gè)文件:<xxx>.db,<xxx>-wal,<xxx>-shm。3.當(dāng)寫入數(shù)據(jù)達(dá)到GB級的時(shí)候,數(shù)據(jù)庫性能將下降。4.3.7.0之前的SQLite無法識別啟用了WAL機(jī)制的數(shù)據(jù)庫文件。一般基于Android平臺的應(yīng)用開發(fā)基本都滿足上面缺點(diǎn)中條件,不會(huì)因?yàn)殚_啟WAL帶來額外的性能影響,反而會(huì)帶來如上所述的優(yōu)點(diǎn)。激活和配置WAL的方法:同其他編譯指示PRAGMA不同,啟用WAL是數(shù)據(jù)庫級持久性改變。這意味著,可以在程序里啟動(dòng)WAL,或從SQLite命令shell啟動(dòng),并且之后其他程序?qū)⒃赪AL模式下使用數(shù)據(jù)庫。設(shè)置PRAGMAjournal_mode=WAL是激活WAL的必要命令,該編譯指示的調(diào)用將以字符串形式返回?cái)?shù)據(jù)庫最終的日志模式。SQLiteversion3.7.112012-03-2011:35:50Enter".help"forinstructionsEnterSQLstatementsterminatedwitha";"sqlite>PRAGMAjournal_mode=WAL;walsqlite>如果WAL改變成功,將返回字符串wal,如上所示。如果由于某種原因失敗了,諸如底層主機(jī)不支持必要的共享內(nèi)存要求,日志模式將不會(huì)改變。例如,在默認(rèn)的數(shù)據(jù)庫中,這意味著命令將返回字符串delete,即回滾日志依然有效。在Android系統(tǒng)應(yīng)用開發(fā)過程中,使用數(shù)據(jù)庫之前都需要借助SQLiteDatabaseopenDatabase()函數(shù)來連接數(shù)據(jù)庫,而在openDatabase()函數(shù)中利用setJournalMode()函數(shù)設(shè)置日志模式。查看Android源碼SQLiteDatabase.java文件,發(fā)現(xiàn)系統(tǒng)默認(rèn)開啟的是回滾日志模式,即sqliteDatabase.setJournalMode(path,"TRUNCATE"),而上面的開啟WAL模式命令則需要手動(dòng)執(zhí)行,可以通過修改setJournalMode()函數(shù),設(shè)置系統(tǒng)開機(jī)默認(rèn)日志模式為WAL,即setJournalMode(path,"WAL")。其中,文件SQLiteDatabase.java路徑是:android_frameworks_base/core/java/android/database/sqlite/SQLiteDatabase.java。在步驟S102中,判斷第一數(shù)據(jù)庫是否有刪除記錄,如果是,則執(zhí)行步驟S103。在本發(fā)明實(shí)施例中,如圖2所示,通過以下步驟判斷第一數(shù)據(jù)庫是否有刪除記錄:步驟1、獲取第一數(shù)據(jù)庫變化前的B+tree葉子頁鍵值對列表大小。步驟2、監(jiān)聽第一數(shù)據(jù)庫中的數(shù)據(jù)是否發(fā)生變化。步驟3、當(dāng)監(jiān)聽到第一數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生變化時(shí),獲取第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大小。步驟4、將第一數(shù)據(jù)庫變化前后獲取到的B+tree葉子頁鍵值對列表大小進(jìn)行比較,如果第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大小小于第一數(shù)據(jù)庫變化前的B+tree葉子頁鍵值對列表大小,則判定第一數(shù)據(jù)庫有刪除記錄。具體的,通過下述步驟監(jiān)聽第一數(shù)據(jù)庫中的數(shù)據(jù)是否發(fā)生變化:步驟21、通過函數(shù)ContentResolver.registerContentObserver()注冊需要監(jiān)聽的第一數(shù)據(jù)庫。步驟22、當(dāng)監(jiān)聽到回調(diào)函數(shù)onChange()被執(zhí)行時(shí),判定所監(jiān)聽的第一數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生變化。具體的,通過安卓Android系統(tǒng)中的函數(shù)ContentResolver.registerContentObserver(Uriuri,booleannotifyForDescendents,ContentObserverobserver)注冊需要監(jiān)聽的第一數(shù)據(jù)庫,注冊成功后,可以監(jiān)聽到第一數(shù)據(jù)庫中的數(shù)據(jù)的變化。其中參數(shù)uri是Uri類型,是需要監(jiān)聽的第一數(shù)據(jù)庫的Uri。每一個(gè)數(shù)據(jù)庫文件中可以創(chuàng)建多個(gè)數(shù)據(jù)庫表,可以根據(jù)不同的數(shù)據(jù)庫表的表名組成不同的路徑部分從而構(gòu)成不同的Uri,Uri與所監(jiān)聽的第一數(shù)據(jù)庫的表一一對應(yīng),可以實(shí)現(xiàn)對多個(gè)第一數(shù)據(jù)庫的表進(jìn)行監(jiān)控。其中,可以通過系統(tǒng)表sqliet_mastar中table字段獲取到相應(yīng)的數(shù)據(jù)庫表的表名,參數(shù)notifyForDescendents需設(shè)置為true,即使能監(jiān)聽功能,參數(shù)observer則是需要的ContentObserver。當(dāng)?shù)谝粩?shù)據(jù)庫中的數(shù)據(jù)有變化時(shí),ContentObserver 的onChange(booleanselfChange,Uriuri)回調(diào)函數(shù)就會(huì)被執(zhí)行。當(dāng)監(jiān)聽到回調(diào)函數(shù)被執(zhí)行后,可以判斷得出第一數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生變化,這時(shí),可以獲取第一數(shù)據(jù)庫更新后的B+tree葉子頁鍵值對列表大小,因?yàn)楫?dāng)新數(shù)據(jù)插入第一數(shù)據(jù)庫時(shí),此時(shí)的B+tree葉子頁的鍵值對列表就會(huì)增大,所以通過比較第一數(shù)據(jù)庫更新前后的B+tree葉子頁的鍵值對列表大小即可判斷第一數(shù)據(jù)庫是否有刪除記錄,當(dāng)?shù)谝粩?shù)據(jù)庫更新后的B+tree葉子頁的鍵值對列表大小小于第一數(shù)據(jù)庫更新前的B+tree葉子頁的鍵值對列表大小時(shí),則認(rèn)為第一數(shù)據(jù)庫有刪除記錄。具體的,如圖3所示,可以通過以下步驟獲取到第一數(shù)據(jù)庫更新前后B+tree葉子葉鍵值對列表大小:步驟31、獲取第一數(shù)據(jù)庫文件,所述第一數(shù)據(jù)庫文件是所述第一數(shù)據(jù)庫的表對應(yīng)的數(shù)據(jù)庫文件。由于Android系統(tǒng)中數(shù)據(jù)庫文件以.databases、.tables、.db、和.sqlite作為后綴名,而數(shù)據(jù)庫文件存儲(chǔ)的目錄一般是/data/data/某應(yīng)用/database/中,所以通過掃描系統(tǒng)/data/data目錄中所有后綴為以上的文件,可以獲取到系統(tǒng)中的數(shù)據(jù)庫文件。步驟32、獲取系統(tǒng)表sqlite_master。在SQLite數(shù)據(jù)庫文件中,一旦建表,就會(huì)在系統(tǒng)目錄(SystemCatalogue)下自動(dòng)生成系統(tǒng)表sqlite_master。SQLite數(shù)據(jù)庫中的系統(tǒng)sqlite_master表中的記錄具有固定格式,每條記錄包含五個(gè)字段,包括type、name、tbl_name、rootpage和sql字段,如下所示:CREATETABLEsqlite_master(typeTEXT,nameTEXT,tbl_nameTEXT,rootpageINTEGER,sqlTEXT);其中,字段type取值是table(表)或者index(索引),字段name和tbl_name是對應(yīng)的表名或者索引名,字段rootpage是根頁編號,當(dāng)字段type是表時(shí),字段sql就是相應(yīng)的建表語句,當(dāng)字段type是索引時(shí),字段sql則是相應(yīng)的建索引語句。由于每一數(shù)據(jù)庫文件都有系統(tǒng)表sqlite_master。如上所述,若type字段是‘table’,則name字段就是數(shù)據(jù)庫表的名字。所以,要獲得數(shù)據(jù)庫中所有數(shù)據(jù)庫表的列表,可使用SELECT語句查詢系統(tǒng)表sqlite_master。舉例如下:建立一數(shù)據(jù)庫表,并在/data/data/xxx/database/執(zhí)行以下命令:如上所示,可獲取到系統(tǒng)表sqlite_master的各個(gè)字段值,也即數(shù)據(jù)庫文件中的數(shù)據(jù)庫表的表名為ChannelRecordTable,表列字段為_id、channel_name、channel_id與channel_code。步驟33、根據(jù)第一數(shù)據(jù)庫表的表名在所述系統(tǒng)表中查詢,得到第一數(shù)據(jù)庫表的根頁編號。具體的,根據(jù)數(shù)據(jù)庫表的表名(name字段),可在系統(tǒng)表sqlite_master中查找字段rootpage的值,從而得到數(shù)據(jù)庫表的根頁編號。步驟34、根據(jù)所述根頁編號定位到相應(yīng)的根頁后,分析Btree中所有的頁節(jié)點(diǎn),根據(jù)頁頭中的頁類型標(biāo)志查找到第一數(shù)據(jù)庫表的B+tree葉子頁。具體的,根據(jù)所述根頁編號定位到相應(yīng)的根頁后,分析Btree中所有的頁 節(jié)點(diǎn),根據(jù)頁頭中的頁類型標(biāo)志查找到所有B+tree葉子頁,即可依據(jù)頁的大小偏移量定位到所要查找的表名的數(shù)據(jù)的所在頁。根據(jù)Sqlite中的規(guī)定,頁類型標(biāo)志如下:頁頭中第1個(gè)字節(jié)是區(qū)分B+tree和B-tree的內(nèi)部頁和葉子頁的標(biāo)志位。該字節(jié)的值是OxOD時(shí),表示該頁為B+tree的葉子頁,該字節(jié)的值為0x05時(shí),表示該頁為B+tree的內(nèi)部頁;該字節(jié)的值為OxOA時(shí),表示該頁為B-tree的葉子頁;該字節(jié)的值為0x02時(shí),表示該頁為B-tree的內(nèi)部頁。要查找到數(shù)據(jù)庫表的所有B+tree葉子頁,根據(jù)頁類型標(biāo)志,找到頁起始為“0D”的標(biāo)志,即可找到該數(shù)據(jù)庫表的所有數(shù)據(jù)存儲(chǔ)區(qū)域。步驟35、獲取所述B+tree葉子頁的鍵值對列表大小。具體的,創(chuàng)建一個(gè)指向所述B+tree葉子頁的游標(biāo)后,獲取所述游標(biāo)從第一條記錄移動(dòng)到最后一條記錄所迭代的次數(shù),將所述迭代的次數(shù)作為所述B+tree葉子頁的鍵值對列表大小。更詳細(xì)的,可以根據(jù)函數(shù)sqlite3BtreeCursor()創(chuàng)建一個(gè)指向當(dāng)前B+tree的游標(biāo),然后根據(jù)函數(shù)sqlite3BtreeFirst()和函數(shù)sqlite3BtreeLast()可獲取到所述游標(biāo)從第一條記錄移動(dòng)到最后一條記錄所迭代的次數(shù),將所述迭代的次數(shù)作為獲取到的當(dāng)前B+tree葉子頁的鍵值對列表大小。其中,函數(shù)sqlite3BtreeFirst()是移動(dòng)游標(biāo)至B+tree第一條記錄,函數(shù)sqlite3BtreeLast()是移動(dòng)游標(biāo)至B+tree最后一條記錄。在步驟S103中,根據(jù)所述WAL文件恢復(fù)已刪除的記錄至第二數(shù)據(jù)庫的表中。在本發(fā)明實(shí)施例中,步驟S102中獲取到的系統(tǒng)表中包括第一數(shù)據(jù)庫表的表名和建立所述第一數(shù)據(jù)庫表的語句,可以根據(jù)所述第一數(shù)據(jù)庫表的表名、所述建立所述第一數(shù)據(jù)庫表的語句以及所述WAL日志文件,構(gòu)建第二數(shù)據(jù)庫文件,所述第二數(shù)據(jù)庫文件是所述第二數(shù)據(jù)庫的表對應(yīng)的數(shù)據(jù)庫文件;再對比第一數(shù)據(jù)庫文件和所述第二數(shù)據(jù)庫文件,根據(jù)所述第二數(shù)據(jù)庫文件找到第一數(shù)據(jù) 庫文件中已刪除的記錄,并將所述第二數(shù)據(jù)庫的表中在第一數(shù)據(jù)庫文件中存儲(chǔ)在記錄刪除。詳細(xì)的,根據(jù)系統(tǒng)表中包括第一數(shù)據(jù)庫表的表名和建立所述第一數(shù)據(jù)庫表的語句建立第二數(shù)據(jù)庫文件,比如執(zhí)行步驟S102中獲取的sql建表語句CREATETABLEChannelRecordTable(_idintegerprimarykeyautoincrement,channel_nametextnotnull,channel_idtextnotnull,channel_codetextnotnull)生成xxx.db數(shù)據(jù)庫表。如前所述,SQLite中WAL文件是用于實(shí)現(xiàn)原子事務(wù)的一種機(jī)制,即WAL文件中記錄了數(shù)據(jù)庫所有事務(wù)操作記錄,因此,可以根據(jù)WAL文件在第二數(shù)據(jù)庫表xxx.db中構(gòu)建歷史數(shù)據(jù),即原數(shù)據(jù)庫文件中的數(shù)據(jù),其中xxx.db是第二數(shù)據(jù)庫的表的表名,根據(jù)當(dāng)前監(jiān)聽到的有刪除操作的第一數(shù)據(jù)庫表來命名,且為了不影響上層應(yīng)用對第一數(shù)據(jù)庫表的相關(guān)操作,結(jié)合以上兩點(diǎn)來命名xxx.db。舉例說明,原數(shù)據(jù)庫表為ChannelRecordTable,則可將xxx.db命名為ChannelRecordTable_WAL_DeleteRecord.db,并將該數(shù)據(jù)庫文件保存在與第一數(shù)據(jù)庫表同一路徑下以備恢復(fù)刪除數(shù)據(jù)。當(dāng)有數(shù)據(jù)刪除時(shí),WAL日志文件中記錄了刪除數(shù)據(jù)的相關(guān)事務(wù)操作。如上所述,應(yīng)用層查詢數(shù)據(jù)庫的時(shí)候,調(diào)用的數(shù)據(jù)庫表名還是原來的數(shù)據(jù)庫表名,即ChannelRecordTable,顯示結(jié)果是刪除后的相關(guān)數(shù)據(jù),并不影響當(dāng)前用戶對該數(shù)據(jù)庫表的操作。如上所述,第二數(shù)據(jù)庫表xxx.db中包含了原數(shù)據(jù)庫文件中的數(shù)據(jù),通過對比第一數(shù)據(jù)庫文件和所述第二數(shù)據(jù)庫文件的記錄,根據(jù)所述第二數(shù)據(jù)庫文件找出第一數(shù)據(jù)庫文件中已刪除的記錄,并將第二數(shù)據(jù)庫表xxx.db中在第一數(shù)據(jù)庫文件中存在的數(shù)據(jù)刪除,則第二數(shù)據(jù)庫表xxx.db中僅僅包含已刪除的數(shù)據(jù),實(shí)現(xiàn)了將刪除的記錄恢復(fù)至第二數(shù)據(jù)庫表xxx.db中。需要說明的是:在第一次建立好與有刪除記錄的第一數(shù)據(jù)庫表對應(yīng)的第二數(shù)據(jù)庫表后,后面再不需要建立,只需將每次刪除的記錄增加至第二數(shù)據(jù)庫表中即可。優(yōu)選地,如果預(yù)設(shè)的檢查點(diǎn)條件成立,本發(fā)明實(shí)施例還可以判斷第一數(shù)據(jù)庫是否有修改記錄,如果是,則根據(jù)所述WAL文件恢復(fù)已修改的記錄至第二數(shù)據(jù)庫的表中,以恢復(fù)已修改的記錄。具體的,與判斷第一數(shù)據(jù)庫中是否有刪除記錄時(shí)相似,可以先通過ContentResolver.registerContentObserver監(jiān)聽第一數(shù)據(jù)庫是否有變化,當(dāng)監(jiān)聽到第一數(shù)據(jù)庫發(fā)生變化時(shí),函數(shù)onChange被回調(diào)。數(shù)據(jù)庫支持增刪查改操作,除了查詢記錄操作外,數(shù)據(jù)庫變化時(shí)候,如增加記錄、刪除記錄、修改記錄,函數(shù)onChange都會(huì)被回調(diào)。當(dāng)函數(shù)onChange被回調(diào)的時(shí)候,可以通過對比第一數(shù)據(jù)庫變化前后B+tree葉子頁鍵值對列表大小,如果第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大小大于變換前的B+tree葉子頁鍵值對列表大小,則判斷第一數(shù)據(jù)庫有增加記錄;如果第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大小小于變換前的B+tree葉子頁鍵值對列表大小,則判斷第一數(shù)據(jù)庫有刪除記錄;否則判斷第一數(shù)據(jù)庫有修改記錄。詳細(xì)的,首先分別提取每個(gè)數(shù)據(jù)庫文件中的記錄,然后存儲(chǔ)到CSV格式的文件中,再通過對比兩個(gè)CSV格式的文件中的記錄,最后區(qū)分出經(jīng)過插入、修改和刪除前的歷史記錄。優(yōu)選的,在當(dāng)前數(shù)據(jù)庫文件被完整恢復(fù)出來后,需要提取其中的數(shù)據(jù)庫記錄進(jìn)行取證分析??衫脭?shù)據(jù)庫shell命令.tables[pattern]查看并提取其中的數(shù)據(jù)庫表。再利用命令select提取數(shù)據(jù)庫中包含的所有數(shù)據(jù)庫表的記錄。由于SQLite數(shù)據(jù)庫文件屬于結(jié)構(gòu)化的文件,一旦文件不完整或部分拼裝錯(cuò)誤,則不能正確解碼。本發(fā)明實(shí)施例中采用SQL命令提取數(shù)據(jù)庫文件中的記錄則恰好可以驗(yàn)證其完整性以及數(shù)據(jù)庫文件恢復(fù)算法的有效可用性。進(jìn)一步地,在已刪除的記錄成功恢復(fù)后,還可以繼續(xù)監(jiān)測預(yù)寫日志W(wǎng)AL文件是否滿足預(yù)設(shè)的檢查點(diǎn)條件,如果,預(yù)寫日志W(wǎng)AL文件滿足預(yù)設(shè)的檢查點(diǎn)條件,則將WAL文件中的內(nèi)容修改到數(shù)據(jù)庫文件中去,同時(shí)將WAL文件清空。在步驟S104中,在確定需要數(shù)據(jù)恢復(fù)時(shí)候,根據(jù)所述的第二數(shù)據(jù)庫恢復(fù)數(shù)據(jù)至第一數(shù)據(jù)庫中。在本發(fā)明實(shí)施例中,將第一數(shù)據(jù)庫中已刪除的記錄恢復(fù)至第二數(shù)據(jù)庫的表中之后,可以生成一個(gè)提示框,提示用戶是否將已刪除的記錄恢復(fù)至第一數(shù)據(jù)庫中,如果用戶選擇是,則可以將第二數(shù)據(jù)庫恢復(fù)數(shù)據(jù)至第一數(shù)據(jù)庫中。在步驟S105中,將所述WAL文件中的內(nèi)容修改到數(shù)據(jù)庫文件中。在本發(fā)明實(shí)施例中,若預(yù)設(shè)的檢查點(diǎn)條件是WAL文件積累到1000頁,則檢測到WAL文件積累到1000頁時(shí),系統(tǒng)會(huì)將WAL文件中的內(nèi)容修改到數(shù)據(jù)庫文件中。本實(shí)施例,在預(yù)寫日志W(wǎng)AL文件滿足預(yù)設(shè)的檢查點(diǎn)條件之前,也就是在WAL文件清空之前,發(fā)現(xiàn)第一數(shù)據(jù)庫有刪除記錄,則先根據(jù)WAL文件,將WAL文件中關(guān)于刪除記錄的數(shù)據(jù)及時(shí)恢復(fù)至第二數(shù)據(jù)庫的表中,再在確定需要數(shù)據(jù)恢復(fù)時(shí)候,根據(jù)所述的第二數(shù)據(jù)庫恢復(fù)數(shù)據(jù)至第一數(shù)據(jù)庫中。可以先于WAL日志文件清空前將WAL文件中關(guān)于刪除記錄的數(shù)據(jù)恢復(fù),從而避免因WAL文件已清空且有數(shù)據(jù)刪除的情況下,無法恢復(fù)該刪除數(shù)據(jù)的問題,可實(shí)現(xiàn)基于Android平臺的設(shè)備進(jìn)行數(shù)據(jù)恢復(fù)。并且,相比現(xiàn)有的數(shù)據(jù)恢復(fù)的方法,實(shí)現(xiàn)簡單,且不會(huì)出現(xiàn)當(dāng)刪除數(shù)據(jù)之前所在頁面部分或者完全被新寫入的數(shù)據(jù)覆蓋時(shí),無法恢復(fù)已刪除的數(shù)據(jù)的問題。本領(lǐng)域普通技術(shù)人員可以理解實(shí)現(xiàn)上述各實(shí)施例方法中的全部或部分步驟是可以通過程序來指令相關(guān)的硬件來完成,相應(yīng)的程序可以存儲(chǔ)于一計(jì)算機(jī)可讀取存儲(chǔ)介質(zhì)中,所述的存儲(chǔ)介質(zhì),如ROM/RAM、磁盤或光盤等。實(shí)施例二圖4示出了本發(fā)明實(shí)施例二提供的數(shù)據(jù)恢復(fù)的裝置的具體結(jié)構(gòu)框圖,為了便于說明,僅示出了與本發(fā)明實(shí)施例相關(guān)的部分。該數(shù)據(jù)恢復(fù)的裝置可以是內(nèi)置于Android設(shè)備的軟件單元、硬件單元或者軟硬件結(jié)合的單元,該數(shù)據(jù)恢復(fù)的裝置4包括:第一判斷單元41、第二判斷單元42、第一恢復(fù)單元43和第二 恢復(fù)單元44。其中,第一判斷單元41,用于判斷是否滿足預(yù)設(shè)的檢查點(diǎn)條件,所述的檢查點(diǎn)條件在預(yù)寫日志W(wǎng)AL文件滿足清空條件之前;第二判斷單元42,用于如果預(yù)設(shè)的檢查點(diǎn)條件成立,則判斷第一數(shù)據(jù)庫是否有刪除記錄;第一恢復(fù)單元43,用于如果第一數(shù)據(jù)庫有刪除記錄,則根據(jù)所述WAL文件恢復(fù)已刪除的記錄至第二數(shù)據(jù)庫的表中;第二恢復(fù)單元44,用于在確定需要數(shù)據(jù)恢復(fù)時(shí)候,根據(jù)所述的第二數(shù)據(jù)庫恢復(fù)數(shù)據(jù)至第一數(shù)據(jù)庫中。進(jìn)一步地,所述裝置4,還包括:第三恢復(fù)單元,用于如果預(yù)設(shè)的檢查點(diǎn)條件成立,則判斷第一數(shù)據(jù)庫是否有修改記錄,如果是,則根據(jù)所述WAL文件恢復(fù)已修改的記錄至第二數(shù)據(jù)庫的表中。具體的,所述第二判斷單元42,包括:第一獲取模塊,用于獲取第一數(shù)據(jù)庫變化前的B+tree葉子頁鍵值對列表大小;監(jiān)聽模塊,用于監(jiān)聽第一數(shù)據(jù)庫中的數(shù)據(jù)是否發(fā)生變化;第二獲取模塊,用于當(dāng)監(jiān)聽到第一數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生變化時(shí),獲取第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大??;判斷模塊,用于將第一數(shù)據(jù)庫變化前后獲取到的B+tree葉子頁鍵值對列表大小進(jìn)行比較,如果第一數(shù)據(jù)庫變化后的B+tree葉子頁鍵值對列表大小小于第一數(shù)據(jù)庫變化前的B+tree葉子頁鍵值對列表大小,則判定第一數(shù)據(jù)庫有刪除記錄。具體的,所述監(jiān)聽模塊,包括:注冊子模塊,用于通過函數(shù)ContentResolver.registerContentObserver()注冊需要監(jiān)聽的第一數(shù)據(jù)庫;判斷子模塊,用于當(dāng)監(jiān)聽到回調(diào)函數(shù)onChange()被執(zhí)行時(shí),判定所監(jiān)聽的第一數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生變化。具體的,所述獲取模塊,包括:第一獲取子模塊,用于獲取第一數(shù)據(jù)庫文件,所述第一數(shù)據(jù)庫文件是所述第一數(shù)據(jù)庫的表對應(yīng)的數(shù)據(jù)庫文件;第二獲取子模塊,用于獲取所述第一數(shù)據(jù)庫文件中的系統(tǒng)表;第三獲取子模塊,用于根據(jù)第一數(shù)據(jù)庫表的表名在所述系統(tǒng)表中查詢,得到第一數(shù)據(jù)庫表的根頁編號;第四獲取子模塊,用于根據(jù)所述根頁編號定位到相應(yīng)的根頁后,分析Btree中所有的頁節(jié)點(diǎn),根據(jù)頁頭中的頁類型標(biāo)志查找到第一數(shù)據(jù)庫表的B+tree葉子頁;第五獲取子模塊,用于獲取所述B+tree葉子頁的鍵值對列表大小。具體的,所述第五獲取子模塊,包括:創(chuàng)建微模塊,用于創(chuàng)建一個(gè)指向所述B+tree葉子頁的游標(biāo);獲取微模塊,用于獲取所述游標(biāo)從第一條記錄移動(dòng)到最后一條記錄所迭代的次數(shù),將所述迭代的次數(shù)作為所述B+tree葉子頁的鍵值對列表大小。具體的,所述系統(tǒng)表中包括第一數(shù)據(jù)庫表的表名和建立所述第一數(shù)據(jù)庫表的語句,所述第一恢復(fù)單元,包括:構(gòu)建模塊,用于根據(jù)所述第一數(shù)據(jù)庫表的表名、所述建立所述第一數(shù)據(jù)庫表的語句以及所述WAL日志文件,構(gòu)建第二數(shù)據(jù)庫文件,所述第二數(shù)據(jù)庫文件是所述第二數(shù)據(jù)庫的表對應(yīng)的數(shù)據(jù)庫文件;恢復(fù)模塊,用于對比第一數(shù)據(jù)庫文件和所述第二數(shù)據(jù)庫文件,根據(jù)所述第二數(shù)據(jù)庫文件找到第一數(shù)據(jù)庫文件中已刪除的記錄,并將所述第二數(shù)據(jù)庫的表中在第一數(shù)據(jù)庫文件中存儲(chǔ)在記錄刪除。本發(fā)明實(shí)施例提供的數(shù)據(jù)恢復(fù)的裝置可以應(yīng)用在前述對應(yīng)的方法實(shí)施例一中,詳情參見上述實(shí)施例一的描述,在此不再贅述。值得注意的是,上述裝置實(shí)施例中,所包括的各個(gè)單元只是按照功能邏輯進(jìn)行劃分的,但并不局限于上述的劃分,只要能夠?qū)崿F(xiàn)相應(yīng)的功能即可;另外,各功能單元的具體名稱也只是為了便于相互區(qū)分,并不用于限制本發(fā)明的保護(hù)范圍。以上所述僅為本發(fā)明的較佳實(shí)施例而已,并不用以限制本發(fā)明,凡在本發(fā)明的精神和原則之內(nèi)所作的任何修改、等同替換和改進(jìn)等,均應(yīng)包含在本發(fā)明的保護(hù)范圍之內(nèi)。當(dāng)前第1頁1 2 3 當(dāng)前第1頁1 2 3