3

我試圖通過每次請求失敗時調度線程handler.postDelayed(...)來重試失敗的http調用來實現指數退避。問題在於,我正在調度第一個線程之後死亡的IntentService,因此處理程序無法自行調用。我收到以下錯誤:從IntentService調度遞歸處理程序以重試http調用

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重試最好/最乾淨的方式是什麼?

回答

4

使用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您使用手柄在後臺做了電話,然後安排的意圖,在每次失敗時重新發送,通過多少次重試以及最後一次重試延遲的時間來增加附加功能。

注意:如果您嘗試向該服務發送多個意向,並且多個意向失敗並且必須重新安排AlarmManager,則根據Intent.filterEquals(Intent intent),如果意圖被認爲相等,則只會傳遞最新的意向。如果您的意圖與附加附加內容的意圖完全相同,則這將是一個問題,並且您在創建PendingIntent時必須使用唯一的requestCode用於每個要重新安排的意圖。沿着這些路線的東西:

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

我想你可以用你的共享偏好來存儲每一個你必須安排一個重試時間遞增的請求的代碼。

+1

非常好,合乎邏輯,謝謝!我不得不通過將getService的最後一個參數更改爲'PendingIntent.FLAG_UPDATE_CURRENT' – Crocodile