使用 Appwrite 的 Android SDK

126 篇文章 12 订阅
45 篇文章 0 订阅

Appwrite 0.9 的一大亮点是对 Android 的官方支持。我们还发布了一个全新的 Android SDK 来配合它😉!

在本教程中,我们将学习设置 Appwrite 的 Android SDK,与 Appwrite 的 Accounts API 交互,并学习在您的应用程序中设置 OAuth 登录!我们将广泛使用JetPack 组件,例如 Fragments、ViewModels、Data Binding、LiveData 和 Kotlin Coroutines!我希望你和我一样兴奋!让我们开始吧!

📝 先决条件

在这个阶段,我们假设您已经有一个 Appwrite 实例启动并运行。如果您还没有安装 Appwrite,您可以按照appwrite.io上的超级简单安装步骤进行操作。这不是一个错字。真的只有1步!

您还应该使用 Appwrite 设置 OAuth 提供程序,以便能够遵循本教程的 OAuth 部分。您可以通过阅读我们的教程来学习在 Appwrite 中设置 OAuth 提供程序。

🛠️ 创建一个新的 Android 项目

设置您的 Android 项目并选择Empty Activity Template。为您的项目命名,选择Android 6.0 (Marshmallow)作为您的最低 SDK,然后单击Finish。


现在也是将我们的 Android 应用程序添加为 Appwrite 控制台中的平台的好时机。转到您的AndroidManifest.xml并找到您的应用程序的包名称。它应该看起来像com.example.myapplication.

在您的 Appwrite 仪表板中,单击Add Platform并选择一个New Android App。为您的应用命名,添加包名称,然后单击注册。


完成后,是时候回到我们的 Android 项目添加我们的依赖项了。

👷 设置 Appwrite 的 Android SDK

Appwrite 的 Android SDK 托管在 Maven Central 上,因此您首先需要将mavenCentral()存储库添加到项目build.gradle文件中(如果它不存在的话)。

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

然后将 Appwrite Android SDK 添加到您的应用程序build.gradle文件中。

dependencies {
    // ... Other dependencies
    implementation 'io.appwrite:sdk-for-android:0.0.1'
}

我们还将使用数据绑定,我们需要在我们的应用程序build.gradle文件中启用它

android {
    // ...
    buildFeatures {
        dataBinding true
    }
}

同步您的 gradle 依赖项,如果没有错误,我们就可以继续了!

⌨️ 创建辅助类

第一步是Client()从 Appwrite SDK 初始化类。为此,我们将创建一个新文件utils/Client.kt,我们将在其中创建一个单例对象以在我们的应用程序中使用。

package com.example.myapplication.utils

import android.content.Context
import io.appwrite.Client

object Client {
    lateinit var client : Client

    fun create(context: Context) {
        client = Client(context)
             // Replace with your own endpoint and project ID
            .setEndpoint("https://demo.appwrite.io/v1")
            .setProject("6070749e6acd4")
    }
}

如果您的 Appwrite 设置在 localhost 上运行,您需要确保 localhost 可以通过使用代理从您的模拟器访问。否则,您可以替换为您可以在 UNIX 系统上localhost使用命令找到的私有 IP 地址。hostname -I

如果您在localhost上使用 Appwrite ,则需要将该setSelfSigned(true)标志设置为 true。

client = Client(context)
            .setEndpoint("https://192.168.1.35/v1")
            .setProject("6070749e6acd4")
            .setSelfSigned(true)

接下来,我们将创建另一个名为utils/Event.kt. 这个实用程序类将允许我们处理LiveData 事件(只需要观察一次的更改)。

package com.example.myapplication.utils

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

🏗️ 创建布局

现在,我们将更新布局activity_main.xml以允许我们托管片段。activity_main.xml用此代码段替换 的内容。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

