2

给定一个 Android 意图服务,其工作是进行后台网络通信(例如,进行 REST 调用以同步数据),当意图服务捕获到 时IOException,从错误中恢复的良好做法是什么?

假设传输的数据量足够小,我们可以从头开始重试网络操作。如果设备失去网络连接,我们希望在连接恢复时收到通知,然后重试。如果我们没有失去连接,我们假设服务器或其网络链接已关闭,并希望在延迟后重试。

尽快完成通信操作并不重要,但更快确实意味着更好的用户体验,尽管它必须权衡带宽使用和电池寿命。

希望这是一个常见的要求,并且该功能已融入 Android。如果是,它在哪里,或者如果不是,智能重启 Intent 服务的代码会是什么样子?

4

1 回答 1

3

我创建了一个帮助程序类,它检查网络状态并等待网络可用,或者如果可用,则使用指数退避延迟。

/**
 * 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();
        }
    }
}
于 2013-11-06T16:32:10.017 回答