Network

https://developer.android.com/develop/connectivity/network-ops
https://developer.android.com/develop/connectivity/network-ops/connecting
https://developer.android.com/develop/connectivity/network-ops/managing
https://developer.android.com/develop/connectivity/network-ops/reading-network-state
https://developer.android.com/develop/connectivity/network-ops/network-access-optimization
https://developer.android.com/develop/connectivity/network-ops/data-saver
https://developer.android.com/training/monitoring-device-state/connectivity-status-type
https://developer.android.com/privacy-and-security/security-ssl
https://developer.android.com/privacy-and-security/security-config
https://developer.android.com/privacy-and-security/security-gms-provider
https://developer.android.com/privacy-and-security/security-android-protected-confirmation
JSON

JavaScript Object Notation — формат обмена данными, который легко читается людьми и обрабатывается компьютерами.

• Cтрого типизирован: данные должны соответствовать одному из типов.

• Ключи в объектах должны быть уникальными.

{
  "user": {
    "id": 123,
    "name": "Alice",
    "email": "alice@example.com",
    "preferences": ["reading", "gaming", "traveling"],
    "isPremium": true,
    "subscription": null
  }
}
Object

Набор пар ключ-значение, заключенных в фигурные скобки {}.

• Ключи всегда строки.

• Значения могут быть любого типа из поддерживаемых JSON.

{
  "name": "Alice",
  "age": 25,
  "isStudent": false
}
Array

Упорядоченный список значений, заключенный в квадратные скобки [].

• Элементы массива могут быть разного типа.

• Порядок элементов имеет значение.

{
  "users": [
    {
      "id": 1,
      "name": "Alice"
    },
    {
      "id": 2,
      "name": "Bob"
    },
    {
      "id": 3,
      "name": "Charlie"
    }
  ]
}
String

Текстовые данные, заключенные в двойные кавычки "".

• Поддерживаются экранированные символы (например, \n, \t).

• Строки используют стандарт кодировки Unicode.

{
  "name": "Bob Miller",
  "profilePicture": "https://example.com/profile.jpg"
}
Number

Целые или дробные числа.

• Допускаются отрицательные числа.

• Не поддерживаются специальные значения, такие как NaN, Infinity.

{
  "age": 29,
  "height": 167.5,
  "accountBalance": -150.75
}
Boolean

Представляет истину или ложь.

• Возможные значения: true или false.

{
  "isActive": true,
  "hasPremiumAccount": false
}
Null

Указывает на отсутствие значения. Используется для обозначения пустого значения или неизвестного поля.

{
  "email": null
}
WebSockets

Это коммуникационный протокол, обеспечивающий двустороннее, постоянное соединение между клиентом и сервером через один TCP-сокет. В отличие от HTTP, WebSockets позволяют обмениваться данными в реальном времени без необходимости повторного установления соединения, что снижает задержки и повышает эффективность обмена информацией.

class WebSocketClient {
    private val client = OkHttpClient()
    private lateinit var webSocket: WebSocket

    private val request: Request = Request.Builder()
        .url("wss://echo.websocket.org") // URL сервера WebSocket
        .build()

    fun start() {
        webSocket = client.newWebSocket(request, object: WebSocketListener() {
            override fun onOpen(webSocket: WebSocket, response: Response) {
                // Соединение установлено
                webSocket.send("Hello, WebSocket!") // Отправка сообщения
            }

            override fun onMessage(webSocket: WebSocket, text: String) {
                // Получено сообщение в текстовом формате
                println("Received text: $text")
            }

            override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
                // Получено сообщение в байтовом формате
                println("Received bytes: ${bytes.hex()}")
            }

            override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
                // Сервер закрывает соединение
                webSocket.close(1000, null)
                println("Closing WebSocket: $code / $reason")
            }

            override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
                // Произошла ошибка
                t.printStackTrace()
            }
        })
    }

    fun stop() {
        webSocket.close(1000, "Goodbye!")
    }
}
SSL pinning