现在,为我们的片段创建布局文件layout/fragment_account.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/constraint_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="16dp">

            <EditText
                android:id="@+id/responseTV"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:background="@null"
                android:enabled="true"
                android:fadeScrollbars="false"
                android:focusable="true"
                android:longClickable="true"
                android:scrollbars="vertical"
                android:textIsSelectable="true"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <EditText
                android:id="@+id/email"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:hint="Email"
                android:inputType="textEmailAddress"
                android:text="test@test.com"
                app:layout_constraintStart_toStartOf="@id/responseTV"
                app:layout_constraintTop_toBottomOf="@id/responseTV" />


            <EditText
                android:id="@+id/password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:hint="password"
                android:inputType="textPassword"
                android:text="testtest"
                app:layout_constraintStart_toStartOf="@id/email"
                app:layout_constraintTop_toBottomOf="@id/email" />

            <EditText
                android:id="@+id/name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:hint="name"
                android:inputType="text"
                app:layout_constraintStart_toStartOf="@id/password"
                app:layout_constraintTop_toBottomOf="@id/password" />

            <Button
                android:id="@+id/login"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:text="Login"
                app:layout_constraintEnd_toStartOf="@+id/signup"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintHorizontal_chainStyle="spread"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/name" />

            <Button
                android:id="@+id/signup"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:text="Signup"
                app:layout_constraintEnd_toStartOf="@id/getUser"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toEndOf="@+id/login"
                app:layout_constraintTop_toBottomOf="@+id/name" />

            <Button
                android:id="@+id/getUser"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:text="Get User"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toEndOf="@+id/signup"
                app:layout_constraintTop_toBottomOf="@+id/name" />

            <Button
                android:id="@+id/oAuth"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:text="Login with Facebook"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintEnd_toStartOf="@+id/logout"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintHorizontal_chainStyle="spread"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/signup" />

            <Button
                android:id="@+id/logout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Logout"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toEndOf="@+id/oAuth"
                app:layout_constraintTop_toTopOf="@id/oAuth" />

        </androidx.constraintlayout.widget.ConstraintLayout>
    </ScrollView>

</layout>

🔨 创建 ViewModel

现在让我们创建一个 ViewModelui/accounts/AccountsViewModel.kt来管理我们的应用程序状态。

package com.example.myapplication.ui.accounts

import android.text.Editable
import androidx.activity.ComponentActivity
import androidx.lifecycle.*
import com.example.myapplication.utils.Client.client
import com.example.myapplication.utils.Event
import io.appwrite.exceptions.AppwriteException
import io.appwrite.services.Account
import kotlinx.coroutines.launch
import org.json.JSONObject


class AccountsViewModel : ViewModel() {

    private val _error = MutableLiveData<Event<Exception>>().apply {
        value = null
    }
    val error: LiveData<Event<Exception>> = _error

    private val _response = MutableLiveData<Event<String>>().apply {
        value = null
    }
    val response: LiveData<Event<String>> = _response

    private val accountService by lazy {
        Account(client)
    }

    fun onLogin(email: Editable , password : Editable) {
        viewModelScope.launch {
            try {
                var response = accountService.createSession(email.toString(), password.toString())
                var json = response.body?.string() ?: ""
                json = JSONObject(json).toString(8)
                _response.postValue(Event(json))
            } catch (e: AppwriteException) {
                _error.postValue(Event(e))
            }
        }

    }

    fun onSignup(email: Editable , password : Editable, name: Editable) {
        viewModelScope.launch {
            try {
                var response = accountService.create(email.toString(), password.toString(), name.toString())
                var json = response.body?.string() ?: ""
                json = JSONObject(json).toString(2)
                _response.postValue(Event(json))
            } catch (e: AppwriteException) {
                _error.postValue(Event(e))
            }
        }

    }

    fun onGetUser() {
        viewModelScope.launch {
            try {
                var response = accountService.get()
                var json = response.body?.string() ?: ""
                json = JSONObject(json).toString(2)
                _response.postValue(Event(json))
            } catch (e: AppwriteException) {
                _error.postValue(Event(e))
            }
        }
    }

    fun onLogout() {
        viewModelScope.launch {
            try {
                var response = accountService.deleteSession("current")
                var json = response.body?.string()?.ifEmpty { "{}" }
                json = JSONObject(json).toString(4)
                _response.postValue(Event(json))
            } catch (e: AppwriteException) {
                _error.postValue(Event(e))
            }
        }
    }

}

我们的 ViewModel 有 2 个 LiveData 对象:一个用于跟踪错误,一个用于跟踪响应。

我们还有4个功能

  1. onLogin - 登录按钮的 onClick 处理程序
  2. onSignup - 注册按钮的 onClick 处理程序
  3. onLogout - 注销按钮的 onClick 处理程序
  4. onGetUser - 获取用户按钮的 onClick 处理程序

📝 创建片段

惊人的!让我们继续创建我们的片段ui/accounts/AccountsFragment.kt

package com.example.myapplication.ui.accounts

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.myapplication.R
import com.example.myapplication.databinding.FragmentAccountBinding


class AccountsFragment : Fragment() {

    private lateinit var binding: FragmentAccountBinding
    private lateinit var viewModel: AccountsViewModel

