Email5.0 代碼結構,email5.0代碼結構
Email5.0 代碼結構
1核心介面IEmailService
IEmailService是Email的核心介面。定義了Email的準系統。如發送郵件(sendmail),下載附件(loadAttachment),同步(sync)搜尋郵件(searchMessages)等。EmailServiceStub實現了IEmailService介面。主要實現POP3和IMAP郵箱共同的一些方法。EmailServiceStub為抽象類別,它有兩個子類。這兩個子類分別是POP3郵箱和IMAP郵箱對該介面的實現。這兩個子類針對POP3和IMAP協議的不同重寫了父類的某些方法。如POP3協議不支援單獨的下載附件,在POP3的子類中重寫了父類的loadAttachment。這兩個子類分別為Pop3Service和ImapService的匿名內部類。Exchange協議的IEmailService實現位於EasService的匿名內部類中。
1.1主要介面功能介紹
①sendmail 發送郵件。
POP3/IMAP郵箱使用SMTP協議發送郵件,sendmail功能在EmailServiceStub中實現。
Exchange郵箱通過HTTP協議發送郵件,郵件內容為XML格式。通過POST方法上傳到伺服器。Exchange郵箱並沒有實現sendmail,而是由syncOutbox函數實現發送郵件的功能。
②loadAttachment下載附件。
POP3協議不支援單獨的下載附件,當下載附件時要把整個郵件全部下載下來。IMAP和Exchange協議可以實現單獨的下載附件功能。
③searchMessages 搜尋郵件
searchMessages不是搜尋本機資料庫中的郵件,而是搜尋郵件伺服器中的郵件內容。Email用戶端向伺服器發出搜尋命令,伺服器把包含關鍵字的郵件返回給用戶端。POP3協議沒有搜尋命令,因此POP3郵箱沒有搜尋功能。
④sync 同步
同步功能主要同步三項內容
1、同步伺服器的資料夾清單
2、同步outbox中的郵件,其實就是發送outbox中的郵件。
3、同步inbox中的郵件,最主要的同步操作。
1.2 C/S結構
Exchange位於獨立的模組中(不知道為什麼把Exchange獨立出來,一種可能原因是日曆和連絡人模組也要使用Exchange的功能)。Email和Exchange屬於不同的進程,Exchange只負責功能實現,沒有顯示介面,郵件內容在Email模組顯示,Email和Exchange通過Binder機制進行通訊。為了和Exchange的代碼保持一致POP3和IMAP郵箱代碼也採用了C/S結構(在4.4之前Email不是這種結構)。採用Binder通訊,代碼分為Proxy(client)和Stub(Service)兩部分。只不過POP3和IMAP的Service位於Email進程中,不是跨進程的通訊。Exchange的Service位於Exchange進程中,是跨進程的通訊。
IEmailService介面在代理端也有相應的實現,實作類別為EmailServiceProxy(代理類只是把請求轉交給Stub)。上層代碼只和EmailServiceProxy互動,EmailServiceProxy根據不同
的賬戶類型綁定(bind)不同的實作類別。這種結構最大的好處是上層代碼只需要針對IEmailService介面編程,不用關心三種協議的差別。
當需要執行一項郵件操作時,就建立一個EmailServiceProxy對象。然後調用相應的函數執行。例如下載附件時執行如下語句:
new EmailServiceProxy(context, class).loadAttachment(attachmentId, callback)
根據參數class綁定不同的服務,執行不同協議的下載代碼。POP郵箱綁定Pop3Service,IMAP郵箱綁定ImapService,Exchange郵箱綁定EasService。Pop3Service,ImapService,EasService會在onbind函數中返回IEmailService介面的實現。EmailServiceProxy會建立一個非同步任務,在非同步任務中請求服務端執行下載附件操作。附件下載結束後代理端斷開和伺服器的串連。因此一個EmailServiceProxy對象只能執行一次郵件操作。
2同步架構
從4.4 平台開始,Email採用了Android的同步架構實現郵件的同步功能。以POP3郵箱為例,說明同步架構同步郵件的過程。
2.1同步架構介紹
為了使架構層能夠調用Email的同步代碼,Email必須提供一個 Bound Service類。該類在onbind函數中返回給架構層一個實現了同步操作的binder引用。當需要同步時同步架構通過該引用遠程調用onPerformSync函數執行同步操作。該binder是串連架構層和我們應用程式的關鍵。那麼如何建立該Bind對象?
首先該對象必須是一個Bind對象,而且必須實現了onPerformSync函數。Google提供了AbstractThreadedSyncAdapter類,我們做的工作只需要繼承AbstractThreadedSyncAdapter重寫onPerformSync。
POP3郵箱中建立該binder的代碼如下:
private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
public SyncAdapterImpl(Context context) {
super(context, true /* autoInitialize */);
}
@Override
public void onPerformSync(android.accounts.Account account, Bundle extras,
String authority, ContentProviderClient provider, SyncResult syncResult) {
PopImapSyncAdapterService.performSync(getContext(), account, extras, provider,
syncResult);
}
}
一般在Bound Service的onCreate函數中建立該binder的引用。在onBinder函數中調用getSyncAdapterBinder返回binder。Bound Service的onCreate和onBind 函數如下:
@Override
public void onCreate() {
super.onCreate();
mSyncAdapter = new SyncAdapterImpl(getApplicationContext());
}
@Override
public IBinder onBind(Intent intent) {
return mSyncAdapter.getSyncAdapterBinder();
}
該bound Service除了需要返回一個特殊的binder之外,在聲明時還有一些特殊。必須在intent-filter中加入android.content.SyncAdapter action,這樣架構層可以發出intent綁定該服務。還需要在meta-data中加入中繼資料檔案。該檔案位於/res/xml/目錄中,檔案描述了我們要同步的賬戶類型,應用使用的 content provider的authority,同步控制等詳見官方文檔。
adt-bundle-windows-x86-20140702\sdk\docs\training\sync-adapters\index.html
Email中負責POP3郵箱同步的bound Service聲明如下:
<service
android:name="com.android.email.service.Pop3SyncAdapterService"
android:exported="true">
<intent-filter>
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter_pop3" />
</service>
中繼資料syncadapter_pop3的內容如下:
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="@string/authority_email_provider"
android:accountType="@string/account_manager_type_pop3"
android:supportsUploading="true"
android:allowParallelSyncs="true"
/>
其中allowParallelSyncs表示同一類型的賬戶可以建立多個,並發的執行同步操作。例如可以建立兩個Pop3類型的郵箱,這兩個郵箱可以同時執行同步操作。
android:supportsUploading設定是否必須notifyChange通知才能同步
defaults to true and if true an upload-only sync will be requested for all syncadapters associated with an authority whenever that authority's content provider does a notifyChange(android.net.Uri, android.database.ContentObserver, boolean) with syncToNetwork set to true.
如果supportsUploading設定為true,當執行ContentResolver. notifyChange時,會執行同步操作。POP3/IMAP郵箱supportsUploading=true。之前對該選項不瞭解,造成了很大的麻煩。當執行了郵件加星操作時,會立刻同步到伺服器。當時一直到不到同步代碼。後來發現是執行ContentResolver. notifyChange時執行的同步。Exchange郵箱supportsUploading=false
當更改了郵件狀態時必須手動請求架構執行同步。
在Email中用於POP3郵箱的同步服務為Pop3SyncAdapterService,IMAP郵箱為LegacyImapSyncAdapterService,Exchange郵箱為EmailSyncAdapterService。Exchange郵箱還可以同步連絡人和日曆,同步服務分別為ContactsSyncAdapterService和CalendarSyncAdapterService。
2.2使用同步架構執行同步
在Email中執行同步操作有兩種情況,一種是手動重新整理執行同步,一種時周期性的執行同步。使用同步架構可以非常簡單的實現這兩種操作。手動同步時只需執行 ContentResolver.requestSync(),架構層就會回調onPerformSync函數執行同步操作。
周期性同步時調用ContentResolver.addPeriodicSync(),在參數中指定同步周期,就可以周期性的執行同步操作。在手動同步時可以指定
使用同步架構執行同步操作有很多好處,首先我們編寫同步代碼更簡單,只需要提供介面讓架構層調用。之前Email周期性同步是使用AlarmManager實現的,代碼比同步架構要複雜很多。其次同步架構在執行同步是會檢查網路連結情況,如果沒有網路連結則不會執行同步操作。再次同步架構還提供了使用者驗證機制。
3協議層介面(Exchange 部分)
Exchange協議主要通過HTTP協議與伺服器互動。發送的命令和返回的結果都是XML格式。
執行Exchange命令其實就是向伺服器發送POST請求並解析響應結果。命令中包含很多參數,這些參數都放在xml檔案的對應標籤中。Exchange協議對每個命令的作用和包含的參數都有詳細的描述,具體內容可以參考微軟的文檔
https://msdn.microsoft.com/en-us/library/ee200913(v=exchg.80).aspx
執行Exchange命令通常就是發送POST請求,處理常見錯誤,解析返回結果。
Exchange在EasOperation類中的performOperation方法定義這一套流程performOperation是一個模板方法,調用了很多虛函數實現發送請求,解析結果的操作。EasOperation有很多子類,每一個類對應一個Exchange操作。子類只需要重寫一些虛函數就可以實現定義的操作。
①EasMoveItems 實現郵件位置移動
②EasLoadAttachment 實現下載附件功能
③EasFullSyncOperation 實現整個郵箱的同步功能
④EasFolderSync 同步資料夾列表。
⑤EasOutboxSync 實現發送郵件功能
⑥EasSearch 實現搜尋郵件功能
⑦EasSync 實現某個檔案夾同步郵件功能(upload)
⑧EasSyncBase實現某個檔案夾同步郵件功能(download)
EasSync 和EasSyncBase共同實現檔案夾的同步
EasFullSyncOperation類實現的功能並不對應具體的某個Exchange命令,而是在performOperation調用其他的類實現整個郵箱的同步。