给定一个Android意图服务,其工作是进行后台网络通信(例如,进行REST调用以同步数据),当意图服务捕获IOException
时,从错误中恢复的好方法是什么? / p>
让我们假设传输的数据量足够小,以便我们从头开始重试网络操作。如果设备丢失了网络连接,我们希望在恢复连接时收到通知,然后重试。如果我们没有丢失连接,我们假设服务器或其网络链接已关闭,并希望在延迟后再试一次。
尽快完成通信操作并不重要,但更快意味着更好的用户体验,尽管必须与带宽使用和电池寿命进行权衡。
希望这是一个常见的要求,功能已融入Android。如果是这样,它在哪里,或者如果没有,那么智能重启意图服务的代码会是什么样的呢?
答案 0 :(得分:2)
我创建了一个辅助类,用于检查网络状态并等待网络可用,或者如果可用,则使用指数退避延迟。
/**
* Handles error recovery for background network operations.
*
* Recovers from inability to perform background network operations by applying a capped exponential backoff, or if connectivity is lost, retrying after it is
* restored. The goal is to balance app responsiveness with battery, network, and server resource use.
*
* Methods on this class are expected to be called from the UI thread.
* */
public final class ConnectivityRetryManager {
private static final int INITIAL_DELAY_MILLISECONDS = 5 * 1000;
private static final int MAX_DELAY_MILLISECONDS = 5 * 60 * 1000;
private int delay;
public ConnectivityRetryManager() {
reset();
}
/**
* Called after a network operation succeeds. Resets the delay to the minimum time and unregisters the listener for restoration of network connectivity.
*/
public void reset() {
delay = INITIAL_DELAY_MILLISECONDS;
}
/**
* Retries after a delay or when connectivity is restored. Typically called after a network operation fails.
*
* The delay increases (up to a max delay) each time this method is called. The delay resets when {@link reset} is called.
*/
public void retryLater(final Runnable retryRunnable) {
// Registers to retry after a delay. If there is no Internet connection, always uses the maximum delay.
boolean isInternetAvailable = isInternetAvailable();
delay = isInternetAvailable ? Math.min(delay * 2, MAX_DELAY_MILLISECONDS) : MAX_DELAY_MILLISECONDS;
new RetryReciever(retryRunnable, isInternetAvailable);
}
/**
* Indicates whether network connectivity exists.
*/
public boolean isInternetAvailable() {
NetworkInfo network = ((ConnectivityManager) App.getContext().getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
return network != null && network.isConnected();
}
/**
* Calls a retry runnable after a timeout or when the network is restored, whichever comes first.
*/
private class RetryReciever extends BroadcastReceiver implements Runnable {
private final Handler handler = new Handler();
private final Runnable retryRunnable;
private boolean isInternetAvailable;
public RetryReciever(Runnable retryRunnable, boolean isInternetAvailable) {
this.retryRunnable = retryRunnable;
this.isInternetAvailable = isInternetAvailable;
handler.postDelayed(this, delay);
App.getContext().registerReceiver(this, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
@Override
public void onReceive(Context context, Intent intent) {
boolean wasInternetAvailable = isInternetAvailable;
isInternetAvailable = isInternetAvailable();
if (isInternetAvailable && !wasInternetAvailable) {
reset();
handler.post(this);
}
}
@Override
public void run() {
handler.removeCallbacks(this);
App.getContext().unregisterReceiver(this);
retryRunnable.run();
}
}
}