Изменения конфигурации привязки данных / оперативных данных с помощью Retrofit / Rxjava - PullRequest
0 голосов
/ 13 июня 2018

Пожалуйста, посмотрите на этот код:

Основная деятельность:

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        MutableLiveData<User> mutableUser = new MutableLiveData<>();
        mutableUser.setValue(new User("John","Gordon","Homeless"));

        activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        activityMainBinding.setHandler(new MainActivityHandler());

        activityMainBinding.setLifecycleOwner(this);
        activityMainBinding.setUser(mutableUser);

        setSupportActionBar(activityMainBinding.toolbar);
    }

MutableLiveData определена в формате XML:

<variable name="user" type="android.arch.lifecycle.MutableLiveData&lt;test.databindingtemplate.ViewModels.User&gt;"/>

Каждое поле пользователя связано с привязкой данных:

<EditText
                android:id="@+id/editTextName"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginBottom="8dp"
                android:layout_marginEnd="8dp"
                android:layout_marginStart="8dp"
                android:layout_marginTop="8dp"
                android:ems="10"
                android:inputType="textPersonName"
                android:text="@={user.firstName}"
                app:layout_constraintBottom_toTopOf="@+id/textViewSecondName"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/textViewSecondName"
                app:layout_constraintTop_toTopOf="parent"/>

Это работает нормально, изменения конфигурации отслеживаются и заполняются в графическом интерфейсе (например, когда я что-то набираю в поле «Имя», значениесохраняется при повороте экрана).

Далее я хочу реализовать обновление сведений о пользователе из rest webservice - Retrofit / RxJava и хочу показать индикатор занятости, когда идет загрузка данных, например, в методе onCreate:

showBusyIndicator(); 
testService.getUserDetails(headers)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(user ->
                {
                  hideBusyIndicator();
                  activityMainBinding.getUser().setValue(user);
                }, throwable -> 
                {
                    Log.d(TAG, "ERROR loading user");
                    hideBusyIndicator();
                });

Теперь я не уверен, как правильно обрабатывать изменения конфигурации - когда отображается индикатор занятости, он должен быть воссоздан после изменения поворота экрана.Поскольку MutableLiveData хорошо обрабатывает изменения конфигурации, я не могу найти никакого решения (кроме "классического" способа), чтобы решить мою проблему с индикатором загрузки и обработать результат в этом случае.

Можете ли вы указать мне правильное направление?

[править] Объявление getUserDetails:

@NonNull
@GET("/user")
Observable<User> getUserDetails(@HeaderMap Map<String, String> headers);

@ user8035311

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

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        this.progressDialog = new ProgressDialog(this);
        this.progressDialog.setMessage("Loading...");

        final ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mainBinding.setLifecycleOwner(this);

        mainBinding.button2.setOnClickListener(view ->
        {
            RxViewModel.RxViewModelFactory factory = new RxViewModel.RxViewModelFactory();
            final RxViewModel model = ViewModelProviders.of(MainActivity.this, factory).get(RxViewModel.class);
            LiveData<User> liveData = model.getLiveData();
            progressDialog.show();
            liveData.observe(MainActivity.this, s ->
            {
                progressDialog.dismiss();
                mainBinding.setUser(s);
            });

        });
    }

Но это не работает.Задача прерывается при повороте экрана, за исключением:

06-27 09: 05: 18.737 11829-11829 / app.rxrotation.com.architecturecomponentsrxrotation E / WindowManager: android.view.WindowLeaked: приложение активности.rxrotation.com.architecturecomponentsrxrotation.MainActivity просочилась в окно DecorView @ 5b16e38 [], которое было первоначально добавлено здесь на android.view.ViewRootImpl. (ViewRootImpl.java:485) на android.view.WindowManagerGlobal.addView (WindowManagerG):на android.view.WindowManagerImpl.addView (WindowManagerImpl.java:93) на android.app.Dialog.show (Dialog.java:330) на app.rxrotation.com.architecturecomponentsrxrotation.MainActivity.lambda $ onCreate $ 1 $ MainActivity (MainActivity).java: 35) на app.rxrotation.com.architecturecomponentsrxrotation.MainActivity $$ Lambda $ 0.onClick (неизвестный источник: 4) на android.view.View.performClick (View.java:6294) на android.view.View $ PerformClick.запустить (View.java:24770) на android.os.Handler.handleCallback (Handler.java:790) на android.os.Handler.dispatchСообщение (Handler.java:99) на android.os.Looper.loop (Looper.java:164) на android.app.ActivityThread.main (ActivityThread.java:6494) на java.lang.reflect.Method.invoke (роднойMethod) в com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run (RuntimeInit.java:438) в com.android.internal.os.ZygoteInit.main (ZygoteInit.java:807)

После второго нажатия кнопки загрузка данных даже не выполняется.

1 Ответ

0 голосов
/ 14 июня 2018

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

public class UserLiveData extends LiveData<User> {
    public UserLiveData() {
        setValue(new User());
    }

    public UserLiveData(Headers headers) {
        Observable<User> observable = Observable.defer(new Callable<ObservableSource<? extends User>>() {
            @Override
            public ObservableSource<? extends User> call() throws Exception {
                User user = testService.getUserDetails(headers);
                return Observable.just(user);
            }
        });

        observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeWith(new DefaultObserver<String>() {
                @Override
                public void onNext(User value) {
                    setValue(value);
                }

                @Override
                public void onError(Throwable e) {
                    throw new RuntimeException(e);
                }

                @Override
                public void onComplete() {

                }
        });
    }
}

Тогда ваш UserDetailsViewModel выглядит следующим образом:

public class UserDetailsViewModel extends ViewModel {
    private LiveData<User> userLiveData;

    public UserDetailsViewModel() {
        userLiveData = new UserLiveData();    
    }

    public void observeLiveDate(LifecycleOwner owner,
                              Observer<User> observer) {
        userLiveData.observe(owner, observer);
    }

    public void loadLiveData(Headers headers) {
        userLiveData = new UserLiveData(someArgs);
    }
}

И тогда ваш MainActivity может быть следующим:

public class MainActivity extends AppCompatActivity {
    private ProgressDialog progressDialog;
    private UserViewModel viewModel;
    private ActivityMainBinding mainBinding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.progressDialog = new ProgressDialog(this);
        this.progressDialog.setMessage("Loading...");

        mainBinding = DataBindingUtil.setContentView(this,
            R.layout.activity_main);
        mainBinding.setLifecycleOwner(this);

        viewModel = ViewModelProviders.of(this).get(UserViewModel.class);

        observeLiveData();
        mainBinding.button2.setOnClickListener(v -> {
            viewModel.initLiveData(<your headers>);
            observeLiveData();
        });
    }

    private void observeLiveData() {
        progressDialog.show();
        progressDialog.setCanceledOnTouchOutside(false);
        viewModel.observeRxLiveDate(this, user -> {
            progressDialog.dismiss();
            mainBinding.setUser(user);
        });
    }
}

Я создал очень простой пример здесь

...