    override fun onCreateView(
        inflater: LayoutInflater ,
        container: ViewGroup? ,
        savedInstanceState: Bundle?
    ): View {
        viewModel = ViewModelProvider(this).get(AccountsViewModel::class.java)
        binding = DataBindingUtil.inflate(
            inflater,
            R.layout.fragment_account,
            container,
            false
        )
        binding.lifecycleOwner = viewLifecycleOwner
        binding.login.setOnClickListener{
            viewModel.onLogin(binding.email.text, binding.password.text)
        }

        binding.signup.setOnClickListener{
            viewModel.onSignup(binding.email.text, binding.password.text, binding.name.text)
        }

        binding.getUser.setOnClickListener{
            viewModel.onGetUser()
        }

        binding.logout.setOnClickListener{
            viewModel.onLogout()
        }

        viewModel.error.observe(viewLifecycleOwner, Observer { event ->
            event?.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled
                Toast.makeText(requireContext(), it.message , Toast.LENGTH_SHORT).show()
            }
        })

        viewModel.response.observe(viewLifecycleOwner, Observer { event ->
            event?.getContentIfNotHandled()?.let {
                binding.responseTV.setText(it)
            }
        })

        return binding.root
    }
}

🔧 更新主活动

最后,让我们更新我们的 MainActivity.kt,它将初始化我们的Client单例并将其添加AccountsFragment到FragmentContainerView。

package com.example.myapplication

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.fragment.app.add
import androidx.fragment.app.commit
import com.example.myapplication.utils.Client

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Client.create(applicationContext)

        if (savedInstanceState == null) {
            supportFragmentManager.commit {
                setReorderingAllowed(true)
                add<AccountsFragment>(R.id.fragment_container_view)
            }
        }
    }
}

您现在应该能够运行您的应用程序并创建用户、登录、注销并获取有关当前登录用户的信息!

🔐 添加 OAuth 支持

您会注意到我们的 UI 中有一个Login With Facebook按钮,但它实际上并没有做任何事情。现在让我们将 Facebook OAuth 添加到我们的应用程序中!

第一步是在AndroidManifest.xml文件中添加回调活动。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">
    <application

        <!-- Other Activities -->
        <activity android:name="io.appwrite.views.CallbackActivity" >
            <intent-filter android:label="android_web_auth">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="appwrite-callback-6070749e6acd4"  />
            </intent-filter>
        </activity>

    </application>
</manifest>

确保将项目 ID 替换为您自己的。

接下来,我们将向 ViewModel 添加一个函数来调用createOAuth2Session()Appwrite SDK 的方法。

fun oAuthLogin(activity: ComponentActivity) {
        viewModelScope.launch {
            try {
                accountService.createOAuth2Session(activity, "facebook", "appwrite-callback-6070749e6acd4://demo.appwrite.io/auth/oauth2/success", "appwrite-callback-6070749e6acd4://demo.appwrite.io/auth/oauth2/failure")
            } catch (e: Exception) {
                _error.postValue(Event(e))
            } catch (e: AppwriteException) {
                _error.postValue(Event(e))
            }
        }
    }

成功和失败 URL 采用以下形式
appwrite-callback-[PROJECT-ID]://[YOUR APPWRITE ENDPOINT]/auth/oauth2/[ success | failure ]

确保将项目 ID 和端点替换为您自己的。

最后一步是调用这个函数AccountsFragment.kt

override fun onCreateView(
        inflater: LayoutInflater ,
        container: ViewGroup? ,
        savedInstanceState: Bundle?
    ): View? {

        // ... Existing Methods

        binding.oAuth.setOnClickListener{
            viewModel.oAuthLogin(activity as ComponentActivity)
        }
    }

重新运行您的应用程序,您现在应该能够触发您的 Facebook OAuth Flow!有了它,您现在知道如何在您的 Android 应用程序中与 Appwrite 的 Accounts API 进行交互!

**一个零基础的新人,我认为坚持是最最重要的。**我的很多朋友都找我来学习过,我也很用心的教他们,可是不到一个月就坚持不下来了。我认为他们坚持不下来有两点主要原因:

他们打算入行不是因为兴趣,而是因为所谓的IT行业工资高,或者说完全对未来没有任何规划。

刚开始学的时候确实很枯燥,这确实对你是个考验,所以说坚持下来也很不容易,但是如果你有兴趣就不会认为这是累,不会认为这很枯燥,总之还是贵在坚持。

技术提升遇到瓶颈了?缺高级Android进阶视频学习提升自己吗?还有大量大厂面试题为你面试做准备!

文末可以免费领取安卓资料


对于很多Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些知识图谱希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

不论遇到什么困难,都不应该成为我们放弃的理由!

如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!

最后祝各位新人都能坚持下来,学有所成,如有需要可以点击下方微信卡片即可免费领取

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值