전문가: Kotlin을 사용하는 Android의 택배 앱 MVVM Jetpack(HMS 계정 및 AuthService) - 파트 1

19111 단어
개요
이 기사에서는 HMS Account 및 AuthService Kit와 같은 HMS Core 키트를 통합할 Kotlin을 사용하여 Courier Android 애플리케이션을 만듭니다.
앱은 DataBinding, AndroidViewModel, Observer, LiveData 등과 같은 Jetpack 구성 요소를 사용하여 Android MVVM 클린 아키텍처를 사용합니다.
이 기사에서는 Observable 패턴을 사용하여 DataBinding을 구현하려고 합니다.

Huawei ID 서비스 소개
Huawei ID 로그인은 간단하고 안전하며 빠른 로그인 및 인증 기능을 제공합니다. 계정과 암호를 입력하고 인증을 기다리는 대신 사용자는 HUAWEI ID로 로그인 버튼을 탭하기만 하면 HUAWEI ID로 빠르고 안전하게 앱에 로그인할 수 있습니다.

전제 조건
1.Huawei 전화 EMUI 3.0 이상.
2. Android 4.4 이상(API 레벨 19 이상)을 사용하는 타사 휴대폰.
3.HMS 코어 APK 4.0.0.300 이상
4.안드로이드 스튜디오
5.AppGallery 계정

앱 갤러리 통합 프로세스
1. AppGallery Connect 포털에서 로그인하고 프로젝트를 생성하거나 선택합니다.
2. 프로젝트 설정으로 이동하여 구성 파일을 다운로드합니다.
3.일반 정보로 이동한 다음 데이터 저장소 위치를 제공합니다.

앱 개발
1. 필수 종속성 추가:
• Android 스튜디오를 시작하고 새 프로젝트를 만듭니다. 일단 프로젝트가 준비되었습니다.
• Gradle 스크립트 폴더로 이동하여 build.gradle(모듈: 앱)을 엽니다.

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.huawei.agconnect'

