AlarmManager и BroadcastReceiver вместо Service — это плохо? (Тайм-аут)

СПРАВОЧНАЯ ИНФОРМАЦИЯ:

Мне нужно обновлять некоторые данные из Интернета примерно каждый час или около того, даже когда мое приложение закрыто. Обновление самих данных занимает от 40 секунд до 1 минуты. Затем он сохраняется как Serializable в файл. Этот файл читается при запуске моего приложения.

ЭТО ПОДХОД Я ПРИНЯЛ НА МОМЕНТ (не используя Службу)

используйте AlarmManager и BroadcastReceiver следующим образом:

private void set_REFRESH_DATA_Alarm(){
    mContext = Main.this;
    alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    broadcast_intent = new Intent(mContext, 
            RepeatingAlarmReceiver_REFRESH_DATA.class);
    pendingIntent = PendingIntent.getBroadcast(mContext, 0,  broadcast_intent, 0);
    // do a REFRESH every hour, starting for the first time in 30 minutes from now ...
    Calendar now = Calendar.getInstance();
    long triggerAtTime = now.getTimeInMillis()+ (1 * 30 * 60 * 1000); // starts in 30 minutes
    long repeat_alarm_every = (1 * 60 * 60 * 1000); // repeat every 60 minutes
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtTime, 
            repeat_alarm_every, pendingIntent);
}

Мой RepeatingAlarmReceiver_REFRESH_DATA.class отвечает за обновление данных из Интернета:

public class RepeatingAlarmReceiver_REFRESH_DATA extends BroadcastReceiver {

    public static Context mContext;
    ConnectivityManager mConnectivity;

    @Override
    public void onReceive(Context context, Intent intent) {
        mContext = context;
        // if Network connection is OK (Wifi or Mobile) then Load data ...
        mConnectivity = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        Log.i("Hub",
                "mConnectivity.getNetworkInfo(0)="
                        + mConnectivity.getNetworkInfo(0));
        Log.i("Hub",
                "mConnectivity.getNetworkInfo(1)="
                        + mConnectivity.getNetworkInfo(1));
        if ((mConnectivity.getNetworkInfo(0).getState() == NetworkInfo.State.CONNECTED)
                || (mConnectivity.getNetworkInfo(1).getState() == NetworkInfo.State.CONNECTED)) {
            Log.i("Hub", "Connectivity OK ...");
            Refresh_HIST_DATA();
        } else {
            // else Show Dialog "No network connection" ...
            Log.i("Hub",
                    "No network connection for the moment... will try again later!");
        }
    }

    // =========================================================================
    private void Refresh_HIST_DATA() {
        Log.i("Hub", "Refresh_HIST_DATA()... Starting ...");
        // etc...
    }
}

В манифесте у меня есть:

<receiver android:name="com.cousinHub.myapp.RepeatingAlarmReceiver_REFRESH_DATA" android:process=":remote" />

ПРОБЛЕМА:

Аварийный сигнал срабатывает вовремя, и обновление начинается, но затем примерно через 10 секунд оно останавливается (тайм-аут):

06–25 11:55:05.278: WARN/ActivityManager(76): тайм-аут трансляции BroadcastRecord{44bb4348 null} — Receiver=android.os.BinderProxy@44bcc670

06-25 11:55:05.278: WARN/ActivityManager(76): Получатель во время ожидания: ResolveInfo{44bb42c0 com.cousinHub.myapp.RepeatingAlarmReceiver_REFRESH_DATA p=0 o=0 m=0x0}

06-25 11:55:05.278: ИНФО/Процесс(76): Отправка сигнала. PID: 819 SIG: 9

06-25 11:55:05.298: INFO/ActivityManager(76): процесс com.cousinHub.myapp:remote (pid 819) умер.

ps: как ни странно, этот «тайм-аут» не происходит примерно через 10 секунд на моем HTC Hero (по-прежнему на Android 1.5 - уровень API 4), но хорошо на моем Nexus One (2.1-update1)

