可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an account type "mypackage.account" and a content authority "mypackage". I have a Service that provides an implementation of AbstractAccountAuthenticator, the addAccount method is implemented like this:
/** * 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; }
I provide an 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" />
and I define this Service in AndroidManifest.xml like this:
<!-- 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>
That's the setup, now when I want to have a screen with the list of my account type accounts on the device with an action to add a new account, I have the add account action that looks like this:
final Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT); intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[]{ "mypackage" }); startActivity(intent);
At this point I'm led to an account type picker that shows "mypackage.account" and "anotherpackage.account" as options. ("anotherpackage.account" is defined in another app I work on) This doesn't seem to be like the intended behavior. I've checked about 20 times that the authorities defined by both apps are different - and they are. Can someone show me what I'm missing?
回答1:
Android decoupling came to bite me again. It appears that both apps needed to also have a sync_adapter.xml like:
<!-- 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"/>
and connect that to the sync service in the 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>
For completeness, my Service is implemented as follows:
/** * 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; }
and the 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 } }
Even though I'm not actually syncing any data (the apps are not even linked to any server right now), the Android framework appears to be using the settings of the SyncAdapter to figure out which account authenticator respond to the add account request.
回答2:
I know the question is old and closed nevertheless...
Documentation of Settings.ACTION_ADD_ACCOUNT:
The account types available to add may be restricted by adding an EXTRA_AUTHORITIES extra to the Intent with one or more syncable content provider's authorities. Only account types which can sync with that content provider will be offered to the user.
Settings.EXTRA_ACCOUNT_TYPES instead (I did not try it for two projects like you did but it works like a charm in a project I am currently developing and I do not need sync-adapters at all):
intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, new String[] { "mypackage.account" });
回答3:
Instead of calling the intent...
final Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT); intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[]{ "mypackage" }); startActivity(intent);
You can use the following to go straight into the Authentication process given the account type. This would not require you to have a 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());