Техника безопасности, используемая для защиты приложений от атак «человек посередине» (MITM) при установлении защищенных соединений через HTTPS. Эта техника помогает убедиться, что приложение общается только с заранее известными и доверенными серверами, предотвращая возможность подключения к злоумышленным серверам, даже если их сертификаты подписаны доверенными центрами сертификации (CA). OkHttp поддерживает SSL pinning из коробки.

fun createOkHttpClient(): OkHttpClient {
    val certificatePinner = CertificatePinner.Builder()
        .add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") // Хэш сертификата
        .build()
    
    return OkHttpClient.Builder()
        .certificatePinner(certificatePinner)
        .build()
}
OkHttp

Библиотека для выполнения HTTP-запросов в Android и Java. Она упрощает создание, отправку и обработку HTTP-запросов и ответов, поддерживая такие важные функции, как кэширование, сжатие, повторные попытки запроса и управление соединениями. OkHttp разрабатывается компанией Square и широко используется в Android-приложениях для работы с REST API.

OkHttpClient

Основной класс в библиотеке OkHttp, который управляет выполнением HTTP-запросов и предоставляет API для их настройки. Через OkHttpClient можно настроить таймауты, кэширование, перехватчики (interceptors), поведение подключения, обработку повторных попыток, настройку SSL и другие параметры работы с сетью.

val client = OkHttpClient()
Interceptor

Позволяет перехватывать и изменять HTTP-запросы и ответы до того, как они будут отправлены или получены. Interceptor дает возможность добавлять общую логику для всех запросов и ответов в одном месте, что полезно для таких задач, как добавление заголовков, аутентификация, кэширование и логирование.

class AuthInterceptor: Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val requestWithAuth = originalRequest.newBuilder()
            .header("Authorization", "Bearer <token>")
            .build()
        return chain.proceed(requestWithAuth) // Выполняем запрос с новым заголовком
    }
}

// Подключаем кастомный Interceptor
val client = OkHttpClient.Builder()
    .addInterceptor(AuthInterceptor())
    .build()
Application Interceptor

Перехватчик, который работает на уровне приложения и позволяет перехватывать и изменять HTTP-запросы и ответы до или после их отправки, но без непосредственного взаимодействия с сетью. Он используется для внесения изменений в запросы и ответы, добавления заголовков, логирования и другой обработки, которая не зависит от сети.

val client = OkHttpClient.Builder()
    .addInterceptor { chain ->
        val originalRequest = chain.request()
        val modifiedRequest = originalRequest.newBuilder()
            .header("Authorization", "Bearer <token>")
            .build()
        
        chain.proceed(modifiedRequest) // Продолжаем с измененным запросом
    }
    .build()
Network Interceptor

Тип перехватчика, который работает на сетевом уровне и перехватывает запросы и ответы после обработки кэшем и перед отправкой в сеть. Network Interceptor предоставляет более низкоуровневый доступ к запросам и ответам, чем Application Interceptor, и позволяет управлять данными, которые фактически отправляются по сети и возвращаются с сервера.

val client = OkHttpClient.Builder()
    .addNetworkInterceptor { chain ->
        val request = chain.request()
        println("Отправка запроса: ${request.url} с заголовками ${request.headers}")
        
        val response = chain.proceed(request)
        println("Получен ответ: ${response.code} с заголовками ${response.headers}")
        
        response // Возвращаем ответ
    }
    .build()
Cache

Класс, который управляет кэшированием HTTP-ответов, помогая сократить количество сетевых запросов, ускорить время загрузки и снизить потребление трафика. Cache сохраняет HTTP-ответы в локальном хранилище и использует их для ответа на повторяющиеся запросы, если данные еще актуальны.

val cacheSize = 10 * 1024 * 1024 // 10 MB
val cache = Cache(File("cacheDirectory"), cacheSize)

