Android Java - plánované (časované) operace

Programování pro mobilní telefony, hodinky, Android, iOS, Windows Phone

Moderátor: Moderátoři Živě.cz

Odeslat příspěvekod Squad leader 4. 11. 2019 11:58

Ahoj,

měl bych obecný dotaz k plánovaným (časovaným) operacím na Androidu z aplikace.

Začínám tak, že si odvodím třídu z BroadcastReceiveru

Kód: Vybrat vše
public class MyBroadcastReceiver extends BroadcastReceiver {

@Override
    public void onReceive(Context context, Intent intent) {

// Zde provedu akci, která se má provést při plánování

}
}


Na tuto třídu použiji referenci v mojí aktivitě, třeba takto:

Kód: Vybrat vše
public void onClickBtnClick(View v)   // Při kliknutí na buttonek naplánuj:
    {
      AlarmManager alarmMgr = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
       Intent intent = new Intent(this, MyBroadcastReceiver.class); 
     pendingIntent = PendingIntent.getBroadcast(this, 1, intent,
                                PendingIntent.FLAG_UPDATE_CURRENT);
                alarmMgr.set(AlarmManager.RTC_WAKEUP, 20000000,  pendingIntent);
  }


Ovšem někdy se to nechová tak, jak by mělo.
Obecně jsem vypozoroval, že při krátkých, několikaminutových intervalech
to funguje docela spolehlivě. Ovšem pokud tam zadám interval v řádu hodin, tak většinou žádné samočinné upozornění neproběhne. Upozornění proběhne však okamžitě, jakmile mobil probudím.

Všimnul jsem si, že podobně nespolehlivě se chovají i profesionální aplikace na GooglePlay.
Například Bluemail a podobně.

Mám mobil Asus Zenfone Max Pro M1 ZB602KL.

Potřeboval bych se prosím zeptat, zda tento problém souvisí obecně s agresivním uspáváním na mobilech, nebo zda jsem něco přehlédl.
Co jsem četl na internetu, tak u nových systémů se doporučuje spíše používat jinou třídu namísto AlarmManager.

Na straně druhé je internet plný podobných příspěvků, kdy se lidem nechce alarm aktivovat a v podstatě tam nejsou kloudné odpovědi. Respektive toto jsem odkoukal, jako "best practise" postup.

Každý android obsahuje aplikaci budík, která funguje spolehlivě.
A pak je otázkou zda je taková aplikace nějak privilegovaná nebo jen dobře napsaná.

Tak zdá se, tady jsem našel smutnou odpověď, přímo v dokumentaci:
Note: Beginning in API 19, the trigger time passed to this method is treated as inexact: the alarm will not be delivered before this time, but may be deferred and delivered some time later. The OS will use this policy in order to "batch" alarms together across the entire system, minimizing the number of times the device needs to "wake up" and minimizing battery use. In general, alarms scheduled in the near future will not be deferred as long as alarms scheduled far in the future.

With the new batching policy, delivery ordering guarantees are not as strong as they were previously. If the application sets multiple alarms, it is possible that these alarms' actual delivery ordering may not match the order of their requested delivery times. If your application has strong ordering requirements there are other APIs that you can use to get the necessary behavior; see setWindow(int, long, long, android.app.PendingIntent) and setExact(int, long, android.app.PendingIntent).

Applications whose targetSdkVersion is before API 19 will continue to get the previous alarm behavior: all of their scheduled alarms will be treated as exact.
Squad leader
Junior

Odeslat příspěvekod Nargon 4. 11. 2019 16:41

S programováním pro android zkušenosti nemám, takže co za třídu použít ti neporadím. Ale kolega v práci měl podobný problém i s budíkem. Bylo to před pár lety, tehdy měl nějaký samsung (asi S4) a měl stejný problém. Pokud budík nastavil, aby zvonil za pár minut tak vše OK, ale jakmile tam nastavil třeba hodinu, na telefon nesahal, tak nezvonil. Začal zvonit až když telefon probudil. Jak to tehdy vyřešil ani nevím, ale vím že si na to stěžoval a říkal že je to naprd, když ho budík ráno nevzbudí a začne zvonit až když se vzbudí sám a chce se na telefon podívat kolik je hodin.

