简化三重嵌套循环,以避免在Android Retrofit2中回调地狱(通过RxJava2吗?)

时间:2019-01-30 20:41:55

标签: java android retrofit2 rx-java2

上下文

使用android retrofit2,需要访问深度嵌套的name字符串以获取并显示其Details(其中Detail对象引用了Group和{{1 }}对象,用于获取User

DetailJSON的列表组成,每个列表包含Group的列表,每个列表包含User name的列表,其中在以下模型中捕获:

String

使用public class Group { @SerializedName("id") public String id; @SerializedName("users") public List<User> users; } public class User { @SerializedName("id") public String id; @SerializedName("detailNames") public List<String> detailNames; } public class Detail { // allow access to objects used to get detail public Group group; public User user; @SerializedName("name") public String name; @SerializedName("description") public String description; } 填充模型:

UserApi

目标

目标是使用给定的public interface UserApi { @GET("groups") Call<List<Group>> getGroups(); @GET("groups/{group_id}/users/{user_id}/details/{detail_name}") Call<Detail> getDetail( @Path("group_id") String groupId, @Path("user_id") String userId, @Path("detail_name") String detailName ); } 发出和解析显示UserApi的请求,格式为:

Dialog

问题

问题是当前解决方案请求Group1 (expandable heading) User1 (expandable heading) Detail1 (checkbox) Detail2 (checkbox) ... Group2 (expandable heading) User2 (expandable heading) Detail1 (checkbox) ... ... ... 并使用三层嵌套Group循环来访问和获取for中的每个Detail

name

由于三重循环为每个private void fetchDetails(List<Group> groupList) { ArrayList<Group> groups = (ArrayList<Group>) groupList; if (groups != null && groups.size() > 0) { for (Group group : groups) { for (User user: group.users) { for (String detailName : user.detailNames) { fetchDetail(group, user, detailName); } } } } } 发出了一个请求,并且问题在name getGroups回调中完成,因此问题变得更加难以理解/无法维护:

onResponse

建议使用RxJava2解决方案来避免像上述实现那样嵌套回调,但由于在管理3层嵌套以访问protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mUserApi = UserApiClient.getApi(); fetchGroups(); } private void fetchGroups() { Callback<List<Group>> groupsCall = new Callback<List<Group>>() { @Override public void onResponse(Call<List<Group>> call, Response<List<Group>> response) { int statusCode = response.code(); switch (statusCode) { case HttpURLConnection.HTTP_OK: List<Group> groups = response.body(); fetchDetails(groups); break; } } @Override public void onFailure(Call<List<Group>> call, Throwable t) {} }; mUserApi.getGroups().enqueue(groupsCall); } private void fetchDetail(final Group group, final User user, String detailName) { Callback<Detail> detailCallback= new Callback<Detail>() { @Override public void onResponse(Call<Detail> call, Response<Detail> response) { int statusCode = response.code(); switch (statusCode) { case HttpURLConnection.HTTP_OK: MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { // display details in ListView } }); break; } } @Override public void onFailure(Call<Detail> call, Throwable t) {} }; mUserApi.getDetail(group.id, user.id, detailName).enqueue(detailCallback); } 方面存在困惑,因此未完成:

names

存在一些有关RxJava中嵌套的问题(例如RxJava multiple loop with condition),但仍不确定如何将其应用于深度嵌套的Observable<List<Group>> groupCall = mUserApi.getGroups(); groupCall.flatMapIterable(x -> x) .flatMap(group -> { Observable.fromIterable(group.users) .flatMap(user -> { Observable.fromIterable(user.detailNames) .map(detailName -> { mUserApi.getDetail(group.id, user.id, detailName) .flatMap(detail -> { detail.group = group; detail.user = user; return Observable.just(detail) }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<List<Group>>() { @Override public void onSubscribe(Disposable d) {} @Override public void onNext(List<Detail> value) { mDetails = (ArrayList<Detail>) value; } @Override public void onError(Throwable e) {} @Override public void onComplete() {} }); }); } })

问题

是否可以使用RxJava2避免回调地狱并简化三重name循环,还有另一种方法,还是该解决方案应诉诸for / AsyncTask中的同步请求?

1 个答案:

答案 0 :(得分:1)

正如我在评论中提到的,我认为您已经拥有的几乎是最简单的表格。但是似乎您有兴趣在没有循环的情况下执行此操作,因此这里有一些建议(但不一定更好):

方法1:容器类

如果您愿意创建可以在单个对象中容纳组,用户,详细信息名称的中间容器类,则可以执行以下操作:

首先,创建以下容器类:

public class UserWithGroup {
    final Group group;
    final User user;

    public UserWithGroup(Group group, User user) {
        this.group = group;
        this.user = user;
    }
}

public class DetailWithUser {
    final Group group;
    final User user;
    final String detailName;

    public DetailWithUser(Group group, User user, String detailName) {
        this.group = group;
        this.user = user;
        this.detailName = detailName;
    }
}

然后,使用Java 8 Stream的代码可以是:

private void fetchDetails(List<Group> groupList) {
    groupList.stream()
            .flatMap(g -> g.users.stream().map(u -> new UserWithGroup(g, u)))
            .flatMap(ug -> ug.user.detailNames.stream().map(n -> new DetailWithUser(ug.group, ug.user, n)))
            .forEach(d -> fetchDetail(d.group, d.user, d.detailName));
}

或使用RxJava:

private void fetchDetails2(List<Group> groupList) {
    Observable.fromIterable(groupList)
            .flatMap(g -> Observable.fromIterable(g.users).map(u -> new UserWithGroup(g, u)))
            .flatMap(ug -> Observable.fromIterable(ug.user.detailNames).map(n -> new DetailWithUser(ug.group, ug.user, n)))
            .flatMap(d -> mUserApi.getDetail(d.group.id, d.user.id, d.detailName)
                    .map(detail -> {
                        detail.group = d.group;
                        detail.user = d.user;
                        return detail
                    }))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(detail -> {
                ...
            });
}

方法2:

Android.util.Pair一个容器类,可以容纳任何两个对象。如果您使用此方法而不是创建中间容器,并且可以使用,则可以进一步简化代码。

Java 8流和配对:

private void fetchDetails3(List<Group> groupList) {
    groupList.stream()
            .flatMap(g -> g.users.stream().map(u -> Pair.create(g, u)))
            .flatMap(p -> p.second.detailNames.stream().map(n -> Pair.create(p, n)))
            .forEach(p -> fetchDetail(p.first.first, p.first.second, p.second));
}

RxJava和配对:

private void fetchDetails4(List<Group> groupList) {
    Observable.fromIterable(groupList)
            .flatMap(g -> Observable.fromIterable(g.users).map(u -> Pair.create(g, u)))
            .flatMap(p -> Observable.fromIterable(p.second.detailNames).map(n -> Pair.create(p, n)))
            .flatMap(p -> fetchDetail2(p.first.first, p.first.second, p.second)
                    .map(detail -> {
                        detail.group = d.group;
                        detail.user = d.user;
                        return detail
                    }))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(detail -> {
                ...
            });
}