Search
Write a publication
Pull to refresh
3
0.1
Send message

Еще есть поддержка страниц 16 кб для гугл плэя. Обновляться еще прикольнее. Когда нужно пересобрать нативные заброшенные под лет так 10 назад библиотеки.

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

Тот момент когда упор идет на диск, а тут еще лог пишется.

Мне уже переводят видосы на английский.
В настройках стоит английский. Переключаешь на русский и видео на русском.
Переводит AI. На слух пока очень не очень.

Просто задержка перед повтором операции. Если есть желание повесить поток, то можно убрать.

В целом для холодного потока ограничение во флоу через delay нормальная практика.

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

Так же в документации есть замечательные методы для соединения потоков данных (combine и zip), так же для переключения потоков данных flatMapLatest и другие. Возможности выделить из цепочки преобразований данные onEach.

При желании из холодного потока можно сделать горячий через stateIn. (Каждый подписчик на холодный поток вызывает всю цепочку преобразований) Но если перед передачей к подписчикам сделать его горячим, то холодный будет только один до горячего, а дальше расходиться к каждому подписчику.

В целом тема обширная и дает очень много возможностей.

На данный момент лучший выбор для архитектуры UDF

Интересные сравнения. В году так 14 писали еще на 7 жабе. А на седьмой без лямд, без стримов естественно было больше кода. Лучше бы с Котлином сравнили

В обычном Андройде на котлине пишу юзкейсы обновления. В них данные обновляются в RoomDatabase. А из нее через corutine Flow данные летят к экрану. Из минусов, что больше кода нужно писать, и много маперов.

Если делать миграции в базе данных, то добавление полей не большая проблема, ведь можно и дефолтное задать.

Так как это база данных, то можно делать различные джоины и запрашивать, комбинировать, только то что нужно для э4рана.

Чтобы внести изменение в код нужно понять этот код.

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

Новый человек не всегда может придерживаться архитектуры и сделать как то не так.

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

Выбор без выбора. Дайте денег и куплю даже хрущевку. Зарплаты такие, что дети еще будут за это рассчитываться. А вы тут про какой то стиль жизни.

На своей первой работе был я наладчиком оборудования. У меня был свой кабинет с кучей приборов, и соответственно еще и рабочими столами, где я мог разобрать прибор, все разложить и ничего не потерять, на это требуется пространство. И в один прекрасный день начальник такой.

" Нам нужен стол, у тебя их много, поэтому заберём у тебя"

Естественно мне стало неудобно работать.

Прошло немного времени, в офис купили новый стол другому сотруднику, а старый захотели выбросить на свалку, с трудом отвоевал его себе. Смешно то, что выносить на свалку доверили мне, и он естественно поехал ко мне в подсобку, потому что он был мне нужен.

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

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

Я наоборот не хочу логиниться ни пальцем ни лицом. Считаю эти функции крайне не безопасными и при утечке биометрии получить проблемы похуже одной взломанной учетки.

Я из тех кого около трех лет пинали hr. Никаких собеседований вообще. Причина, нет коммерческого опыта. И плевать они хотели на фрилансы, пет проекты и. др.

Но на работу таки устроился и именно с тестового задания. Вообще впринципе на собеседования попадал только с тестовых заданий. А взяли работать со второго собеса.

В общем. Если вам нужна работа, то вы и тестовое сделаете.

После некоторых раздумий над отправкой данных и обработок ошибок при отправке решил немного доработать. Всего то заменить.

    // добавить
    private data class Input(
        val data: String,
        val statusReceiver: (SendStatus) -> Unit
    )

..............
   // заменить
   private val input = Channel<Input>()
..............
  // заменить
  input.consumeEach { income ->
      runCatching {
          send(Frame.Text(income.data))
          workScope.launch { income.statusReceiver(SendStatus.OK) }
      }.onFailure {
          workScope.launch { income.statusReceiver(SendStatus.Error(it)) }
      }
  }
....................
    // заменить и лишний удалить.
    fun send(
        obj: O,
        statusHandler: (SendStatus) -> Unit = {}
    ) = workScope.launch {
        input.send(Input(encoder(obj), statusHandler))
    }

Так отправляющий сможет не только отправить данные, но и обработать ошибку

Сегодня и я задался такой задачкой. И как истинный джун не полез искать костыли, а сделал свой.

Сам сокет выглядит вот так:

package org.example

import io.ktor.client.*
import io.ktor.client.plugins.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.flow.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