Jinak jako řešení by mohlo být použití krátkého alarmu a trochu aplikační logiky.
Například víš že potřebuješ vykonat nějakou činnost ve 21:30 (tj např za 5 hodin). Funkce pro zavolání alarmu bude kontrolovat zda je čas plánu delší než např 10 minut. Pokud ne, tak nastaví alarm na požadovaný čas. Pokud bude delší tak nastaví alarm jen na 10 minut. Jinými slovy, alarm se nastaví max na 10 minut do budoucnosti.
Při probuzení zkontrolovat zda je již požadovaný čas 21:30. Pokud ano, tak provést požadovanou činnost. Pokud ne, tak znovu naplánovat alarm.
Tím pádem se bude telefon stále každých 10 minut probouzet a kontrolovat zda už má něco dělat nebo dále spát. Asi to bude žrát baterku, ale snad né moc.

Je mi jasné že je to celkem prasácké řešení, ale když ti android háže klacky pod nohy, tak mu je tam házej taky.
Desktop: Ryzen 7 1800X (3.95GHz, 1.35V), Asus Crosshair VI Hero, 16GB DDR4 Ram (3200MHz), 128GB SSD + 3TB HDD, Nvidia GTX 1080
Notebook: Asus UL50VT 15.6" (SU7300@1.7GHz, 4GB ram, 500GB HDD, Intel GMA 4500MHD + nVidia G210M, dlouha vydrz cca 7+ hod)
Nargon
Moderátor

Odeslat příspěvekod Squad leader 4. 11. 2019 18:02

Díky Nargone za postřeh.

Přesně tento problém řeším. A popisuji ho ke konci citováním dokumentace.
Tam to v podstatě popisují jako "zdravé chování", za účelem šetření baterii.
Squad leader
Junior

Odeslat příspěvekod MrFail 6. 11. 2019 14:49

Som len pokročilý začiatočník (to je teda oxymoron) a s týmto som ešte nerobil (moje maximum je zatiaľ práca s firebase), ale osobne by som povedal, že android ti tú aktivitu zabíja - pár minút ti ju nechá bežať ale potom ti ".destroy()"ne
https://developer.android.com/guide/com ... -lifecycle
Trochu Googlením som našiel https://developer.android.com/reference ... duler.html
Ale až od API 21, keď tak nezabudni prepísať gradle.
MrFail
Junior

Odeslat příspěvekod Squad leader 6. 11. 2019 16:36

Ano, děkuji za rady.
Scheduler je přesně ta alternativa, kterou jsem myslel, ale nedokázal jsem si vzpomenout na název.

Perfektní článek zdarma je zde:
https://blog.teamtreehouse.com/scheduling-work-jobscheduler
Hned jsem si ho stahl, pro případ, že by zmizel.

Nicméně třída neobsahuje sama o sobě něco, co by umožňovalo na čas plánovat.
To do ní musí zřejmě nějak přidat uživatel-programátor.
Například pomocí periodického setPeriodic.

V každém případě by mě moc zajímalo, jak funguje takový WhatsApp nebo kecálek Facebooku. Tyto programy totiž dokáží vždy spolehlivě upozornit na novou zprávu a vzbudit mobil ihed.

Tak by mě hodně zajímalo, zda implementují nějaké otrocké čekání. Nebo naopak jsou navázáni na nějakou "chytrou" podmínku "Network data change", kde kontrolují jen při jejím splnění efektivně podmínku. :hm

