Android.os.NetworkOnMainThreadException с помощью rxjava на Android

У меня возникли проблемы с реализацией rxJava, чтобы проверить, есть ли подключение к Интернету в android, я делаю это следующим образом:

в моей активности запуска я имею это в onCreate:

AndroidObservable.bindActivity(this,
            Observable.just(Utils.isActiveInternetConnection(Launcher.this)))
            .subscribeOn(Schedulers.newThread())
            .subscribe(new Action1<Boolean>() {
                @Override
                public void call(Boolean aBoolean) {
                    if (aBoolean) {
                        Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
                    }
                }
            });

У меня есть класс Utils, он является последним классом со статическими методами, которые используются в методе наблюдения:

    public static boolean isActiveInternetConnection(Context context) {
    if (isNetworkAvailable(context)) {
        try {
            HttpURLConnection urlc = (HttpURLConnection) (new URL("http://www.google.com").openConnection());
            urlc.setRequestProperty("User-Agent", "Test");
            urlc.setRequestProperty("Connection", "close");
            urlc.setConnectTimeout(1500);
            urlc.connect();
            return (urlc.getResponseCode() == 200);
        } catch (IOException e) {
            Log.e("network", "Error checking internet connection", e);
        }
    } else {
        Log.d("network", "No network available!");
    }
    return false;
}

private static boolean isNetworkAvailable(Context context){
    ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
    if (null != activeNetwork) {
        return true;
    }
    else {
        return false;
    }
}

Я получаю android.os.NetworkOnMainThreadException, и я не могу найти причину, спасибо заранее.

Ответ 1

Observable.just(...) вызывается сразу в вызывающем потоке (в этом случае основной поток). Ваш код - это просто встроенная версия:

boolean activeConn = Utils.isActiveInternetConnection(Launcher.this);
AndroidObservable.bindActivity(this, 
        Observable.just(activeConn))
        .subscribeOn(...)
        ...

Вы попытались переместить его из основного потока, вызвав subscribeOn() - но вызов уже произошел.

Как мы справляемся с этим (и я не уверен, что это лучший способ, но он работает) состоит в том, чтобы отложить сеть или заблокировать вызов до тех пор, пока не произойдет подписка, настройте наблюдаемое для выполнения на правильных потоках и затем подпишитесь:

AndroidObservable.bindActivity(this,
        Observable.defer(new Func0<Boolean>() {
            @Override
            public Observable<Observable<Boolean>> call() {
                return Observable.just(Utils.isActiveInternetConnection(Launcher.this));
            }
        })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<Boolean>() {
            @Override
            public void call(Boolean aBoolean) {
                if (aBoolean) {
                    Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
                }
            }
        });

Ответ 2

Я думаю, just просто вызывает метод синхронно, поскольку он ожидает логическое значение и пытается его получить.

Я плохо себя чувствую при RxJava, но вы можете попробовать что-то вроде этого:

Observable<Boolean> onlineObservable = Observable.create(new Observable.OnSubscribe<Boolean>() {
    @Override
    public void call(Subscriber subscriber) {
        subscriber.onNext(Utils.isActiveInternetConnection(context));
    }
});
onlineObservable.subscribeOn(Schedulers.newThread()).subscribe(result -> {...});

Ответ 3

это мои данные из базы данных с помощью кода RXAndroid:

    Observable.create(new Observable.OnSubscribe<List<GRO_VO>>() {
        @Override
        public void call(Subscriber<? super List<GRO_VO>> subscriber) {

            String jsonIn;
            jsonIn =retrieveDataFromDB();
            Gson gson = new Gson();
            Type listType = new TypeToken<List<GRO_VO>>() {

            }.getType();   
            eventJoinList = gson.fromJson(jsonIn, listType);
            Log.d("RX",jsonIn);
            subscriber.onNext(eventJoinList);
        }
    })

                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<GRO_VO>>() {
        @Override
        public void call(List<GRO_VO> eventJoinList) {
            Log.d("RX", ".subscribe");
       recyclerView.setAdapter(new EventJoinAdapter(eventJoinList));
        }
    });

Я думаю, что оператор just немедленно выдаст данные, поэтому нецелесообразно извлекать данные из базы данных через сеть. Он очень прост в использовании, но его можно использовать только для данных, уже находящихся в мобильном телефоне.

У меня также была такая проблема, как у @Baniares, но после того, как я использую оператор create, проблема исчезает...

Из документации RXJava:

static <T> Observable<T> create(Observable.OnSubscribe<T> f)

Возвращает наблюдаемое, которое будет выполнять указанную функцию, когда подписчик подписывается на нее.

Используя оператор create, можно установить стандартный процесс:

1 .subscribe(...) Подписчик (подкласс класса Observer) запустит соединение с Observable.

2 .subscribeOn(Schedulers.io()) собрать обратный поток из RX-ThreadPool

3 .create(...) извлекать данные с сервера... во время некоторого netWork..etc

4 .observeOn(AndroidSchedulers.mainThread()) это означает, что данные будут заданы с помощью UI-Thread

5 Как только мы получим данные, мы можем установить данные в методе onNext() в .subscribe( ), данные будут установлены в пользовательском интерфейсе UI-Thread, поскольку мы создаем UI-поток для работы над .observerOn(AndroidSchedulers.mainThread())

цепочка методов просто не выполняется порядком кода.

Обратите внимание, что если вы используете оператор .create(), вы должны закончить свое наблюдение в .create(), другой оператор, такой как map, flatMap не будет выполнен после оператора .create().

Ответ 4

AdamS правильно, однако RxJava 2 теперь предлагает Observable.fromCallable() отложить наблюдаемую операцию до подписки. Хорошая рекомендация: https://caster.io/lessons/fromcallable-converting-slow-methods-into-an-observable/

Пример кода примера из моего прецедента:

Single.fromCallable(new Callable<Response>() {
        @Override
        public Response call() throws Exception {
            return NetworkGateway.networkRequest();
        }
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(observer);