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

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

ارسال مجدد کد یکبار مصرف (00:60)
  • 02171058559
  • 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 را دوباره نشان می دهیم. از نظر قابلیت استفاده ، باید به این موضوع رسیدگی شود.
آموزش جامع کار با پکیج Audio Waveforms در فلاتر؛ ضبط و نمایش موج صدا به‌صورت زنده
خواندن این مقاله
قدرت گرفته از افزونه نوشته‌های مرتبط هوشمند

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

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

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

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

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

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

اضافه کردن dependencies


 
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 را مطابق شکل زیر ایجاد کنید.

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 مشخص می کنیم.

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 را مصرف کنند.

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 را ایجاد کنیم.

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&lt;User> {
        return apiService.getUsers()
    }
}

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

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

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 ایجاد کنید

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&lt;User>
) : RecyclerView.Adapter&lt;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&lt;User>) {
        users.addAll(list)
    }
 
}

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

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&lt;User>) : MainState()
    data class Error(val error: String?) : MainState()
 
}

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

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&lt;MainIntent>(Channel.UNLIMITED)
    private val _state = MutableStateFlow&lt;MainState>(MainState.Idle)
    val state: StateFlow&lt;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 برای انجام عمل بر روی آن هستیم.

اصول Coroutines | Coroutines برای تازه کارها
خواندن این مقاله
قدرت گرفته از افزونه نوشته‌های مرتبط هوشمند

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

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

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

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 &lt;T : ViewModel?> create(modelClass: Class&lt;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 کد زیر را می نویسیم :

&lt;?xml version="1.0" encoding="utf-8"?>
&lt;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">
 
    &lt;androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />
 
    &lt;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" />
 
    &lt;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" />
 
&lt;/androidx.constraintlayout.widget.ConstraintLayout>

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

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

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&lt;User>) {
        recyclerView.visibility = View.VISIBLE
        users.let { listOfUsers -> listOfUsers.let { adapter.addData(it) } }
        adapter.notifyDataSetChanged()
    }
}

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

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

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

	&lt;uses-permission android:name="android.permission.INTERNET"/>

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

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

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

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

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

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

تهران، ازگل ، مجتمع تجاری الماس ایرانیان، پارک علم و فناوری فردا

021-71058559

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

ورود
با شماره موبایل
با آدرس ایمیل
آیا هنوز عضو نشده اید؟ اکنون ثبت نام کنید
بازنشانی رمزعبور
با شماره موبایل
با آدرس ایمیل
ثبت نام
با شماره موبایل
با آدرس ایمیل
قبلا عضو شده اید؟ اکنون وارد شوید
محافظت شده توسط