基於MVVM架構的遊戲App如何整合華為遊戲服務(一)——登入認證

華為開發者論壇發表於2021-07-19

本文將介紹華為遊戲服務,並講解如何在 MVVM架構的移動遊戲 App中使用遊戲服務實現賬號登入及認證功能。

遊戲服務是華為為移動開發者提供的用於滿足各種移動遊戲開發需求的服務。使用遊戲服務,您可以輕鬆地在遊戲中實現排行榜、事件、成就、存檔等功能。遊戲服務有效簡化遊戲的開發過程,讓您可以使用簡短有序的程式碼結構建立遊戲專案。

華為遊戲服務,為您提供以下開發能力,幫助您更高效地開發遊戲:

·         帳號登入

·         遊戲防沉迷

·         浮標

·         成就

·         事件

·         排行榜

·         遊戲存檔

·         玩家資料統計

·         遊戲基本資訊獲取

 

  •     軟體及硬體要求

·         Android Studio 3.X   及以上版本

·         JDK 1.8  及以上版本

·         您的應用應滿足以下條件:

— minSdkVersion: 17

— targetSdkVersion: 29

— compileSdkVersion: 29

 

  • 開發流程

1.   整合

首先,您要先註冊成為華為開發者,並將 HMS Core整合到專案中。 您可以從下面的連結訪問有關該步驟的材料。

https://medium.com/huawei-developers/android-integrating-your-apps-with-huawei-hms-core-1f1e2a090e98

 

2 新增依賴

整合 HMS Core到專案中並在 AGC控制檯開啟遊戲服務之後,將所需依賴的庫新增到 app目錄下的 build.gradle檔案中,如下所示:

dependencies {
implementation 'com.huawei.agconnect:agconnect-auth:1.5.1.300'
implementation 'com.huawei.hms:base: 5.0.5.300'
implementation 'com.huawei.hms:hwid: 5.2.0.300'
implementation 'com.huawei.hms:iap:5.0.1.300'
implementation 'com.huawei.hms:game: 5.0.4.302'
}

3 建立 Application

當您的應用啟動時,遊戲服務由 Application類觸發啟動。遊戲服務是在這個類中啟動的,而此類和專案一同啟動。建立 BaseApplication類後,您需要在 Manifest檔案中定義它。

class BaseApplication : Application(){
    companion object{
        lateinit var instance: BaseApplication
            private set
    }
 
    override fun onCreate() {
        super.onCreate()
 
        instance = this
 
        HuaweiMobileServicesUtil.setApplication(this)
 
        HwAds.init(this)
    }
}

4.  設計登入頁面

在所有許可權和庫都新增到專案中後,您可以開始為您的遊戲開發登入頁面。此時就用到了華為認證服務。認證服務使您可以在控制檯上檢視遊戲的所有使用者。此外,認證服務還提供了多種登入方式:包含 FacebookTwitterGoogle、郵箱、電話號碼或華為 ID。我將分享一個通用的登入頁面設計。以下是一個使用華為 ID登入的示例。

您可以在以下連結檢視認證服務的文件:

https://developer.huawei.com/consumer/cn/doc/development/AppGallery-connect-Guides/agc-auth-introduction-0000001053732605?ha_source=hms1

