Google 拥抱的 Kotlin 是否为 Android 应用开发的最佳选择?

原文:Authenticating Android Apps Developed in Kotlin
作者:Bruno Krebs
翻译:陈云龙
审校:苏宓,欢迎技术投稿、约稿、给文章纠错,请发送邮件至tumin@csdn.net


本文中,我们将学习使用 JWTs 设计安全的 Kotlin Android App, 并用 AuthOto 生成 token (令牌)与 API 进行通信。

今年 5 月,在 Google I/O 大会的 keynote 演讲中,Android 团队宣布对 Kotlin 提供一级的支持。由此可见,Kotlin 是 Android 开发的不错选择。否则,Android 团队也不会有这般举动。此外,在过去的 4 年中,Android Studio 开发也是基于 IntelliJ 平台,而 IntelliJ 亦是创造了 Kotlin 语言的 JetBrains 的一款产品。因此,我们可以放心地选择 Kotlin 开发下一个 Android 项目,开发体验也将会无比顺畅。至少从 IDE 支持的角度来看是这样的。

Kotlin 与 Java 有何不同?

Kotlin 是一门有着不同语法的全新编程语言。但对于 Java 开发者而言,读懂 Kotlin 编写的项目源码却易如反掌。尽管 Java 开发者在深入 Kotlin 之前应该学习许多差异性和许多吊渣天的功能。

如果你之前从未使用过 Kotlin,你也可以跟随这篇文章的步伐,因为它没有那么多高级主题。但是,为了完整起见,这里列出了关于 Kotlin 的学习资源清单:

除此以外,请注意,JetBrains 已经投入大量工具来帮助 Java 开发者迁移至 Kotlin。例如,IntelliJ 和 Android Studio 3.0 都附带了自动将 Java 代码转化为 Kotlin 代码的相关工具。

使用 Kotlin 开发一个 Android 应用

要开始一个项目,你可以利用 Android Studio 自带的创建新项目向导。其用法非常简单,只需几个步骤,如官网教程所示。但是,为了加快速度,我们将直接把这个库复制到我们的本地服务器。

本文是使用 Android Studio 3 Canary 3 进行开发的。只要我们更新 Kotlin 插件和 Gradle 封装器的版本,新版本的 IDE 是支持的。

git clone https://github.com/auth0-blog/kotlin-app.git

紧接着,我们将通过 Android Studio 的 Open an existing Android Studio project 选项来打开这个项目。

如果这是你第一次使用 Android Studio,你需要安装一个 Android 虚拟机或者将你自己的 Android 设备与 IDE 进行整合。我通过 IDE 成功在本地安装了新的 Android 虚拟机。但是,如果不知道什么原因,你发现你无法成功安装,你可以点击这里,看看其是否对你有帮助。

为了确保一切能跟预期一样正常运行,让我们运行此应用。点击Run app 键( IDE 顶部的一个绿色的播放按钮),这将在选中的虚拟机(或者在你自己的设备上)运行一个 Hello World! 应用。

我们用 Kotlin 构建什么应用?

如上所述,我们将构建一个简单的应用。此应用包括三个功能:

  1. 一个待办事项列表。该列表的数据将从一个公开可用的 RESTful API 中获取。
  2. 一个输入框和一个按钮,让用户能在现有的列表中添加新的项目。这仅适用于登录用户。
  3. Auth0 提供登录注册功能。我们从 Auth0 获取 access_token,并使用它与安全的终端进行交互。

启动 RESTful API

我们使用到的 RESTful API 是一个可以在这里找到的 Express 应用程序。因此,在开发我们的 Kotlin 应用之前,我们需要复制这个库。

git clone https://github.com/auth0-blog/nodejs-auth0/

cd nodejs-auth0

npm install

由于我们的 RESTful API 受到 Auth0 的保护,所以我们现在去创建一个免费的账号(如果我们还没有账号)。因为 Auth0 基于 OAuth 和 OpenID Connect 的标准来管理身份,所以我们将遵循最佳实践,并在基于 Web 的管理工具中创建我们后端的 API。在此页面中,点击 Create API 按钮并填写如下所示的表单:

