再次理解Android账号管理体系

news2024/12/23 22:48:31

目录

✅ 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,模版代码如下:

<service
    android:name=".portal.feature.account.service.AccountService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.accounts.AccountAuthenticator" />
    </intent-filter>
    <meta-data
        android:name="android.accounts.AccountAuthenticator"
        android:resource="@xml/authenticator" />
</service>

        步骤二:在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.agg.account"
    android:icon="@drawable/ic_test"
    android:label="AggAccount"
    android:smallIcon="@drawable/ic_test" />

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

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

class Authenticator(context: Context) : AbstractAccountAuthenticator(context) {

    override fun editProperties(
        response: AccountAuthenticatorResponse?, accountType: String?
    ): Bundle? {
        return null
    }

    override fun addAccount(
        response: AccountAuthenticatorResponse?,
        accountType: String?,
        authTokenType: String?,
        requiredFeatures: Array<out String>?,
        options: Bundle?
    ): Bundle? {
        return null
    }

    override fun getAuthToken(
        response: AccountAuthenticatorResponse?,
        account: Account,
        authTokenType: String?,
        options: Bundle?
    ): Bundle? {
        return null
    }

    override fun confirmCredentials(
        response: AccountAuthenticatorResponse?, account: Account?, options: Bundle?
    ): Bundle? {
        return null
    }

    override fun getAuthTokenLabel(authTokenType: String?): String {
        return ""
    }

    override fun updateCredentials(
        response: AccountAuthenticatorResponse?,
        account: Account?,
        authTokenType: String?,
        options: Bundle?
    ): Bundle? {
        return null
    }

    override fun hasFeatures(
        response: AccountAuthenticatorResponse?, account: Account?, features: Array<out String>?
    ): Bundle? {
        return null
    }

}

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

class AccountService : Service() {

    private var authenticator: Authenticator? = null

    override fun onCreate() {
        super.onCreate()
        authenticator = Authenticator(this)
    }

    override fun onBind(intent: Intent?): IBinder? {
        return authenticator?.iBinder
    }

}

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

2.3 操作账户-增删改查

        1)获取所有账号

    /**
     * 获取所有账号
     */
    fun getAllAccount(context: Context) {
        val accountManager = context.getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
        Log.e(TAG, "getAllAccount: size = ${accountManager.accounts.size}")
        for (account in accountManager.accounts) {
            Log.e(TAG, "getAllAccount: $account")
        }
    }

         2)添加账号

    /**
     * 添加账号
     */
    fun addAccount(
        context: Context, account: Account, password: String, authToken: String
    ) {
        val bundle = Bundle()
        bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name)
        bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type)
        bundle.putString(AccountManager.KEY_AUTHTOKEN, authToken)
        bundle.putString(AccountManager.KEY_PASSWORD, password)
        AccountManager.get(context).addAccountExplicitly(account, password, bundle)
    }

         3)更新某个账号

    /**
     * 更新某个账号
     */
    fun updateAccount(
        context: Context, account: Account, password: String = "", authToken: String = ""
    ) {
        var updatePassword = password
        var updateAuthToken = authToken
        AccountManager.get(context).apply {
            if (updatePassword.isEmpty()) {
                updatePassword = getUserData(account, AccountManager.KEY_PASSWORD)
            }
            if (updateAuthToken.isEmpty()) {
                updateAuthToken = getUserData(account, AccountManager.KEY_AUTHTOKEN)
            }
            removeAccountExplicitly(account)
        }
        addAccount(context, account, updatePassword, updateAuthToken)
    }

         4)删除某个账号

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

          5)删除所有账号

    /**
     * 删除所有账号
     */
    fun delAllAccount(context: Context) {
        val accountManager = AccountManager.get(context)
        for (account in accountManager.accounts) {
            val isRemoveSuccess = accountManager.removeAccountExplicitly(account)
            Log.e(TAG, "delAllAccount: isRemoveSuccess = $isRemoveSuccess, $account")
        }
    }

💠 3. 源码流程

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1010096.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

