Java 如何在android中以编程方式添加自定义帐户?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24227274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to add programmatically a custom account in android?
提问by user3673503
I am trying to create an account for my app, where I will be able to have my contacts against my account like facebook, viber, whatsapp etc. I want my account to be visible in the account section of the settings also. Any ideas? I have googled a lot, but couldn't find a right answer where to start. Please help. What I have tried to create an account is as below. Which leads me to an error.
我正在尝试为我的应用程序创建一个帐户,在那里我可以将我的联系人与我的帐户(如 facebook、viber、whatsapp 等)联系起来。我希望我的帐户也能在设置的帐户部分中可见。有任何想法吗?我用谷歌搜索了很多,但找不到正确的答案从哪里开始。请帮忙。我试图创建一个帐户如下。这导致我出错。
Account account = new Account("Title", "com.package.nom");
String password = "password";
AccountManager accountManager =
(AccountManager) MainPanel.this.getSystemService(
ACCOUNT_SERVICE);
accountManager.addAccountExplicitly(account, password, null);
采纳答案by ben75
You need to setup multiple components to be able to create an account programmatically. You need:
您需要设置多个组件才能以编程方式创建帐户。你需要:
- an AccountAuthenticator
- a Service to provide access to the AccountAuthenticator
- some permissions
- 帐户验证器
- 提供对 AccountAuthenticator 的访问的服务
- 一些权限
The authenticator
验证器
The authenticator is an object that will make the mapping between the account type and the autority (i.e. the linux-user) that have rights to manage it.
身份验证器是一个对象,它将在帐户类型和有权管理它的自治组织(即 linux 用户)之间进行映射。
Declaring an authenticatoris done in xml :
在 xml 中声明身份验证器:
- create a file
res/xml/authenticator.xml
- 创建文件
res/xml/authenticator.xml
with the following content :
具有以下内容:
<?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"/>
Note the accountType : it must be reused in code when you create the Account. The icons and label will be used by the "Settings" app to display the accounts of that type.
请注意 accountType :创建帐户时必须在代码中重用它。“设置”应用程序将使用图标和标签来显示该类型的帐户。
Implementing the AccountAuthenticator
实现 AccountAuthenticator
You must extends AbstractAccountAuthenticator
to do that. This will be use by third party app to access Account data.
你必须扩展AbstractAccountAuthenticator
才能做到这一点。这将被第三方应用程序用于访问帐户数据。
The following sample don't allow any access to 3rd-party app and so the implementation of each method is trivial.
以下示例不允许任何访问第 3 方应用程序,因此每个方法的实现都是微不足道的。
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.
}
}
The Service exposing the Account Type
暴露账户类型的服务
Create a Service to manipulate the Accounts of that type :
创建一个服务来操作该类型的帐户:
public class AuthenticatorService extends Service {
@Override
public IBinder onBind(Intent intent) {
CustomAuthenticator authenticator = new CustomAuthenticator(this);
return authenticator.getIBinder();
}
}
Declare the service in your manifest:
在清单中声明服务:
<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>
Here, the filter and the meta-data referring to the xml resource declaring the authenticator are the key points.
在这里,过滤器和引用声明验证器的 xml 资源的元数据是关键点。
The permissions
权限
In your manifest be sure to declare the following permissions
请务必在您的清单中声明以下权限
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
(not all required for the sample code presented in this post, but you will probably have some more code about account management and at the end all of them will be useful)
(并非本文中提供的示例代码都需要,但您可能会有更多关于帐户管理的代码,最后所有这些代码都会很有用)
Create an account in code
在代码中创建一个帐户
Now that everything is ready you create an account with the following code. Note the boolean
returned by addAccountExplicitly
informing you about the success or failure.
现在一切准备就绪,您可以使用以下代码创建一个帐户。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");
}
Final tips
最后提示
Don't install your app on external storage
不要在外部存储上安装您的应用
If your app is installed on external storage, there are good chance that Android delete your Account data when sdcard is unmounted (since the authenticator for that account will not be accessible anymore). So to avoid this loss (on every reboot !!!) you must install the App declaring the authenticator on internal storage only :
如果您的应用程序安装在外部存储上,那么当卸载 sdcard 时,Android 很有可能会删除您的帐户数据(因为该帐户的身份验证器将无法再访问)。因此,为了避免这种损失(在每次重新启动时!!!),您必须安装仅在内部存储上声明身份验证器的应用程序:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="internalOnly"
...
In case of trouble
遇到麻烦时
Read the logs carefully, The AccountManger is outputing many logs to help you to debug your code.
仔细阅读日志,AccountManger 正在输出许多日志来帮助您调试代码。
回答by user3673503
here a code snipped I am doing it ( sorry for german commetns ) don't forget to set the propper permissions in the manifest file.
这里有一段代码剪断了我正在做(对不起德国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()
i hope this helps a little bit.
我希望这会有所帮助。
回答by Ali Nem
I have written a libraryfor this, which gets you free from doing the chores needed for managing android accounts, such as defining a bound service, authenticator xml, etc. Working with that is in 5 simple steps:
我为此编写了一个库,它使您免于执行管理 android 帐户所需的琐事,例如定义绑定服务、身份验证器 xml 等。 使用它只需 5 个简单步骤:
Step 1
第1步
Add this to dependencies for build.gradle of the app:
将此添加到应用程序 build.gradle 的依赖项中:
compile 'com.digigene.android:account-authenticator:1.3.0'
Step 2
第2步
Define your authentication account type as a string in strings.xml
:
将您的身份验证帐户类型定义为字符串strings.xml
:
<string name="auth_account_type">DigiGene</string>
Replace ‘DigiGene' with your own account type. This is what appears in Android Accounts in this screenshot.
将“DigiGene”替换为您自己的帐户类型。这就是此屏幕截图中 Android 帐户中显示的内容。
Step 3
第 3 步
Design your registration layout for registering the users (e.g. this image):
设计用于注册用户的注册布局(例如此图像):
<?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>
and make a new class, say MyRegistrationActivity.java
, with the following code:
并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);
}
}
Step 4
第四步
Make an entry layout as in here:
做一个条目的布局在这里:
<?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>
This layout goes with the following class:
此布局与以下类一起使用:
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);
}
}
Step 5
第 5 步
This is the last step in which the methods needed to connect to the server for registration and sign-in purposes and after that are implemented. In the following, contrary to a real case, server connections are mocked, just to demonstrate the functionality of the library. You may replace the following implementation with your own real one.
这是为了注册和登录目的而连接到服务器所需的方法的最后一步,然后实现。在下面,与实际情况相反,模拟服务器连接,只是为了演示库的功能。您可以用自己的真实实现替换以下实现。
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;
}
}
Results
结果
The following shows the library in action.
You can find the complete tutorial hereand about how AccountManager
in android works in these three posts from my website:part 1, part 2, part 3.
下面显示了正在运行的库。你可以在这里找到完整的教程,以及AccountManager
在我网站的这三篇文章中关于android如何工作的内容:第 1部分、第 2部分、第 3 部分。