جستجو برای:
سبد خرید 0
  • خانه
  • دوره های آموزشی
    • دوره های حضوری و آنلاین
      • دوره جامع برنامه نویسی اندروید
      • دوره جامع برنامه نویسی فلاتر
      • دوره برنامه نویسی React Native
      • دوره آموزشی برنامه نویسی iOS
    • دوره های متخصص و حرفه ای
      • دوره متخصص اندروید (پروژه محور)
      • دوره متخصص فلاتر (پروژه محور)
      • دوره آموزش امنیت در اندروید
      • دوره درآمدزایی دلاری از گوگل پلی در ایران
  • آموزش رایگان
    • دوره رایگان اندروید
    • دوره رایگان فلاتر
  • مشاورهجدید
  • دوره VIP
  • وبلاگ
ورود
گذرواژه خود را فراموش کرده اید؟
عضویت

اطلاعات شخصی شما برای پردازش سفارش شما استفاده می‌شود، و پشتیبانی از تجربه شما در این وبسایت، و برای اهداف دیگری که در سیاست حفظ حریم خصوصی توضیح داده شده است.

ارسال مجدد کد یکبار مصرف (00:60)
  • 02188945907
  • info@amooznegar.com
  • لیست علاقه مندی ها
آکادمی آموزنگار
  • خانه
  • دوره های آموزشی
    • دوره های حضوری و آنلاین
      • دوره جامع برنامه نویسی اندروید
      • دوره جامع برنامه نویسی فلاتر
      • دوره برنامه نویسی React Native
      • دوره آموزشی برنامه نویسی iOS
    • دوره های متخصص و حرفه ای
      • دوره متخصص اندروید (پروژه محور)
      • دوره متخصص فلاتر (پروژه محور)
      • دوره آموزش امنیت در اندروید
      • دوره درآمدزایی دلاری از گوگل پلی در ایران
  • آموزش رایگان
    • دوره رایگان اندروید
    • دوره رایگان فلاتر
  • مشاورهجدید
  • دوره VIP
  • وبلاگ
شروع کنید
آخرین اطلاعیه ها
لطفا برای نمایش اطلاعیه ها وارد شوید
0

وبلاگ

آکادمی آموزنگار > اخبار > برنامه نویسی > اندروید > آموزش معماری MVI در اندروید به صورت گام به گام

آموزش معماری MVI در اندروید به صورت گام به گام

1400-11-30
ارسال شده توسط آموزنگار
اندروید، فلاتر
آموزش معماری MVI در اندروید

الگوهای معماری در اندروید روز به روز در حال پیشرفت هستند. همانطور که ما برنامه ها را توسعه می دهیم ، با چالش ها و مشکلات جدیدی روبرو می شویم. با ادامه حل چالش های مشابه، الگوهای جدیدی کشف خواهند شد. به عنوان توسعه دهندگان اندروید ،  MVC ، MVP و MVVM را به عنوان متداول ترین الگوهای مورد استفاده داریم. همه آنها از یک رویکرد برنامه نویسی ضروری استفاده می کنند. با این رویکرد، حتی اگر بیشتر چالش‌های ما حل شود ، ما همچنان با چالش‌هایی در رابطه با ایمنی thread ها ، حفظ حالت‌های برنامه مواجه هستیم . با این کار، بیایید ببینیم الگوی معماری MVI چیست، چگونه این چالش ها را حل می کند، و چگونه می توان با MVI شروع به کار کرد.

در این آموزش شما را راهنمایی خواهم کرد :

  • معماری MVI چیست
  • MVI چگونه کار می کند
  • مزایا و معایب MVI
  • نحوه ایجاد پروژه با معماری MVI

معماری MVI چیست؟

MVI مخفف Model-View-Intent است. این الگو به تازگی در اندروید معرفی شده است. بر اساس اصل جریان به صورت یک طرفه و براساس چارچوب Cycle.js کار می کند.

