如何处理来自 ContentProvider 的异常?

How handle exception from ContentProvider?

我有一个执行数据库或网络请求的内容提供程序。如果没有网络,则会引发 IOException,我想再做一次请求(这次在数据库上)。

我的代码看起来像

/**
 * Content provider
 */
//...
public Cursor query (Uri uri /*,...*/) {
    switch(uriMatcher.match(uri)) {
        case NETWORK:
            JSONObject json;
            try {
                json = new HttpTask().getItems(); // If no network throws IoException
            } catch (IOException e) {
                //Do something
            }
            return new Cursor(/* ... */);
            break;
        case DATABASE:
            //Access database
            return new Cursor(/* ... */);
            break;
    }
}
//...

我应该:

  1. 仅在contentProvider处理异常,出错直接移至数据库case?
  2. 处理 HttpTask 中的异常,因此 json 将 "just" 为 null
  3. 像这样创建自定义 cursorloader one 和自定义 RuntimeException ?如果是这样,在内容提供程序中我将能够抛出异常并在 CursorLoader 中捕获它。

编辑 1

或者我应该在使用前检查网络状态:

ConnectivityManager connectivityManager = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager != null) {
    NetworkInfo ni = connectivityManager.getActiveNetworkInfo();
    if (ni.getState() != NetworkInfo.State.CONNECTED) {
        // record the fact that there is not connection
        isConnected = false;
    }
}

编辑 2 下面是使用自定义异常和自定义加载器的方法

NoNetworkException.java

public class NoNetworkException extends RuntimeException {
    public NoNetworkException(String s) {
        super(s);
    }
}

NoNetworkSafeCursorLoader.java

public class NoNetworkSafeCursorLoader extends CursorLoader {
    private NoNetworkException exception;

    public NoNetworkSafeCursorLoader(Context context, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        super(context, uri, projection, selection, selectionArgs, sortOrder);
    }

    @Override
    public Cursor loadInBackground() {
        try {
            return super.loadInBackground();
        } catch(NoNetworkException exception) {
            this.exception = exception;
        }
        return null;
    }

    public NoNetworkException getException() {
        return exception;
    }
}

现在在内容提供者中你需要抛出 NoNetworkException

//...
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) throws NoNetworkException {
    //...
}
//...

并且在回调中你必须检查是否抛出了异常

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) throws NoNetworkException {
    NoNetworkSafeCursorLoader l = (NoNetworkSafeCursorLoader) loader;

    if(l.getException() != null) {
        //No network, do somethings
    }
    else {
        //Network ok, do otherthings
    }
}

你一定要先检查一下网络状态。然后,如果网络请求失败,请在 catch-block

中访问数据库
public Cursor query (Uri uri /*,...*/) {
    Cursor cursor;
    switch(uriMatcher.match(uri)) {
        case NETWORK:
            JSONObject json;
            try {
                 json = new HttpTask().getItems(); // If no network throws IoException
                 cursor = new Cursor(/* convert json */)
            } catch (IOException e) {
                cursor = //access the database
            }
            break;
        case DATABASE:
            //Access database
            cursor = new Cursor(/* ... */);
            break;
        }
    return cursor;
}

您还应该看看 Sync Adapters,google 告诉您应该这样做。根据您的用例,它可能会有很大帮助,因为同步适配器已经处理网络检查和其他事情以最大限度地延长电池寿命。

在这种情况下,您当然可以处理的事情很少,

  1. 是的,正如@leoderprofi 所说,您必须检查网络是否可以用于您想要的 HTTP 任务。

  2. 而不是使用Try Catch 来管理异常抛出之后。您可以做的是在此之前检查对象的可用性。比如,在解析之前检查 JSON 是否为 null,在获取游标之后检查它是否为 null,如果不是 null 则它实际上是否有任何数据初始化,很可能会出现异常在游标中存在游标对象,但其中没有任何数据,当您从中获取数据时会出现异常。

  3. 是的,您可以创建一个自定义加载器并在那里处理和抛出异常,您还可以为使用接口管理它的成功和错误回调创建一个。

希望对您有所帮助。 谢谢。

一个合适的解决方案...封装您的网络功能,如果网络不可用则故障转移到数据库,不需要第二次查询调用。

除非您的客户真的想知道网络尝试失败,否则我不会创建自定义加载程序和异常,但如果它只是用数据库请求跟进它,那么为什么还要通知任何人,但那就是取决于您的用例。

在不知道您实际需求的情况下;摆脱 NETWORK 和 DATABASE 内容 URI 并只使用一个 MY_DATA 会更简洁。您的客户是否真的知道或关心它是否请求 either/or?

public Cursor query (Uri uri /*,...*/) {
    switch(uriMatcher.match(uri)) {
        case NETWORK:
            // check network
            if ( isNetworkAvailable(context) ) {
                JSONObject json;
                try {
                    json = new HttpTask().getItems(); // If no network throws IoException
                    return new Cursor(/* ... */);
                } catch (IOException e) {
                    // Log a warning
                }
            }
            // delete this break;
            // failover to DB
        case DATABASE:
            //Access database
            return new Cursor(/* ... */);
            break;
    } 
}

private boolean isNetworkAvailable(Context ctx) {
    boolean isConnected = false;
    ConnectivityManager connectivityManager = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connectivityManager != null)
        NetworkInfo ni = connectivityManager.getActiveNetworkInfo();
        isConnected = ni.getState() == NetworkInfo.State.CONNECTED;
    }
    return isConnected;
}

您甚至可以制作 public static boolean isNetworkAvailable(...) 作为您可以在其他地方使用的简单实用方法。