【问题标题】:Custom Account Type with Android AccountManager使用 Android AccountManager 自定义帐户类型
【发布时间】:2012-11-07 18:27:22
【问题描述】:

我有一个帐户类型“mypackage.account”和一个内容授权“mypackage”。我有一个Service,它提供了AbstractAccountAuthenticator 的实现,addAccount 方法是这样实现的:

    /**
     * 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, otherwise our activity will just pass the user's credentials on to
     * the account manager.
     */
    @Override
    public Bundle addAccount(AccountAuthenticatorResponse response, String account_type, String auth_token_type,
                             String[] required_features, Bundle options) throws NetworkErrorException {
        final Intent intent = new Intent(_context, ConnectAccountActivity.class);
        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
        final Bundle reply = new Bundle();
        reply.putParcelable(AccountManager.KEY_INTENT, intent);

        return reply;
    }

我提供authenticator.xml

<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
                   android:accountType="mypackage.account"
                   android:icon="@drawable/ic_launcher"
                   android:smallIcon="@drawable/ic_launcher"
                   android:label="@string/app_name"
                   android:accountPreferences="@xml/account_preferences" />

我在AndroidManifest.xml 中这样定义Service

<!-- Account authentication service that provides the methods for authenticating KeepandShare accounts to the
     AccountManager framework -->
<service android:exported="true" android:name=".authenticator.AccountAuthenticatorService" android:process=":auth" tools:ignore="ExportedService">
    <intent-filter>
        <action android:name="android.accounts.AccountAuthenticator"/>
    </intent-filter>
    <meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator"/>
</service>

这就是设置,现在当我想在设备上显示一个包含我的帐户类型帐户列表的屏幕以及添加新帐户的操作时,我的添加帐户操作如下所示:

final Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[]{ "mypackage" });
startActivity(intent);

此时我被引导到一个帐户类型选择器,其中显示“mypackage.account”和“anotherpackage.account”作为选项。 (“anotherpackage.account”在我工作的另一个应用程序中定义)这似乎不像预期的行为。我已经检查了大约 20 次,这两个应用程序定义的权限是不同的——而且确实如此。有人可以告诉我我缺少什么吗?