这个表单的第一个字段 Name,它只是这个工具的友好名称,它的值对我们来说并不重要。第二个字段 Identifier,它是我们 API 的观众。当配置后端或者配置 Kotlin 的 Android 应用时我们将使用到该值。最后一个字段 Signing Algorithm,定义了我们的令牌将如何签名。RS256 则是使用私钥对令牌进行签名并使用公钥对其进行验证。想要了解更多有关签名过程是如何运行的,可参考这篇文章

当我们完成 API 的创建时,Auth0 同时也会创建一个测试客户端。如果我们前往 Clients 菜单,我们可以看到其中有一个叫 Kotlin To Do App (Test Client) 的客户端。让我们访问这个客户端并复制其中的域值。

有了这个域值,我们就可以运行我们的 RESTful API 了。在复制的库的根目录中,让我们执行以下命令:

export AUTH0_AUDIENCE=kotlin-todo-app

export AUTH0_DOMAIN=krebshaus.auth0.com

export AUHT0_ALGORITHM=RS256

node index

请注意,以上导出的值是指在 Auth0 上我自己的帐号。你自己的 AUTH0_DOMAIN 值会有所不同。另外两个值,AUTH0_AUDIENCEAUHT0_ALGORITHM,如果你在配置 API 时没有更改任何内容,那么这两个值和我的将会是一样的。

运行最后的一行命令 node index后,我们的 RESTful API 将会启动并运行。我们可以发出两个简单的请求来测试它:

# get to-do items

curl http://localhost:8080

# which will result on: ["Feed the dogs","Mow the lawn","Buy pizza"]

# try to post a new item without an authorization token

curl -d 'new item' -X POST -v http://localhost:8080

# which will result on a 401 status code (unauthorized)

用 Kotlin 来调用 RESTful API

要开始用 Kotlin 开发 Android 应用,我们将首先处理与后端的通信。为了能与 RESTFul API 进行交互,我们将在项目中进行三个地方的修改。第一,我们需要添加一个 Volley 的依赖。这个可以通过修改 ./app/build.gradle 文件来实现,修改如下:

// ...

dependencies {

    // ...

    compile 'com.android.volley:volley:1.0.0'

}

// ...

第二,明确定义我们的 Android 应用程序将使用互联网连接。这可以通过编辑 ./app/src/main/AndroidManifest.xml 文件来完成,如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.auth0.samples.kotlinapp">

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

    <!-- ... application definition -->

</manifest>

第三,在定义了对 Volley 的依赖关系以及我们的应用程序需要使用互联网的权限之后,我们将创建一个实用类,它将处理与我们后端的所有交互。我们将这个类定义为 MainActivityClass 的一个同级类,名为 ToDoAPIUtils.kt,代码如下:

package com.auth0.samples.kotlinapp

import android.app.Activity

import android.widget.ArrayAdapter

import android.widget.ListView

import android.widget.Toast

import com.android.volley.Request

import com.android.volley.RequestQueue

import com.android.volley.Response

import com.android.volley.toolbox.JsonArrayRequest

import org.json.JSONArray

val ENDPOINT = "http://10.0.2.2:8080/"

fun getItems(activity: Activity, queue: RequestQueue, listView: ListView) {

    val jsonArrayRequest = JsonArrayRequest(Request.Method.GET, ENDPOINT, null,

            Response.Listener<JSONArray> { response ->

                val list = ArrayList<String>()

                (0 until response.length()).mapTo(list) {

                    response[it].toString()

                }

                val adapter = ArrayAdapter(activity,

                        android.R.layout.simple_list_item_1, list)

                listView.adapter = adapter

            },

            Response.ErrorListener { error ->

                Toast.makeText(

                        activity.applicationContext,

                        error.toString(),

                        Toast.LENGTH_SHORT).show()

            }

    )

    //add getItems to queue

    queue.add(jsonArrayRequest)

}

(目前)该文件包含一个单一的功能,也就是发送GET 请求给后端,然后使用后端响应的数据填充ListView。请注意,在 Kotlin 中,函数可以定义在文件的顶层,这意味着你不需要创建一个类来保存一个函数。