abstract class WebSocketStream<I : Any, O>(
    private val client: HttpClient,
    private val workScope: CoroutineScope,
    private val urlString: String,
    val encoder: (O) -> String,
    private val decoder: (String) -> I,
) {

    companion object {

        inline operator fun <reified I : Any, reified O> invoke(
            client: HttpClient,
            workScope: CoroutineScope,
            urlString: String,
            noinline encoder: (O) -> String = { defaultSerializer.encodeToString(it) },
            noinline decoder: (String) -> I = { defaultSerializer.decodeFromString(it) }
        ): WebSocketStream<I, O> {
            return object : WebSocketStream<I, O>(
                client = client,
                workScope = workScope,
                urlString = urlString,
                encoder = encoder,
                decoder = decoder
            ){}
        }

        val defaultSerializer = Json {
            ignoreUnknownKeys = true
            allowSpecialFloatingPointValues = true
        }

    }


    sealed interface Output<out O> {
        data class Income<T : Any>(val data: T) : Output<T>
        data class Something<T : Any>(val frame: Frame) : Output<T>
        data class Error(val e: Throwable) : Output<Nothing>
    }


    sealed interface Status {
        data object Connect : Status
        data object Connected : Status
        data object Disconnect : Status
    }

    private val _stream = MutableSharedFlow<Output<I>>()
    val output = _stream.asSharedFlow()

    private val input = Channel<String>()

    private val _status = MutableStateFlow<Status>(Status.Disconnect)
    val status = _status.asStateFlow()

    private var job: Job? = null


    fun connectWhile(
        keepOpen: () -> Boolean
    ) {
        job?.cancel()
        job = workScope.launch {
            while (keepOpen()) {
                runCatching {
                    _status.value = Status.Connect
                    connectSuspend(keepOpen)
                    _status.value = Status.Disconnect
                }.onFailure {
                    _stream.emit(Output.Error(it))
                }
                if (keepOpen()) {
                    delay(1000)
                }
            }
        }
    }

    private suspend fun connectSuspend(keepOpen: () -> Boolean) {
        client.webSocket(urlString) {

            val connectJob = launch {

                incoming.receiveAsFlow()
                    .onStart {
                        _status.value = Status.Connected
                    }.onEach { message ->
                        when (message) {
                            is Frame.Text -> {
                                val rad = message.readText()
                                try {
                                    _stream.emit(Output.Income(decoder(rad)))
                                } catch (e: Exception) {
                                    _stream.emit(Output.Error(e))
                                }
                            }

                            else -> {
                                _stream.emit(Output.Something(message))
                            }
                        }
                    }.launchIn(this)

                input.consumeEach {
                    runCatching {
                        send(Frame.Text(it))
                    }
                }

            }

            while (keepOpen()) delay(1000)
            _status.value = Status.Disconnect
            connectJob.cancel()
            close(reason = CloseReason(CloseReason.Codes.NORMAL, "leave"))
        }

    }

    private suspend fun send(data: String) {
        return input.send(data)
    }

    fun send(
        obj: O
    ) = workScope.launch { send(encoder(obj)) }


}

А пользоваться этим примерно так:

// Есть какой то класс оболочка на прием, а можно и без, всего то заменяем decoder
@Serializable
data class Data(
    @SerialName("data")
    val data: String
) : SerializedAny()

// Есть какой то класс оболочка на отправку, а можно и без, всего то заменяем encoder
@Serializable
data class Output(
    @SerialName("data")
    val data: String
)

private suspend fun open(client: HttpClient, text: String) {
    // Создаем наш сокет
  
     val stream = WebSocketStream<Data, Output>(
        client = client,
        workScope = CoroutineScope(Dispatchers.IO),
        urlString = "ws://localhost:8078/ws"
    )

    // вешаем слушателей
    CoroutineScope(Dispatchers.IO).launch {
        stream.output
            .onEach {
                println("$text $it")
            }.launchIn(this)

        stream.status
            .onEach {
                println(it)
            }.launchIn(this)
    }

    // флаг, что нужно продолжать работу
    var keep = true

    // сокет с некоторой переодчностью сам спрашивает, нужно ли ему работать дальше
    // если связь оборвется, то будет пробовать переподключться
    stream.connectWhile { keep }

    // Тут мы отправляем данные, в ответ получаем Job. Можно ждать завершения посылки, а можно и не ждать
    repeat(5) {
        // job создается от workScope. Если workScope закрыть, то и производные закроются.
        stream.send(Output(text)).join()
        delay(1000)
    }

    // В конце говорим что продолжать не нужно и сокет сам отключится.
    keep = false
}

Вероятность увольнения прямо зависит от времени. Это время зависит от психологии сотрудника.

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

Влияет возраст. Более зрелые люди стремятся к меньшим потрясениям в жизни и больше держатся за место. А вчерашних студентов с большой вероятностью заберут в армию.

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

Это то, что мне сейчас в голову пришло. Но думаю, тут можно и еще моментов накинуть.

Как сложно =) На самом деле если вы используете иньекцию зависимостей, то в вашу VM придёт переменная someUseCase: SomeUseCase. А потом вы ее вызовите как будто бы обычный метод someUseCase(someID). И выглядит совсем не страшно) Ну и invoke если уж совсем явно хотим.

А чтобы стать врачом нужно сначала пойти санитаром.

Преимущества XML.

  1. Четкое разделение UI и логики

  2. Легкая читаемость сложных UI

  3. Мгновенное применение изменений (не нужно перекомпилировать)

  4. Можно перейти к элементу просто нажав на часть UI в превью.

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

Как указано я пытался скопировать папку из библиотеки. Но файлы там немного отличаются от ваших и в результате получаю ошибку libsqlitejdbc.so is not an ABI. С вашими же все работает.

ps. Разобрался. Значит копируем из библиотеки папку org.sqlite.native.Linux-Android, переименовываем aarch64 в arm64-v8a и arm в armeabi-v7a.
Подключаем:
android {
sourceSets { getByName("main") { jniLibs.srcDirs("src/main/native/Linux-Android")
}
}
}

В моем случае прокатило.

А почему бы не давать тестовое, а во время собеседования не просить сделать дополнительную фичу?

1

Information

Rating
7,203-rd
Registered
Activity