如何在Android上使用Retrofit处理"无互联网连接"

Ale*_*exV 111 android retrofit

我想处理没有互联网连接的情况.通常我会跑:

ConnectivityManager cm =
    (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
                  activeNetwork.isConnectedOrConnecting();
Run Code Online (Sandbox Code Playgroud)

(从此处)在将请求发送到网络之前,如果没有互联网连接则通知用户.

从我看到的Retrofit没有具体处理这种情况.如果没有互联网连接我会RetrofitError以超时为理由.

如果我想将这种检查结合到每个使用Retrofit的HTTP请求中,我应该怎么做?或者我应该这样做.

谢谢

亚历克斯

Ale*_*exV 59

我最终做的是创建一个自定义的Retrofit客户端,它在执行请求之前检查连接并抛出异常.

public class ConnectivityAwareUrlClient implements Client {

    Logger log = LoggerFactory.getLogger(ConnectivityAwareUrlClient.class);

    public ConnectivityAwareUrlClient(Client wrappedClient, NetworkConnectivityManager ncm) {
        this.wrappedClient = wrappedClient;
        this.ncm = ncm;
    }

    Client wrappedClient;
    private NetworkConnectivityManager ncm;

    @Override
    public Response execute(Request request) throws IOException {
        if (!ncm.isConnected()) {
            log.debug("No connectivity %s ", request);
            throw new NoConnectivityException("No connectivity");
        }
        return wrappedClient.execute(request);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在配置时使用它 RestAdapter

RestAdapter.Builder().setEndpoint(serverHost)
                     .setClient(new ConnectivityAwareUrlClient(new OkHttpClient(), ...))
Run Code Online (Sandbox Code Playgroud)

  • 您的 NetworkConnectivityManager 类来自哪里?风俗? (2认同)
  • OkHttpClient不起作用,而是使用OKClient()。BDW不错的答案。谢谢。 (2认同)

Muh*_*ifi 45

自改造以来,1.8.0这已被弃用

retrofitError.isNetworkError()
Run Code Online (Sandbox Code Playgroud)

你必须使用

if (retrofitError.getKind() == RetrofitError.Kind.NETWORK)
{

}
Run Code Online (Sandbox Code Playgroud)

您可以处理多种类型的错误:

NETWORK 与服务器通信时发生IOException,例如超时,无连接等...

CONVERSION 在(de)序列化主体时抛出异常.

HTTP 从服务器接收到非200 HTTP状态代码,例如502,503等......

UNEXPECTED尝试执行请求时发生内部错误.最佳做法是重新抛出此异常,以便应用程序崩溃.

  • 避免在变量上使用 `equals`,始终使用 `CONSTANT.equals(variable)` 来避免可能的 NullPointerException。或者在这种情况下甚至更好,枚举接受 == 比较所以 `error.getKind() == RetrofitError.Kind.NETWORK` 可能是更好的方法 (8认同)

sag*_*nav 35

@AlexV你确定当没有互联网连接时,RetrofitError包含超时作为原因(当调用getCause()时SocketTimeOutException)?

据我所知,当没有互联网连接时,RetrofitError包含一个ConnectionException作为原因.

如果你实现了ErrorHandler,你可以这样做:

public class RetrofitErrorHandler implements ErrorHandler {

    @Override
    public Throwable handleError(RetrofitError cause) {
        if (cause.isNetworkError()) {
            if (cause.getCause() instanceof SocketTimeoutException) {
                return new MyConnectionTimeoutException();
            } else {
                return new MyNoConnectionException();
            }
        } else {
            [... do whatever you want if it's not a network error ...]  
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

  • cause.isNetworkError()是**弃用**:使用`error.getKind()== RetrofitError.Kind.NETWORK` (23认同)
  • 你会如何将这个实现到你的界面,客户端?我的意思是,你把这门课连接到什么地方? (2认同)

Kev*_*vin 33

使用Retrofit 2,我们使用OkHttp Interceptor实现在发送请求之前检查网络连接.如果没有网络,则根据需要抛出异常.

这允许人们在点击Retrofit之前专门处理网络连接问题.

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Response;
import io.reactivex.Observable

public class ConnectivityInterceptor implements Interceptor {

    private boolean isNetworkActive;

    public ConnectivityInterceptor(Observable<Boolean> isNetworkActive) {
       isNetworkActive.subscribe(
               _isNetworkActive -> this.isNetworkActive = _isNetworkActive,
               _error -> Log.e("NetworkActive error " + _error.getMessage()));
    }

    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        if (!isNetworkActive) {
            throw new NoConnectivityException();
        }
        else {
            Response response = chain.proceed(chain.request());
            return response;
        }
    }
}

public class NoConnectivityException extends IOException {

    @Override
    public String getMessage() {
        return "No network available, please check your WiFi or Data connection";
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 所以你正在制作一个`OkHttpClient.Builder.addInterceptor(新的ConnectivityInterceptor(HERE))`这里应该有什么? (3认同)
  • 请问您能否包含该代码,以便人们全面了解情况? (3认同)
  • 这应该是公认的答案。此处有更多示例:http://www.migapro.com/detect-offline-error-in-retrofit-2/ (3认同)
  • 如果您打开飞行模式然后将其关闭,则存在缺陷,因为您只需设置一次变量即可使可观察到的功能失效。 (2认同)

KBo*_*Bog 15

这是我所做的API 29 & API 30

1.我创建了一个简单的 WiFiService 类,它将保存连接管理器:

   class WifiService {
    private lateinit var wifiManager: WifiManager
    private lateinit var connectivityManager: ConnectivityManager

    companion object {
        val instance = WifiService()
    }

    fun initializeWithApplicationContext (context: Context) {
        wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
        connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    }

    // Helper that detects if online
    fun isOnline(): Boolean {
        val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
        if (capabilities != null) {
            when {
                capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> return true
                capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> return true
                capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> return true
            }
        }
        return false
      }
   }
Run Code Online (Sandbox Code Playgroud)

2. 创建 ConnectivityInterceptor 来检查互联网访问:

   class ConnectivityInterceptor: Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        if (!WifiService.instance.isOnline()) {
            throw IOException("No internet connection")
        } else {
            return chain.proceed(chain.request())
        }
     }
   }
Run Code Online (Sandbox Code Playgroud)

3.在Retrofit2中使用如下:

  class RestApi {
    private val okHttpClient by lazy {
        OkHttpClient.Builder()
            .addInterceptor(ConnectivityInterceptor())
            .build()
    }

    // Define all the retrofit clients
    private val restApiClient by lazy {
        Retrofit.Builder()
            .baseUrl("http://localhost:10000")
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
     }

     // ...
  }
Run Code Online (Sandbox Code Playgroud)

4. 最后初始化 WifiService,如下所示:

class MainApplication: Application() {
  companion object {
    lateinit var instance:  MainApplication
  }

  override fun onCreate() {
    super.onCreate()
    instance = this

    setupServices()
  }

  private fun setupServices() {
    WifiService.instance.initializeWithApplicationContext(this)
  }
}
Run Code Online (Sandbox Code Playgroud)


Igo*_*sky 6

改装1

当您Throwable从http请求中收到错误消息时,可以使用以下方法检测是否为网络错误:

String getErrorMessage(Throwable e) {
    RetrofitError retrofitError;
    if (e instanceof RetrofitError) {
        retrofitError = ((RetrofitError) e);
        if (retrofitError.getKind() == RetrofitError.Kind.NETWORK) {
            return "Network is down!";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

只是这样做,即使有诸如此类的问题,您也会收到通知

UnknownHostException

SocketTimeoutException

和别的。

 @Override public void onFailure(Call<List<BrokenGitHubRepo>> call, Throwable t) {  
if (t instanceof IOException) {
    Toast.makeText(ErrorHandlingActivity.this, "this is an actual network failure :( inform the user and possibly retry", Toast.LENGTH_SHORT).show();
    // logging probably not necessary
}
else {
    Toast.makeText(ErrorHandlingActivity.this, "conversion issue! big problems :(", Toast.LENGTH_SHORT).show();
    // todo log to some central bug tracking service
} }
Run Code Online (Sandbox Code Playgroud)