竞赛 基于大数据的时间序列股价预测分析与可视化 - lstm

文章目录 1 前言2 时间序列的由来2.1 四种模型的名称&#xff1a; 3 数据预览4 理论公式4.1 协方差4.2 相关系数4.3 scikit-learn计算相关性 5 金融数据的时序分析5.1 数据概况5.2 序列变化情况计算 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &…

2004-2020年中小企业板上市公司财务报表股票交易董事高管1200+变量数据及说明

2004-2020年中小企业板上市公司财务报表股票交易董事高管1200变量数据及说明 1、时间&#xff1a;2004-2020年 2、范围&#xff1a;中小企业板上市公司&#xff0c;具体名单参看下文链接内数据预览 3、指标&#xff1a;1200变量 变量说明、证券代码、证券代码-字符串、年份…

HTML基础标签 CSS选择器 JavaScript基础语法 WebAPI_ 页面设计 HTTP协议

第 1 题&#xff08;简答题&#xff09; 题目名称&#xff1a; 编写博客, 总结 HTML 中的常用标签用法 题目内容&#xff1a; 编写博客, 总结 HTML 中的常用标签用法 第 2 题&#xff08;简答题&#xff09; 题目名称&#xff1a; image 标签的 alt 和 title 属性有什么…

基于红黑树对map和set容器的封装

本章代码gitee仓库&#xff1a;map和set模拟实现、stl_map_set_tree源码 文章目录 &#x1f431;1. 红黑树的泛型&#x1f408;1.1 红黑树节点&#x1f408;1.2 红黑树迭代器&#x1f408;1.3 仿函数 &#x1f42f;2. 对set的封装&#x1f984;3. 对map的封装 &#x1f431;1. …

android获取设备的IMEI号

在Android 8.0 (API级别26)和更高版本的平台上的 &#xff0c;64位数字(表示为十六进制字符串)&#xff0c;对于应用程序签名密钥、用户和设备的每个组合都是唯一的。ANDROID_ID的值由签名密钥和用户限定作用域。如果在设备上执行出厂重置或如果APK签名密钥更改&#xff0c;则该…

【校招VIP】前端操作系统之存储管理加密

考点介绍 加密算法有很多&#xff0c;如不可逆的摘要算法MD5、SHA&#xff08;安全哈希算法&#xff09;&#xff0c;可逆的Base64编码&#xff0c;对称加密算法DES、AES&#xff0c;还有非对称加密算法DH、RSA等。那是不是说明我们可以使用任何一种加密算法就能保证网站的安全…

认养一头牛———众筹+合伙人商业模式解析

2016年成立以来&#xff0c;认养一头牛致力于打造数字化乳业第一品牌&#xff0c;只为一杯好牛奶。公司在创立三年内完成了10个亿销售目标&#xff0c;被业界称为新消费品牌黑马&#xff0c;一举闯入互联网新消费梯队的视线。未来三年&#xff0c;认养一头牛将着力打造全国最大…

【精品】将两个字符串进行加密和解密的Java工具类

原理 使用一个分隔符将两个字符串连接在一起&#xff0c;并对连接后的字符串进行加密。加密过程中&#xff0c;首先生成一个随机下标列表&#xff0c;然后根据随机下标打乱字符串的顺序&#xff0c;并使用Base64进行编码。解密过程中&#xff0c;根据之前生成的随机下标列表将…

Definition and properties of the Gamma function

See https://math.libretexts.org/Bookshelves/Analysis/Complex_Variables_with_Applications_(Orloff)/14%3A_Analytic_Continuation_and_the_Gamma_Function/14.02%3A_Definition_and_properties_of_the_Gamma_function 勒让德倍元公式&#xff08;Legendre duplication fo…

无涯教程-JavaScript - N函数

描述 N函数返回一个转换为数字的值。 语法 N (value) 争论 Argument描述Required/OptionalValue 要转换的值或对值的引用。 N转换下表中列出的值。 Required 值 N的返回值一个数字那个数字日期,采用Microsoft Excel中可用的内置日期格式之一该日期的序列号 TRUE 1 FALSE…

