4ntoine
4ntoine

Reputation: 20420

How to prevent ANR if using worker thread?

My app downloads and unzips some data from raw file at first launch. All long-running processes are performed in service in background thread so i believe there is no obvious reason for ANR (correct me).

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        ResultReceiver receiver = intent.getParcelableExtra(RECEIVER);
        job = extractJob(intent);
        InstallResultSender sender = new InstallResultSender(receiver);

        // add one more receiver
        synchronized (senders) {
            senders.add(sender);
        }

        if (thread == null) {
            thread = new InitThread(job);
            thread.start();
        }

        // no need to restart service or redeliver intent
        return Service.START_NOT_STICKY;
    }

The progress and status messages are sent to activity using android ResultReceiver:

private ResultReceiver receiver;

public void sendProgress(int percent) {
        Bundle data = new Bundle();
        data.putInt(InstallResultReceiver.DATA_PROGRESS, percent);
        receiver.send(InstallResultReceiver.RESULT_PROGRESS, data);
    }

The activity just shows ProgressDialog and updates percentage and status message.

The problem is that while downloading/installing ANR happens and then Android sends signal 6 (abort). How can i prevent/walk-around this?

ANR reason is amazon in-app purchase billing receiver, but it does not matter i believe.

Log:

04-04 13:01:51.016: DEBUG/ActivityInstallListener(1912): on started INSTALL ArmstrongNumber c (1)
04-04 13:01:53.695: DEBUG/ActivityInstallListener(1912): saved repository (9233 bytes)
04-04 13:01:53.695: DEBUG/ActivityInstallListener(1912): on started INSTALL HarmonicNumberSeries c (1)
04-04 13:01:53.885: ERROR/ActivityManager(275): ANR in name.mycompany.android.myapp
        Reason: Broadcast of Intent { act=com.amazon.inapp.purchasing.NOTIFY flg=0x10000010 pkg=name.mycompany.android.myapp cmp=name.mycompany.android.myapp/com.amazon.inapp.purchasing.ResponseReceiver (has extras) }
        Load: 6.71 / 6.75 / 3.23
        CPU usage from 0ms to 5942ms later:
        59% 1912/name.mycompany.android.myapp: 50% user + 8.4% kernel / faults: 809 minor
        24% 275/system_server: 18% user + 6.3% kernel / faults: 1131 minor
        4% 29/mmcqd: 0% user + 4% kernel
        0.6% 505/com.android.phone: 0.4% user + 0.2% kernel / faults: 150 minor
        1% 47/adbd: 0% user + 1% kernel
        0% 519/com.amazon.tcomm: 0% user + 0% kernel / faults: 135 minor
        0% 1715/com.amazon.client.metrics: 0% user + 0% kernel / faults: 236 minor
        0% 12/pdflush: 0% user + 0% kernel
        0.8% 40/mediaserver: 0.1% user + 0.6% kernel
        0% 552/com.amazon.imp: 0% user + 0% kernel / faults: 123 minor
        0.1% 909/com.android.systemui: 0% user + 0% kernel / faults: 104 minor
        0% 459/com.lab126.softkeybar: 0% user + 0% kernel / faults: 110 minor
        0% 539/com.android.providers.downloads: 0% user + 0% kernel / faults: 99 minor
        0.3% 545/android.process.media: 0.1% user + 0.1% kernel / faults: 167 minor
        0% 988/com.nuance.swype.input: 0% user + 0% kernel / faults: 97 minor
        0.5% 13/kswapd0: 0% user + 0.5% kernel
        0.1% 319/logcat: 0% user + 0.1% kernel / faults: 20 minor
        0.3% 1343/com.amazon.ags.app: 0.3% user + 0% kernel / faults: 174 minor
        0% 1//init: 0% user + 0% kernel / faults: 85 minor
        0% 1944/com.amazon.mas.test: 0% user + 0% kernel / faults: 19 minor
        100% TOTAL: 73% user + 25% kernel + 0.1% irq + 0.1% softirq
        CPU usage from 5132ms to 5817ms later:
        62% 1912/name.mycompany.android.myapp: 56% user + 6.4% kernel / faults: 12 minor
        35% 1912/ndroid.myapp: 35% user + 0% kernel
        33% 1957/Thread-208: 22% user + 11% kernel
        1.6% 1919/Compiler: 1.6% user + 0% kernel
        15% 275/system_server: 5.7% user + 10% kernel / faults: 1 minor
        11% 293/ActivityManager: 4.3% user + 7.2% kernel
        13% 29/mmcqd: 0% user + 13% kernel
        0.7% 12/pdflush: 0% user + 0.7% kernel
        1.4% 40/mediaserver: 0% user + 1.4% kernel
        1.4% 40/mediaserver: 0% user + 1.4% kernel
        0.4% 47/adbd: 0% user + 0.4% kernel
        0.4% 47/adbd: 0% user + 0.4% kernel
        100% TOTAL: 62% user + 35% kernel + 1.4% irq
04-04 13:01:53.925: INFO/ActivityManager(275): Tablet:Platform:package=name.mycompany.android.myapp;DV;1,event=app-anr;DV;1:NR
04-04 13:01:53.925: INFO/Process(275): Sending signal. PID: 1912 SIG: 6

Upvotes: 0

Views: 972

Answers (3)

4ntoine
4ntoine

Reputation: 20420

Sorry for not publishing the full source code and the reason was in it: i was handling the results from the service right in ResultReceiver.onReceiveResult(). The problem was resolved by using Handler, so i just had to put processing in runnable queue. Also i reduced state saving invocations count after each intent is received (which reduced invocations count on ui thread) and saved it once in the end.

private Handler handler = new Handler();

/**
 *
 */
private class ReceiveResultRunnable implements Runnable {

    private int resultCode;
    private Bundle resultData;

    public ReceiveResultRunnable(int resultCode, Bundle resultData) {
        this.resultCode = resultCode;
        this.resultData = resultData;
    }

    public void run() {
        switch (resultCode) {
            case RESULT_STARTED:
                listener.onStarted();
                break;

            case RESULT_MESSAGE:
                String message = resultData.getString(DATA_MESSAGE);
                listener.onMessage(message);
                break;

            case RESULT_PROGRESS:
                int progress = resultData.getInt(DATA_PROGRESS);
                listener.onProgress(progress);
                break;

            case RESULT_ERROR:
                Throwable t = (Throwable) resultData.getSerializable(DATA_ERROR);
                listener.onError(t);
                break;

            case RESULT_OK:
                int version = resultData.getInt(DATA_REPOSITORY_VERSION);
                listener.onFinished(version);
                break;

            // ...

        }
    }
}

@Override
protected void onReceiveResult(final int resultCode, final Bundle resultData) {
    super.onReceiveResult(resultCode, resultData);

    handler.post(new ReceiveResultRunnable(resultCode, resultData));
}

Upvotes: 0

Tas Morf
Tas Morf

Reputation: 3075

onStartCommand is called in your application's main thread. Have a look at this. Instead, if you use an IntentService, you should override onHandleIntent (Intent intent), which actually gets called in the worker thread.

Upvotes: 0

rperryng
rperryng

Reputation: 3253

The documentation http://developer.android.com/guide/components/services.html states

Caution: A services runs in the same process as the application in which it is declared and in the main thread of that application, by default. So, if your service performs intensive or blocking operations while the user interacts with an activity from the same application, the service will slow down activity performance. To avoid impacting application performance, you should start a new thread inside the service.

Furthermore, you can check the state of threads in eclipse by opening the Debug view and seeing the status of your threads.

I'm a little confused as to why you have chosen to use a service over an AsyncTask or IntentService, which handles creating a non-ui thread for you.

Upvotes: 1

Related Questions