ANR, avagy a programozó lustasága

By | 2012. május 6.

Ha Androidot futattó telefonotok van bizonyára sokan találkoztatok már az ANR (Application Not Responding) jelenséggel. Ezt az üzenetet akkor írja ki a rendszer, ha a felhasználónak több, mint 5 másodpercet kell várnia arra, hogy az aktuálisan futó program feldolgozza az inputot. Ennek a folyamatnak a megértéséhez azonban egy kicsit részletesebben is meg kell ismerni az Android működését.
A probléma ott gyökeredzik, hogy az operációs rendszer alapértelmezésben nem különíti el a grafikus felület rajzolásáért és a számításokért felelős szálakat. Ez alapjába véve még nem lenne probléma, de a fejlesztők egy része megfeletkezik erről és nem teszi meg a szükséges lépéseket az üzenet elkerülése érdekében. Természetesen nem túl nehéz úgy megírni egy programot, hogy az működjön akkor is, ha nagyobb számításokat végez, de azért vannak buktatói.
Először is nézzünk meg egy egyszerű példát. Az alábbi kódban megfigyelhető, hogy az erőforrásigényes feladatot az onCreate (a main Androidos megfelelője) függvényben indítjuk el, ezzel pedig blokkoljuk a UI Thread-et.

package com.pshegger.test.anr;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class ANR_TestActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        szamol();
    }

    private void szamol() {
        try {
            Thread.sleep(15000);
            TextView tv = (TextView) findViewById(R.id.status);
            tv.setText("Befejeztük");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Ez az egyszerű példa tökéletesen bemutatja az ANR mögött rejlő hibát. Ennek a kijavítására természetesen van lehetőség, nincs más dolgunk, mint egy külön szálon indítani a szamol függvényt. Ekkor már nem fog hibaüzenetet dobni a rendszer azért, mert a program nem válaszol, de beleütközünk egy másik problémába. A 21. sorban megpróbáljuk átállítani a képernyőn megjelenő szöveget, hogy tükrözze az aktuális állapotot. Itt azonban a rendszer futásidejű hibát fog dobni, mert a felhasználói felület ellemeit csak a UI Thread módosíthatja. Szerencsére erre is létezik egy igen hasznos függvény, ami képes egy Runnable-t a UI Thread-en futtatni. Módisítsuk hát a programot, és nézzük meg mi történik.

package com.pshegger.test.anr;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class ANR_TestActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Thread szamolo = new Thread(new Runnable() {
            public void run() {
                szamol();
            }
        });
        szamolo.start();
    }

    private void szamol() {
        try {
            Thread.sleep(15000);
            runOnUiThread(new Runnable() {
                public void run() {
                    TextView tv = (TextView) findViewById(R.id.status);
                    tv.setText("Befejeztük");
                }
            });
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Ezek után a módosítások után a program már úgy viselkedik, ahogy szeretnénk az egyetlen probléma már csak az, hogy a felhasználó nem tud a háttérben futó folyamatról, de ez már a jövő zenéje.

  • Kristóf Timur

    WPF-es körökben csak úgy szoktuk mondani: “Don’t freeze the UI thread!” – és milyen igaz. 🙂