android {
    compileSdkVersion 30
    buildToolsVersion "29.0.3"

    buildFeatures {

        dataBinding = true
        viewBinding = true
    }


•동일한 build.gradle 파일에서 수명 주기 라이브러리를 종속성에 추가합니다. 이 라이브러리는 UI를 ViewModel 및 LiveData에 연결하는 데 도움이 됩니다.

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    //noinspection GradleDependency
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'androidx.annotation:annotation:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'


•HMS 키트에 대한 다음 종속성 추가

//HMS Kits
implementation 'com.huawei.agconnect:agconnect-core:1.5.0.300'
implementation 'com.huawei.hms:hwid:5.3.0.302'


• Gradle 스크립트 폴더로 이동하여 build.gradle(프로젝트: 앱)을 엽니다.

classpath 'com.huawei.agconnect:agcp:1.4.2.300'
maven {url 'https://developer.huawei.com/repo/'}


2. 코드 구현
• 다음 패키지 모델, 이벤트, 뷰모델 생성.
모델: 기본 폴더에서 새 패키지를 만들고 이름을 모델로 지정합니다. 그런 다음 이 패키지에 User.kt 파일을 만듭니다.

data class User(
    var from: String,
    var to: String
)


ViewModel: ViewModel을 사용하면 UI에서 데이터 변경 사항을 쉽게 업데이트할 수 있습니다. 기본 폴더에 viewmodel이라는 패키지를 만듭니다. 그런 다음 새 파일을 만들고 이름을 FactoryViewModelProviders와 함께 LoginViewModel.kt, OrderViewModel.kt로 지정합니다.
LoginViewModel.kt:

class LoginViewModel(application: Application) : AndroidViewModel(application), Observable {

    private var mAuthManager: AccountAuthService? = null
    private var mAuthParam: AccountAuthParams? = null
LoginViewModelFactory.kt:


클래스 LoginViewModelFactory : ViewModelProvider.Factory {
재정의 재미 create(modelClass: Class): T {
if(modelClass.isAssignableFrom(LoginViewModel::class.java)){
LoginViewModel()을 T로 반환
}
IllegalArgumentException("UnknownViewModel") 발생
}

}

OrderViewModel.kt:


클래스 OrderViewModel : ViewModel(), Observable {

@Bindable
val from = MutableLiveData<String>()

@Bindable
val to = MutableLiveData<String>()

var data = MutableLiveData<User>()

OrderViewModelFactory.kt:
클래스 OrderViewModelFactory : ViewModelProvider.Factory {
재정의 재미 create(modelClass: Class): T {
if (modelClass.isAssignableFrom(OrderViewModel::class.java)){
OrderViewModel()을 T로 반환
}
IllegalArgumentException("UnknownViewModel") 발생
}
}

• Xml layout DataBinding
To include data binding in the UI, enclose all content with <layout></layout>.
The ViewModel is introduced to the layout in the <data></data> section, as shown. Ensure that the type value points to the specific folder that has the required ViewModel.
activity_main.xml:




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

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPrimary">

    <com.google.android.material.appbar.AppBarLayout
        style="@style/AppTheme.AppBarOverlay"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            style="@style/AppTheme.PopupOverlay"
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            android:background="@color/colorPrimaryDark" />
    </com.google.android.material.appbar.AppBarLayout>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:gravity="center">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:padding="16dp">


            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="5dp"
                android:text="SigIn / SignUp "
                android:textAlignment="center"
                android:textColor="@color/white"
                android:textSize="34sp"
                android:textStyle="bold" />


            <Button
                android:id="@+id/btn_sign"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:layout_marginBottom="5dp"
                android:background="@color/colorPrimaryDark"
                android:text="Login With Huawei Id"
                android:onClick="@{()->LoginViewModel.login()}"
                android:textColor="@color/white"
                android:textStyle="bold" />
        </LinearLayout>

    </ScrollView>

</RelativeLayout>



activity_order.xml:




<data>
    <variable
        name="orderViewModel"
        type="com.hms.corrieraap.viewmodel.OrderViewModel" />
</data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.google.android.material.appbar.AppBarLayout
        style="@style/AppTheme.AppBarOverlay"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            style="@style/AppTheme.PopupOverlay"
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            android:background="@color/colorPrimaryDark" />
    </com.google.android.material.appbar.AppBarLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_vertical_margin"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/heading_from_text_view"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/activity_horizontal_margin"
            android:layout_marginTop="@dimen/activity_vertical_margin"
            android:layout_marginRight="@dimen/activity_horizontal_margin"
            android:text="@string/heading_from"
            android:textAllCaps="true"
            android:textSize="20sp" />

        <FrameLayout
            android:id="@+id/place_autocomplete_frame"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/activity_vertical_margin"
            android:layout_weight="1">

            <TextView
                android:id="@+id/place_autocomplete_from"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:hint="@string/hint_address"
                android:text="@={orderViewModel.from}"
                android:textSize="20sp" />
        </FrameLayout>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_vertical_margin"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/heading_to_text_view"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/activity_horizontal_margin"
            android:layout_marginTop="@dimen/activity_vertical_margin"
            android:layout_marginRight="@dimen/activity_horizontal_margin"
            android:text="@string/heading_to"
            android:textAllCaps="true"
            android:textSize="20sp" />

        <FrameLayout
            android:id="@+id/place_autocomplete_frame2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/activity_vertical_margin"
            android:layout_weight="1">

            <TextView
                android:id="@+id/place_autocomplete_to"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:hint="@string/hint_address"
                android:text="@={orderViewModel.to}"
                android:textSize="20sp" />
        </FrameLayout>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_vertical_margin"
        android:layout_marginBottom="@dimen/activity_vertical_margin"
        android:gravity="center_horizontal"
        android:orientation="horizontal"
        android:weightSum="1.0">

        <Button
            android:id="@+id/neworder_button_calculate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="start"
            android:layout_marginEnd="0dp"
            android:layout_marginRight="0dp"
            android:layout_weight="0.4"
            android:text="@string/heading_calculate" />

        <Button
            android:id="@+id/neworder_button_additional"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="0.4"
            android:text="@string/neworder_alert_additional" />

        <Button
            android:id="@+id/neworder_button_call"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:onClick="@{()->orderViewModel.onDataChanged()}"
            android:layout_weight="0.4"
            android:text="@string/heading_call" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/neworder_textview_summary"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="@dimen/activity_vertical_margin"
            android:layout_marginBottom="@dimen/activity_vertical_margin"
            android:layout_weight="0.2"
            android:gravity="center_horizontal"
            android:textAlignment="center"
            android:textAllCaps="true" />
    </LinearLayout>

</LinearLayout>



• LiveMessageEvent: Implemented LiveMessageEvent to provide LifeCycleOwner


class LiveMessageEvent : SingleLiveEvent<(T.() -> Unit)?>() {

fun setEventReceiver(owner: LifecycleOwner, receiver: T) {
    observe(owner, Observer { event ->
        if (event != null) {
            receiver.event()
        }
    })
}

fun sendEvent(event: (T.() -> Unit)?) {
    value = event
}

}

• SingleEvent: Implemented SingleLiveEvent for LifeCycleObserver


오픈 클래스 SingleLiveEvent : MutableLiveData() {

private val mPending = AtomicBoolean(false)

@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {

    if (hasActiveObservers()) {
        Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
    }

    super.observe(owner, Observer { t ->
        if (mPending.compareAndSet(true, false)) {
            observer.onChanged(t)
        }
    })
}

@MainThread
override fun setValue(t: T?) {
    mPending.set(true)
    super.setValue(t)
}

@MainThread
fun call() {
    value = null
}

companion object {

    private val TAG = "SingleLiveEvent"
}

}

• ActivityNavigation.kt: Implemented interface for activity transition.


인터페이스 ActivityNavigation {
재미있는 startActivityForResult(의도: Intent?, requestCode: Int)
}

• MainActivity.kt: Implemented for Huawei ID login.



클래스 MainActivity : AppCompatActivity(), ActivityNavigation {

private lateinit var viewModel: LoginViewModel
private lateinit var dataBinding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    val factory = LoginViewModelFactory()
    viewModel = ViewModelProviders.of(this, factory).get(LoginViewModel::class.java)
    dataBinding.loginViewModel = viewModel
    dataBinding.lifecycleOwner=this
    viewModel.startActivityForResultEvent.setEventReceiver(this, this)
}


public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    viewModel.onResultFromActivity(requestCode, data)
    super.onActivityResult(requestCode, resultCode, data)
}

}

• OrderActivity.kt: Implemented for Order new Courier.



클래스 OrderActivity : AppCompatActivity() {

private lateinit var activityOrderBinding: ActivityOrderBinding
private lateinit var orderViewModel: OrderViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    activityOrderBinding = DataBindingUtil.setContentView(this, R.layout.activity_order)
    val factory = OrderViewModelFactory()
    orderViewModel = ViewModelProviders.of(this, factory).get(OrderViewModel::class.java)
    activityOrderBinding.lifecycleOwner = this

    orderViewModel.data.observe(this, Observer {
        Toast.makeText(this, "Placed Order", Toast.LENGTH_SHORT).show();
    })

}

}






## App Build Result

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/no4zgyinjz1l96nvm06k.png)


## Tips and Tricks

Identity Kit displays the HUAWEI ID registration or sign-in page first. The user can use the functions provided by Identity Kit only after signing in using a registered HUAWEI ID.
Conclusion
In this article, we have learned how to integrate Huawei ID in Android application. After completely read this article user can easily implement Huawei ID in the Courier android application using Kotlin.
Thanks for reading this article. Be sure to like and comment to this article, if you found it helpful. It means a lot to me.
**

## 
**References
HMS Docs: 
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/introduction-0000001050048870
Account Kit Training Video:
https://developer.huawei.com/consumer/en/training/course/video/101603872818951100

좋은 웹페이지 즐겨찾기