Jinak dotaz, který řešil totéž, co já je zde: (Jen použil novější JobScheduler
https://stackoverflow.com/questions/56011217/jobscheduler-scheduled-every-10-min-does-not-always-work

A nedopadl také slavně, tam mu zase snad doporučili použít to, co já použil neúspěšně.
Squad leader
Junior

Odeslat příspěvekod MrFail 6. 11. 2019 19:37

Prešiel som Google (aj mňa to celkom zaujíma) a pred Androidom 8 sa to riešilo službou (Service) - čo je logické.
Možno skús toto:
https://developer.android.com/training/ ... te-service
MrFail
Junior

Odeslat příspěvekod Squad leader 7. 11. 2019 16:36

Mmmm. Toto si nemyslím že bude řešením.

Na tom tvojem odkaze píší ukázku:
Kód: Vybrat vše
public class RSSPullService extends IntentService { ......


Čili je odvozena servisní třída od IntentService.

A už na začátku v dotaze uvadím:
Kód: Vybrat vše
Intent intent = new Intent(this, MyBroadcastReceiver.class); 


Takže si při vší úctě nemyslím, že tudy povede cesta.

Ale vrtá mi v hlavě, jak jsou navrženy moderní kecálky typu WhatsApp atd..., které spolehlivě mobil probudí kdykoliv, respektive při přijmu nové zprávy kdykoliv. :hm
Teď jsem se do toho ponořil trochu více.
Super článek, přes service tady:
https://www.vogella.com/tutorials/AndroidServices/article.html

-- 8. 11. 2019 20:24 --

Tak jsem nakonec neodolal a implementoval to přes service a odvozenou třídu od timeru v tom service:
Obrázek

Popisek není přesný, je to doba timeru v service (v ms).
Takto implementovaný timer nemá problém vzbudit zařízení přesně i po 1h či 2h jsem to zkoušel.
Vše bez problému. Tedy až na jednu věc.

MyActivityClass:

Kód: Vybrat vše
public class MainActivity extends AppCompatActivity {

    EditText editText;
    Intent serviceIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText = (EditText) findViewById(R.id.editText);

    }
    // Start the service
    public void startService(View view) {


        serviceIntent = new Intent(this, MyService.class);
        serviceIntent.putExtra("cas", editText.getText().toString());

        startService(serviceIntent);
    }
    // Stop the service
    public void stopService(View view) {

        stopService(serviceIntent);
    }
}



Servisní třída service (MyServiceClass) :

Kód: Vybrat vše
public class MyService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return  null;
    }
    @Override
    public void onCreate() {
        Toast.makeText(this, "Service was Created", Toast.LENGTH_LONG).show();
    }

    private Context context;
    private MediaPlayer player;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        player = MediaPlayer.create(this, Settings.System.DEFAULT_RINGTONE_URI);
        // This will play the ringtone continuously until we stop the service.
        player.setLooping(true);
        // It will start the player
        //player.start();
        this.context = this;
        Bundle extras = intent.getExtras();
        int zaCas = Integer.parseInt(extras.getString("cas"));
        new MyCountDownCalculator(zaCas, 1000) {
            public void onTick(long millisUntilFinished) {

            }

            public void onFinish() {
                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
                PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "myapp:app_name");
                wl.acquire();

                player = MediaPlayer.create(context, Settings.System.DEFAULT_RINGTONE_URI);
                // This will play the ringtone continuously until we stop the service.
                player.setLooping(true);
                player.start();

                NotificationHelper notification = new NotificationHelper(context);
                notification.createNotification("Vzbuzuji device","Úspěch");
                wl.release();
            }
        }.start();

        Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
        return START_STICKY;
    }
    @Override
    public void onDestroy() {
        Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
        super.onDestroy();
        if(player != null)
        {
            try{
                player.stop();
                player.release();
            }finally {
                player = null;
            }
        }


    }
}


Pokud zadám nějaký drobnější časový interval, řekněme 4 minuty.
Tak se zařízení vzbudí a je možnost kliknout na tlačítko STOP SERVICE, které obsahuje v aktivitě MainActivity referenci na službu.
Ovšem pokud je interval delší, řekněme hodinu nebo dvě, tak se reference vymaže.
Vizuálně to není nijak vidět, ale probíhá stále zvonění !

Kód: Vybrat vše
   player = MediaPlayer.create(context, Settings.System.DEFAULT_RINGTONE_URI);
                // This will play the ringtone continuously until we stop the service.
                player.setLooping(true);
                player.start();

A vtip je v tom, že zvonění už nejde nikterak vypnout z mojí aktivity, protože té se vymaže reference na tu službu. Jediná možnost, jak se zbavit toho zvonění je ručně killnout celý proces.

-- 9. 11. 2019 23:30 --

ča :-)

Viz. https://stackoverflow.com/questions/51460417/service-is-killed-in-sleep-mode-why

Výsledek: Nijak nevyřešeno. Plno odkazů, které ve výsledku nefungují spolehlivě.
To na straně jedné. Na straně druhé tu máme provokující WhatsApp nebo Facebook kecálek, které, ač jsou doinstalované, tak jsou vždy schopny upozornit na novou zprávu, i když je device ve sleep modu.

ča. Už mi z toho hrabe.
Squad leader
Junior


Kdo je online

Uživatelé procházející toto fórum: Žádní registrovaní uživatelé a 0 návštevníků