Вопросы:

  1. Почему этот таймаут? Любой простой способ избежать этого?
  2. Правильно ли я настроил свой BroadcastReceiver в манифесте? Нужно ли мне что-то добавить (чтобы избежать этого тайм-аута)?
  3. Должен ли я обязательно воспользоваться услугой для такого рода функций «Обновление из Интернета»? (учитывая эту статью: http://www.androidguys.com/2009/09/09/diamonds-are-forever-services-are-not/) Если ДА (мне следует переключиться на службу): любые хорошие фрагменты кода/учебник для этого .. .

Как всегда, спасибо за вашу помощь.

H.


person Hubert    schedule 25.06.2010    source источник


Ответы (3)


Почему этот таймаут?

Вы работаете в основном потоке приложения. Вы не можете работать в основном потоке приложения более нескольких секунд. Кроме того, делая это, вы наносите ущерб производительности устройства (поскольку вы работает с приоритетом переднего плана), например вызывает потерю частоты кадров в играх или видео.

Любой простой способ избежать этого?

Не выполняйте значительную работу (> 100 мс) в основном потоке приложения. Попросите вашего BroadcastReceiver делегировать IntentService, возможно, WakefulIntentService.

Правильно ли я настроил свой BroadcastReceiver в манифесте?

Пожалуйста, пожалуйста, пожалуйста, пожалуйста, пожалуйста, избавьтесь от android:process=:remote. Он вам не нужен, он вам не помогает, а еще больше снижает производительность устройства.

Должен ли я обязательно воспользоваться услугой для такого рода функций «Обновление из Интернета»? (учитывая эту статью: http://www.androidguys.com/2009/09/09/diamonds-are-forever-services-are-not/) Если ДА (мне следует переключиться на службу): любые хорошие фрагменты кода/учебник для этого .. .

ИМХО, да. Затем снова я написал этот пост в блоге. Пример см. в проекте WakefulIntentService.

person CommonsWare    schedule 25.06.2010
comment
и WakefulIntentService сможет прожить более 30 секунд и обновить все мои данные? будет ли он иметь доступ к файлу, который затем будет читать мое приложение? - person Hubert; 25.06.2010
comment
и WakefulIntentService сможет прожить более 30 секунд и обновить все мои данные? IntentService будет существовать до тех пор, пока ваш onHandleIntent() (или doWakefulWork() в WakefulIntentService ) делает свое дело. будет ли он иметь доступ к файлу, который затем будет читать мое приложение? Столько же, сколько сегодня делает ваш BroadcastReceiver. Обратите внимание, что я не вы, и поэтому не писал ваш код, поэтому я понятия не имею, что это за файл, который будет читать мое приложение. - person CommonsWare; 25.06.2010
comment
Последний вопрос, если позволите: как лучше всего получить доступ к контексту приложения (mContext =› возможно, Context mContext = (Context) getApplicationContext();) в AppService.doWakefulWork? Цель состоит в том, чтобы получить доступ к настройкам моих приложений с помощью pref = PreferenceManager.getDefaultSharedPreferences(mContext); и мои файлы с FileOutputStream fOut = mContext.openFileOutput(Globals.mFileName, Context.MODE_WORLD_READABLE); - person Hubert; 26.06.2010
comment
ДА ЭТО РАБОТАЕТ с контекстом mContext = (Context) getApplicationContext(); - person Hubert; 26.06.2010
comment
@CommonsWare: не могли бы вы помочь мне с этим, пожалуйста: stackoverflow.com/questions/15187100/ - person Shoshi; 04.03.2013

Для информации, я пробовал с новым потоком, и он работает при подключении к Wi-Fi (требуется около 1'30 дюймов, чтобы обновить данные, когда телефон спит, он не «убивается»!

//let's try with a new separate thread ?
        new Thread(new Runnable() {
            public void run() {
                Refresh_HIST_DATA();
            }
          }).start();

но НЕ на мобильном телефоне (GPRS), так как он отключается примерно через 10 секунд!

На данный момент это половинчатое решение, и я попробую решение CommonsWare для более чистого / более устойчивого подхода...

Давайте посмотрим, всегда ли новое решение для потоков работает нормально или это просто удача (я тестировал только пару часов) ...

Если у кого-то еще есть другое предложение, пожалуйста, опубликуйте его.

person Hubert    schedule 25.06.2010
comment
новое решение потока не всегда работает, поэтому избегайте ! Тогда давайте перейдем к решению CommonsWare. - person Hubert; 26.06.2010

Вместо нити. Вы можете запустить AsyncTask из метода onRecive() вашего широковещательного приемника. Это не будет блокировать поток пользовательского интерфейса. Я сам сделал то же самое в своих проектах того же характера, т.е. он должен публиковать данные каждый час.

public void onReceive(Context context, Intent intent) {
        // start your Asynctask from here. which will post data in doInBackground() method
}
person Gaurav Gupta    schedule 28.08.2012