بیایید ببینیم که نقش هر یک از اجزای MVI چیست.

  • Model : برخلاف سایر الگوها، در مدل MVI وضعیت رابط کاربری را نشان می دهد. به عنوان مثال، UI ممکن است حالت های مختلفی مانند بارگیری داده، تغییر در رابط کاربری با اقدامات کاربر، خطاها، وضعیت های موقعیت فعلی صفحه نمایش کاربر داشته باشد. هر حالت شبیه به شی در مدل ذخیره می شود.
  • View : View در MVI رابط ما است که می تواند در Activities و Fragment ها پیاده سازی شود. این به معنای داشتن یک ظرف است که می تواند حالت های مختلف مدل را بپذیرد و آن را به عنوان یک رابط کاربری نمایش دهد. آنها از مقاصد قابل مشاهده استفاده می کنند برای پاسخ به اقدامات کاربر.
  • Intent : این Intent آنطور که اندروید قبلاً معرفی کرده بود نیست. نتیجه اقدامات کاربر به عنوان یک مقدار ورودی به Intent ارسال می شود. به نوبه خود، می توانیم بگوییم که مدل هایی را به عنوان ورودی به Intent ها ارسال خواهیم کرد که می توانند آن را از طریق Views بارگیری کنند.

MVI چگونه کار می کند؟

عملی  که کاربر انجام می دهد یک Intent خواهد بود. Intent وضعیتی است که به عنوان ورودی به Model داده می شود و Model آن وضعیت را ذخیره می کند و وضعیت درخواستی را به View ارسال می کند View وضعیت را از Model  بارگیری می کند و برای کاربر نمایش دهد . داده ها همیشه از طریق کاربر منتشر می شوند و از طریق Intent به کاربر ختم می

شوند. این نمی تواند راه دیگری باشد، بنابراین معماری یک جهته نامیده می شود. اگر کاربر یک عمل دیگر انجام دهد، همان چرخه تکرار می شود.

معماری MVI

مزایا و معایب MVI

بیایید ببینیم مزایا و معایب MVI چیست

مزایای MVI

  • حفظ وضعیت دیگر چالشی با این معماری نیست، زیرا عمدتاً بر روی وضعیت ها تمرکز دارد.
  • از آنجایی که یک جهته است ، جریان داده را می توان به راحتی ردیابی و پیش بینی کرد.
  • این امر ایمنی نخ را تضمین می کند زیرا حالت اشیاء تغییرناپذیر هستند.
  • اشکال زدایی در هنگام وقوع خطا آسان است .
  • از آنجایی که هر جزء مسئولیت خود را انجام می دهد، بیشتر جدا شده است.
  • آزمایش برنامه نیز آسان تر خواهد بود زیرا می توانیم منطق تجاری را برای هر وضعیت ترسیم کنیم.

معایب MVI

  • این منجر به تعداد زیادی کد بویلر می شود زیرا ما باید برای هر اقدام کاربر یک حالت یا وضعیتی را حفظ کنیم.
  • همانطور که می دانیم، برای همه حالت ها object (شی) های زیادی ایجاد می کند. این امر مدیریت حافظه برنامه را بسیار سخت می کند.
  • در حالی که مدیریت تغییرات پیکربندی، مدیریت موقعیت های هشدار می تواند چالش برانگیز باشد. برای مثال اگر اینترنت وجود نداشته باشد، یک snack bar را نشان می دهیم ، با تغییر تنظیمات، snack bar را دوباره نشان می دهیم. از نظر قابلیت استفاده ، باید به این موضوع رسیدگی شود.
آشنایی با قابلیت Sliver در برنامه نویسی فلاتر
خواندن این مقاله
قدرت گرفته از افزونه نوشته‌های مرتبط هوشمند

با این پیش زمینه که خوندید ، بیایید یک برنامه کوچک با MVI ایجاد کنیم

ایجاد یک پروژه با معماری MVI

بیایید با راه اندازی پروژه اندروید شروع کنیم.

ایجاد یک پروژه با معماری MVI

ساخت یک پروژه :

  • شروع یک پروژه جدید در اندروید
  • انتخاب یک Empty Activity و بعد برروی دکمه Next بزنید
  • نام برنامه : MVI-Architecture-Android-Beginners
  • نام پکیج : mindorks.framework.mvi
  • زبان: کاتلین
  • برروی دکمه Finish بزنید
  • پروژ تان در حال آماده شدن است

اضافه کردن dependencies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
implementation 'android.arch.lifecycle:extensions:1.1.1'
  
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
  
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
  
implementation 'com.github.bumptech.glide:glide:4.11.0'
  
  
//retrofit
  
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
  
implementation "com.squareup.retrofit2:converter-moshi:2.6.2"
  
  
//Coroutine
  
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6"
  
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6"

ساختار پروژه

برای پروژه ، ما یک نسخه ساده از MVI را پیاده سازی می کنیم و پکیج های ما در پروژه به شکل زیر خواهد بود.

