ViewModel 不应该引用视图。如果你这样做,那么你会得到内存泄漏。如何不违反这个规则?
ViewModel should never reference a view. If you do, then you'll get memory leaks. How to not violate this rule?
A ViewModel must never reference a view, Lifecycle, or any class that
may hold a reference to the activity context.
View model should never reference a view. If you do, then you'll get
memory leaks.
public class FirstActivityViewModel extends ViewModel {
private int displayWidth;
private int displayHeight;
private double widthMultiplier;
private double heightMultiplier;
private int testedWidth = 1200;
private int testedHeight = 1920;
public void setDisplayCorrectedSizes(RelativeLayout relativeLayout){
displayWidth = relativeLayout.getWidth();
widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
displayHeight = relativeLayout.getHeight();
heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
}
public double getWidthMultiplier(){
return widthMultiplier;
}
public double getHeightMultiplier(){
return heightMultiplier;
}
}
或者这样的代码违反了该规则?
public class FirstActivityViewModel extends ViewModel {
private RelativeLayout relativeLayout;
private int displayWidth;
private int displayHeight;
private double widthMultiplier;
private double heightMultiplier;
private int testedWidth = 1200;
private int testedHeight = 1920;
public void setDisplayCorrectedSizes(RelativeLayout relativeLayout){
displayWidth = relativeLayout.getWidth();
widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
displayHeight = relativeLayout.getHeight();
heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
this.relativeLayout = relativeLayout;
}
public double getWidthMultiplier(){
return widthMultiplier;
}
public double getHeightMultiplier(){
return heightMultiplier;
}
}
或者两者都违规?
我只是想了解我是否以正确的方式理解它。
........................................
这是我想从视图替换为 ViewModel 的全部代码。
void actionsForUISizesOptimizationProcess(){
RelativeLayout relativeLayout2 = findViewById(R.id.rootLayoutSmallBoard);
int displayWidth = relativeLayout2.getWidth();
int displayHeight = relativeLayout2.getHeight();
int testedWidth = 1200;
double widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
int testedHeight = 1920;
double heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
// firstActivityViewModel.setDisplayCorrectedSizes(relativeLayout2);
// double widthMultiplier = firstActivityViewModel.getWidthMultiplier();
// double heightMultiplier = firstActivityViewModel.getHeightMultiplier();
if (limitOfOnWindowFocusChangedOperationForSmallBoard == 0) { /
//Log.i("userTest2020", "display width = " + displayWidth + "\n" + "display height = " + displayHeight);
int viewWidth;
int viewHeight;
viewWidth = (int) getResources().getDimension(R.dimen.tv10WidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.tv10HeightSmallBoard);
tvSmallBoardResolutionInfoValue.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
tvSmallBoardResolutionInfoValue.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
viewWidth = (int) getResources().getDimension(R.dimen.tv11WidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.tv11HeightSmallBoard);
tvSmallBoardScoreValue.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
tvSmallBoardScoreValue.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
viewWidth = (int) getResources().getDimension(R.dimen.btn1WidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.btn1HeightSmallBoard);
btnSmallBoardGetTheResolutionInfo.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
btnSmallBoardGetTheResolutionInfo.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
viewWidth = (int) getResources().getDimension(R.dimen.btnNewRoundWidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.btnNewRoundHeightSmallBoard);
btnNewRoundSmallBoard.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
btnNewRoundSmallBoard.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
ImageView ivBtnGreen = findViewById(R.id.ivNewRoundSmallBoard);
viewWidth = (int) getResources().getDimension(R.dimen.ivNewRoundWidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.ivNewRoundHeightSmallBoard);
ivBtnGreen.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
ivBtnGreen.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
FrameLayout flNewBoard = findViewById(R.id.newRoundViewGroupSmallBoard);
viewWidth = flNewBoard.getWidth();
viewHeight = flNewBoard.getHeight();
flNewBoard.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
flNewBoard.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
FrameLayout flBiggerBoard = findViewById(R.id.biggerBoardViewGroupSmallBoard);
viewWidth = (int) getResources().getDimension(R.dimen.BiggerBoardViewGroupSmallBoard);
flBiggerBoard.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
viewHeight = (int) getResources().getDimension(R.dimen.btnNextBoardHeightSmallBoard);
btnNextBoardSmallBoard.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
ImageView ivNextBoard = findViewById(R.id.ivNextBoardSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.ivNextBoardHeightSmallBoard);
ivNextBoard.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
limitOfOnWindowFocusChangedOperationForSmallBoard = 1;
}
}
为什么我认为我必须将此代码替换为 ViewModel?看这里https://android.jlelse.eu/mvvm-how-view-and-viewmodel-should-communicate-8a386ce1bb42(第一条规则)
为什么我不知道该怎么做?另一方面,还有另一条规则:视图模型不应该引用视图。如果你这样做,那么你会得到
内存泄漏。
在避免内存泄漏方面,您的第二个 FirstActivityViewModel
违反了规则。您有一个 Java 字段,其中包含对 RelativeLayout
的引用。如果 FirstActivity
由于配置更改而被销毁并重新创建,直到 setDisplayCorrectedSizes()
被第二个 activity 实例调用,您的视图模型正在泄漏第一个 activity 实例。
更一般地说,我会避免使用这两种方法中的任何一种。 viewmodel 负责准备要呈现到屏幕的数据,但是关于像素和大小的问题是 UI 层的工作,而不是 viewmodel,恕我直言。
此外,FWIW,您可能希望迁移到 ConstraintLayout
,因为它比 RelativeLayout
更强大且维护得更好。
理论上,第二个违反规则而第一个不违反规则,但理想情况下,viewModel 永远不应该关心视图。在编写单元测试时,使用相对布局作为参数会出现问题。我们的目标应该是尽可能将 viewModel 与 view 分开。我建议像这样重写你的第二种方法
public void setDisplayCorrectedSizes(int displayWidth, int displayHeight){
widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
}
另一个建议是将 getter 方法替换为发出值供视图观察的可观察对象。我会像这样重写你的 viewModel
public class FirstActivityViewModel extends ViewModel {
private LiveData<Double> widthMultiplierLiveData = new MutableLiveData();
private LiveData<Double> heightMultiplierLiveData = new MutableLiveData();
private final int testedWidth = 1200;
private final int testedHeight = 1920;
public void setDisplayCorrectedSizes(int displayWidth, int displayHeight){
double widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
double heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
widthMultiplierLiveData.value = widthMultiplier;
heightMultiplierLiveData.value = heightMultiplier;
}
}
A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.
View model should never reference a view. If you do, then you'll get memory leaks.
public class FirstActivityViewModel extends ViewModel {
private int displayWidth;
private int displayHeight;
private double widthMultiplier;
private double heightMultiplier;
private int testedWidth = 1200;
private int testedHeight = 1920;
public void setDisplayCorrectedSizes(RelativeLayout relativeLayout){
displayWidth = relativeLayout.getWidth();
widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
displayHeight = relativeLayout.getHeight();
heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
}
public double getWidthMultiplier(){
return widthMultiplier;
}
public double getHeightMultiplier(){
return heightMultiplier;
}
}
或者这样的代码违反了该规则?
public class FirstActivityViewModel extends ViewModel {
private RelativeLayout relativeLayout;
private int displayWidth;
private int displayHeight;
private double widthMultiplier;
private double heightMultiplier;
private int testedWidth = 1200;
private int testedHeight = 1920;
public void setDisplayCorrectedSizes(RelativeLayout relativeLayout){
displayWidth = relativeLayout.getWidth();
widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
displayHeight = relativeLayout.getHeight();
heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
this.relativeLayout = relativeLayout;
}
public double getWidthMultiplier(){
return widthMultiplier;
}
public double getHeightMultiplier(){
return heightMultiplier;
}
}
或者两者都违规?
我只是想了解我是否以正确的方式理解它。
........................................
这是我想从视图替换为 ViewModel 的全部代码。
void actionsForUISizesOptimizationProcess(){
RelativeLayout relativeLayout2 = findViewById(R.id.rootLayoutSmallBoard);
int displayWidth = relativeLayout2.getWidth();
int displayHeight = relativeLayout2.getHeight();
int testedWidth = 1200;
double widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
int testedHeight = 1920;
double heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
// firstActivityViewModel.setDisplayCorrectedSizes(relativeLayout2);
// double widthMultiplier = firstActivityViewModel.getWidthMultiplier();
// double heightMultiplier = firstActivityViewModel.getHeightMultiplier();
if (limitOfOnWindowFocusChangedOperationForSmallBoard == 0) { /
//Log.i("userTest2020", "display width = " + displayWidth + "\n" + "display height = " + displayHeight);
int viewWidth;
int viewHeight;
viewWidth = (int) getResources().getDimension(R.dimen.tv10WidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.tv10HeightSmallBoard);
tvSmallBoardResolutionInfoValue.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
tvSmallBoardResolutionInfoValue.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
viewWidth = (int) getResources().getDimension(R.dimen.tv11WidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.tv11HeightSmallBoard);
tvSmallBoardScoreValue.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
tvSmallBoardScoreValue.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
viewWidth = (int) getResources().getDimension(R.dimen.btn1WidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.btn1HeightSmallBoard);
btnSmallBoardGetTheResolutionInfo.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
btnSmallBoardGetTheResolutionInfo.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
viewWidth = (int) getResources().getDimension(R.dimen.btnNewRoundWidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.btnNewRoundHeightSmallBoard);
btnNewRoundSmallBoard.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
btnNewRoundSmallBoard.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
ImageView ivBtnGreen = findViewById(R.id.ivNewRoundSmallBoard);
viewWidth = (int) getResources().getDimension(R.dimen.ivNewRoundWidthSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.ivNewRoundHeightSmallBoard);
ivBtnGreen.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
ivBtnGreen.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
FrameLayout flNewBoard = findViewById(R.id.newRoundViewGroupSmallBoard);
viewWidth = flNewBoard.getWidth();
viewHeight = flNewBoard.getHeight();
flNewBoard.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
flNewBoard.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
FrameLayout flBiggerBoard = findViewById(R.id.biggerBoardViewGroupSmallBoard);
viewWidth = (int) getResources().getDimension(R.dimen.BiggerBoardViewGroupSmallBoard);
flBiggerBoard.getLayoutParams().width = (int) (viewWidth * widthMultiplier);
viewHeight = (int) getResources().getDimension(R.dimen.btnNextBoardHeightSmallBoard);
btnNextBoardSmallBoard.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
ImageView ivNextBoard = findViewById(R.id.ivNextBoardSmallBoard);
viewHeight = (int) getResources().getDimension(R.dimen.ivNextBoardHeightSmallBoard);
ivNextBoard.getLayoutParams().height = (int) (viewHeight * heightMultiplier);
limitOfOnWindowFocusChangedOperationForSmallBoard = 1;
}
}
为什么我认为我必须将此代码替换为 ViewModel?看这里https://android.jlelse.eu/mvvm-how-view-and-viewmodel-should-communicate-8a386ce1bb42(第一条规则) 为什么我不知道该怎么做?另一方面,还有另一条规则:视图模型不应该引用视图。如果你这样做,那么你会得到 内存泄漏。
在避免内存泄漏方面,您的第二个 FirstActivityViewModel
违反了规则。您有一个 Java 字段,其中包含对 RelativeLayout
的引用。如果 FirstActivity
由于配置更改而被销毁并重新创建,直到 setDisplayCorrectedSizes()
被第二个 activity 实例调用,您的视图模型正在泄漏第一个 activity 实例。
更一般地说,我会避免使用这两种方法中的任何一种。 viewmodel 负责准备要呈现到屏幕的数据,但是关于像素和大小的问题是 UI 层的工作,而不是 viewmodel,恕我直言。
此外,FWIW,您可能希望迁移到 ConstraintLayout
,因为它比 RelativeLayout
更强大且维护得更好。
理论上,第二个违反规则而第一个不违反规则,但理想情况下,viewModel 永远不应该关心视图。在编写单元测试时,使用相对布局作为参数会出现问题。我们的目标应该是尽可能将 viewModel 与 view 分开。我建议像这样重写你的第二种方法
public void setDisplayCorrectedSizes(int displayWidth, int displayHeight){
widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
}
另一个建议是将 getter 方法替换为发出值供视图观察的可观察对象。我会像这样重写你的 viewModel
public class FirstActivityViewModel extends ViewModel {
private LiveData<Double> widthMultiplierLiveData = new MutableLiveData();
private LiveData<Double> heightMultiplierLiveData = new MutableLiveData();
private final int testedWidth = 1200;
private final int testedHeight = 1920;
public void setDisplayCorrectedSizes(int displayWidth, int displayHeight){
double widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
double heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
widthMultiplierLiveData.value = widthMultiplier;
heightMultiplierLiveData.value = heightMultiplier;
}
}