Мобильная разработка: приложение для Android

Структура Android-приложения, жизненный цикл активности, разметка и Compose, локальная база Room, запросы через Retrofit, разрешения и публикация — что нужно для курсовой.

Мобильное приложение — выигрышная тема для диплома: результат наглядно демонстрируется на защите. Разберём минимальный набор, которого хватает для полноценной работы.

Структура проекта

app/
├── src/main/
│   ├── java/com/example/app/
│   │   ├── MainActivity.kt          — точка входа
│   │   ├── data/                    — модели, база, сеть
│   │   ├── ui/                      — экраны и компоненты
│   │   └── viewmodel/               — логика представления
│   ├── res/
│   │   ├── layout/                  — XML-разметка (если не Compose)
│   │   ├── values/strings.xml       — все тексты, обязательно отсюда
│   │   └── drawable/                — иконки и изображения
│   └── AndroidManifest.xml          — разрешения, активности
└── build.gradle.kts                 — зависимости
Тексты держат в strings.xml, а не в коде: это требование проверяется линтером и упрощает добавление второго языка. В отчёте по курсовой на это обращают внимание как на показатель качества кода.

Жизненный цикл активности

МетодКогда вызываетсяЧто делать
onCreateСоздание экранаИнициализация, привязка разметки
onStartЭкран становится видимымНачать обновления интерфейса
onResumeЭкран получает фокусЗапустить анимации, датчики, камеру
onPauseТеряет фокусСохранить данные, остановить датчики
onStopСтановится невидимымОсвободить тяжёлые ресурсы
onDestroyУничтожениеФинальная очистка

Ключевой момент, который спрашивают на защите: при повороте экрана активность уничтожается и создаётся заново. Данные, лежащие в её полях, теряются — поэтому состояние держат в ViewModel, которая переживает пересоздание.

class MainActivity : AppCompatActivity() {

    private val viewModel: TaskViewModel by viewModels()

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

        val adapter = TaskAdapter { task -> viewModel.toggleDone(task) }
        findViewById<RecyclerView>(R.id.list).adapter = adapter

        // Подписка на данные: обновится сама при изменении
        viewModel.tasks.observe(this) { tasks ->
            adapter.submitList(tasks)
            findViewById<TextView>(R.id.empty).isVisible = tasks.isEmpty()
        }

        findViewById<Button>(R.id.add).setOnClickListener {
            val text = findViewById<EditText>(R.id.input).text.toString().trim()
            if (text.isEmpty()) {
                Toast.makeText(this, R.string.error_empty, Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            viewModel.add(text)
        }
    }
}

Локальная база данных Room

@Entity(tableName = "tasks")
data class Task(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val title: String,
    val isDone: Boolean = false,
    val createdAt: Long = System.currentTimeMillis()
)

@Dao
interface TaskDao {
    @Query("SELECT * FROM tasks ORDER BY isDone, createdAt DESC")
    fun observeAll(): Flow<List<Task>>

    @Insert suspend fun insert(task: Task): Long
    @Update suspend fun update(task: Task)
    @Delete suspend fun delete(task: Task)

    @Query("SELECT COUNT(*) FROM tasks WHERE isDone = 0")
    suspend fun countPending(): Int
}

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

Room проверяет SQL-запросы на этапе компиляции: опечатка в имени столбца не даст собрать проект. Это существенное преимущество перед прямой работой с SQLite, и его стоит упомянуть в обосновании выбора технологии.

Работа с сетью

interface ApiService {
    @GET("products")
    suspend fun getProducts(@Query("page") page: Int = 1): List<ProductDto>

    @POST("orders")
    suspend fun createOrder(@Body order: OrderRequest): OrderResponse
}

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/v1/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

// Запрос в ViewModel: не блокирует интерфейс
class ProductViewModel : ViewModel() {
    private val _state = MutableStateFlow<UiState>(UiState.Loading)
    val state: StateFlow<UiState> = _state

