Internet check, where to place when using MVP, RX and Retrofit

后端 未结 1 962
清酒与你
清酒与你 2020-12-25 09:34

I have went through this and this post. So I really agree with the second post that presenter should not be aware of android specific thing. So what I am thinking is putting

相关标签:
1条回答
  • 2020-12-25 10:14

    First we create a utility for checking internet connection, there are two ways we can create this utility, one where the utility emits the status only once, which looks like this,

    public class InternetConnection {
        public static Observable<Boolean> isInternetOn(Context context) {
            ConnectivityManager connectivityManager
                    = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
            return Observable.just(activeNetworkInfo != null && activeNetworkInfo.isConnected());
        }
    }
    

    Other way of creating this utility is, where the utility keeps emitting the connection status if it changes, which looks like this,

    public class InternetConnection {
        public Observable<Boolean> isInternetOn(Context context) {
            final IntentFilter filter = new IntentFilter();
            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    
            return Observable.create(new Observable.OnSubscribe<Boolean>() {
                @Override
                public void call(final Subscriber<? super Boolean> subscriber) {
                    final BroadcastReceiver receiver = new BroadcastReceiver() {
                        @Override
                        public void onReceive(Context context, Intent intent) {
                            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
                            NetworkInfo netInfo = cm.getActiveNetworkInfo();
                            subscriber.onNext(netInfo != null && netInfo.isConnected());
                        }
                    };
    
                    context.registerReceiver(receiver, filter);
    
                    subscriber.add(unsubscribeInUiThread(() -> context.unregisterReceiver(receiver)));
                }
            }).defaultIfEmpty(false);
        }
    
        private Subscription unsubscribeInUiThread(final Action0 unsubscribe) {
            return Subscriptions.create(() -> {
                if (Looper.getMainLooper() == Looper.myLooper()) {
                    unsubscribe.call();
                } else {
                    final Scheduler.Worker inner = AndroidSchedulers.mainThread().createWorker();
                    inner.schedule(() -> {
                        unsubscribe.call();
                        inner.unsubscribe();
                    });
                }
            });
        }
    }
    

    Next, in your dataSource or Presenter use switchMap or flatMap to check for internet connection before doing any network operation which looks like this,

    private Observable<List<GitHubUser>> getGitHubUsersFromRetrofit() {
        return isInternetOn(context)
                .filter(connectionStatus -> connectionStatus)
                .switchMap(connectionStatus -> gitHubApiInterface.getGitHubUsersList()
                        .map(gitHubUserList -> {
                           gitHubUserDao.storeOrUpdateGitHubUserList(gitHubUserList);
                            return gitHubUserList;
                        }));
    }
    

    Note that, we are using switchMap instead of flatMap. why switchMap? because, we have 2 data stream here, first is internet connection and second is Retrofit. first we will take connection status value (true/false), if we have active connection, we will create a new Retrofit stream and return start getting results, down the line if we the status of the connection changes, switchMap will first stop the existing Retrofit connection and then decide if we need to start a new one or ignore it.

    EDIT: This is one of the sample, which might give better clarity https://github.com/viraj49/Realm_android-injection-rx-test/blob/master/app-safeIntegration/src/main/java/tank/viraj/realm/dataSource/GitHubUserListDataSource.java

    EDIT2:

    So you mean switch map will try it itself once internet is back?

    Yes and No, let's first see the difference between flatMap & switchMap. Let's say we have an editText and we search some info from network based on what user types, every time user adds a new character we have to make a new query (which can be reduced with debounce), now with so many network calls only the latest results are useful, with flatMap we will receive all the results from all the calls we made to the network, with switchMap on the other hand, the moment we make a query, all previous calls are discarded.

    Now the solution here is made of 2 parts,

    1. We need an Observable that keeps emitting current state of Network, the first InternetConnection above sends the status once and calls onComplete(), but the second one has a Broadcast receiver and it will keep sending onNext() when network status changes. IF you need to make a reactive solution go for case-2

    2. Let's say you choose InternetConnection case-2, in this case use switchMap(), cause when network status changes, we need to stop Retrofit from whatever it is doing and then based on the status of network either make a new call or don't make a call.

    How do I let my view know that the error is internet one also will this be scalable because I need to do with every network call, any suggestions regarding writing a wrapper?

    Writing a wrapper would be excellent choice, you can create your own custom response which can take multiple entries from a set of possible responses e.g. SUCCESS_INTERNET, SUCCESS_LOGIN, ERROR_INVALID_ID

    EDIT3: Please find an updated InternetConnectionUtil here https://github.com/viraj49/Realm_android-injection-rx-test/blob/master/app-safeIntegration/src/main/java/tank/viraj/realm/util/InternetConnection.java

    More detail on the same topic is here: https://medium.com/@Viraj.Tank/android-mvp-that-survives-view-life-cycle-configuration-internet-changes-part-2-6b1e2b5c5294

    EDIT4: I have recently created an Internet utility using Android Architecture Components - LiveData, you can find full source code here, https://github.com/viraj49/Internet-Utitliy-using-AAC-LiveData

    A detailed description of the code is here, https://medium.com/@Viraj.Tank/internet-utility-using-android-architecture-components-livedata-e828a0fcd3db

    0 讨论(0)
提交回复
热议问题