val client = OkHttpClient.Builder()
    .cache(cache)
    .build()
cacheControl

Функция в Request.Builder, которая позволяет задать поведение кэширования для конкретного HTTP-запроса. Он принимает объект CacheControl, который определяет правила кэширования, такие как максимальный срок хранения данных, игнорирование кэша или принудительное использование данных из кэша.

• Настройка времени хранения в кэше

val request = Request.Builder()
    .url("https://api.example.com/data")
    .cacheControl(CacheControl.Builder()
        .maxAge(10, TimeUnit.MINUTES) // Использовать кэш до 10 минут
        .build())
    .build()

• Принудительное использование кэша

val request = Request.Builder()
    .url("https://api.example.com/data")
    .cacheControl(CacheControl.FORCE_CACHE) // Использовать только кэш, даже если есть сеть
    .build()

• Игнорирование кэша

val request = Request.Builder()
    .url("https://api.example.com/data")
    .cacheControl(CacheControl.FORCE_NETWORK) // Всегда обращаться к серверу, игнорируя кэш
    .build()
REST

REST (Representational State Transfer) — это архитектурный стиль для веб-сервисов, использующий стандартные HTTP-протоколы для взаимодействия с ресурсами в различных форматах (например, JSON, XML) и обеспечивающий статeless-коммуникацию между клиентом и сервером.

Header

Метаданные, передаваемые с запросами и ответами. Они могут содержать информацию о клиенте, формате данных, авторизации, кэшировании и многом другом.

@GET("endpoint")
fun getData(
    @Header("Authorization") authToken: String,
    @Header("Custom-Header") customValue: String
): Call<DataResponse>
Authorization

Используется для передачи токена доступа.

@GET("user/profile")
fun getUserProfile(
    @Header("Authorization") authToken: String
): Call<UserProfile>
val request = Request.Builder()
    .url("https://api.example.com/user/profile")
    .header("Authorization", "Bearer <token>")
    .build()
Content-Type

Указывает формат данных, которые отправляются на сервер.

@POST("data")
@Headers("Content-Type: application/json")
fun sendData(
    @Body data: DataRequest
): Call<ResponseBody>
val requestBody = RequestBody.create("application/json".toMediaType(), jsonData)
val request = Request.Builder()
    .url("https://api.example.com/data")
    .post(requestBody)
    .build()
Accept

Указывает желаемый формат ответа от сервера.

@GET("data")
fun fetchData(
    @Header("Accept") accept: String = "application/json"
): Call<DataResponse>
val request = Request.Builder()
    .url("https://api.example.com/data")
    .header("Accept", "application/json")
    .build()
User-Agent

Передает информацию о приложении, которое отправляет запрос.

@GET("status")
fun checkStatus(
    @Header("User-Agent") userAgent: String = "MyApp Android"
): Call<StatusResponse>
val request = Request.Builder()
    .url("https://api.example.com/status")
    .header("User-Agent", "MyApp Android")
    .build()
Cache-Control

Управляет кэшированием данных.

@GET("content")
fun getContent(
    @Header("Cache-Control") cacheControl: String = "no-cache"
): Call<ContentResponse>
val request = Request.Builder()
    .url("https://api.example.com/content")
    .header("Cache-Control", "no-cache")
    .build()
Body

Используется для передачи объектов как тела запроса, обычно в формате JSON. Применяется в методах с POST, PUT, или PATCH.

@POST("users")
fun createUser(@Body user: User): Call<Response>
Path

Используется для вставки переменных в URL-адрес, что позволяет динамически изменять конечную точку API. Переменные задаются в фигурных скобках.

@GET("users/{id}")
fun getUser(@Path("id") userId: String): Call<User>
Query

Используется для добавления параметров запроса к URL-адресу. Эти параметры добавляются после знака ? в URL и разделяются символом &.

