Site icon AppTractor

Реализация Server-Driven UI в Android

Вы когда-нибудь задумывались о том, как приложения в вашем смартфоне не обновляясь меняют свой пользовательский интерфейс за одну ночь? Возьмем для примера любую крупную технологическую платформу, такую как Flipkart от Amazon, Square, AirBnB или Swiggy. Они меняют свой пользовательский интерфейс в зависимости от различных факторов, таких как местоположение. Как они это делают?

Server-driven UI в Swiggy на основе местоположения

Что такое Server-Driven UI?

Управляемый сервером пользовательский интерфейс (Server-driven UI) — это сценарий, в котором клиент получает информацию от API о том, какие компоненты и контент следует показать. В этом могут использоваться все три основные платформы — iOS, Android и веб. Я считаю, что подобная разработка улучшает отзывчивость и управляемость нативных приложений. Сегодня мы поговорим о том, как этого добиться.

Существует множество способов добиться этого в Android, но мы будем использовать самый популярный и простой тип данных — JSON.

Поскольку JSON легко читать и управлять им. Давайте посмотрим на пример JSON-файла.

{
  "card": {
    "log_id": "div2_sample_card",
    "states": [
      {
        "state_id": 0,
        "div": {
          "items": [
            {
              "type": "container",
              "width": {
                "type": "match_parent"
              },
              "height": {
                "type": "match_parent"
              },
              "items": [
                {
                  "type": "image",
                  "image_url": "https://upload.wikimedia.org/wikipedia/commons/2/2f/Google_2015_logo.svg",
                  "width": {
                    "type": "fixed",
                    "value": 50
                  },
                  "height": {
                    "type": "fixed",
                    "value": 50
                  },
                  "preload_required": true,
                  "scale": "fit"
                },
                {
                  "type": "text",
                  "text": "Hello Server Driven UI ",
                  "width": {
                    "type": "match_parent"
                  },
                  "text_alignment_horizontal": "center"
                }
              ],
              "content_alignment_horizontal": "center",
              "content_alignment_vertical": "center",
              "background": [
                {
                  "type": "solid",
                  "color": "#ffeeff"
                }
              ]
            }
          ],
          "background": [
            {
              "color": "#F1EBDC",
              "type": "solid"
            }
          ],
          "height": {
            "type": "match_parent"
          },
          "orientation": "overlap",
          "type": "container"
        }
      }
    ]
  }
}

В левой части изображения вы можете увидеть схему JSON, которая создает красивый пользовательский интерфейс в правой части с изображением, текстом и цветом фона.

Позвольте мне немного объяснить эти JSON-схемы. Каждый div используется как контейнер, внутри которого будет список элементов.

Существует несколько основных типов. Давайте рассмотрим некоторые из них подробнее.

Есть и другие типы, но для этой статьи нам понадобится только это, чтобы понять основы серверного пользовательского интерфейса.

Теперь, когда данные UI созданы, посмотрим как их отрисовать в приложениях.

Давайте погрузимся в кодовую часть этого процесса, чтобы его можно было получить на Android, iOS и в вебе.

Есть классная библиотека DivKit, разработанная командой Яндекса, которая прекрасно справляется с этой задачей. Она также используется в продакшене.

Ссылку на Github с примером проекта DivKit можно найти здесь, а ссылку на мой проект с чистым кодом — здесь.

Я объясню, как легко интегрировать Divkit без написания кода в XML.

Шаг 1. Внедрение Divkit в build.gradle:

dependencies {
 implementation "com.yandex.div:div:29.14.0"
 implementation "com.yandex.div:div-core:29.14.0"
 implementation "com.yandex.div:div-json:29.14.0"
}

Шаг 2. Внутри файла main.xml вашей Активити:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" />

Шаг 3. Напишите этот код в файле MainActivity.kt:

class MainActivity : AppCompatActivity() {

    private lateinit var context: Div2Context

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

        val divConfiguration = DivConfiguration.Builder(CoilDivImageLoader(this))
            .actionHandler(object : DivActionHandler() {
                override fun getUseActionUid(): Boolean {
                    Log.i("TAG", "getUseActionUid: ")
                    return super.getUseActionUid()
                }

                override fun handleAction(action: DivAction, view: DivViewFacade): Boolean {
                    Log.i("TAG", "handleAction: " + action.url?.evaluate(view.expressionResolver))
                    val stringUri = action.url?.evaluate(view.expressionResolver)!!
                    when (stringUri.scheme) {
                        "submit" -> { 
                            // Write your code here on button press (actions in JSON)
                          }
                    }
                    return super.handleAction(action, view)
                }

                override fun handlePayload(payload: JSONObject) {
                    Log.i("TAG", "handlePayload: $payload")
                    super.handlePayload(payload)
                }
            })
            .build()
        this.context = Div2Context(
            baseContext = this,
            configuration = divConfiguration,
            lifecycleOwner = this
        )

        updateUIWithJSON("YOUR JSON FILE NAME GOES HERE!!")
    }

    private fun updateUIWithJSON(jsonFileName: String) {
        runOnUiThread {
            val linearLayout = findViewById<LinearLayout>(R.id.container)
            linearLayout.removeAllViews()

            val divJson = DivAssetReader(context).read("$jsonFileName.json")
            var templateJson = divJson.optJSONObject("templates")
            val cardJson = divJson.getJSONObject("card")
            val div = DivViewFactory(context, templateJson).createView(cardJson)

            linearLayout.addView(div)
            div.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
            div.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
        }
    }
}

class DivViewFactory(
    private val context: Div2Context,
    private val templatesJson: JSONObject? = null
) {

    private val environment = DivParsingEnvironment(ParsingErrorLogger.ASSERT).apply {
        if (templatesJson != null) parseTemplates(templatesJson)
    }

    fun createView(cardJson: JSONObject): Div2View {
        val divData = DivData(environment, cardJson)
        return Div2View(context).apply {
            setData(divData, DivDataTag(divData.logId))
        }
    }
}

Вот и все. Как только вы запустите код, появится тот же самый пользовательский интерфейс, что и выше. Не нужно писать никакого кода, и самое приятное, что этот JSON можно изменять с сервера, добавляя и удаляя любые дополнения без необходимости менять код приложения.

Это очень простой пример отображения пользовательского интерфейса с сервера. Вы можете прочитать об этом подробнее, а также разобраться и создать любой сложный пользовательский интерфейс с помощью DivKit.

На этом все. Счастливого кодинга 😁.

Еще про Server-Driven UI

Exit mobile version