Android studio

HTTP-запрос (GET и POST) в Kotlin для Android

Добавьте зависимости

В файле build.gradle (Module: app) добавьте следующие зависимости:

dependencies {

    implementation 'com.squareup.retrofit2:retrofit:2.9.0'

    implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // Для работы с JSON

    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'

    implementation 'com.google.code.gson:gson:2.10.1'

}

Создайте модель данных

package com.coop.myapplication1.retrofit

data class Product(

    val id: Int,

    val device_id: String,

    val name: String,

    val last_seen: String,

    val is_active: Boolean

)

Определите API-интерфейс

Создайте интерфейс для описания запросов:

package com.coop.myapplication1.retrofit

import retrofit2.http.GET

import retrofit2.http.Path

interface ProductApi {

    @GET("device/{id}")

    suspend fun getProductById(@Path("id") id: Int): Product

}

В Main добавим следующий код:

val retrofit = Retrofit.Builder().baseUrl("http://192.168.50.70:8000/api/")

            .addConverterFactory(GsonConverterFactory.create()).build()

        val productApi = retrofit.create(ProductApi::class.java)

        val btn = findViewById<Button>(R.id.button2)

        var result = findViewById<TextView>(R.id.textView2)

        btn.setOnClickListener {

            CoroutineScope(Dispatchers.IO).launch {

                try {

                    val product = productApi.getProductById(1)

                    runOnUiThread {

                        result.text = product?.name ?: "Product not found"

                    }

                } catch (e: Exception) {

                    runOnUiThread {

                        result.text = "Error: ${e.message}"

                    }

                }

            }

        }

Для современных версий Android (API 23+) при использовании HTTP (а не HTTPS) может потребоваться дополнительная настройка для разрешения небезопасных соединений, так как ваш код использует http://192.168.50.70:8000/api/. Для этого нужно:

Создать файл res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">192.168.50.70</domain>
    </domain-config>
</network-security-config>

Итоговый манифест с этой настройкой будет выглядеть так:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication1"
        android:networkSecurityConfig="@xml/network_security_config"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

View Binding


Как подключить View Binding
В файле build.gradle (уровень модуля) добавьте:
groovy

android {
    ...
    buildFeatures {
        viewBinding = true
    }
}

В MainActivity вы подключаете View Binding так:
kotlin

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.myapp.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Доступ к элементам
        binding.textView.text = "View Binding работает!"
        binding.button.setOnClickListener {
            binding.textView.text = "Кнопка нажата!"
        }
    }
}

SharedPreferences


import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    private val PREFS_NAME = "MyPrefs"
    private val KEY_USERNAME = "username"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Получение SharedPreferences
        val prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
        val editor = prefs.edit()

        // Сохранение данных
        editor.putString(KEY_USERNAME, "Иван")
        editor.apply() // Асинхронно сохраняет данные

        // Чтение данных
        val username = prefs.getString(KEY_USERNAME, "Гость") // "Гость" — значение по умолчанию
        println("Имя пользователя: $username")
    }
}

При наличии конфликта классов:
configurations.all {
    exclude(group = "com.intellij", module = "annotations")
}


Room Database

gradle

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    id("kotlin-kapt")
}

dependencies {
    kapt(libs.androidx.room.compiler)
    implementation(libs.androidx.room.ktx)
   }

Модель данных (Entity):


import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "tasks")
data class Task(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val title: String,
    val isCompleted: Boolean
)

DAO (Data Access Object):

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query

@Dao
interface TaskDao {
    @Insert
    suspend fun insert(task: Task)

    @Query("SELECT * FROM tasks")
    suspend fun getAllTasks(): List<Task>

    @Query("DELETE FROM tasks WHERE id = :taskId")
    suspend fun deleteById(taskId: Int)
}

База данных:

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(entities = [Task::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun taskDao(): TaskDao
}

Main

 var db = Room.databaseBuilder(
        applicationContext,
        AppDatabase::class.java,
        "task_database"
    ).build()

    btn.setOnClickListener {
        CoroutineScope(Dispatchers.IO).launch {
            val taskDao = db.taskDao()

            // Добавление задачи
            taskDao.insert(Task(title = "Купить молоко", isCompleted = false))

            // Получение всех задач
            val tasks = taskDao.getAllTasks()

            // Переход на главный поток
            launch(Dispatchers.Main) {
                res.text = tasks.joinToString("\n") { it.toString() }
            }
        }
    }

Работа с JSON


implementation 'com.google.code.gson:gson:2.10.1'

Пример JSON

json

{
    "id": 1,
    "name": "Иван",
    "age": 25
}

Модель данных:


kotlin

data class User(
    val id: Int,
    val name: String,
    val age: Int
)

Парсинг:


kotlin

import com.google.gson.Gson

val json = """{"id": 1, "name": "Иван", "age": 25}"""
val gson = Gson()
val user = gson.fromJson(json, User::class.java)
println("Пользователь: ${user.name}, возраст: ${user.age}")

// Сериализация обратно в JSON
val jsonString = gson.toJson(user)
println("JSON: $jsonString")

Как установить buildozer для kivy

sudo apt update sudo apt install python3 python3-pip pip3 install --user --upgrade buildozer sudo apt install -y git zip unzip openjdk-17-jd...