ساختار پروژه mvi

پیاده سازی لایه data (Data Layer)

حال در این قسمت لایه data را راه اندازی می کنیم.

در پکیج داده ، بسته های api ، model و repository خواهیم داشت. ما این بسته ها را ایجاد خواهیم کرد و دررون  هر پکیج کلاس هایی اضافه می کنیم.

بیایید کلاس هایی را اضافه کنیم که از API پشتیبانی می کنند.

ما به مدلی نیاز داریم که براساس پاسخی که  به آن داده می شود تغییر کند. کلاس داده (data class) User.kt را مطابق شکل زیر ایجاد کنید.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mindorks.framework.mvi.data.model
  
import com.squareup.moshi.Json
  
data class User(
    @Json(name = "id")
    val id: Int = 0,
    @Json(name = "name")
    val name: String = "",
    @Json(name = "email")
    val email: String = "",
    @Json(name = "avatar")
    val avatar: String = ""
)

توجه: ما از کلمه کلیدی suspend برای پشتیبانی از کتابخانه Coroutines استفاده کرده ایم تا بتوانیم آن را از یک Coroutine یا تابع تعلیق دیگری فراخوانی کنیم.

در این پروژه از کتابخانه های Kotlin-Coroutines و Flow API استفاده شده است.

یک کلاس ApiService.kt ایجاد کنید که در آن روش های HTTP را برای برقراری ارتباط با API مشخص می کنیم.

1
2
3
4
5
6
7
8
9
package com.mindorks.framework.mvi.data.api
  
import com.mindorks.framework.mvi.data.model.User
  
interface ApiHelper {
  
    suspend fun getUsers(): List<User>
  
}

اکنون سازنده retrofit را اضافه کنید که URL نقطه پایانی را می سازد و خدمات REST را مصرف کنند.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.mindorks.framework.mvi.data.api
  
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
  
object RetrofitBuilder {
  
    private const val BASE_URL = "https://5e510330f2c0d300147c034c.mockapi.io/"
  
    private fun getRetrofit() = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(MoshiConverterFactory.create())
        .build()
  
  
    val apiService: ApiService = getRetrofit().create(ApiService::class.java)
  
}

اکنون باید این رابط را برای واکشی <List<Users> پیاده سازی کنیم، ApiHelperImpl.kt را ایجاد کنیم.

1
2
3
4
5
6
7
8
9
10
package com.mindorks.framework.mvi.data.api
  
import com.mindorks.framework.mvi.data.model.User
  
class ApiHelperImpl(private val apiService: ApiService) : ApiHelper {
  
    override suspend fun getUsers(): List<User> {
        return apiService.getUsers()
    }
}

اکنون ما آماده هستیم تا با سرویس های restful در لایه داده خود ارتباط برقرار کنیم.

برای درخواست داده ها به یک repository نیاز داریم. در مورد ما، ما متد getUsers را از Activity از طریق ViewModel فراخوانی می کنیم تا لیست کاربران را دریافت کنیم.حالا کلاسی به نام MainRepository.kt را ایجاد کنید.

1
2
3
4
5
6
7
8
9
10
package com.mindorks.framework.mvi.data.repository
  
import com.mindorks.framework.mvi.data.api.ApiHelper
  
  
class MainRepository(private val apiHelper: ApiHelper) {
  
    suspend fun getUsers() = apiHelper.getUsers()
  
}

بنابراین لایه داده ما آماده است. اکنون که به بخش رابط کاربری می‌رسیم، به یک adapter برای recyclerview ، Intent برای ذخیره اقدامات کاربر، main activity ما، MainViewModel در viewModel، حالت View که در آن حالت‌های مختلفی را تعریف کرده‌ایم که باید داده‌ها را در viewها بارگذاری کنیم، نیاز داریم.

MainAdapter را در adapter package ایجاد کنید

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.mindorks.framework.mvi.ui.main.adapter
  
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.mindorks.framework.mvi.R
import com.mindorks.framework.mvi.data.model.User
import kotlinx.android.synthetic.main.item_layout.view.*
  
class MainAdapter(
    private val users: ArrayList<User>
) : RecyclerView.Adapter<MainAdapter.DataViewHolder>() {
  
    class DataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(user: User) {
            itemView.textViewUserName.text = user.name
            itemView.textViewUserEmail.text = user.email
            Glide.with(itemView.imageViewAvatar.context)
                .load(user.avatar)
                .into(itemView.imageViewAvatar)
        }
    }
  
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        DataViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.item_layout, parent,
                false
            )
        )
  
    override fun getItemCount(): Int = users.size
  
    override fun onBindViewHolder(holder: DataViewHolder, position: Int) =
        holder.bind(users[position])
  
    fun addData(list: List<User>) {
        users.addAll(list)
    }
  
}

اکنون MainState.kt را در بسته viewstate اضافه می کنیم. این مهمترین بخش MVI است. در این کلاس حالت های Idle, loading, users, error را تعریف می کنیم. هر حالت  وضیعت را می توان با intent در view بارگذاری کرد.

1
2
3
4
5
6
7
8
9
10
11
12
package com.mindorks.framework.mvi.ui.main.viewstate
  
import com.mindorks.framework.mvi.data.model.User
  
sealed class MainState {
  
    object Idle : MainState()
    object Loading : MainState()
    data class Users(val user: List<User>) : MainState()
    data class Error(val error: String?) : MainState()
  
}

یک کلاس ViewModel ایجاد کنید :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.mindorks.framework.mvi.ui.main.viewmodel
  
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mindorks.framework.mvi.data.repository.MainRepository
import com.mindorks.framework.mvi.ui.main.intent.MainIntent
import com.mindorks.framework.mvi.ui.main.viewstate.MainState
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.launch
  
@ExperimentalCoroutinesApi
class MainViewModel(
    private val repository: MainRepository
) : ViewModel() {
  
    val userIntent = Channel<MainIntent>(Channel.UNLIMITED)
    private val _state = MutableStateFlow<MainState>(MainState.Idle)
    val state: StateFlow<MainState>
        get() = _state
  
    init {
        handleIntent()
    }
  
    private fun handleIntent() {
        viewModelScope.launch {
            userIntent.consumeAsFlow().collect {
                when (it) {
                    is MainIntent.FetchUser -> fetchUser()
                }
            }
        }
    }
  
    private fun fetchUser() {
        viewModelScope.launch {
            _state.value = MainState.Loading
            _state.value = try {
                MainState.Users(repository.getUsers())
            } catch (e: Exception) {
                MainState.Error(e.localizedMessage)
            }
        }
    }
}

در اینجا در ViewModel، ما در حال مشاهده userIntent برای انجام عمل بر روی آن هستیم.

چرخه حیات lifecycle اکتیویتی در اندروید
خواندن این مقاله
قدرت گرفته از افزونه نوشته‌های مرتبط هوشمند

و بر اساس پاسخ لایه داده، داخل متد fetchUser حالت را تغییر می دهیم. و این حالت در Main Activity مشاهده می شود.

و حالا ViewModelFactory را تحت پکیج util راه اندازی کنیم.

در این کلاس ما از viewModel خود یک نمونه می سازیم  و نمونه ViewModel را برمی گردانیم.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.mindorks.framework.mvi.util
  
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.mindorks.framework.mvi.data.api.ApiHelper
import com.mindorks.framework.mvi.data.repository.MainRepository
import com.mindorks.framework.mvi.ui.main.viewmodel.MainViewModel
  
class ViewModelFactory(private val apiHelper: ApiHelper) : ViewModelProvider.Factory {
  
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
            return MainViewModel(MainRepository(apiHelper)) as T
        }
        throw IllegalArgumentException("Unknown class name")
    }
  
}

حالا XML layout را طراحی می کنیم.

در پوشه layout، داخل پوشه activity_main.xml کد زیر را می نویسیم :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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=".ui.main.view.MainActivity">
  
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />
  
    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:visibility="gone"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
    <Button
        android:id="@+id/buttonFetchUser"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/fetch_user"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

به کلاس MainAcitvity.kt تان بروید و پکیج under view را اضافه کنید . این اکتیویتی است که ورودی را از کاربر دریافت می کند، بر اساس این MVI وضعیت های ذکر شده در viewModel را بررسی می کند و حالت خاص را در view بارگذاری می کند.