上面代码的另一个重要方面是,ENDPOINT 硬编码为http://10.0.2.2:8080/。该 URL 指的是我们之前已经启动的 RESTful API,如果你不是使用虚拟机,你可能需要把该值改为你本地网络上计算机的IP地址。

渲染待办事项

因为我们已经有了从后台获取事项的功能,所以我们要使用它来呈现给用户。由于项目列表是公开可用的,所以我们将在第一个 view 中显示它, activity_main.xml布局是由 MainActivity 进行引用的,下面让我们来定义这个文件的内容:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    xmlns:android="http://schemas.android.com/apk/res/android">

    <ListView xmlns:android="http://schemas.android.com/apk/res/android"

        android:id="@+id/list_todo"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content" />

</LinearLayout>

之后,我们将进一步修改 MainActivity的代码:

package com.auth0.samples.kotlinapp

import android.os.Bundle

import android.support.v7.app.AppCompatActivity

import android.widget.ListView

import com.android.volley.toolbox.Volley

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        // setting up a Volley RequestQueue

        val queue = Volley.newRequestQueue(this)

        // getting a reference for the ListView

        val listToDo = findViewById(R.id.list_todo) as ListView

        // passing the activity, the queue and the ListView to the function

        // that consumes the RESTful endpoint

        getItems(this, queue, listToDo)

    }

}

为了能查看待办事项列表,这是我们需要在我们的应用程序上进行的最后一项修改。让我们在虚拟机(或我们自己的设备)上运行应用看看效果如何。

使用 Auth0 安全开发 Kotlin 应用

由于我们已经在我们的应用程序中呈现了待办事项的公开列表,我们现在将在认证层面上进行开发。这使得我们的用户能够登录进去我们的应用程序(还有注册),这将生成一个 JWT(JSON Web 令牌)。这个名为 access_token 的 JWT 将由我们的后端验证以允许或拒绝用户将新项目添加到待办事项列表。

要将我们的应用程序与 Auth0 进行整合,我们需要为此开源库添加依赖关系。我们通过修改 ./app/build.gradle 文件来完成依赖关系,如下所示:

// ...

android {

    // ...

    dataBinding {

        enabled = true

    }

}

dependencies {

    // ...

    kapt 'com.android.databinding:compiler:3.0.0-alpha4'

    compile 'com.auth0.android:auth0:1.8.0'

}

// ...

除了添加新的依赖关系以外,我们还需要配置我们的应用程序使用数据绑定,这将用于定义某些特定组件何时呈现或何时隐藏。

之后,我们将修改 MainActivity 及其布局文件,为其添加一个按钮,当用户点击它时,启动验证进程。我们首先在 app / src / main / res / layout / activity_main.xml 文件中添加验证按钮,其代码如下:

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <import type="android.view.View" />

        <variable name="loggedIn" type="java.lang.Boolean" />

    </data>

    <LinearLayout android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:orientation="vertical">

        <Button android:id="@+id/login_button"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:text="Please, identify yourself."

            android:visibility="@{loggedIn ? View.GONE : View.VISIBLE}" />

        <ListView android:id="@+id/list_todo"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content" />

    </LinearLayout>

</layout>

我们通常会在 strings.xml 文件中定义标签和静态文本,但是为了简化教程,我们将其直接定义在布局文件中。

我们必须将此文件的主要元素更改为layout,这样就可以正确地定义 loggedIn 变量。此变量用于在用户被成功识别时隐藏登录按钮。它的值将被 MainActivity 类管理(并绑定)。

我们现在要关注的是更新MainActivity类,让其与 Auth0 的库进行整合,并且还可以控制 loggedIn变量的状态:

package com.auth0.samples.kotlinapp

import android.app.Dialog

import android.content.Intent

import android.databinding.DataBindingUtil

import android.os.Bundle

import android.support.v7.app.AppCompatActivity

import android.widget.ListView

import android.widget.Toast

import com.android.volley.toolbox.Volley

import com.auth0.android.Auth0

