【问题标题】:Scheduling recursive handlers from an IntentService for retrying http calls从 IntentService 调度递归处理程序以重试 http 调用
【发布时间】:2015-06-04 17:09:44
【问题描述】:

我正在尝试通过在每次我的请求失败时使用handler.postDelayed(...) 调度一个线程来实现指数退避,以重试失败的 http 调用。问题是我从 IntentService 执行此操作,该 IntentService 在调度第一个线程后死亡,因此处理程序无法调用自身。我收到以下错误:

java.lang.IllegalStateException: Handler (android.os.Handler) {2f31b19b} sending message to a Handler on a dead thread

我的 IntentService 课程:

@Override
    protected void onHandleIntent(Intent intent) {

        ......

        Handler handler = new Handler();
        HttpRunnable httpRunnable = new HttpRunnable(info, handler);
        handler.postDelayed(httpRunnable, 0);

}

我的自定义 Runnable:

public class HttpRunnable implements Runnable {

  private String info;
  private static final String TAG = "HttpRunnable";
  Handler handler = null;
  int maxTries = 10;
  int retryCount = 0;
  int retryDelay = 1000; // Set the first delay here which will increase exponentially with each retry

  public HttpRunnable(String info, Handler handler) {
    this.info = info;
    this.handler = handler;
  }

  @Override
  public void run() {
    try {
        // Call my class which takes care of the http call
        ApiBridge.getInstance().makeHttpCall(info);
    } catch (Exception e) {
        Log.d(TAG, e.toString());
        if (maxTries > retryCount) {
        Log.d(TAG,"%nRetrying in " + retryDelay / 1000 + " seconds");
            retryCount++;
            handler.postDelayed(this, retryDelay);
            retryDelay = retryDelay * 2;
        }
    }
  }
}

有没有办法让我的处理程序保持活力?用指数退避安排我的 http 重试的最佳/最干净的方法是什么?

【问题讨论】:

    标签: java android multithreading android-handler android-intentservice


    【解决方案1】:

    使用IntentService 的主要优点是它在onHandleIntent(Intent intent) 方法中为您处理所有后台线程。在这种情况下,您没有理由自己管理处理程序。

    您可以使用AlarmManager 来安排向您的服务交付意图的方法。您会将重试信息保留在要交付的意图中。

    我在想这样的事情:

    public class YourService extends IntentService {
    
        private static final String EXTRA_FAILED_ATTEMPTS = "com.your.package.EXTRA_FAILED_ATTEMPTS";
        private static final String EXTRA_LAST_DELAY = "com.your.package.EXTRA_LAST_DELAY";
        private static final int MAX_RETRIES = 10;
        private static final int RETRY_DELAY = 1000;
    
        public YourService() {
            super("YourService");
        }
    
        @Override
        protected final void onHandleIntent(Intent intent) {
    
            // Your other code obtaining your info string.
    
            try {
                // Make your http call.
                ApiBridge.getInstance().makeHttpCall(info);
            } catch (Exception e) {
                // Get the number of previously failed attempts, and add one.
                int failedAttempts = intent.getIntExtra(EXTRA_FAILED_ATTEMPTS, 0) + 1;
                // if we have failed less than the max retries, reschedule the intent
                if (failedAttempts < MAX_RETRIES) {
                    // calculate the next delay
                    int lastDelay = intent.getIntExtra(EXTRA_LAST_DELAY, 0);
                    int thisDelay;
                    if (lastDelay == 0) {
                        thisDelay = RETRY_DELAY;
                    } else {
                        thisDelay = lastDelay * 2;
                    }
                    // update the intent with the latest retry info
                    intent.putExtra(EXTRA_FAILED_ATTEMPTS, failedAttempts);
                    intent.putExtra(EXTRA_LAST_DELAY, thisDelay);
                    // get the alarm manager
                    AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
                    // make the pending intent
                    PendingIntent pendingIntent = PendingIntent
                            .getService(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
                    // schedule the intent for future delivery
                    alarmManager.set(AlarmManager.RTC_WAKEUP,
                            System.currentTimeMillis() + thisDelay, pendingIntent);
                }
            }
        }
    }
    

    这只是让您正在使用的 IntentService 在后台处理调用,然后安排在每次失败时重新发送 Intent,向其添加额外内容,包括重试了多少次以及多长时间上次重试延迟是。

    注意:如果您尝试向该服务发送多个意图并且多个意图失败并且必须使用AlarmManager 重新安排,则如果根据Intent.filterEquals(Intent intent) 认为意图相等,则只会传递最新的意图。如果您的意图与附加的附加内容相同,这将是一个问题,并且您必须在创建PendingIntent 时为每个被重新安排的意图使用唯一的请求代码。大致如下:

    int requestCode = getNextRequestCode();
    PendingIntent pendingIntent = PendingIntent
        .getService(getApplicationContext(), requestCode, intent, 0);
    

    我想您可以使用共享首选项来存储请求代码,每次您必须安排重试时该代码都会增加。

    【讨论】:

    • 非常好,合乎逻辑,谢谢!我不得不通过将 getService 的最后一个参数更改为 'PendingIntent.FLAG_UPDATE_CURRENT' 来做一个小修复
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-10
    相关资源
    最近更新 更多