بیایید ببینیم MainActivity چگونه از درخواست داده ها، مدیریت وضعیت ها مراقبت می کند.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package com.mindorks.framework.mvi.ui.main.view
  
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProviders
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.mindorks.framework.mvi.R
import com.mindorks.framework.mvi.data.api.ApiHelperImpl
import com.mindorks.framework.mvi.data.api.RetrofitBuilder
import com.mindorks.framework.mvi.data.model.User
import com.mindorks.framework.mvi.util.ViewModelFactory
import com.mindorks.framework.mvi.ui.main.adapter.MainAdapter
import com.mindorks.framework.mvi.ui.main.intent.MainIntent
import com.mindorks.framework.mvi.ui.main.viewmodel.MainViewModel
import com.mindorks.framework.mvi.ui.main.viewstate.MainState
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
  
@ExperimentalCoroutinesApi
class MainActivity : AppCompatActivity() {
  
    private lateinit var mainViewModel: MainViewModel
    private var adapter = MainAdapter(arrayListOf())
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setupUI()
        setupViewModel()
        observeViewModel()
        setupClicks()
    }
  
    private fun setupClicks() {
        buttonFetchUser.setOnClickListener {
            lifecycleScope.launch {
                mainViewModel.userIntent.send(MainIntent.FetchUser)
            }
        }
    }
  
  
    private fun setupUI() {
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.run {
            addItemDecoration(
                DividerItemDecoration(
                    recyclerView.context,
                    (recyclerView.layoutManager as LinearLayoutManager).orientation
                )
            )
        }
        recyclerView.adapter = adapter
    }
  
  
    private fun setupViewModel() {
        mainViewModel = ViewModelProviders.of(
            this,
            ViewModelFactory(
                ApiHelperImpl(
                    RetrofitBuilder.apiService
                )
            )
        ).get(MainViewModel::class.java)
    }
  
    private fun observeViewModel() {
        lifecycleScope.launch {
            mainViewModel.state.collect {
                when (it) {
                    is MainState.Idle -> {
  
                    }
                    is MainState.Loading -> {
                        buttonFetchUser.visibility = View.GONE
                        progressBar.visibility = View.VISIBLE
                    }
  
                    is MainState.Users -> {
                        progressBar.visibility = View.GONE
                        buttonFetchUser.visibility = View.GONE
                        renderList(it.user)
                    }
                    is MainState.Error -> {
                        progressBar.visibility = View.GONE
                        buttonFetchUser.visibility = View.VISIBLE
                        Toast.makeText(this@MainActivity, it.error, Toast.LENGTH_LONG).show()
                    }
                }
            }
        }
    }
  
    private fun renderList(users: List<User>) {
        recyclerView.visibility = View.VISIBLE
        users.let { listOfUsers -> listOfUsers.let { adapter.addData(it) } }
        adapter.notifyDataSetChanged()
    }
}

در اینجا، ما قصد واکشی (fetch) داده ها را با کلیک روی دکمه (User Action) ارسال می کنیم.

همچنین، ما در حال مشاهده حالت ViewModel برای تغییرات وضعیت هستیم. و با استفاده از شرط “when” وضعیت هدف پاسخ را مقایسه کرده و حالات مربوطه را بارگذاری می کنیم.

در نهایت مجوز اینترنت را به پروژه خود اضافه کنید. موارد زیر را در فایل AndroidManifest.xml اضافه کنید:

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

اکنون پروژه را بسازید و برنامه را روی دستگاه اجرا کنید. باید داده ها را در رابط کاربری را بارگذاری کند.

برچسب ها: معماری mvi
قبلی 25 برنامه Remote Desktop برای اندروید
بعدی کاربرد اصول SOLID در طراحی برنامه‌نویسی اندروید

دیدگاهتان را بنویسید لغو پاسخ

جستجو برای:
دسته‌ها
  • GoLang
  • jetpack compose
  • PHP
  • اپلیکیشن
  • امنیت
  • اندروید
  • اوپن سورس
  • برنامه نویسی
  • برنامه نویسی iOS
  • برنامه نویسی react native
  • پادکست صوتی
  • تکنولوژی
  • جاوا
  • طراح رابط کاربری
  • طراحی رابط کاربری
  • طراحی وب
  • عمومی
  • فریلنسر
  • فلاتر
  • فناوری
  • کاتلین
  • کتاب های آموزشی
  • کسب و کار
  • هوش مصنوعی
  • وردپرس
برچسب‌ها
admob coroutine dagger dagger-hilt jetpack nft rxandroid rxjava spring swift ارز دیجیتال امنیت در اندروید دارت فایربیس فوشیا مصاحبه کاری معماری mvi نقشه راه برنامه نویسی کاتلین گوگل

