提问者:小点点

ViewModel永远不应引用视图。如果这样做,就会导致内存泄漏。如何不违反这一规则?


ViewModel绝不能引用可能包含对activity上下文引用的视图,生命周期或任何类。

视图模型永远不应引用视图。如果这样做,就会导致内存泄漏。

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;
    }
}

还是两人都在违规?

我只想知道我是否以正确的方式去理解它。

。。。。。。。。。。。。。。。。。。。。。

下面是我想从view替换到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-communication-8a386ce1bb42(第一条规则),为什么我不知道怎么做?因为另一方面,还有另一条规则:视图模型永远不应该引用视图。如果这样做,就会导致内存泄漏。


共2个答案

匿名用户

在避免内存泄漏方面,您的第二个FirstActivityViewModel违反了规则。您有一个保存对RelativeLayout的引用的Java字段。如果FirstActivity由于配置更改而被销毁并重新创建,则在第二个activity实例调用SetDisplayCorretedSize()之前,您的viewmodel正在泄漏第一个activity实例。

更一般说来,我会避免这两种方法中的任何一种。viewmodel负责准备要呈现到屏幕上的数据,但是像素和大小是UI层的工作,而不是viewmodel。

另外,FWIW,您可能希望迁移到ConstraintLayout,因为它是比RelativeLayout更强大,维护更好的选项。

匿名用户

理论上,第二个违反了规则,而第一个没有,但理想情况下,viewModel从不关心视图。在编写单元测试时,使用相对布局作为参数会有问题。我们的目的应该是尽可能地将viewModel从视图中分离出来。我建议重写您的第二种方法,如下所示

null

public void setDisplayCorrectedSizes(int displayWidth, int displayHeight){
    widthMultiplier = ((double) displayWidth) / ((double) testedWidth);
    heightMultiplier = ((double) displayHeight) / ((double) testedHeight);
}