如何在android中以编程方式添加自定义帐户?
我正在尝试为我的应用程序创build一个帐户,在那里我可以使我的联系人对我的帐户,如Facebook,Viber,WhatsApp等我希望我的帐户在设置的帐户部分也可见。 有任何想法吗? 我GOOGLE了很多,但找不到正确的答案从哪里开始。 请帮忙。 我试图创build一个帐户如下。 这导致我的错误。
Account account = new Account("Title", "com.package.nom"); String password = "password"; AccountManager accountManager = (AccountManager) MainPanel.this.getSystemService( ACCOUNT_SERVICE); accountManager.addAccountExplicitly(account, password, null);
您需要设置多个组件才能以编程方式创build帐户。 你需要:
- 一个AccountAuthenticator
- 提供访问AccountAuthenticator的服务
- 一些权限
authentication者
身份validation器是一个对象,它将使帐户types和有权pipe理它的autority(即linux用户)之间的映射。
在xml中声明一个validation器:
- 创build一个文件
res/xml/authenticator.xml
具有以下内容:
<?xml version="1.0" encoding="utf-8"?> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="com.company.demo.account.DEMOACCOUNT" android:icon="@drawable/ic_launcher" android:smallIcon="@drawable/ic_launcher" android:label="@string/my_custom_account"/>
请注意accountType:创build账户时必须在代码中重用。 图标和标签将被“设置”应用程序用来显示该types的帐户。
实施AccountAuthenticator
您必须扩展AbstractAccountAuthenticator
才能做到这一点。 这将由第三方应用程序使用访问帐户数据。
下面的示例不允许任何访问第三方应用程序,所以每个方法的实现是微不足道的。
public class CustomAuthenticator extends AbstractAccountAuthenticator { public CustomAuthenticator(Context context) { super(context); } @Override public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse, String s, String s2, String[] strings, Bundle bundle) throws NetworkErrorException { return null; //To change body of implemented methods use File | Settings | File Templates. } @Override public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse, String s) { return null; //To change body of implemented methods use File | Settings | File Templates. } @Override public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, Bundle bundle) throws NetworkErrorException { return null; //To change body of implemented methods use File | Settings | File Templates. } @Override public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException { return null; //To change body of implemented methods use File | Settings | File Templates. } @Override public String getAuthTokenLabel(String s) { return null; //To change body of implemented methods use File | Settings | File Templates. } @Override public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException { return null; //To change body of implemented methods use File | Settings | File Templates. } @Override public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String[] strings) throws NetworkErrorException { return null; //To change body of implemented methods use File | Settings | File Templates. } }
该服务暴露帐户types
创build一个服务来操作该types的帐户:
public class AuthenticatorService extends Service { @Override public IBinder onBind(Intent intent) { CustomAuthenticator authenticator = new CustomAuthenticator(this); return authenticator.getIBinder(); } }
在清单中声明服务 :
<service android:name="com.company.demo.account.AuthenticatorService" android:exported="false"> <intent-filter> <action android:name="android.accounts.AccountAuthenticator"/> </intent-filter> <meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator"/> </service>
在这里,filter和引用声明validation者的xml资源的元数据是关键点。
权限
在您的清单中一定要声明以下权限
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/> <uses-permission android:name="android.permission.GET_ACCOUNTS"/> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
(并不是这篇文章中提供的示例代码都需要,但是您可能会有更多关于帐户pipe理的代码,最后所有这些代码都会有用)
在代码中创build一个帐户
现在,一切准备就绪,您可以使用以下代码创build一个帐户。 注意由addAccountExplicitly
返回的boolean
addAccountExplicitly
通知您成功或失败。
AccountManager accountManager = AccountManager.get(this); //this is Activity Account account = new Account("MyAccount","com.company.demo.account.DEMOACCOUNT"); boolean success = accountManager.addAccountExplicitly(account,"password",null); if(success){ Log.d(TAG,"Account created"); }else{ Log.d(TAG,"Account creation failed. Look at previous logs to investigate"); }
最后的提示
不要将您的应用程序安装在外部存储上
如果您的应用程序安装在外部存储上,则当sdcard被卸载时(由于该帐户的身份validation程序将无法再访问),Android很可能会删除您的帐户数据。 所以要避免这种损失(每次重启!!!),您必须安装声明身份validation器的应用程序在内部存储:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" ...
遇到麻烦
仔细阅读日志,AccountManger输出许多日志以帮助您debugging代码。
在这里,我正在做一个代码(对于德语commetns抱歉)不要忘记在清单文件中设置propper权限。
/** * ueberprueft, ob es den account fuer diese app schon gibt und legt ihn * gegebenenfalls an. * * @param none * @return void */ public void verifyAccount() { if (debug) Log.i(TAG, "verifyAccount() "); boolean bereitsAngelegt = false; String accountType; accountType = this.getPackageName(); AccountManager accountManager = AccountManager .get(getApplicationContext()); Account[] accounts = accountManager.getAccounts(); for (int i = 0; i < accounts.length; i++) { if (debug) Log.v(TAG, accounts[i].toString()); if ((accounts[i].type != null) && (accounts[i].type.contentEquals(accountType))) { bereitsAngelegt = true; if (debug) Log.v(TAG, "verifyAccount(): bereitsAngelegt " + accounts[i].type); } } if (!bereitsAngelegt) { if (debug) Log.v(TAG, "verifyAccount(): !bereitsAngelegt "); // This is the magic that addes the account to the Android Account // Manager AccountManager accMgr = AccountManager.get(this); String password = "some_password"; if (debug) Log.d(TAG, "verifyAccount(): ADD: accountName: " + Konst.accountName + " accountType: " + accountType + " password: " + password); final Account account = new Account(Konst.accountName, accountType); if (debug) Log.v(TAG, "verifyAccount(): nach final Account account "); try { accMgr.addAccountExplicitly(account, password, null); } catch (Exception e1) { if (debug) Log.v(TAG, "verifyAccount(): Exception e1 " + e1.toString()); this.finish(); } if (debug) Log.v(TAG, "verifyAccount(): nach accMgr.addAccountExplicitly() "); } else { if (debug) Log.v(TAG, "verifyAccount(): bereitsAngelegt "); } } // end of public void verifyAccount()
我希望这会有所帮助。
我已经为此写了一个库 ,从而免除了pipe理Android账户所需的杂务,例如定义一个绑定服务,authentication者xml等等。使用这个库有5个简单的步骤:
步骤1
将此添加到应用程序的build.gradle的依赖项中:
compile 'com.digigene.android:account-authenticator:1.3.0'
第2步
在strings.xml
中将您的身份validation帐户types定义为strings.xml
:
<string name="auth_account_type">DigiGene</string>
用您自己的帐户typesreplace“DigiGene”。 这是在这个屏幕截图中显示在Android帐户中的内容。
第3步
为注册用户devise注册布局(例如: 这个图像 ):
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.digigene.authenticatortest.MainActivity"> <EditText android:id="@+id/account_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:hint="User Name" /> <EditText android:id="@+id/password" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/account_name" android:gravity="center_horizontal" android:hint="Password" android:inputType="textPassword" /> <Button android:id="@+id/register" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/password" android:text="register" android:onClick="startAuthentication"/> </RelativeLayout>
并使用以下代码创build一个新类,比如说MyRegistrationActivity.java
:
import com.digigene.accountauthenticator.activity.RegistrationActivity; public class MyRegistrationActivity extends RegistrationActivity { private EditText accountNameEditText, passwordEditText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.registration_layout); accountNameEditText = (EditText) findViewById(R.id.account_name); passwordEditText = (EditText) findViewById(R.id.password); } public void startAuthentication(View view) { register(accountNameEditText.getText().toString(), passwordEditText.getText().toString(), null, null); } }
步骤4
做一个入口布局在这里 :
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.digigene.authenticatortest.MainActivity"> <EditText android:id="@+id/account_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:hint="User Name" /> <Button android:id="@+id/register" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/account_name" android:text="Sign in" android:onClick="signIn"/> <Button android:id="@+id/add" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/register" android:text="Add user" android:onClick="addUser"/> </RelativeLayout>
这个布局与以下类一起使用:
import com.digigene.accountauthenticator.AuthenticatorManager; public class MainActivity extends Activity { EditText accountNameEditText; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); accountNameEditText = (EditText) findViewById(R.id.account_name); } public void signIn(View view) { AuthenticatorManager authenticatorManager = new AuthenticatorManager(MainActivity.this, getString(R.string.auth_account_type), this, MyRegistrationActivity.class, MyInterfaceImplementation.class); String authTokenType = "REGULAR_USER"; AuthenticatorManager.authenticatorManager = authenticatorManager; authenticatorManager.getAccessToken(accountNameEditText.getText().toString(), authTokenType, null); } public void addUser(View view) { AuthenticatorManager authenticatorManager = new AuthenticatorManager(MainActivity.this, getString(R.string.auth_account_type), this, MyRegistrationActivity.class, MyInterfaceImplementation.class); String authTokenType = "REGULAR_USER"; AuthenticatorManager.authenticatorManager = authenticatorManager; authenticatorManager.addAccount(authTokenType, null, null); } }
第5步
这是连接到服务器进行注册和login以及之后所需的方法的最后一步。 在下面,与真实情况相反,服务器连接被模拟,仅仅是为了演示库的function。 你可以用你自己的真实replace下面的实现。
import com.digigene.accountauthenticator.AbstractInterfaceImplementation; import com.digigene.accountauthenticator.AuthenticatorManager; import com.digigene.accountauthenticator.result.RegisterResult; import com.digigene.accountauthenticator.result.SignInResult; import com.digigene.accountauthenticator.result.SignUpResult; public class MyInterfaceImplementation extends AbstractInterfaceImplementation { public static int accessTokenCounter = 0; public static int refreshTokenCounter = 0; public static int demoCounter = 0; public static int accessTokenNo = 0; public static int refreshTokenNo = 0; public final int ACCESS_TOKEN_EXPIRATION_COUNTER = 2; public final int REFRESH_TOKEN_EXPIRATION_COUNTER = 5; public final int DEMO_COUNTER = 15; @Override public String[] userAccessTypes() { return new String[]{"REGULAR_USER", "SUPER_USER"}; } @Override public void doAfterSignUpIsUnsuccessful(Context context, Account account, String authTokenType, SignUpResult signUpResult, Bundle options) { Toast.makeText(context, "Sign-up was not possible due to the following:\n" + signUpResult .errMessage, Toast.LENGTH_LONG).show(); AuthenticatorManager.authenticatorManager.addAccount(authTokenType, null, options); } @Override public void doAfterSignInIsSuccessful(Context context, Account account, String authTokenType, String authToken, SignInResult signInResult, Bundle options) { demoCounter = demoCounter + 1; Toast.makeText(context, "User is successfully signed in: \naccessTokenNo=" + accessTokenNo + "\nrefreshTokenNo=" + refreshTokenNo + "\ndemoCounter=" + demoCounter, Toast.LENGTH_SHORT).show(); } @Override public SignInResult signInToServer(Context context, Account account, String authTokenType, String accessToken, Bundle options) { accessTokenCounter = accessTokenCounter + 1; SignInResult signInResult = new SignInResult(); signInResult.isSuccessful = true; synchronized (this) { try { this.wait(2000); } catch (InterruptedException e) { e.printStackTrace(); } } if ((accessTokenCounter > ACCESS_TOKEN_EXPIRATION_COUNTER || demoCounter > DEMO_COUNTER)) { signInResult.isSuccessful = false; signInResult.isAccessTokenExpired = true; if (demoCounter < DEMO_COUNTER) { signInResult.errMessage = "Access token is expired"; return signInResult; } } return signInResult; } @Override public SignUpResult signUpToServer(Context context, Account account, String authTokenType, String refreshToken, Bundle options) { SignUpResult signUpResult = new SignUpResult(); synchronized (this) { try { this.wait(2000); } catch (InterruptedException e) { e.printStackTrace(); } } refreshTokenCounter = refreshTokenCounter + 1; signUpResult.isSuccessful = true; signUpResult.accessToken = "ACCESS_TOKEN_NO_" + accessTokenNo; signUpResult.refreshToken = "REFRESH_TOKEN_NO_" + refreshTokenNo; if (demoCounter > DEMO_COUNTER) { signUpResult.isSuccessful = false; signUpResult.errMessage = "You have reached your limit of using the demo version. " + "Please buy it for further usage"; return signUpResult; } if (refreshTokenCounter > REFRESH_TOKEN_EXPIRATION_COUNTER) { refreshTokenCounter = 0; signUpResult.isSuccessful = false; signUpResult.errMessage = "User credentials have expired, please login again"; return signUpResult; } if (accessTokenCounter > ACCESS_TOKEN_EXPIRATION_COUNTER) { accessTokenCounter = 0; accessTokenNo = accessTokenNo + 1; signUpResult.accessToken = "ACCESS_TOKEN_NO_" + accessTokenNo; } return signUpResult; } @Override public RegisterResult registerInServer(Context context, Account account, String password, String authTokenType, String[] requiredFeatures, Bundle options) { RegisterResult registerResult = new RegisterResult(); registerResult.isSuccessful = false; synchronized (this) { try { this.wait(2000); } catch (InterruptedException e) { e.printStackTrace(); } } if (true) { // password is checked here and, if true, refresh token is generated for the // user refreshTokenNo = refreshTokenNo + 1; accessTokenNo = accessTokenNo + 1; registerResult.isSuccessful = true; registerResult.refreshToken = "REFRESH_TOKEN_NO_" + refreshTokenNo; } return registerResult; } @Override public boolean setDoesCallbackRunInBackgroundThread() { return false; } }
结果
以下显示了图书馆的行动。 您可以在这里find完整的教程,以及关于Android中的AccountManager
如何在我的网站上的这三篇文章中工作: 第1 部分 , 第2 部分 , 第3部分 。