【问题讨论】:

    标签: android android-intent android-service accountmanager


    【解决方案1】:

    Android 解耦又来咬我了。看来这两个应用程序还需要有一个sync_adapter.xml,例如:

    <!-- The attributes in this XML file provide configuration information for the SyncAdapter. -->
    <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
              android:contentAuthority="mypackage"
              android:accountType="mypackage.account"
              android:supportsUploading="true"
              android:userVisible="true"
              android:allowParallelSyncs="false"
              android:isAlwaysSyncable="true"/>
    

    并将其连接到AndroidManifest.xml 中的同步服务:

    <!-- Data sync service that provides the SyncAdapter to the SyncManager framework. The SyncAdapter is used to
         maintain that the set of data on the device is a subset of the data on the server -->
    <service android:exported="true" android:name=".data.sync.SyncService" tools:ignore="ExportedService">
        <intent-filter>
            <action android:name="android.content.SyncAdapter"/>
        </intent-filter>
        <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/sync_adapter"/>
    </service>
    

    为了完整起见,我的Service实现如下:

    /**
     * Service that provides sync functionality to the SyncManager through the {@link SyncAdapter}.
     */
    public class SyncService extends Service {
    
        @Override
        public void onCreate() {
            synchronized (_sync_adapter_lock) {
                if (_sync_adapter == null)
                    _sync_adapter = new SyncAdapter(getApplicationContext(), false);
            }
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return _sync_adapter.getSyncAdapterBinder();
        }
    
        private static final Object _sync_adapter_lock = new Object();
        private static SyncAdapter _sync_adapter = null;
    }
    

    还有SyncAdapter

    /**
     * Sync adapter for KeepandShare data.
     */
    public class SyncAdapter extends AbstractThreadedSyncAdapter {
    
        public SyncAdapter(Context context, boolean should_auto_initialize) {
            super(context, should_auto_initialize);
    
            //noinspection ConstantConditions,PointlessBooleanExpression
            if (!BuildConfig.DEBUG) {
                Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                    @Override
                    public void uncaughtException(Thread thread, Throwable throwable) {
                        Log.e("Uncaught sync exception, suppressing UI in release build.", throwable);
                    }
                });
            }
        }
    
        @Override
        public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
                                  SyncResult sync_result) {
            // TODO: implement sync
        }
    }
    

    即使我实际上并没有同步任何数据(应用程序现在甚至没有链接到任何服务器),Android 框架似乎正在使用 SyncAdapter 的设置来确定哪个帐户身份验证器响应添加帐户请求。

    【讨论】:

    • 这是对这个问题的一个非常全面和有用的答案。我在 SDK 中的“SampleSyncAdaptor”示例之后添加了帐户管理,虽然它做了一些好事,例如当“添加帐户”被触摸时跳转到我的应用程序,但添加的帐户不会显示在手机设置中。我添加了 Dandre 的代码和宾果游戏 - 它有效。不过要注意“Ln”——你不需要 roboguice 来完成这项工作——可能应该从代码中删除。
    • @TimT 感谢您的评论。我实际上从 Roboguice 中删除了 Ln,我个人使用 Dagger 代替。看来我在这里只使用了一次Ln,所以我将其编辑为使用Log
    • 看起来很奇怪,你需要添加一个同步适配器,即使你没有使用它。尝试按照这篇文章的说明创建您自己的身份验证器,看看问题是否仍然存在:udinic.wordpress.com/2013/04/24/…
    • @Udinic 您是否尝试过使用两个单独的应用程序来做这件事,两个应用程序都有自己的自定义帐户? Settings.ACTION_ADD_ACCOUNT 似乎使用权限来确定帐户范围。似乎sync_adapter.xml 是将帐户类型与权限相关联的唯一位置和方式。我认为这是我从我的经验中收集到的。
    • 为什么需要创建2种账户类型?您可以创建一种帐户类型并使用不同的身份验证令牌类型来提供不同的访问权限。您没有看到 Google 为日历、云端硬盘和 Gmail 创建 2 种帐户类型。 Google 为其提供的不同服务提供一种帐户类型和多种身份验证令牌类型。
    【解决方案2】:

    我知道这个问题已经过时而且已经结束了......

    Documentation 中的Settings.ACTION_ADD_ACCOUNT:

    可通过向具有一个或多个可同步内容提供商权限的 Intent 添加额外的 EXTRA_AUTHORITIES 来限制可添加的帐户类型。 只有可以与该内容提供商同步的帐户类型才会提供给用户。

    我认为这就是您必须实现一个空同步适配器的原因。在使用Settings.EXTRA_ACCOUNT_TYPES 时参考文档,这应该是不必要的(我没有像你那样在两个项目中尝试过,但它在我目前正在开发的项目中就像一个魅力,我不需要同步适配器全部):

    intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, new String[] { "mypackage.account" });
    

    【讨论】:

      【解决方案3】:

      而不是调用意图...

      final Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
      intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[]{ "mypackage" });
      startActivity(intent);
      

      在给定帐户类型的情况下,您可以使用以下内容直接进入身份验证过程。这将要求您拥有SyncAdapter

      AccountManager accountManager = AccountManager.get(this);
      accountManager.addAccount(AccountAuthenticator.ACCOUNT_TYPE, AccountAuthenticator.AUTHTOKEN_TYPE_FULL_ACCESS, null, null, this, new AccountManagerCallback<Bundle>() {
              @Override
              public void run(AccountManagerFuture<Bundle> future) {
      
              }
      }, new Handler());
      

      【讨论】:

        猜你喜欢
        • 2014-12-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-15
        • 2011-10-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多