• úvod
  • témata
  • události
  • tržiště
  • diskuze
  • nástěnka
  • přihlásit
    registrace
    ztracené heslo?
    LWEEKAndroid development
    LWEEK
    LWEEK --- ---
    DRIZDIK: V dokumentaci jsem se dočetl, že:

    Coroutines are the standard means of leaving the main thread in Kotlin. However, they leave memory once the app closes. For persistent work, use WorkManager.

    Ale je pravda, že nevím jestli tím close myslí jako killnutí apky nebo jenom to že je suspendovaná.

    Můj problém je, že jsem iOS vývojář, takže dost často uvažuju v kontextu iOS a na Androidu to bývá kolikrát dost jinak. Možná bude lepší vysvětlit o co mi jde.

    Mám firebase messaging foreground servisu. Jakmile přijde silent zpráva určitého formátu, tak se mají ze serveru stáhnout nová data a uložit do DB.

    Business logiku mám v repozitáři. Vím, že by tam měl ještě před tím voláním být usecase. Ten samo vyrobím. Takže to co potřebuju je dostat se k tomu usecase (skrze dagger componenty v application) a provolat funkci, která může v extrémním případě chvíli trvat. Volat to přes corutiny mě trochu děsí, protože nevím kolik času ta servisa má než ji systém utne a pokud bych volal asynchronně tak předpokládám, že se apka uspí jakmile projede lifecyklus té servisy.
    DRIZDIK
    DRIZDIK --- ---
    LWEEK: Samozřejmě umí: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
    Co přesně jsi načetl o korutinách, že neběží v pozadí? Můžou běžet klidně i na stejném threadu.
    LWEEK
    LWEEK --- ---
    Možná něco takového? Ale nevím jak moc to je "deadlock" odolné. Ideální by bylo mít možnost ten job cancelnout když se canceluje worker, ale takovou metodu Worker nejspíš nemá.

    class ActivitiesUpdateWorker @Inject constructor(
    ctx: Context,
    params: WorkerParameters,
    private val repository: ActivitiesRepository
    ) : Worker(ctx, params) {

    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.IO + job)

    override fun doWork(): Result {
    scope.launch {
    repository.getAllScheduledActivities(true)
    }

    while (!job.complete()) { continue }

    return Result.success()
    }
    }
    LWEEK
    LWEEK --- ---
    DRIZDIK: No, tahle. Tvůrce týhle aplikace ji napsal kompletně s corutinama. Jestli jsem si načetl problematiku správně, tak Corutiny neumí běžet v pozadí když je apka uspaná. A chápu že nemohu volat ve workeru něco asynchroně, ale přeci nebudu duplikovat model jenom proto abych měl jednu synchronní a jednu asynchronní verzi? Neexistuje možnost volat suspendovanou funkci synchronně?
    DRIZDIK
    DRIZDIK --- ---
    LWEEK: Neděláš snad ve workerovi něco asynchronně? :-)
    DRIZDIK
    DRIZDIK --- ---
    LWEEK: Určitě nechceš injektovat uvnitř a nejlépe injektovat konstruktorem, aby instance vždy byla správně inicializovaná a dobře testovatelná.
    A než to řešit s Daggerem 2, tak by raději refactoroval na Hilt :-D
    LWEEK
    LWEEK --- ---
    Našel jsem si ale článek který zmiňuje použití workera s Daggerem, tak půjdu podle něj. Ale pořád ještě musím zjistit jak vyřešit blamáž s corutinou.
    LWEEK
    LWEEK --- ---
    Dagger 2 .)

    Ještě mě napadlo injektovat uvnitř workeru.

    class ActivitiesUpdateWorker(
    ctx: Context,
    params: WorkerParameters,
    ) : Worker(ctx, params) {

    @Inject
    lateinit var repository: ActivitiesRepository

    override fun doWork(): Result {
    (applicationContext as? App)?.appComponent?.inject(this)

    //repository.getAllScheduledActivities(true)

    return Result.failure()
    }
    }

    Nicméně nevím jestli applicationContext je opravdu instance App a druhá věc je že ty metody v repozitáři jsou psaný pro corutiny. Takže si nejsem ani jistý jak je z workeru zavolat.

    Worker používám protože potřebuju aby to pracovalo když je apka v pozadí.
    DRIZDIK
    DRIZDIK --- ---
    LWEEK: To je samozřejmě dost blbost :-D Protože ti jen použije class z té instance, kterou pak zahodí :-D
    Co používáš jako DI container? Hilt? Koin? Ale v základu je to o vytvoření WorkerFactory, která ti ty workery vytváří a injektuje. Oba nástroje na to mají už hotový tooling, kterým buď anotuješ Workery nebo vytváříš worker factory v Koinu .

    WorkManager | Koin
    https://insert-koin.io/docs/reference/koin-android/workmanager/
    https://developer.android.com/reference/androidx/hilt/work/HiltWorker
    LWEEK
    LWEEK --- ---
    Ahoj, snažím se zjistit jak si injectnout repozitář do Workera. ChatGPT mi navrhnul tohle:

    val myWorker = MyWorker(context, workerParams, myComponent.provideMyModel())
    myComponent.inject(myWorker)

    val workRequest = OneTimeWorkRequest.Builder(myWorker::class.java).build()
    WorkManager.getInstance(context).enqueue(workRequest)

    To mi ale přijde jako blbost. :) Nebo se mýlím? Každopádně ví někdo jak injectovat do Workera?
    LWEEK
    LWEEK --- ---
    DRIZDIK: Já si říkal, že to nemůže být až tak složité. Respektive jsem si říkal proč by to mělo být tak složité a na StackOverflow žádné info. Mělo mě to trknout. :) Díky moc!
    DRIZDIK
    DRIZDIK --- ---
    LWEEK: Ano dostaneš, je to ta stejná instance, kterou budeš mít ve foreground appce. Lifecycle bys měl obsluhovat velmi podobně jako v aktivitě, service může service kdykoliv zabít, stejně jako aktivitu. Doporučuji logovat pomocí Timberu, pokud už nepoužíváš.
    LWEEK
    LWEEK --- ---
    DRIZDIK: To je zvláštní protože mi přišlo, že když jsem logoval ze servisy tak to nešlo do konzole, ale v logcatu to bylo. Takže teoreticky bych si mohl v servise i nějak dostat k Application instanci?
    DRIZDIK
    DRIZDIK --- ---
    LWEEK: Service běží na Androidu defaultně ve stejném procesu a vykonává se na Main Threadu, pokud nedefinuješ explicitně "android:process". Separátní proces běžně není třeba a vystačíš si s multithreadingem/coroutinama, pokud neřešíš problémy s pamětí, GC a možnost přežít crash hlavní appky.
    LWEEK
    LWEEK --- ---
    DRIZDIK: Respektive, asi se blbě ptám. Je nějak možnost olvivnit jestli servisa běží na stejném procesu nebo vlastním? V manifestu to mám takto:
    <service
        android:name=".utils.TMFirebaseMessagingService"
        android:exported="false">
        <!-- // android:directBootAware="true" -->
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>
    LWEEK
    LWEEK --- ---
    DRIZDIK: Respektive, asi se blbě ptám. Je nějak možnost olvivnit jestli servisa běží na stejném procesu nebo vlastním? V manifestu to mám takto:
    LWEEK
    LWEEK --- ---
    DRIZDIK: Chápu, ale ta Firebase messaging servisa běží defaultně na separátním procesu, nebo se mýlím?
    DRIZDIK
    DRIZDIK --- ---
    LWEEK: Dokud to nemáš v jiném procesu, tak by update neměl být problém a závisí jen na tvém modelu, jak moc atomicky ten update potřebuješ udělat. Instanci Realmu sdílíš se zbytkem appky, takže základy pro to, aby ti k ničemu takovému nedošlo bys měl už mít částečně na místě.
    LWEEK
    LWEEK --- ---
    Ahoj, mám dotaz na nějakého zkušenějšího android vývojáře. Potřebuju vyřešit zadání kdy aplikace na základě silent notifikace aktualizuje data na pozadí. Silent notifikaci FCM umím přijímat. Pak stáhnu data přes Workera, ale co si nejsem vůbec jist je jak updatovat data v Realm databázi aby nemohlo dojít k nějaké kolizi nebo zamčení DB ze servisy apod. Jak se to standardně řeší?
    CLOVICEK
    CLOVICEK --- ---
    DRIZDIK: perfektní, pomohlo to přepsat na getDefaultVoice() a už mi krásně mluví česky. Díky moc za nakopnutí
    Kliknutím sem můžete změnit nastavení reklam