, , ,

آموزش اتصال به سرور با Ktor در Android

آموزش کامل: Ktor + Hilt در Android

ویدیوی آموزش در ادامه آموزش های برنامه نویسی اندروید می باشد. اما محتوی متنی کاملا مستقل و قابل استفاده در پروژه های جدید می باشد.

مرحله ۱: اضافه کردن وابستگی‌ها

در build.gradle.kts (app-level):

plugins {
    // بقیه پلاگین‌ها
    id("com.google.dagger.hilt.android")
    id("com.google.devtools.ksp")
    kotlin("plugin.serialization") version "KOTLIN_VERSION"
}

dependencies {
    // Ktor
    val ktor_version = "3.5.2"
    implementation("io.ktor:ktor-client-core:$ktor_version")
    implementation("io.ktor:ktor-client-android:$ktor_version")
    implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
    implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
    implementation("io.ktor:ktor-client-logging:$ktor_version")

    // Kotlinx Serialization
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")

    // Hilt
    implementation("com.google.dagger:hilt-android:2.60.1")
    ksp("com.google.dagger:hilt-compiler:2.60.1")
}

در build.gradle.kts (project-level):

plugins {
    id("com.google.dagger.hilt.android") version "2.60.1" apply false
    id("com.google.devtools.ksp") version "KOTLIN_VERSION" apply false
}

مرحله ۲: Application Class

@HiltAndroidApp
class MyApplication : Application()

در AndroidManifest.xml:

<!--  افزودن دسترسی به اینترنت  -->    
<uses-permission android:name="android.permission.INTERNET"/>
<!--   افزودن اپلیکیشن -->
<application
    android:name=".MyApplication"
    ...>

مرحله ۳: NetworkModule

// di/NetworkModule.kt
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Provides
    @Singleton
    fun provideHttpClient(): HttpClient {
        return HttpClient(Android) {
            install(ContentNegotiation) {
                json(Json {
                    ignoreUnknownKeys = true
                    isLenient = true
                })
            }
            install(Logging) {
                level = LogLevel.BODY
            }
        }
    }
}

مرحله ۴: Data Model

مدل User برای شبکه

// data/remote/dto/UserDto.kt
@Serializable
data class UserDto(
    val id: Int,
    @SerialName("first_name") val firstName: String,
    @SerialName("last_name") val lastName: String,
    val debt: Long = 0
)

@Serializable
data class UsersResponse(
    val users: List<UserDto>
)

//یوزر دیتابیس ما این شکلیه
@Entity
//data class User(
//    @PrimaryKey(autoGenerate = true)
//    val uid: Int=0,
//    @ColumnInfo(name = "first_name")
//    val firstName: String,
//    @ColumnInfo(name = "last_name")
//    val lastName: String,
//    @ColumnInfo(name = "debt")
//    val debt: Long=0
//)

و یه extension برای تبدیل به Entity:

fun UserDto.toEntity(): User = User(
    firstName = firstName,
    lastName = lastName,
    debt = debt
)

مرحله ۵: Repository

// data/repository/UserRepository.kt
class UserRepository @Inject constructor(
    private val client: HttpClient
) {
    suspend fun getUsers(): Result<List<UserDto>> = runCatching {
        val response: UsersResponse = client.get("https://esfandune.ir/FAKE_API_FOR_TUTORIAL/users.json")
            .body<UsersResponse>()
        response.users
    }
}

مرحله ۶: ViewModel

// ui/UserViewModel.kt
@HiltViewModel
class UserViewModel @Inject constructor(
    private val repository: UserRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow(UserUiState())
    val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()

    init {
        fetchUsers()
    }

    fun fetchUsers() {
        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true, error = null) }

            repository.getUsers()
                .onSuccess { users ->
                    _uiState.update { it.copy(isLoading = false, users = users) }
                }
                .onFailure { e ->
                    _uiState.update { it.copy(isLoading = false, error = e.message) }
                }
        }
    }
}

data class UserUiState(
    val isLoading: Boolean = false,
    val users: List<UserDto> = emptyList(),
    val error: String? = null
)

مرحله ۷: UI

@Composable
fun UsersScreen(
    viewModel: UserViewModel = hiltViewModel()
) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()

    Box(modifier = Modifier.fillMaxSize()) {
        when {
            state.isLoading -> CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
            state.error != null -> Text(
                text = "خطا: ${state.error}",
                modifier = Modifier.align(Alignment.Center)
            )
            else -> LazyColumn {
                items(state.users) { user ->
                    Text("${user.firstName} ${user.lastName}")
                }
            }
        }
    }
}

فراموش نکن Activity رو با @AndroidEntryPoint علامت‌گذاری کنی:

@AndroidEntryPoint
class MainActivity : ComponentActivity() { ... }

فایل JSON برای سرور

{
  "users": [
    { "id": 1, "first_name": "علی", "last_name": "محمدی", "debt": 150000 },
    { "id": 2, "first_name": "سارا", "last_name": "احمدی", "debt": 0 },
    { "id": 3, "first_name": "رضا", "last_name": "کریمی", "debt": 320000 },
    { "id": 4, "first_name": "مریم", "last_name": "حسینی", "debt": 75000 },
    { "id": 5, "first_name": "امیر", "last_name": "رضایی", "debt": 500000 }
  ]
}

جریان کلی:
API ← HttpClient (Ktor) ← Repository ← ViewModel (Hilt) ← Compose UI

Comments

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *