ANR в IntentService с собственным процессом

У меня есть IntentService со своим собственным процессом, вот так:

AndroidManifest.xml

<service 
    android:name="com.app.services.UpdateDatabaseService"
    android:process=":updateDatabaseService"
    android:label="@string/service_name"
    android:exported="false" >
</service>

Затем, когда IntentService работает, он создает два потока. Одна из задач этого потока — это длинная задача (более 1 мин). И я получаю проблему ANR.

10-24 18:51:27.923: E/ActivityManager(148): ANR in com.app:FooService
10-24 18:51:27.923: E/ActivityManager(148): Reason: Executing service com.app/.services.FooService
10-24 18:51:27.923: E/ActivityManager(148): Load: 1.08 / 0.48 / 0.21
10-24 18:51:27.923: E/ActivityManager(148): CPU usage from 13433ms to 1122ms ago:
10-24 18:51:27.923: E/ActivityManager(148):   92% 1375/com.emanga:updateMangaDatabase: 87% user + 5.7% kernel / faults: 5740 minor
10-24 18:51:27.923: E/ActivityManager(148):   0.4% 148/system_server: 0.2% user + 0.1% kernel / faults: 13 minor
10-24 18:51:27.923: E/ActivityManager(148):   0.2% 45/adbd: 0% user + 0.2% kernel
10-24 18:51:27.923: E/ActivityManager(148):   0.1% 758/logcat: 0% user + 0.1% kernel
10-24 18:51:27.923: E/ActivityManager(148):   0% 266/com.android.phone: 0% user + 0% kernel / faults: 2 minor
10-24 18:51:27.923: E/ActivityManager(148): 93% TOTAL: 87% user + 6.2% kernel + 0% irq
10-24 18:51:27.923: E/ActivityManager(148): CPU usage from 1804ms to 2442ms later:
10-24 18:51:27.923: E/ActivityManager(148):   70% 1375/com.emanga:updateMangaDatabase: 65% user + 4.9% kernel / faults: 123 minor

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

ИЗМЕНИТЬ 1:

Я изменил IntentService на Service, но все еще получаю ту же проблему. Я постараюсь объяснить, что я хочу получить. В архитектуре моего приложения есть действия, которые показывают данные, восстановленные из базы данных с помощью загрузчиков. Если в базе данных нет запрошенных данных, то она запросит службу, которая восстановит данные из Интернета (служба управляет интернет-запросами, анализирует некоторые html, обновляет базу данных новыми данными, и в конце служба уведомляет об изменениях). )

У этой службы есть собственный процесс, потому что, хотя служба улавливает исключения, которые могут возникнуть, таким образом приложение становится более устойчивым к сбоям в Интернете или синтаксическом анализаторе (приложение не аварийно завершает работу).

Цель загрузчиков — получить данные из базы данных, а цель службы — получить данные из Интернета и обновить базу данных. Связь между загрузчиками и службами предназначена только для уведомления об изменениях или запросах.

Наконец, у этой службы есть очередь задач и исполнитель, который запускает задачи.

public class UpdateDatabaseService extends Service {

    private static final String ACTION = "com.app.services.UpdateDatabaseService";
    public static final String ACTION_TASK_1 = ACTION + ".latestChapters";
    public static final String ACTION_TASK_2 = ACTION + ".latestMangas";

    private static final byte PARALLELTASKS = 2;

    public LinkedBlockingQueue<Runnable> tasks = new LinkedBlockingQueue<Runnable>();

    private ExecutorService executor = Executors.newFixedThreadPool(PARALLELTASKS);

    private final IBinder mBinder = new MyBinder();

    public class MyBinder extends Binder {
        public UpdateDatabaseService getService() {
            return UpdateDatabaseService.this;
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }

    @Override
    public void onCreate(){
        // By default always it does DefaultTask1
        tasks.put(new DefaultTask1());

        if(!isEmptyTable("tableFoo")){
            tasks.put(new DefaultTask2());
        }

        executor();
    }

    public int onStartCommand(Intent intent, int flags, int startId) {

        if(intent != null && intent.getAction() != null) {

            String action = intent.getAction();
            if(action == ACTION_TASK_1){
                        tasks.put(new Task1());
            } else
            if(action == ACTION_TASK_2){
                tasks.put(new Task2());
            } 
        }


        // We want this service to continue running until it is explicitly
            // stopped, so return sticky.
        return START_STICKY;
    }

    private void executor(){

        while(true){    
                executor.execute(tasks.take());

    }


}

person Fran b    schedule 24.10.2013    source источник
comment
Разместите какой код вы можете из вашего сервиса, здесь нет ничего полезного.   -  person Nathan Schwermann    schedule 24.10.2013


Ответы (1)


У каждого процесса есть основной поток приложения (UI). Это поток, который вызывает методы жизненного цикла вашего компонента, такие как onStartCommand() из Service. И вы не можете завязать эту нить, иначе вы получите ANR.

Обычно это не проблема с IntentService. Ни один подкласс IntentService не должен создавать свои собственные потоки, поэтому есть только два важных потока: основной поток приложения и фоновый поток, предоставленный IntentService (тот, который вызывает onHandleIntent()). Убедитесь, что вся ваша работа находится в onHandleIntent(). Если вы разветвляете дополнительные потоки самостоятельно, либо избавьтесь от них, либо переключитесь с IntentService на обычный Service, чтобы вы могли организовать закрытие Service только тогда, когда все ваши потоки сделали свою работу, а не когда onHandleIntent() сделано.

Кроме того, я настоятельно рекомендую вам избавиться от android:process, так как вы тратите ЦП и ОЗУ без видимой дополнительной выгоды для пользователя.

person CommonsWare    schedule 24.10.2013
comment
Полезное объяснение. Я добавляю некоторый код и информацию об этой проблеме (Редактировать 1), потому что мне нужен этот процесс для заполнения базы данных приложения, а затем служба будет управлять базой данных обновлений с помощью интернет-ресурсов. Я думаю, что это полезно для пользователя и только в первом случае критично. - person Fran b; 26.10.2013
comment
@Franb: у этой службы есть собственный процесс, потому что, хотя служба улавливает исключения, которые могут возникнуть, таким образом приложение становится более устойчивым к сбоям в Интернете или синтаксическом анализаторе (приложение не будет аварийно завершать работу) - это не является веской причиной для иметь несколько процессов в среде с ограниченными ресурсами, такой как мобильное устройство. Опять же, вы тратите впустую оперативную память, процессор и батарею. - person CommonsWare; 27.10.2013
comment
Итак, когда вы используете отдельный процесс вместо потока? или В каких случаях передовая практика использует процесс? - person Fran b; 03.11.2013
comment
@Franb: В каких случаях передовая практика использует процесс? -- Никак не могу придумать. Любой случай, когда люди используют его, может быть обработан другими способами без необходимости второго процесса. Например, некоторые люди используют его, чтобы получить больше места в куче на Android 1.x/2.x, но вы можете использовать NDK для аналогичной цели. - person CommonsWare; 03.11.2013