标签:android email http协议 exchange
Email5.0 代码结构
IEmailService是Email的核心接口。定义了Email的基本功能。如发送邮件(sendmail),下载附件(loadAttachment),同步(sync)搜索邮件(searchMessages)等。EmailServiceStub实现了IEmailService接口。主要实现POP3和IMAP邮箱共同的一些方法。EmailServiceStub为抽象类,它有两个子类。这两个子类分别是POP3邮箱和IMAP邮箱对该接口的实现。这两个子类针对POP3和IMAP协议的不同重写了父类的某些方法。如POP3协议不支持单独的下载附件,在POP3的子类中重写了父类的loadAttachment。这两个子类分别为Pop3Service和ImapService的匿名内部类。Exchange协议的IEmailService实现位于EasService的匿名内部类中。
①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中的邮件,最主要的同步操作。
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对象只能执行一次邮件操作。
从4.4 平台开始,Email采用了Android的同步框架实现邮件的同步功能。以POP3邮箱为例,说明同步框架同步邮件的过程。
为了使框架层能够调用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。
在Email中执行同步操作有两种情况,一种是手动刷新执行同步,一种时周期性的执行同步。使用同步框架可以非常简单的实现这两种操作。手动同步时只需执行 ContentResolver.requestSync(),框架层就会回调onPerformSync函数执行同步操作。
周期性同步时调用ContentResolver.addPeriodicSync(),在参数中指定同步周期,就可以周期性的执行同步操作。在手动同步时可以指定
使用同步框架执行同步操作有很多好处,首先我们编写同步代码更简单,只需要提供接口让框架层调用。之前Email周期性同步是使用AlarmManager实现的,代码比同步框架要复杂很多。其次同步框架在执行同步是会检查网络链接情况,如果没有网络链接则不会执行同步操作。再次同步框架还提供了用户验证机制。
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操作。子类只需要重写一些虚函数就可以实现定义的操作。
②EasLoadAttachment 实现下载附件功能
③EasFullSyncOperation 实现整个邮箱的同步功能
④EasFolderSync 同步文件夹列表。
⑤EasOutboxSync 实现发送邮件功能
⑥EasSearch 实现搜索邮件功能
⑦EasSync 实现某个文件夹同步邮件功能(upload)
⑧EasSyncBase实现某个文件夹同步邮件功能(download)
EasSync 和EasSyncBase共同实现文件夹的同步
EasFullSyncOperation类实现的功能并不对应具体的某个Exchange命令,而是在performOperation调用其他的类实现整个邮箱的同步。
标签:android email http协议 exchange
原文地址:http://blog.csdn.net/fx1ts/article/details/46597251