• 再次理解Android账号管理体系


    目录

    ✅ 0. 需求

    📂 1. 前言

    🔱 2. 使用

    2.1 账户体系前提

    2.2 创建账户服务

    2.3 操作账户-增删改查

    💠 3. 源码流程


    ✅ 0. 需求

            试想,自己去实现一个账号管理体系,该如何做呢?

            ——————————

            最近有遇到这样一个需求:AR眼镜只支持同时与一个手机绑定,即:AR眼镜已与手机绑定过的话,不再支持与其他手机绑定;如要绑定其他手机,需要先解绑当前手机才能重新绑定。

            理解一下需求,即是:眼镜端需要账号管理体系,主要用来存储已绑定过手机的token。

            AR眼镜绑定手机时序图,如下所示:


     

    📂 1. 前言

            我们知道,Android的账号管理体系是用来管理用户在Android设备上的身份验证和授权的系统,包括了对账号的创建、授权、修改和删除等操作的管理。

            那么,我们为什么要使用Android的账号管理体系?

            ——————————

            尽管,我们可以自己使用SP、MMKV、文件或数据库等方式来存储、更新、删除账户、密码或AuthToken;但其实涉及到跨进程通信,实现起来其实是稍显麻烦的;并且对于数据安全,信息加密这块的可靠性也有待商榷。

            另外,从本文第一张图可见,我们在架构设计中规划的账号体系服务,是寄生于Android启动时system_server开启的服务,然后通过binder方式,提供给其他进程使用。

            然而,在技术预研时发现,强大的Android早已想到了这点,在Android 2.0开始就加入了新包android.accounts,为我们准备好了这样一个服务ACCOUNT_SERVICE。

            而且,该包功能已十分强大,我们可以直接拿来使用,功能主要包括:

    1. 集中式的账户管理API,可以安全地存储和访问认证的令牌和密码;
    2. 可以在同一个设备中管理同一应用的多个不同账号,能够自动批量的同步服务器更新账户,甚至可以和不同服务器进行数据同步和安全认证;
    3. 把账户的验证过程、AuthToken的获取过程分离出来,降低程序的耦合性;
    4. 并且会在”设置”应用中添加一个账户入口;
    5. 方便应用间账号共享。

    🔱 2. 使用

    2.1 账户体系前提

            1)账号体系共享前提:使用相同的签名

            2)权限申明:

    <uses-permission android:name="android.permission.GET_ACCOUNTS" />

    2.2 创建账户服务

            步骤一:定义一个action为android.accounts.AccountAuthenticator的Intent的Service,并在meta-data的resource属性指定该Account基本显示信息的xml文件authenticator,模版代码如下:

    1. <service
    2. android:name=".portal.feature.account.service.AccountService"
    3. android:enabled="true"
    4. android:exported="true">
    5. <intent-filter>
    6. <action android:name="android.accounts.AccountAuthenticator" />
    7. intent-filter>
    8. <meta-data
    9. android:name="android.accounts.AccountAuthenticator"
    10. android:resource="@xml/authenticator" />
    11. service>

            步骤二:在res下的xml文件夹中新建authenticator.xml文件:

    1. "1.0" encoding="utf-8"?>
    2. <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:accountType="com.agg.account"
    4. android:icon="@drawable/ic_test"
    5. android:label="AggAccount"
    6. android:smallIcon="@drawable/ic_test" />

            :android:accountType表示的是Account类型,它必须是唯一的,一般是包名。

            步骤三:创建继承自AbstractAccountAuthenticator的Authenticator文件,如下:

    1. class Authenticator(context: Context) : AbstractAccountAuthenticator(context) {
    2. override fun editProperties(
    3. response: AccountAuthenticatorResponse?, accountType: String?
    4. ): Bundle? {
    5. return null
    6. }
    7. override fun addAccount(
    8. response: AccountAuthenticatorResponse?,
    9. accountType: String?,
    10. authTokenType: String?,
    11. requiredFeatures: Array<out String>?,
    12. options: Bundle?
    13. ): Bundle? {
    14. return null
    15. }
    16. override fun getAuthToken(
    17. response: AccountAuthenticatorResponse?,
    18. account: Account,
    19. authTokenType: String?,
    20. options: Bundle?
    21. ): Bundle? {
    22. return null
    23. }
    24. override fun confirmCredentials(
    25. response: AccountAuthenticatorResponse?, account: Account?, options: Bundle?
    26. ): Bundle? {
    27. return null
    28. }
    29. override fun getAuthTokenLabel(authTokenType: String?): String {
    30. return ""
    31. }
    32. override fun updateCredentials(
    33. response: AccountAuthenticatorResponse?,
    34. account: Account?,
    35. authTokenType: String?,
    36. options: Bundle?
    37. ): Bundle? {
    38. return null
    39. }
    40. override fun hasFeatures(
    41. response: AccountAuthenticatorResponse?, account: Account?, features: Array<out String>?
    42. ): Bundle? {
    43. return null
    44. }
    45. }

            步骤四:创建帐户Service,并在Service的onBind中调AbstractAccountAuthenticator的getIBinder()返回其用于远程调用的IBinder,如下所示:

    1. class AccountService : Service() {
    2. private var authenticator: Authenticator? = null
    3. override fun onCreate() {
    4. super.onCreate()
    5. authenticator = Authenticator(this)
    6. }
    7. override fun onBind(intent: Intent?): IBinder? {
    8. return authenticator?.iBinder
    9. }
    10. }

            :运行起来程序后,在“设置”应用-“帐户”-“添加帐户”列表中就已可以发现自己的app了。

    2.3 操作账户-增删改查

            1)获取所有账号

    1. /**
    2. * 获取所有账号
    3. */
    4. fun getAllAccount(context: Context) {
    5. val accountManager = context.getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
    6. Log.e(TAG, "getAllAccount: size = ${accountManager.accounts.size}")
    7. for (account in accountManager.accounts) {
    8. Log.e(TAG, "getAllAccount: $account")
    9. }
    10. }

             2)添加账号

    1. /**
    2. * 添加账号
    3. */
    4. fun addAccount(
    5. context: Context, account: Account, password: String, authToken: String
    6. ) {
    7. val bundle = Bundle()
    8. bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name)
    9. bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type)
    10. bundle.putString(AccountManager.KEY_AUTHTOKEN, authToken)
    11. bundle.putString(AccountManager.KEY_PASSWORD, password)
    12. AccountManager.get(context).addAccountExplicitly(account, password, bundle)
    13. }

             3)更新某个账号

    1. /**
    2. * 更新某个账号
    3. */
    4. fun updateAccount(
    5. context: Context, account: Account, password: String = "", authToken: String = ""
    6. ) {
    7. var updatePassword = password
    8. var updateAuthToken = authToken
    9. AccountManager.get(context).apply {
    10. if (updatePassword.isEmpty()) {
    11. updatePassword = getUserData(account, AccountManager.KEY_PASSWORD)
    12. }
    13. if (updateAuthToken.isEmpty()) {
    14. updateAuthToken = getUserData(account, AccountManager.KEY_AUTHTOKEN)
    15. }
    16. removeAccountExplicitly(account)
    17. }
    18. addAccount(context, account, updatePassword, updateAuthToken)
    19. }

             4)删除某个账号

    1. /**
    2. * 删除某个账号
    3. */
    4. fun delAccount(context: Context, account: Account) {
    5. val isRemoveSuccess = AccountManager.get(context).removeAccountExplicitly(account)
    6. Log.e(TAG, "delAllAccount: isRemoveSuccess = $isRemoveSuccess, $account")
    7. }

              5)删除所有账号

    1. /**
    2. * 删除所有账号
    3. */
    4. fun delAllAccount(context: Context) {
    5. val accountManager = AccountManager.get(context)
    6. for (account in accountManager.accounts) {
    7. val isRemoveSuccess = accountManager.removeAccountExplicitly(account)
    8. Log.e(TAG, "delAllAccount: isRemoveSuccess = $isRemoveSuccess, $account")
    9. }
    10. }

    💠 3. 源码流程

    • 从系统启动system_server进程,进而启动ACCOUNT_SERVICE服务开始;
    • AccountManager是一个面向应用程序开发的组件,它提供了一套对应于IAccountManager协议的应用程序接口;
    • 这组接口通过Binder机制与系统服务AccountManagerService进行通信,协作完成帐号相关的操作;
    • 同时AccountManager接收authenticators提供的回调,以便在帐号操作完成之后向调用此帐号服务的业务返回对应的接口,同时触发这个业务对结果的处理。

  • 相关阅读:
    Activiti工作流学习笔记(四)——工作流引擎中责任链模式的建立与应用原理
    【Vue】构建vue项目的几种方法以及区别
    volatile关键字_java培训
    『互联网架构』kafka集群原理
    Spring使用注解开发
    24.Ubuntu新磁盘挂载
    Fiddler 手机抓包代理设置(针对华为荣耀60S)
    多维时序 | Matlab实现CPO-BiTCN-BiGRU冠豪猪优化时间卷积神经网络双向门控循环单元多变量时间序列预测模型
    NX许可证错误:VD is starting, please check vendor daemon s status in debug log
    centos环境搭建nsq单点
  • 原文地址:https://blog.csdn.net/Agg_bin/article/details/132876416