Databinding/Live 数据句柄配置更改为 Retrofit/Rxjava
Databinding/Live data handle configuration changes with Retrofit/Rxjava
请看这段代码:
主要activity:
@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<test.databindingtemplate.ViewModels.User>"/
>
用户的每个字段都绑定到数据绑定控件:
<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"/>
这工作正常,配置更改被跟踪并填充到 gui(例如,当我在 "Name" 字段中键入内容时,屏幕旋转时值仍然存在)。
接下来,我想实现从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
很好地处理配置更改,我无法找到任何解决方案(除了 "classic" 方式)来解决我在这种情况下加载指示器和处理结果的问题。
你能给我指明正确的方向吗?
[编辑]
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: Activity
app.rxrotation.com.architecturecomponentsrxrotation.MainActivity has
leaked window DecorView@5b16e38[] that was originally added here
at android.view.ViewRootImpl.(ViewRootImpl.java:485)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:346)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.app.Dialog.show(Dialog.java:330)
at app.rxrotation.com.architecturecomponentsrxrotation.MainActivity.lambda$onCreate$MainActivity(MainActivity.java:35)
at app.rxrotation.com.architecturecomponentsrxrotation.MainActivity$$Lambda[=21=].onClick(Unknown
Source:4)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
第二次点击按钮后,数据加载甚至没有执行。
您可以使用 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);
});
}
}
我创建了一个非常简单的例子here
请看这段代码:
主要activity:
@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<test.databindingtemplate.ViewModels.User>"/
>
用户的每个字段都绑定到数据绑定控件:
<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"/>
这工作正常,配置更改被跟踪并填充到 gui(例如,当我在 "Name" 字段中键入内容时,屏幕旋转时值仍然存在)。
接下来,我想实现从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
很好地处理配置更改,我无法找到任何解决方案(除了 "classic" 方式)来解决我在这种情况下加载指示器和处理结果的问题。
你能给我指明正确的方向吗?
[编辑] 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: Activity app.rxrotation.com.architecturecomponentsrxrotation.MainActivity has leaked window DecorView@5b16e38[] that was originally added here at android.view.ViewRootImpl.(ViewRootImpl.java:485) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:346) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93) at android.app.Dialog.show(Dialog.java:330) at app.rxrotation.com.architecturecomponentsrxrotation.MainActivity.lambda$onCreate$MainActivity(MainActivity.java:35) at app.rxrotation.com.architecturecomponentsrxrotation.MainActivity$$Lambda[=21=].onClick(Unknown Source:4) at android.view.View.performClick(View.java:6294) at android.view.View$PerformClick.run(View.java:24770) at android.os.Handler.handleCallback(Handler.java:790) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
第二次点击按钮后,数据加载甚至没有执行。
您可以使用 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);
});
}
}
我创建了一个非常简单的例子here