转换响应后 Retrofit2 中的 NullPointerException

NullPointerException in Retrofit2 after converting response

我正在尝试从 XML 中获取类别列表。我将 Retrofit2 与 SimpleXmlConverter 和 RxJavaCallAdapter 一起使用。获取响应成功,但它包含空对象引用。因此,当我尝试将我的列表添加到 RecyclerView 的适配器时,我得到了 NPE。我认为,问题出在 POJO 类.

这里是我的可观察和对应的方法:

    restartableLatestCache(GET_CATEGORY_REQUEST,  //method from Nucleus MVP library,
            () -> api.getCategories("ukAXxeJYZN") //API method, returns Observable<Categories> (see below)
                    .subscribeOn(Schedulers.io())
                    .map(Categories::getCategories)
                    .observeOn(mainThread()),
            CategoriesFragment::onItems,
            CategoriesFragment::onNetworkError);

void onItems(List<Category> items) {
    adapter.add(items); //<- exception raises in this method
}

void onNetworkError(Throwable throwable) {
    adapter.hideProgress();
    if (throwable instanceof HttpException) {
        try {
            Log.e(TAG, "Something wrong with your query: " + ((HttpException) throwable).response().errorBody().string() );
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    if (throwable instanceof IOException) {
        Log.e(TAG, "Network Error or bad conversion: ", throwable.getCause());
    }

    Log.e(TAG, "Something goes wrong: ", throwable.getCause());
    Toast.makeText(getActivity(), throwable.getMessage(), Toast.LENGTH_LONG).show();
}

//adapter.add(items);
public void add(List<T> items) {
    int prevSize = this.items.size();
    List<T> list = new ArrayList<>(prevSize + items.size()); <- NPE raises exactly here. items is null, as debugger shows
    list.addAll(this.items);
    list.addAll(items);
    this.items = Collections.unmodifiableList(list);
    notifyItemRangeInserted(prevSize, items.size());
}

有一个例外:

FATAL EXCEPTION: main
              Process: com.example.testapplication, PID: 26290
              java.lang.IllegalStateException: Exception thrown on Scheduler.Worker thread. Add `onError` handling.
                  at rx.android.schedulers.LooperScheduler$ScheduledAction.run(LooperScheduler.java:112)
                  at android.os.Handler.handleCallback(Handler.java:739)
                  at android.os.Handler.dispatchMessage(Handler.java:95)
                  at android.os.Looper.loop(Looper.java:148)
                  at android.app.ActivityThread.main(ActivityThread.java:5438)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629)
               Caused by: rx.exceptions.OnErrorNotImplementedException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
                  at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:386)
                  at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java:383)
                  at rx.internal.util.ActionSubscriber.onError(ActionSubscriber.java:44)
                  at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:157)
                  at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:120)
                  at rx.exceptions.Exceptions.throwOrReport(Exceptions.java:204)
                  at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:144)
                  at rx.internal.operators.OperatorFilter$FilterSubscriber.onNext(OperatorFilter.java:73)
                  at rx.internal.operators.OnSubscribeCombineLatest$LatestCoordinator.drain(OnSubscribeCombineLatest.java:286)
                  at rx.internal.operators.OnSubscribeCombineLatest$LatestCoordinator.combine(OnSubscribeCombineLatest.java:228)
                  at rx.internal.operators.OnSubscribeCombineLatest$CombinerSubscriber.onNext(OnSubscribeCombineLatest.java:383)
                  at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:139)
                  at rx.internal.operators.OperatorFilter$FilterSubscriber.onNext(OperatorFilter.java:73)
                  at rx.internal.operators.OperatorMaterialize$ParentSubscriber.onNext(OperatorMaterialize.java:113)
                  at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:227)
                  at rx.android.schedulers.LooperScheduler$ScheduledAction.run(LooperScheduler.java:107)
                  at android.os.Handler.handleCallback(Handler.java:739) 
                  at android.os.Handler.dispatchMessage(Handler.java:95) 
                  at android.os.Looper.loop(Looper.java:148) 
                  at android.app.ActivityThread.main(ActivityThread.java:5438) 
                  at java.lang.reflect.Method.invoke(Native Method) 
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739) 
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629) 
               Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
                  at com.example.testapplication.util.adapters.SimpleListAdapter.add(SimpleListAdapter.java:29)
                  at com.example.testapplication.ui.cat.CategoriesFragment.onItems(CategoriesFragment.java:74)
                  at com.example.testapplication.ui.cat.CategoriesPresenter.-com_example_testapplication_ui_cat_CategoriesPresenter-mthref-1(CategoriesPresenter.java:43)
                  at com.example.testapplication.ui.cat.CategoriesPresenter$-void_onCreate_android_os_Bundle_savedState_LambdaImpl1.call(CategoriesPresenter.java)
                  at nucleus.presenter.delivery.Delivery.split(Delivery.java:26)
                  at nucleus.presenter.RxPresenter.call(RxPresenter.java:266)
                  at nucleus.presenter.RxPresenter.call(RxPresenter.java:263)
                  at rx.internal.util.ActionSubscriber.onNext(ActionSubscriber.java:39)
                  at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:139)
                  at rx.internal.operators.OperatorFilter$FilterSubscriber.onNext(OperatorFilter.java:73) 
                  at rx.internal.operators.OnSubscribeCombineLatest$LatestCoordinator.drain(OnSubscribeCombineLatest.java:286) 
                  at rx.internal.operators.OnSubscribeCombineLatest$LatestCoordinator.combine(OnSubscribeCombineLatest.java:228) 
                  at rx.internal.operators.OnSubscribeCombineLatest$CombinerSubscriber.onNext(OnSubscribeCombineLatest.java:383) 
                  at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:139) 
                  at rx.internal.operators.OperatorFilter$FilterSubscriber.onNext(OperatorFilter.java:73) 
                  at rx.internal.operators.OperatorMaterialize$ParentSubscriber.onNext(OperatorMaterialize.java:113) 
                  at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:227) 
                  at rx.android.schedulers.LooperScheduler$ScheduledAction.run(LooperScheduler.java:107) 
                  at android.os.Handler.handleCallback(Handler.java:739) 
                  at android.os.Handler.dispatchMessage(Handler.java:95) 
                  at android.os.Looper.loop(Looper.java:148) 
                  at android.app.ActivityThread.main(ActivityThread.java:5438) 
                  at java.lang.reflect.Method.invoke(Native Method) 
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739) 
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629) 