import com.auth0.android.authentication.AuthenticationException

import com.auth0.android.provider.AuthCallback

import com.auth0.android.provider.WebAuthProvider

import com.auth0.android.result.Credentials

import com.auth0.samples.kotlinapp.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    var binding: ActivityMainBinding? = null

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        // setting up a Volley RequestQueue

        val queue = Volley.newRequestQueue(this)

        // referencing the binding object of the view

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        // loggedIn should be false by default to show the button

        binding?.loggedIn = false

        // getting a reference for the ListView

        val listToDo = findViewById(R.id.list_todo) as ListView

        // passing the activity, the queue and the ListView to the function

        // that consumes the RESTful endpoint

        getItems(this, queue, listToDo)

        // triggering the login method when the button is clicked

        val loginButton = findViewById(R.id.login_button)

        loginButton.setOnClickListener { login() }

    }

    // Auth0 triggers an intent on a successful login

    override fun onNewIntent(intent: Intent) {

        if (WebAuthProvider.resume(intent)) {

            return

        }

        super.onNewIntent(intent)

    }

    private fun login() {

        val account = Auth0(getString(R.string.auth0_client_id), getString(R.string.auth0_domain))

        account.isOIDCConformant = true

        WebAuthProvider.init(account)

                .withScheme("demo")

                .withAudience("kotlin-todo-app")

                .start(this, object : AuthCallback {

                    override fun onFailure(dialog: Dialog) {

                        runOnUiThread { dialog.show() }

                    }

                    override fun onFailure(exception: AuthenticationException) {

                        runOnUiThread {

                            Toast.makeText(

                                    this@MainActivity, "Ops, something went wrong!",

                                    Toast.LENGTH_SHORT).show()

                        }

                    }

                    override fun onSuccess(credentials: Credentials) {

                        CredentialsManager.saveCredentials(this@MainActivity, credentials)

                        binding?.loggedIn = true

                    }

                })

    }

}

更改 onCreate函数以获取与布局相关的绑定对象的引用,这是为了将 loggedIn 变量初始化为 false,并为 loginButton 设置监听器。当用户点击登录按钮时调用的登录功能,触发 Auth0 库中公开的 WebAuthProvider 组件。该组件必须使用 Auth0 的实例来初始化,这取决于 Kotlin To Do App (Test Client)的 Client ID 和 Auth0 域。这就是我们在运行后端应用程序之前导出为环境变量的那个域。

这两个属性 auth0_client_idauth0_domain 都是从 strings.xml 文件读取的。让我们打开这个文件,并添加在 Auth0 中为我们创建的 Kotlin To Do App (Test Client)中的值:

<resources>

    <string name="app_name">KotlinApp</string>

    <!-- replace the values with yours account details -->

    <string name="auth0_client_id">4gDhRaCvv2ESmAlL0JAtYX3SD8OkFoi3</string>

    <string name="auth0_domain">krebshaus.auth0.com</string>

</resources>

回到对 MainActivity 类的修改,重要的是要注意,我们还将配置 WebAuthProvider

  • 使用 demo 模式,当一个以 demo:// 开头的链接被调用时,这将帮助 Android 触发我们的应用程序。
  • 使用 kotlin-todo-app ,这是我们在 Auth0 管理工具上创建的 API 的名称。

WebAuthProviderstart 函数通过 AuthCallback 的实现来处理登录和注册的结果。为了定义我们的实现,我们创建了一个内联类,当出现错误时,我们只是渲染一个 Toast 消息,弹出“出错啦”。当登录或注册过程成功时,我们将证书保存到CredentialsManager。这个 manager 不是 Auth0 所提供的库的一部分,我们实际上必须实现它。

CredentialsManager将有两个职责:保存登录用户的证书;并提供一种检索该用户的 access_token 的方法。我们还将创建这个类作为 MainActivity 的同级类,将其命名为 CredentialsManager.kt。此类将包含以下代码:

package com.auth0.samples.kotlinapp

import android.content.Context

import com.auth0.android.result.Credentials

object CredentialsManager {