    fun load() = viewModelScope.launch {
        _state.value = UiState.Loading
        try {
            val items = api.getProducts()
            _state.value = UiState.Success(items)
        } catch (e: IOException) {
            _state.value = UiState.Error("Нет соединения")
        } catch (e: HttpException) {
            _state.value = UiState.Error("Ошибка сервера: ${e.code()}")
        }
    }
}
Сетевой запрос в главном потоке вызывает исключение NetworkOnMainThreadException — Android запрещает это принудительно. Все длительные операции выполняются в корутинах, и обработка трёх состояний (загрузка, успех, ошибка) обязательна для приложения, которое не выглядит недоделанным.

Jetpack Compose

@Composable
fun TaskScreen(viewModel: TaskViewModel = viewModel()) {
    val tasks by viewModel.tasks.collectAsState()
    var input by remember { mutableStateOf("") }

    Column(modifier = Modifier.padding(16.dp)) {
        Row {
            OutlinedTextField(
                value = input,
                onValueChange = { input = it },
                label = { Text(stringResource(R.string.new_task)) },
                modifier = Modifier.weight(1f)
            )
            Button(
                onClick = { viewModel.add(input); input = "" },
                enabled = input.isNotBlank()
            ) { Text(stringResource(R.string.add)) }
        }

        LazyColumn {
            items(tasks, key = { it.id }) { task ->
                TaskRow(task, onToggle = { viewModel.toggleDone(task) })
            }
        }
    }
}

Compose описывает интерфейс кодом вместо XML и перерисовывает только изменившиеся части. Для новой курсовой он предпочтительнее: меньше файлов, меньше шаблонного кода, и это текущий рекомендуемый подход Google.

Разрешения

// В манифесте
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

// Опасные разрешения запрашивают во время работы
private val requestPermission = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { granted ->
    if (granted) openCamera()
    else showExplanation()
}

requestPermission.launch(Manifest.permission.CAMERA)

Что включить в диплом

  1. Обоснование выбора платформы и технологий: почему Kotlin, почему Room, почему Compose.
  2. Диаграмма архитектуры: слои данных, представления, бизнес-логики.
  3. Диаграмма классов и схема локальной базы данных.
  4. Диаграмма вариантов использования и схема навигации между экранами.
  5. Скриншоты всех экранов, включая состояния загрузки и ошибки.
  6. Описание обработки нештатных ситуаций: нет сети, пустой список, отказ в разрешении.
  7. Тестирование: таблица проверок и, желательно, unit-тесты ViewModel.
  8. Сборка APK и инструкция по установке — проверяющий должен запустить приложение сам.
Соберите подписанный APK или AAB и приложите его к работе вместе с исходниками. Возможность установить приложение на телефон прямо на защите производит гораздо большее впечатление, чем эмулятор на ноутбуке.

Частые вопросы

Kotlin или Java для курсовой?

Kotlin: он официально рекомендован Google, короче и безопаснее в отношении null. Java допустима, если этого требует кафедра, но новые примеры в документации выходят уже только на Kotlin.

Что делать, если нет устройства для тестирования?

Достаточно эмулятора в Android Studio. Для отчёта проверьте приложение хотя бы на двух конфигурациях экрана — это покажет корректность адаптивной вёрстки.

Нужно ли публиковать приложение в Google Play?

Для курсовой нет, публикация требует платного аккаунта и модерации. Достаточно собрать APK. Если тема связана с распространением, опишите процесс публикации теоретически.

Читайте также

Сделаем работу по этой теме

Опишите задачу — ответим в течение 15 минут в личных сообщениях ВКонтакте, назовём срок и цену. Предоплаты за оценку нет.

  • Оценка заявки бесплатно
  • Правки по замечаниям преподавателя
  • Работы по всем техническим и IT-дисциплинам

Нажимая кнопку, вы соглашаетесь на обработку указанных данных для ответа на заявку.