آکادمی آموزنگار، جایی برای آغاز یک سفر شگفت‌انگیز در دنیای برنامه‌نویسی است. آموزنگار تلاش می‌کند تا هر فردی را از هر سطحی از زندگی و تجربه به دنیای جذاب برنامه‌نویسی وارد کند.

دسترسی سریع
  • درباره ما
  • تماس با ما
  • حریم خصوصی
  • سوالات متداول
نمادها
شبکه های اجتماعی
Facebook Twitter Youtube icon--white Whatsapp

تهران، میدان ولی عصر،خیابان شهیدان سازش،کوچه سوم پلاک 5 طبقه سوم واحد 31

021-88945907

تمامی حقوق برای آکادمی آموزنگار محفوظ می باشد

اشتراک گذاری در شبکه های اجتماعی
ارسال به ایمیل
https://amooznegar.com/?p=11321
  • Afghanistan (+93)
  • Albania (+355)
  • Algeria (+213)
  • American Samoa (+1)
  • Andorra (+376)
  • Angola (+244)
  • Anguilla (+1)
  • Antigua (+1)
  • Argentina (+54)
  • Armenia (+374)
  • Aruba (+297)
  • Australia (+61)
  • Austria (+43)
  • Azerbaijan (+994)
  • Bahrain (+973)
  • Bangladesh (+880)
  • Barbados (+1)
  • Belarus (+375)
  • Belgium (+32)
  • Belize (+501)
  • Benin (+229)
  • Bermuda (+1)
  • Bhutan (+975)
  • Bolivia (+591)
  • Bonaire, Sint Eustatius and Saba (+599)
  • Bosnia and Herzegovina (+387)
  • Botswana (+267)
  • Brazil (+55)
  • British Indian Ocean Territory (+246)
  • British Virgin Islands (+1)
  • Brunei (+673)
  • Bulgaria (+359)
  • Burkina Faso (+226)
  • Burundi (+257)
  • Cambodia (+855)
  • Cameroon (+237)
  • Canada (+1)
  • Cape Verde (+238)
  • Cayman Islands (+1)
  • Central African Republic (+236)
  • Chad (+235)
  • Chile (+56)
  • China (+86)
  • Colombia (+57)
  • Comoros (+269)
  • Cook Islands (+682)
  • Côte d'Ivoire (+225)
  • Costa Rica (+506)
  • Croatia (+385)
  • Cuba (+53)
  • Curaçao (+599)
  • Cyprus (+357)
  • Czech Republic (+420)
  • Democratic Republic of the Congo (+243)
  • Denmark (+45)
  • Djibouti (+253)
  • Dominica (+1)
  • Dominican Republic (+1)
  • Ecuador (+593)
  • Egypt (+20)
  • El Salvador (+503)
  • Equatorial Guinea (+240)
  • Eritrea (+291)
  • Estonia (+372)
  • Ethiopia (+251)
  • Falkland Islands (+500)
  • Faroe Islands (+298)
  • Federated States of Micronesia (+691)
  • Fiji (+679)
  • Finland (+358)
  • France (+33)
  • French Guiana (+594)
  • French Polynesia (+689)
  • Gabon (+241)
  • Georgia (+995)
  • Germany (+49)
  • Ghana (+233)
  • Gibraltar (+350)
  • Greece (+30)
  • Greenland (+299)
  • Grenada (+1)
  • Guadeloupe (+590)
  • Guam (+1)
  • Guatemala (+502)
  • Guernsey (+44)
  • Guinea (+224)
  • Guinea-Bissau (+245)
  • Guyana (+592)
  • Haiti (+509)
  • Honduras (+504)
  • Hong Kong (+852)
  • Hungary (+36)
  • Iceland (+354)
  • India (+91)
  • Indonesia (+62)
  • Iran (+98)
  • Iraq (+964)
  • Ireland (+353)
  • Isle Of Man (+44)
  • Israel (+972)
  • Italy (+39)
  • Jamaica (+1)
  • Japan (+81)
  • Jersey (+44)
  • Jordan (+962)
  • Kazakhstan (+7)
  • Kenya (+254)
  • Kiribati (+686)
  • Kuwait (+965)
  • Kyrgyzstan (+996)
  • Laos (+856)
  • Latvia (+371)
  • Lebanon (+961)
  • Lesotho (+266)
  • Liberia (+231)
  • Libya (+218)
  • Liechtenstein (+423)
  • Lithuania (+370)
  • Luxembourg (+352)
  • Macau (+853)
  • Macedonia (+389)
  • Madagascar (+261)
  • Malawi (+265)
  • Malaysia (+60)
  • Maldives (+960)
  • Mali (+223)
  • Malta (+356)
  • Marshall Islands (+692)
  • Martinique (+596)
  • Mauritania (+222)
  • Mauritius (+230)
  • Mayotte (+262)
  • Mexico (+52)
  • Moldova (+373)
  • Monaco (+377)
  • Mongolia (+976)
  • Montenegro (+382)
  • Montserrat (+1)
  • Morocco (+212)
  • Mozambique (+258)
  • Myanmar (+95)
  • Namibia (+264)
  • Nauru (+674)
  • Nepal (+977)
  • Netherlands (+31)
  • New Caledonia (+687)
  • New Zealand (+64)
  • Nicaragua (+505)
  • Niger (+227)
  • Nigeria (+234)
  • Niue (+683)
  • Norfolk Island (+672)
  • North Korea (+850)
  • Northern Mariana Islands (+1)
  • Norway (+47)
  • Oman (+968)
  • Pakistan (+92)
  • Palau (+680)
  • Palestine (+970)
  • Panama (+507)
  • Papua New Guinea (+675)
  • Paraguay (+595)
  • Peru (+51)
  • Philippines (+63)
  • Poland (+48)
  • Portugal (+351)
  • Puerto Rico (+1)
  • Qatar (+974)
  • Republic of the Congo (+242)
  • Romania (+40)
  • Reunion (+262)
  • Russia (+7)
  • Rwanda (+250)
  • Saint Helena (+290)
  • Saint Kitts and Nevis (+1)
  • Saint Pierre and Miquelon (+508)
  • Saint Vincent and the Grenadines (+1)
  • Samoa (+685)
  • San Marino (+378)
  • Sao Tome and Principe (+239)
  • Saudi Arabia (+966)
  • Senegal (+221)
  • Serbia (+381)
  • Seychelles (+248)
  • Sierra Leone (+232)
  • Singapore (+65)
  • Sint Maarten (+1)
  • Slovakia (+421)
  • Slovenia (+386)
  • Solomon Islands (+677)
  • Somalia (+252)
  • South Africa (+27)
  • South Korea (+82)
  • South Sudan (+211)
  • Spain (+34)
  • Sri Lanka (+94)
  • St. Lucia (+1)
  • Sudan (+249)
  • Suriname (+597)
  • Swaziland (+268)
  • Sweden (+46)
  • Switzerland (+41)
  • Syria (+963)
  • Taiwan (+886)
  • Tajikistan (+992)
  • Tanzania (+255)
  • Thailand (+66)
  • The Bahamas (+1)
  • The Gambia (+220)
  • Timor-Leste (+670)
  • Togo (+228)
  • Tokelau (+690)
  • Tonga (+676)
  • Trinidad and Tobago (+1)
  • Tunisia (+216)
  • Turkey (+90)
  • Turkmenistan (+993)
  • Turks and Caicos Islands (+1)
  • Tuvalu (+688)
  • U.S. Virgin Islands (+1)
  • Uganda (+256)
  • Ukraine (+380)
  • United Arab Emirates (+971)
  • United Kingdom (+44)
  • United States (+1)
  • Uruguay (+598)
  • Uzbekistan (+998)
  • Vanuatu (+678)
  • Venezuela (+58)
  • Vietnam (+84)
  • Wallis and Futuna (+681)
  • Western Sahara (+212)
  • Yemen (+967)
  • Zambia (+260)
  • Zimbabwe (+263)
مرورگر شما از HTML5 پشتیبانی نمی کند.

سوالی دارید؟ از ما بپرسید، کارشناسان ما در اسرع وقت با شما تماس می گیرند.

آموزنگار

آکادمی آموزنگار

  • 021-88945907
  • شنبه تا چهارشنبه از ساعت 8 تا 17
  • info@amooznegar.com
ورود
استفاده از شماره تلفن
استفاده از آدرس ایمیل
آیا هنوز عضو نشده اید؟ ثبت نام کنید
بازیابی رمز عبور
استفاده از شماره تلفن
استفاده از آدرس ایمیل
ثبت نام
استفاده از شماره تلفن
استفاده از ایمیل
قبلا عضو شده اید؟ ورود به سیستم
محافظت توسط