创建存根身份验证器

同步适配器框架假设您的同步适配器会在与帐户关联的设备存储和需要登录访问权限的服务器存储之间传输数据。因此,框架期望您将名为身份验证器的组件作为同步适配器的一部分提供。此组件插入 Android 帐户和身份验证框架,并提供用于处理用户凭据(例如登录信息)的标准接口。

即使您的应用不使用帐户,您仍然需要提供身份验证器组件。如果您不使用帐户或服务器登录,则身份验证器处理的信息将被忽略,因此您可以提供包含存根方法实现的身份验证器组件。您还需要提供一个绑定Service,以便同步适配器框架可以调用身份验证器的方法。

本课程将向您展示如何定义满足同步适配器框架要求所需的存根身份验证器的所有部分。如果您需要提供处理用户帐户的真实身份验证器,请阅读AbstractAccountAuthenticator的参考文档。

添加存根身份验证器组件

要向您的应用添加存根身份验证器组件,请创建一个扩展AbstractAccountAuthenticator的类,然后通过返回null或抛出异常来模拟所需的方法。

以下代码片段显示了一个存根身份验证器类的示例

Kotlin

/*
 * Implement AbstractAccountAuthenticator and stub out all
 * of its methods
 */
class Authenticator(context: Context) // Simple constructor
    : AbstractAccountAuthenticator(context) {

    // Editing properties is not supported
    override fun editProperties(r: AccountAuthenticatorResponse, s: String): Bundle {
        throw UnsupportedOperationException()
    }

    // Don't add additional accounts
    @Throws(NetworkErrorException::class)
    override fun addAccount(
            r: AccountAuthenticatorResponse,
            s: String,
            s2: String,
            strings: Array<String>,
            bundle: Bundle
    ): Bundle?  = null

    // Ignore attempts to confirm credentials
    @Throws(NetworkErrorException::class)
    override fun confirmCredentials(
            r: AccountAuthenticatorResponse,
            account: Account,
            bundle: Bundle
    ): Bundle?  = null

    // Getting an authentication token is not supported
    @Throws(NetworkErrorException::class)
    override fun getAuthToken(
            r: AccountAuthenticatorResponse,
            account: Account,
            s: String,
            bundle: Bundle
    ): Bundle {
        throw UnsupportedOperationException()
    }

    // Getting a label for the auth token is not supported
    override fun getAuthTokenLabel(s: String): String {
        throw UnsupportedOperationException()
    }

    // Updating user credentials is not supported
    @Throws(NetworkErrorException::class)
    override fun updateCredentials(
            r: AccountAuthenticatorResponse,
            account: Account,
            s: String,
            bundle: Bundle
    ): Bundle {
        throw UnsupportedOperationException()
    }

    // Checking features for the account is not supported
    @Throws(NetworkErrorException::class)
    override fun hasFeatures(
            r: AccountAuthenticatorResponse,
            account: Account,
            strings: Array<String>
    ): Bundle {
        throw UnsupportedOperationException()
    }
}

Java

/*
 * Implement AbstractAccountAuthenticator and stub out all
 * of its methods
 */
public class Authenticator extends AbstractAccountAuthenticator {
    // Simple constructor
    public Authenticator(Context context) {
        super(context);
    }
    // Editing properties is not supported
    @Override
    public Bundle editProperties(
            AccountAuthenticatorResponse r, String s) {
        throw new UnsupportedOperationException();
    }
    // Don't add additional accounts
    @Override
    public Bundle addAccount(
            AccountAuthenticatorResponse r,
            String s,
            String s2,
            String[] strings,
            Bundle bundle) throws NetworkErrorException {
        return null;
    }
    // Ignore attempts to confirm credentials
    @Override
    public Bundle confirmCredentials(
            AccountAuthenticatorResponse r,
            Account account,
            Bundle bundle) throws NetworkErrorException {
        return null;
    }
    // Getting an authentication token is not supported
    @Override
    public Bundle getAuthToken(
            AccountAuthenticatorResponse r,
            Account account,
            String s,
            Bundle bundle) throws NetworkErrorException {
        throw new UnsupportedOperationException();
    }
    // Getting a label for the auth token is not supported
    @Override
    public String getAuthTokenLabel(String s) {
        throw new UnsupportedOperationException();
    }
    // Updating user credentials is not supported
    @Override
    public Bundle updateCredentials(
            AccountAuthenticatorResponse r,
            Account account,
            String s, Bundle bundle) throws NetworkErrorException {
        throw new UnsupportedOperationException();
    }
    // Checking features for the account is not supported
    @Override
    public Bundle hasFeatures(
        AccountAuthenticatorResponse r,
        Account account, String[] strings) throws NetworkErrorException {
        throw new UnsupportedOperationException();
    }
}