<androidx.constraintlayout.motion.widget.MotionLayout
        xmlns:android="
        xmlns:app="
        xmlns:tools="
        android:layout_width="match_parent"
        app:layoutDescription="@xml/motion_scene_splash"
        android:id="@+id/id_09"
        android:layout_height="match_parent">
 
        <ImageView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:src="@drawable/background"
            android:alpha="0.7"
            android:id="@+id/id_11"/>
        <ImageView
            android:id="@+id/imgView_logo"
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:layout_marginTop="80dp"
            android:scaleType="centerInside"
            android:src="@drawable/vs_logo"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
 
        <ImageView
            android:id="@+id/imgView_logo_rays"
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:layout_marginTop="80dp"
            android:scaleType="centerInside"
            android:src="@drawable/vs_logo"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
 
        <ImageView
            android:id="@+id/imgView_cloudLeft"
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:layout_marginTop="16dp"
            android:scaleType="centerInside"
            android:src="@drawable/cloud"
            android:translationX="-20dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:tint="#9E9E9E" />
 
        <ImageView
            android:id="@+id/imgView_cloudRight"
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:layout_marginTop="120dp"
            android:scaleType="centerInside"
            android:src="@drawable/cloud"
            android:translationX="20dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:tint="#607D8B" />
 
        <LinearLayout
            android:id="@+id/linlay_inputs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="30dp"
            android:alpha="1"
            android:layout_marginLeft="30dp"
            android:layout_marginEnd="30dp"
            android:layout_marginRight="30dp"
            android:gravity="center"
            android:layout_marginTop="10dp"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imgView_cloudRight">
 
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:fontFamily="@font/muli_regular"
                android:text="Welcome Back"
                android:textColor="#000000"
                android:textSize="20sp"
                android:id="@+id/id_12" />
 
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:fontFamily="@font/muli_regular"
                android:text="Sign in to continue"
                android:textColor="#000000"
                android:textSize="14sp"
                android:id="@+id/id_13" />
 
            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="30dp"
                android:background="@drawable/et_background"
                android:drawableStart="@drawable/ic_baseline_email_24"
                android:drawableLeft="@drawable/ic_baseline_email_24"
                android:drawablePadding="16dp"
                android:hint="Email"
                android:inputType="textEmailAddress"
                android:padding="16dp"
                android:textSize="14sp"
                android:textColor="#000000"
                android:fontFamily="@font/muli_regular"
                android:id="@+id/id_14"/>
 
            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:background="@drawable/et_background"
                android:drawableStart="@drawable/ic_baseline_lock_24"
                android:drawableLeft="@drawable/ic_baseline_lock_24"
                android:drawableEnd="@drawable/ic_baseline_visibility_24"
                android:drawableRight="@drawable/ic_baseline_visibility_24"
                android:drawablePadding="16dp"
                android:hint="Password"
                android:inputType="textPassword"
                android:padding="16dp"
                android:textSize="14sp"
                android:textColor="#000000"
                android:fontFamily="@font/muli_regular"
                android:id="@+id/id_15"/>
 
            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="30dp"
                android:background="@drawable/button_one"
                android:text="@string/signInButton"
                android:textAllCaps="false"
                android:fontFamily="@font/muli_regular"
                android:textColor="#7E675E"
                android:id="@+id/id_16"/>
 
        </LinearLayout>
 
        <TextView
            android:id="@+id/tv_forgotPassword"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:layout_marginEnd="30dp"
            android:alpha="1"
            android:layout_marginRight="30dp"
            android:text="Forgot Password?"
            android:textColor="#7E675E"
            android:textSize="13sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/linlay_inputs" />
 
        <ImageView
            android:id="@+id/safads"
            android:layout_width="wrap_content"
            android:layout_height="100dp"
            android:src="@drawable/color_logo"
            app:tint="#9E9E9E"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/linlay_inputs"
            android:visibility="invisible"/>
                </androidx.constraintlayout.motion.widget.MotionLayout>

5.  建立 LoginViewModel

LoginViewModel 類將從 View類接收、處理資料並將處理過的資料傳送到 View類。所有的登入操作都將在 LoginViewModel類中完成。

首先,在 HuaweiIdAuthService物件中建立客戶端。

private lateinit var mClient: HuaweiIdAuthService

接下來是開發帳戶登出。這樣做的目的是結束應用中開啟的會話。登出後,我們需要建立 login方法。首先,呼叫 logout方法。然後新增 startActivityForResult方法,並將其在 View類重寫。

LoginViewModel 類如下所示:

class LoginViewModel(private val context: Context): ViewModel(){
 
    private lateinit var mClient: HuaweiIdAuthService
 
    fun login(fragment: Fragment){
        logout()
 
        val huaweiIdAuthParamsHelper =HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM)
        val scopeList: MutableList<Scope> = ArrayList()
        scopeList.add(Scope(HwIDConstant.SCOPE.ACCOUNT_BASEPROFILE))
        huaweiIdAuthParamsHelper.setScopeList(scopeList)
        val authParams = huaweiIdAuthParamsHelper.setAccessToken().createParams()
 
        mClient = HuaweiIdAuthManager.getService(context, authParams)
        fragment.startActivityForResult(mClient.signInIntent, 1002)
    }
 
    fun logout(){
        Log.i(Constants.LOGIN_VIEWMODEL_TAG, "In LogOut Fun.")
        val auth = AGConnectAuth.getInstance()
        auth.signOut()
    }
}

6.  建立 LoginViewModelFactory

建立 LoginViewModelFactory類,並將上下文設定為引數。此類將返回 ViewModel類。

class LoginViewModelFactory(private val context: Context): ViewModelProvider.NewInstanceFactory(){
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return LoginViewModel(context) as T
    }
}

7.  建立 LoginFragment

XML檔案上新增 ViewModel依賴,並將它作為繫結物件。你需要再次開啟 XML檔案,新增變數 “viewmodel”,將 type新增為 ViewModel類目錄。

<data>
    <variable
        name="viewmodel"
        type="com.xxx.xxx.viewmodel.LoginViewModel"/>
</data>

返回 LoginFragment 並新增工廠類、 viewmodel 類,並進行繫結。

private lateinit var binding: FragmentLoginBinding
private lateinit var viewModel: LoginViewModel
private lateinit var viewModelFactory: LoginViewModelFactory

onCreateView 方法上定義這些物件。

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
 
        binding = DataBindingUtil.inflate(inflater, R.layout.activity_splash_login, container,false) as ActivitySplashLoginBinding
        viewModelFactory =LoginViewModelFactory(requireContext() )
        viewModel = ViewModelProviders.of(this, viewModelFactory).get(LoginViewModel::class.java)
 
        return binding.root
    }

建立登入按鈕點選事件監聽,並在 ViewModel 類中呼叫 login 方法。

var gameLoginButton = binding.id16.findViewById<View>(R.id.id_16)
gameLoginButton.setOnClickListener {
    showProgressDialog()
    viewModel.login(this)
}

最後,建立 onActivityResult 方法,檢視登入結果。如果登入成功,您可以看到一些使用者資訊,比如使用者名稱、 ID 、國家 / 地區、年齡等。認證服務提供了豐富的使用者資訊,您可以在 App 中使用認證服務。

override fun onActivityResult(
        requestCode: Int,
        resultCode: Int,
        @Nullable data: Intent?
    ) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 1002) {
            val signInHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data)
            if (signInHuaweiIdTask.isSuccessful) {
                val huaweiAccount = signInHuaweiIdTask.result
                val accessToken = huaweiAccount.accessToken
                Log.w(Constants.LOGIN_FRAGMENT_TAG, "accessToken: $accessToken")
 
                val credential = HwIdAuthProvider.credentialWithToken(accessToken)
                val provider_now = credential.provider
                Log.w(Constants.LOGIN_FRAGMENT_TAG, "provider_now: $provider_now")
 
                AGConnectAuth.getInstance().signIn(credential)
                    .addOnSuccessListener { signInResult ->
                        val user = AGConnectAuth.getInstance().currentUser
                        Log.w(Constants.LOGIN_FRAGMENT_TAG, "Login Success. User Display Name : " + user.displayName)
                        userName = user.displayName
                       //Start another fragment here.
                        (activity as MainActivity?)!!.setSelectedTab(Constants.TAB_LOGIN,Constants.TAB_HOME,false)
 
                        dismisProgressDialog()
 
                    }.addOnFailureListener { e: Exception ->
                        Toast.makeText(context, R.string.authenticationError, Toast.LENGTH_SHORT).show()
                        Log.w(Constants.LOGIN_FRAGMENT_TAG, "sign in for agc failed: " + e.message)
                        dismisProgressDialog()
                    }
            } else {
                Toast.makeText(context, R.string.sıgnInError, Toast.LENGTH_SHORT).show()
                Log.e(Constants.LOGIN_FRAGMENT_TAG,"sign in failed : " + (signInHuaweiIdTask.exception as ApiException).statusCode)
 
                dismisProgressDialog()
            }
        }
    }

結論

現在您可以在遊戲 App上實現登入功能啦。您也可以跟我一樣選擇使用認證服務,因為它可以為您提供多種使用者資訊,您可以在 AGC控制檯中檢視所有使用者。

在下一篇文章中,我將介紹“構建成就”( 為遊戲玩家自定義配置成就(最高 200個),增加玩家的新鮮感和成就感,鼓勵玩家持續參與 ),並提供一個示例。請關注第二篇文章,以簡潔的結構開發您的遊戲應用。

 

參考

遊戲服務文件

認證服務文件


欲瞭解 HMS Core 更多詳情,請參閱:
>> 華為開發者聯盟官網

>> 獲取開發指導文件
>> 參與開發者討論請到 CSDN社群 或者 Reddit 社群
>> 下載 demo 和示例程式碼請到 Github 或者 Gitee
>> 解決整合問題請到 Stack Overflow


原文連結: https://developer.huawei.com/consumer/cn/forum/topic/0201550888629240197?fid=18

原作者:胡椒

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69970551/viewspace-2782022/,如需轉載,請註明出處,否則將追究法律責任。

相關文章