// параметры page и size будут добавлены к URL-адресу, например, users?page=1&size=10.
@GET("users")
fun getUsers(
		@Query("page") page: Int,
		@Query("size") size: Int
): Call<List<User>>
GET

Запрашивает данные с сервера. Он не изменяет состояние ресурса.

@GET("users")
suspend fun getUsers(): List<User>
POST

Отправляет данные на сервер для создания нового ресурса.

@POST("users")
suspend fun createUser(@Body user: User): Response<User>
PUT

Обновляет существующий ресурс, отправляя его полные данные.

@PUT("users/{id}")
suspend fun updateUser(@Path("id") userId: String, @Body user: User): Response<User>
PATCH

Частично обновляет существующий ресурс. Он отправляет только измененные данные.

@PATCH("users/{id}")
suspend fun patchUser(@Path("id") userId: String, @Body user: User): Response<User>
DELETE

Удаляет ресурс на сервере.

@DELETE("users/{id}")
suspend fun deleteUser(@Path("id") userId: String): Response<Unit>
GraphQL

Это синтаксис который описывает как запрашивать данные и в основном используется клиентом для загрузки данных с сервера. GraphQL имеет три основные характеристики:

• Позволяет клиенту точно указать, какие данные ему нужны.

• Облегчает агрегацию данных из нескольких источников.

• Использует систему типов для описания данных.

GraphQL разработали в Facebook для замены REST. Из-за загрузки дополнительных данных приложение работает медленнее. Запросы разбиваются на множество endpoints. Они хранятся в разных БД. REST достигает своего предела. Вместо того чтобы иметь множество глупых endpoins лучше иметь один умный endpoint который будет способен работать со сложными запросами и придавать данным такую форму какую запрашивает клиент.

Cлой GraphQL находится между клиентом и одним или несколькими источниками данных. Он принимает запросы клиентов и возвращает необходимые данные в соответствии с переданными инструкциями.

REST-модель - это заказывать пиццу затем продукты затем химчистку. Три сервиса - три звонка. GraphQL - это передать все помощнику и запрашивать то что нужно.

gRPC
https://developer.android.com/guide/topics/connectivity/grpc

Google Remote Procedure Call. Удаленный вызов процедур. Фреймворк для использования Protocol Buffers. Позволяет приложению и серверу взаимодействовать простым, прозрачным и эффективным способом.

Protocol Buffers

Protobufs. Метод сериализации структурированных данных. Поддерживает различные языки программирования.

