如何构建Android Sync Provider:Part1
原英文:http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-1/
翻译能力有限,如有错误,敬请指出。ThankYou!
翻译人: CJT
个人博客地址:http://blog.csdn.net/nndtdx
Android2.0 SDK带来的一个好东西就是你可以写一个普通的同步供应商程序,并将其与系统的联系薄,日历等集成。唯一的问题是相关的文档十分的少。还有一个糟糕的问题在于,如果你在某一个地方出了错,Android系统就会崩溃重启。面临如此挑战,我依靠这稀少的文档,以及很少的帖子,还有Android地带的一些代码去建立了一个同步程序----Last.fm 。你想要知道怎么去建立你自己的同步程序么?读下去吧
账户验证
第一个令人疑惑的问题就是账户验证问题,你可以从这个地方了解到更多的信息。http://developer.android.com/reference/android/accounts/AbstractAccountAuthenticator.html
这里定义了该账户如何在“账号&同步”设置中出现的。一个账号的的验证需要3部分来实现:1. 一个从onBind方法返回AbstractAccountAuthenticator 子类的一个服务2. 一个Activityt提供用户输入他们的凭据(账号秘密信息),一个xml文件去描述账号信息展示给用户时( an xml file describing how your account should look when displayed to the user.)同时,你也需要在android.mainfest.xml中添加android.permission.AUTHENTICATE_ACCOUNTS权限
服务
验证服务程序期望从一个onBind方法中返回一个AbstractAccountAuthenticator 的子类。如果你坚持不这么做的话,带来的后果就是当你向系统添加一个账号时,android会将会崩溃并且重启。所幸实现AbstractAccountAuthenticator 并不是一件困难的事情,我们只需要实现其中的addAccount方法即可。该方法返回一个Intent,系统将会用他来为用户展示一个登陆框。该如下的实现将会运行我们的service“fm.last.android.sync.LOGIN”.用户等登录完毕后,,会有一个AccountAuthenticatorResponse 对象传出,我们可以用来将其回传给系统。
AccountAuthenticatorService.java
1
import
fm
.
last
.
android
.
LastFm;
2
import
android
.
accounts
.
AbstractAccountAuthenticator;
3
import
android
.
accounts
.
Account;
4
import
android
.
accounts
.
AccountAuthenticatorResponse;
5
import
android
.
accounts
.
AccountManager;
6
import
android
.
accounts
.
NetworkErrorException;
7
import
android
.
app
.
Service;
8
import
android
.
content
.
Context;
9
import
android
.
content
.
Intent;
10
import
android
.
os
.
Bundle;
11
import
android
.
os
.
IBinder;
12
import
android
.
util
.
Log;
13
14
/*
*
15
*
Authenticator
service
that
returns
a
subclass
of
AbstractAccountAuthenticator
in
onBind()
16
*/
17
public
class
AccountAuthenticatorService
extends
Service
{
18
private
static
final
String
TAG
=
"
AccountAuthenticatorService
"
;
19
private
static
AccountAuthenticatorImpl
sAccountAuthenticator
=
null
;
20
21
public
AccountAuthenticatorService()
{
22
super
();
23
}
24
25
public
IBinder
onBind(Intent
intent)
{
26
IBinder
ret
=
null
;
27
if
(intent
.
getAction()
.
equals(android
.
accounts
.
AccountManager
.
ACTION_AUTHENTICATOR_INTENT))
28
ret
=
getAuthenticator()
.
getIBinder();
29
return
ret;
30
}
31
32
private
AccountAuthenticatorImpl
getAuthenticator()
{
33
if
(sAccountAuthenticator
=
=
null
)
34
sAccountAuthenticator
=
new
AccountAuthenticatorImpl(
this
);
35
return
sAccountAuthenticator;
36
}
37
38
private
static
class
AccountAuthenticatorImpl
extends
AbstractAccountAuthenticator
{
39
private
Context
mContext;
40
41
public
AccountAuthenticatorImpl(Context
context)
{
42
super
(context);
43
mContext
=
context;
44
}
45
46
/*
47
*
The
user
has
requested
to
add
a
new
account
to
the
system.
We
return
an
intent
that
will
launch
our
login
screen
if
the
user
has
not
logged
in
yet,
48
*
otherwise
our
activity
will
just
pass
the
user's
credentials
on
to
the
account
manager.
49
*/
50
@Override
51
public
Bundle
addAccount(AccountAuthenticatorResponse
response,
String
accountType,
String
authTokenType,
String[]
requiredFeatures,
Bundle
options)
52
throws
NetworkErrorException
{
53
Bundle
reply
=
new
Bundle();
54
55
Intent
i
=
new
Intent(mContext,
LastFm
.
class
);
56
i
.
setAction(
"
fm.last.android.sync.LOGIN
"
);
57
i
.
putExtra(AccountManager
.
KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
response);
58
reply
.
putParcelable(AccountManager
.
KEY_INTENT,
i);
59
60
return
reply;
61
}
62
63
@Override
64
public
Bundle
confirmCredentials(AccountAuthenticatorResponse
response,
Account
account,
Bundle
options)
{
65
return
null
;
66
}
67
68
@Override
69
public
Bundle
editProperties(AccountAuthenticatorResponse
response,
String
accountType)
{
70
return
null
;
71
}
72
73
@Override
74
public
Bundle
getAuthToken(AccountAuthenticatorResponse
response,
Account
account,
String
authTokenType,
Bundle
options)
throws
NetworkErrorException
{
75
return
null
;
76
}
77
78
@Override
79
public
String
getAuthTokenLabel(String
authTokenType)
{
80
return
null
;
81
}
82
83
@Override
84
public
Bundle
hasFeatures(AccountAuthenticatorResponse
response,
Account
account,
String[]
features)
throws
NetworkErrorException
{
85
return
null
;
86
}
87
@Override
88
public
Bundle
updateCredentials(AccountAuthenticatorResponse
response,
Account
account,
String
authTokenType,
Bundle
options)
{
89
return
null
;
90
}
91
}
92
}
93
该验证服务需要在Android.Mainfest.xml中使用元数据标签定义一下。如下
Snippet from AndroidManifest.xml
1
<
service
android:name
=
"
AccountAuthenticatorService
"
2
android:exported
=
"
true
"
android:process
=
"
:auth
"
>
3
<
intent-filter
>
4
<
action
android:name
=
"
android.accounts.AccountAuthenticator
"
/
>
5
<
/intent-filter
>
6
<
meta-data
android:name
=
"
android.accounts.AccountAuthenticator
"
7
android:resource
=
"
@xml/authenticator
"
/
>
8
<
/service
>
9
Xml文件
账号的xml文件中定义了当应用程序与你的账号互动的时候,应用程序将会看到的东西(是不是向国内其他应用程序使用QQ一些应用一样,首先会有一个授权,询问用户当前的应用程序可以访问你的用户的哪些信息?------我的理解)其中包含了用户可读的名字,你所定义的系统账号类型,图标,对一个包含当用户修改账号时可以看到的PreferenceScreens 的xml文件。
authenticator.xml
1
<
account-authenticator
xmlns:android
=
"
http://schemas.android.com/apk/res/android
"
2
android:accountType
=
"
fm.last.android.account
"
3
android:icon
=
"
@drawable/icon
"
4
android:smallIcon
=
"
@drawable/icon
"
5
android:label
=
"
@string/app_name
"
6
android:accountPreferences
=
"
@xml/account_preferences
"
/
>
7
account_preferences.xml
1
<
PreferenceScreen
2
xmlns:android
=
"
http://schemas.android.com/apk/res/android
"
>
3
<
PreferenceCategory
4
android:title
=
"
General
Settings
"
/
>
5
6
<
PreferenceScreen
7
android:key
=
"
account_settings
"
8
android:title
=
"
Account
Settings
"
9
android:summary
=
"
Sync
frequency,
notifications,
etc.
"
>
10
<
intent
11
android:action
=
"
fm.last.android.activity.Preferences.ACCOUNT_SETUP
"
12
android:targetPackage
=
"
fm.last.android
"
13
android:targetClass
=
"
fm.last.android.activity.Preferences
"
/
>
14
<
/PreferenceScreen
>
15
<
/PreferenceScreen
>
16
集成(putting it all together)
现在我们可以准备开始测试了。Android 账号的设置部分并不能完好的捕捉异常。如果有地方出错了,设备会重启。最好的测试方式是运行模拟器后,运行DevTools,点击AcountsTester
你会看到一个新的账号类型将会同系统内置的“Corporate”类型的账号一样,被添加到列表中。尽管从下拉列表中选择你的账号,然后点击增加按钮,那么,将会呈现一个你所做的登录框。经过验证后 ,你的账号将会出现在按钮下边的列表中。在这一点,使用系统的“Account&sync”设置去移除和修改账户应该是安全的。
准备好“Data & synchronization”的章节了么?让我们开始第二个章节吧。
可供参考的实现源代码你可以从这里下载https://github.com/c99koder/lastfm-android/。(在GNUGeneralPublicLicense主题下边)另外一个单独的实例程序你可以从Apache License 2.0主题下得到https://github.com/c99koder/AndroidSyncProviderDemo。Google同样也有一个他们的示例的同步程序在Androiddeveloperportal http://developer.android.com/resources/samples/SampleSyncAdapter/index.html。该应用程序比我(原作者)的会稍微完整一些。