你可以看看我的XMLhere.

我的 POJO:

@Root(strict = false, name = "categories")
public class Categories {

  @Path("yml_catalog/shop")
  @ElementList(inline = true, entry="category", type = Category.class)
  private List<Category> categories;
  //getters, setters
}

@Root(strict = false)
@Path("yml_catalog/shop/categories")
public class Category {
  @Attribute
  private int id;

  @Text
  private String categoryName;
  //getters, setters
}

那么,如何修复我的 POJO 以获得正确转换的对象?

Getting response is successful

引用语句的问题在于,当 return 与 response.isSuccessful() 为真时,响应可能实际上是 return 状态代码范围。

例如:

response.isSuccessful() returns true 但 response.code() returns 389 表示某种错误,return null object for you.

所以通常您希望以这种方式处理响应:

            @Override
            public void onResponse(Call<Login> call, Response<Login> response) {

                if (response.code() == 200 && response.isSuccessful()) {
                    // all is good
                } else {
                    // something went wrong;
                }

            }

            @Override
            public void onFailure(Call<Login> call, Throwable t) {
                // Log error here since request failed
            }

但这显然需要您进一步调查。 你能确认你从响应中得到的对象是正确的吗?

我已经找到了问题的解决方案,但在这里我尝试更广泛地解释这个问题。

首先,当您像我一样拥有巨大的 XML 文件,其中包含要从中提取的分离模型时,您必须创建自定义转换器。默认情况下,SimpleXML 对 huge XML.

效果不佳

它应该是这样的:

public class CategoriesConverter implements Converter<Categories> {

@Override
public Categories read(InputNode node) throws Exception {
    Categories categories = new Categories(); 
    InputNode childNode = node.getNext(); //InputNode passed there points to the document root, so we're starting from the next one. 
    List<Category> categoriesList = new ArrayList<>(); 
    while( childNode != null ) { // while we're in the document tree
        if (childNode.getName().equals("categories")) {
            InputNode innerChild = childNode.getNext();

            while (innerChild != null) {
                Category category = new Category();
                if (innerChild.getName().equals("category")) {
                    category.setId(Integer.parseInt(innerChild.getAttribute("id").getValue()));
                    category.setText(innerChild.getValue());
                    categoriesList.add(category);
                }

                innerChild = childNode.getNext();
            }
        }

        childNode = node.getNext();
    }

    categories.setCategories(categoriesList);
    return categories;
}

    @Override
    public void write(OutputNode node, Categories value) throws Exception {
        //we don't need to serialize objects now, so it does nothing
    }
}

总之,我们需要遍历文档树,手工获取我们需要的标签的属性和值。需要时,我们也可以填写列表或地图。这种方法只有在您准确了解文档的结构并在您的模型中重复它时才有用。

模型 class(@Attribute@Element 等)中的所有注释字段都应将参数 required 设置为 false,并且 name 参数明确指定,例如:

  @Element(required = false, name = "text")
  private String text;

并且模型 class 应该用 @Root(strict = false)@Convert(MyConverter.class) 注释,其中 MyConverter 是您为此模型编写的转换器。每个模型都应该有自己的转换器。

要使其正常工作,您需要为 SimpeXML 指定 AnnotationStrategy。它注意 @Convert 注释。你可以这样做:

Serializer serializer = new Persister(new AnnotationStrategy());

如果将 Retrofit2 与 SimpleXML 转换器和 Dagger 一起使用,则可以编写以下内容:

@Provides
@Singleton
ServerAPI provideServerAPI() {
    return new Retrofit.Builder()
            .baseUrl(ServerAPI.ENDPOINT)
            .client(client)
            .addConverterFactory(SimpleXmlConverterFactory.createNonStrict(new Persister(new AnnotationStrategy())))
            .build().create(ServerAPI.class);
}

就是这样!希望这会有所帮助。