微视网媒:新媒体时代的宣传与营销新范式

随着数字科技的快速发展&#xff0c;新媒体已经成为了现代社会中不可或缺的一部分。其中&#xff0c;微视网媒作为新兴的网络媒体形态&#xff0c;正在改变着传统的宣传和营销模式&#xff0c;为企业和个人提供了全新的推广平台。本文将对微视网媒的优势、应用场景以及案例进行…

免费开箱即用的微鳄任务管理系统

编者按&#xff1a;基于天翎低代码平台实现的微鳄365任务管理系统&#xff0c;包括有发起任务、重点关注、日程、项目管理等功能&#xff0c;支持私有化部署&#xff0c;免费开箱即用。任务管理系统是组织工作中不可或缺的工具&#xff0c;可以提高工作效率、促进协作、增强任务…

GTS 中testPeakPssOfAllApps fail 详解

0. 前言 GTS 在测试 case armeabi-v7a GtsMemoryHostTestCases 的时候出现下面异常&#xff0c;本文总结一下。 com.google.android.memory.gts.AllAppsMemoryHostTest#testPeakPssOfAllApps 1. error log 09-14 10:16:34 I/TestFailureListener: FailureListener.testFaile…

第十届IEEE电气工程与自动化国际学术论坛(IFEEA 2023)

第十届IEEE电气工程与自动化国际学术论坛&#xff08;IFEEA 2023&#xff09; 2023 10th International Forum on Electrical Engineering and Automation IFEEA论坛属一年一度的国际学术盛会。因其影响力及重要性&#xff0c;IFEEA论坛自创建筹办以来&#xff0c;便受到国内…

【React】面试题5题

1. 说说你对 dangerouslySetInnerHTML 的理解 dangerouslySetInnerHTML是React中的一个属性&#xff0c;用于将HTML代码作为字符串直接插入到组件中的DOM元素中。它可以用来动态地生成HTML内容&#xff0c;但同时也带来了一些潜在的安全风险。 使用dangerouslySetInnerHTML时…

函数递归详解

前言&#xff1a; 函数递归是一种算法&#xff0c;递归是通过将问题分解为更小的子问题来解决问题的办法&#xff0c;递归的优点如下&#xff1a; 简洁性&#xff1a;递归可以用较少的代码实现复杂的功能灵活性&#xff1a;递归可以应对未知深度的数据结构&#xff0c;因为它不…

Spring MVC 中的数据绑定和验证机制是什么,如何使用

在 Spring MVC 应用中&#xff0c;数据绑定和验证是非常重要的一部分&#xff0c;它们可以帮助我们将用户提交的数据绑定到 Java 对象上&#xff0c;并对数据进行验证&#xff0c;保证数据的正确性和可靠性。在 Spring MVC 中&#xff0c;数据绑定和验证机制都是通过注解来实现…

Unity 课时 4 : No.4 模拟面试题

课时 4 : No.4 模拟面试题 C# 1. 请说明字符串中 string str null string str “” string str string.Empty 三者的区别 第一个未作初始化没有值, 第二个为空字符串, 答案&#xff1a; str null 在堆中没有分配内存地址 str "" 和 string.Empty 一样都是…

CentOS7 yum安装报错:“Could not resolve host: mirrorlist.centos.org; Unknown error“

虚拟机通过yum安装东西的时候弹出这个错误&#xff1a; 1、查看安装在本机的网卡 网卡ens33处于disconnected的状态 nmcli d2、输入命令&#xff1a; nmtui3、选择网卡&#xff0c;然后点击edit 4、移动到Automatically connect按空格键选择&#xff0c;然后移动到OK键按空格…

Win7 IIS7解析漏洞复现

一、漏洞说明 文件上传使用白名单做限制&#xff0c;只能上传图片文件&#xff0c;导致脚本文件无法上传&#xff0c;上传图片马绕过白名单文件上传的验证&#xff0c;但是图片马又无法解析&#xff0c;利用IIS7.5文件解析漏洞的特点&#xff1a;任意文件名/任意文件名.php&…