将身份验证器绑定到框架

为了让同步适配器框架访问您的身份验证器,您必须为其创建一个绑定服务。此服务提供一个 Android 绑定器对象,允许框架调用您的身份验证器并在身份验证器和框架之间传递数据。

以下代码片段显示了如何定义绑定Service

Kotlin

/**
* A bound Service that instantiates the authenticator
* when started.
*/
class AuthenticatorService : Service() {

    // Instance field that stores the authenticator object
    private lateinit var mAuthenticator: Authenticator

    override fun onCreate() {
        // Create a new authenticator object
        mAuthenticator = Authenticator(getApplicationContext())
    }

    /*
     * When the system binds to this Service to make the RPC call
     * return the authenticator's IBinder.
     */
    override fun onBind(intent: Intent?): IBinder = mAuthenticator.iBinder
}

Java

/**
 * A bound Service that instantiates the authenticator
 * when started.
 */
public class AuthenticatorService extends Service {
    ...
    // Instance field that stores the authenticator object
    private Authenticator mAuthenticator;
    @Override
    public void onCreate() {
        // Create a new authenticator object
        mAuthenticator = new Authenticator(getApplicationContext());
    }
    /*
     * When the system binds to this Service to make the RPC call
     * return the authenticator's IBinder.
     */
    @Override
    public IBinder onBind(Intent intent) {
        return mAuthenticator.getIBinder();
    }
}

添加身份验证器元数据文件

要将您的身份验证器组件插入同步适配器和帐户框架,您需要向这些框架提供描述该组件的元数据。此元数据声明您为同步适配器创建的帐户类型,并声明如果要使您的帐户类型对用户可见,则系统会显示的用户界面元素。在应用项目中/res/xml/目录中存储的 XML 文件中声明此元数据。您可以为该文件指定任何名称,但通常称为authenticator.xml

此 XML 文件包含单个元素<account-authenticator>,该元素具有以下属性:

android:accountType
同步适配器框架要求每个同步适配器都具有一个帐户类型,形式为域名。框架使用帐户类型作为同步适配器内部标识的一部分。对于需要登录的服务器,帐户类型以及用户帐户将作为登录凭据的一部分发送到服务器。

如果您的服务器不需要登录,您仍然必须提供帐户类型。对于值,请使用您控制的域名。虽然框架使用它来管理您的同步适配器,但该值不会发送到您的服务器。

android:icon
指向包含图标的Drawable资源的指针。如果您通过在res/xml/syncadapter.xml中指定属性android:userVisible="true"来使同步适配器可见,则必须提供此图标资源。它将显示在系统“设置”应用的“帐户”部分中。
android:smallIcon
指向包含图标小版本的Drawable资源的指针。根据屏幕尺寸,此资源可能会在系统“设置”应用的“帐户”部分中替代android:icon使用。
android:label
标识用户帐户类型的可本地化字符串。如果您通过在res/xml/syncadapter.xml中指定属性android:userVisible="true"来使同步适配器可见,则应提供此字符串。它将显示在系统“设置”应用的“帐户”部分中,位于您为身份验证器定义的图标旁边。

以下代码片段显示了前面创建的身份验证器的 XML 文件

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:accountType="example.com"
        android:icon="@drawable/ic_launcher"
        android:smallIcon="@drawable/ic_launcher"
        android:label="@string/app_name"/>

在清单中声明身份验证器

在之前的步骤中,您创建了一个绑定到身份验证器和同步适配器框架的Service。为了让系统识别此服务,请在您的应用清单中声明它,方法是在<application>元素中添加以下<service>元素作为子元素。

    <service
            android:name="com.example.android.syncadapter.AuthenticatorService">
        <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator"/>
        </intent-filter>
        <meta-data
            android:name="android.accounts.AccountAuthenticator"
            android:resource="@xml/authenticator" />
    </service>

<intent-filter>元素设置一个过滤器,该过滤器由系统发送的意图操作android.accounts.AccountAuthenticator触发,用于运行身份验证器。当过滤器被触发时,系统将启动AuthenticatorService,这是您提供的用于包装身份验证器的绑定Service

<meta-data>元素声明身份验证器的元数据。android:name属性将元数据链接到身份验证框架。android:resource元素指定您之前创建的身份验证器元数据文件的名称。

除了身份验证器之外,同步适配器还需要一个内容提供程序。如果您的应用尚未使用内容提供程序,请转到下一课学习如何创建存根内容提供程序;否则,请转到课程创建同步适配器