Вопросы на собесе (19)
  • JSON (1)
    1. Что такое JSON и как его парсить в Android?

      JSON (JavaScript Object Notation) — это легкий формат обмена данными, удобный для чтения и записи человеком, а также для парсинга и генерации машиной. В Android JSON можно парсить с помощью библиотек, таких как Gson или Kotlin Serialization.

  • Websockets (3)
    1. Что такое сокеты?

      Интерфейс для обмена данными между двумя устройствами по сети, обеспечивающий двустороннюю связь.

    1. Как работают сокеты?

      Сокеты работают как точки соединения для обмена данными между устройствами через сеть, используя IP-адреса и порты для установления и поддержания связи.

    1. Как работать с сокетами в Android?

      Использовать OkHttp WebSocket API.

  • SSL (3)
    1. Что такое SSL pinning?

      SSL pinning — это техника безопасности, которая заставляет приложение проверять, что сервер использует конкретный SSL-сертификат, предотвращая атаки типа «человек посередине» (MITM) и обеспечивая доверенное соединение.

    1. Как SSL pinning работает в Android?

      SSL Pinning работает путём закрепления (пиннинга) конкретного сертификата или публичного ключа на стороне клиента, чтобы проверять его при каждом соединении с сервером. Это предотвращает атаки типа MITM, так как соединение будет установлено только если сертификат сервера совпадает с закреплённым на клиенте. В Android SSL Pinning можно реализовать с помощью OkHttp или Network Security Config.

    1. Что такое MITM?

      MITM (Man-in-the-Middle) — это атака, при которой злоумышленник перехватывает и может изменять коммуникацию между двумя сторонами, ставя под угрозу конфиденциальность и целостность данных.

  • OkHttp (3)
    1. Что такое OkHttp, и как его использовать для выполнения сетевых запросов?

      Библиотека для работы с HTTP-запросами в Android, которая обеспечивает эффективное выполнение сетевых запросов и управление соединениями. OkHttp поддерживает асинхронные и синхронные запросы, кэширование, автоматическое управление соединениями и другие функции.

    1. Что под капотом использует OkHttp?

      HttpURLConnection.

    1. Какие бывают виды интерсепторов?

      Application Interceptor — перехватывает запросы до их отправки или после получения ответа, может модифицировать запрос и ответ.

      Network Interceptor — работает на уровне сети, перехватывает запросы и ответы после сетевого слоя.

  • REST (5)
    1. Что такое REST?

      REST (Representational State Transfer) — это архитектурный стиль для проектирования сетевых приложений, использующий стандартные HTTP-методы (GET, POST, PUT, DELETE) для работы с ресурсами по URL. Он обеспечивает простоту, масштабируемость и независимость клиента от сервера.

    1. Разница между GET и POST?

      GET запрашивает данные с сервера и передает параметры через URL, что делает их видимыми. POST отправляет данные на сервер в теле запроса, что обеспечивает большую безопасность и позволяет передавать большие объемы информации.

    1. Могут ли GET-запросы содержать Body?

      Нет, согласно спецификации HTTP. Рекомендуется использовать параметры URL для передачи данных.

    1. Почему нежелательно отправлять конфиденциальные данные через GET?

      Параметры запроса видны в URL, что может привести к утечке информации через журналы сервера, кэш браузера или историю. Кроме того, GET-запросы могут быть легко перехвачены и проанализированы, что увеличивает риск компрометации данных.

    1. Разница между path и query?

      path определяет ресурс, query передает параметры.

  • GraphQL (1)
    1. Что такое GraphQL?

      GraphQL — это язык запросов для API, который позволяет клиентам запрашивать именно те данные, которые им нужны, и получать их в едином ответе. В отличие от REST, где структура ответа фиксирована, GraphQL позволяет более гибко определять структуру данных, что уменьшает количество запросов и объем передаваемых данных.

  • Другие (3)
    1. Какие есть способы сделать сетевой запрос на Android?

      HttpURLConnection — встроенный класс для выполнения HTTP-запросов. Подходит для простых операций, но требует больше кода для обработки потоков и ошибок.

      OkHttp — популярная библиотека от Square, которая упрощает работу с HTTP. Поддерживает асинхронные запросы, кеширование, и повторные попытки.

      Retrofit — библиотека для создания API-клиентов на основе HTTP. Работает на базе OkHttp и использует аннотации для простого создания запросов.

      Volley — библиотека от Google для выполнения сетевых запросов с поддержкой кеширования и обработки изображений.

      Ktor — библиотека от JetBrains, используемая для асинхронных HTTP-запросов в Kotlin.

    1. Как обеспечить безопасность сетевых запросов в Android приложении?

      Используйте HTTPS вместо HTTP для шифрования данных во время передачи.

      Реализуйте SSL pinning для проверки сертификатов сервера.

      Используйте аутентификацию (например, OAuth) для защиты API.

      Шифруйте конфиденциальные данные на устройстве и при передаче.

      Ограничьте доступ к сетевым ресурсам с помощью соответствующих разрешений.

      Используйте библиотеки для обработки сетевых запросов, которые обеспечивают защиту (например, Retrofit).

    1. В чем разница между HTTP и HTTPS?

      HTTP передает данные в открытом виде, что делает их уязвимыми для перехвата и атак. А HTTPS использует SSL/TLS для шифрования данных, передаваемых между клиентом и сервером, что обеспечивает безопасность и конфиденциальность.