    private val PREFERENCES_NAME = "auth0"

    private val ACCESS_TOKEN = "access_token"

    fun saveCredentials(context: Context, credentials: Credentials) {

        val sp = context.getSharedPreferences(

                PREFERENCES_NAME, Context.MODE_PRIVATE)

        sp!!.edit().putString(ACCESS_TOKEN, credentials.accessToken)

                .apply()

    }

    fun getAccessToken(context: Context): String {

        val sp = context.getSharedPreferences(

                PREFERENCES_NAME, Context.MODE_PRIVATE)

        return sp!!.getString(ACCESS_TOKEN, null)

    }

}

请注意,我们使用 object 而不是class来定义此类。这是 Kotlin 定义单例模型的经典方式。

为了将这些更改封装在我们的项目中,我们需要在主要活动的标签中注册一个 intent 过滤器,并使此活动作为单任务启动。让我们打开./app/src/main/AndroidManifest.xml并将其替换为以下内容:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.auth0.samples.kotlinapp">

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

    <application

        android:allowBackup="true"

        android:icon="@mipmap/ic_launcher"

        android:label="@string/app_name"

        android:roundIcon="@mipmap/ic_launcher_round"

        android:supportsRtl="true"

        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity"

                  android:launchMode="singleTask">

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

            <intent-filter>

                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />

                <category android:name="android.intent.category.BROWSABLE" />

                <data

                    android:host="@string/auth0_domain"

                    android:pathPrefix="/android/com.auth0.samples.kotlinapp/callback"

                    android:scheme="demo" />

            </intent-filter>

        </activity>

    </application>

</manifest>

在测试与 Auth0 的整合之前,我们需要返回管理工具中的 Clients 页面,并进行两项修改。首先,我们必须配置 Allowed Callback URLs 选项来接受以下 URL:

demo://krebshaus.auth0.com/android/com.auth0.samples.kotlinapp/callback

当然,这个 URL 中的 krebshaus.auth0.com 域名必须根据在Auth0 上的域进行相应的更改。

其次,我们必须将 Client Type 更改为 Native 以启用 PKCE

现在运行我们的应用程序,显示了待办事项的列表,在其上方有请确认自己的登录按钮 …… 如果点击它,我们将看到 Auth0 的默认登录。

使用访问令牌与后端进行交互

我们已经将 Kotlin 应用程序与 Auth0 成功进行整合,并设法从中获取 access_token。我们现在将专注于使用此令牌与后端进行通信。我们将首先在 MainActivity的布局上添加一个 EditText 和另一个 Button,然后我们将使用它们将新的代办事项添加到待办事项列表中。要在我们的布局中添加这些元素,我们打开 activity_main.xml 文件并将它们作为 LinearLayout元素的第一个子元素插入:

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- ... data element with import and variable -->

    <LinearLayout android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:orientation="vertical">

        <EditText

            android:id="@+id/item"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:inputType="text"

            android:labelFor="@+id/item"

            android:visibility="@{loggedIn ? View.VISIBLE : View.GONE}" />

        <Button

            android:id="@+id/add_item"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:text="Add item"

            android:visibility="@{loggedIn ? View.VISIBLE : View.GONE}" />

        <!-- ... login_button and list_todo remain untouched -->

    </LinearLayout>

</layout>

只有当用户 loggedIn时,才会显示这两个元素,如 android:visibility 属性中所定义的。我们现在可以添加一个监听器,这将触发后端的 POST 请求来获取新的按钮。我们通过改变 MainActivity 来实现这一点:

package com.auth0.samples.kotlinapp

// ...

import android.widget.EditText

class MainActivity : AppCompatActivity() {

    // ...

    override fun onCreate(savedInstanceState: Bundle?) {

        // ...

        val addItemButton = findViewById(R.id.add_item)

        val itemEditText = findViewById(R.id.item) as EditText

        addItemButton.setOnClickListener {

            val item = itemEditText.text.toString()

            addItem(queue, item, CredentialsManager.getAccessToken(this), {

                itemEditText.text.clear()

                Toast.makeText(this, "Item added", Toast.LENGTH_SHORT).show()

                getItems(this, queue, listToDo)

            })

        }

    }

    // ...

}

该按钮的监听器调用 addItem,这是我们仍然需要创建的一个函数。该函数将负责使用用户的 access_token 将 POST HTTP 请求发送到后端。我们在ToDoAPIUtils类中创建它:

// ...

import android.util.Log

import com.android.volley.AuthFailureError

import com.android.volley.toolbox.StringRequest

// ...

fun addItem(queue: RequestQueue, item: String, accessToken: String, done: () -> Unit) {

    val postRequest = object : StringRequest(Request.Method.POST, ENDPOINT,

            Response.Listener<String> {

                done()

            },

            Response.ErrorListener {

                error -> Log.w("APIRequest", error.toString())

            }

    ) {

        @Throws(AuthFailureError::class)

        override fun getBody(): ByteArray {

            return item.toByteArray()

        }

        @Throws(AuthFailureError::class)

        override fun getHeaders(): Map<String, String> {

            val headers: Map<String, String> = hashMapOf(

                    "Authorization" to "Bearer $accessToken",

                    "Content-Type" to "text/plain"

            )

            return headers

        }

    }

    //add POST REQUEST to queue

    queue.add(postRequest)

}

我们上面定义的 addItem 函数使用了由 Volley 提供的 StringRequest 类。首先,我们指示这个类作为一个 POST 请求到后端 ENDPOINT(我们的 RESTful API 的 URL)。然后我们定义两个监听器:一个用于成功的请求,另一个用于失败(我们只记录出错的地方)。

如果一切正常运行,我们触发完成回调,这是 addItem 函数接受的最后一个参数。是的,我们可以轻松地在 Kotlin 中传递这样的回调函数。我们在 MainActivity 类的最后一次修改中定义的这个回调函数负责在 POST 请求成功后更新待办事项列表。

我们还覆盖了 StringRequest类的两个方法:

  • getBody 函数将该项目(由用户输入)作为字符串发送到 POST 请求的正文中。
  • getHeaders 函数将请求的 Content-Type 标记为 text / plain,并添加已验证用户的 access_token

这些更改是我们在 Kotlin 应用程序中与安全的 RESTful 终点正确通信所需的一切。现在,每当用户在文本输入框中输入项目时,我们的 Kotlin 应用程序会向后端发送一个带有该项目和 access_token 的请求。后端然后获取此 access_token,根据由 Auth0 提供的公钥进行验证,然后将接收到的项目添加到待办事项列表。

如果我们现在运行我们的应用程序,我们将能够使用它的所有功能。我们将能够看到待办事项的公开(只读)列表,然后,如果我们自己验证,我们也能够更新此列表。

建立自制安全解决方案

如果我们不想依靠 Auth0 提供的最先进的功能,我们还可以使用 JWTs 开发我们的自制安全解决方案。在此我们不会深入了解重构过程的细节,但是,这里概述了扩展自己的解决方案所需的步骤:

  1. 我们需要重构我们的 RESTful API,为我们生成 access_token 令牌,而不是仅验证它。
  2. 我们还需要重构后端以接受新的注册,密码检索等(默认情况下 Auth0 提供的功能)
  3. 我们需要在我们的 Kotlin 应用程序中添加一个活动来处理登录并注册。
  4. 我们需要添加另一个活动来处理密码检索。

如果我们避免与 Active Directory 社会提供商等高级功能进行整合(这些都与 Auth0 无关),那么 Kotlin 应用程序的更改将不会那么难。关于后端重构,Auth0 的博客提供了许多文章,可以解决不同语言和平台上的自制解决方案,如:

如果需要开发自己的解决方案,您可以看看这些资源。

结论

在这篇文章中,使用 Kotlin 开发一款安全的 Android App 是较为简单的,即使之前没有任何 Kotlin 基础的开发者,也可以畅通无阻的进行开发。现在 Kotlin 与 Java 库和框架的整合使得学习过程更加简单,但是,倘若想要使用 Kotlin 语言的全部功能,则需要大量的编码时间和学习。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值