首页 > 解决方案 > ViewModel 永远不应该引用视图。如果你这样做,那么你会得到内存泄漏。如何不违反这条规则?

问题描述

ViewModel 绝不能引用视图、生命周期或任何可能持有对活动上下文的引用的类。

视图模型不应该引用视图。如果你这样做,那么你会得到内存泄漏。

在此处输入图像描述

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(第一条规则)为什么我不知道该怎么做?另一方面,还有另一条规则:视图模型不应该引用视图。如果你这样做,那么你会得到内存泄漏。

标签: androidmvvmmemory-leaksviewmodel

解决方案


在避免内存泄漏方面,您的第二个FirstActivityViewModel是违反规则。您有一个 Java 字段,其中包含对RelativeLayout. 如果FirstActivity由于配置更改而被销毁并重新创建,直到setDisplayCorrectedSizes()被第二个活动实例调用,您的视图模型正在泄漏第一个活动实例。

更一般地说,我会避免使用这些方法中的任何一种。视图模型负责准备要呈现到屏幕的数据,但是关于像素和大小的问题是 UI 层的工作,而不是视图模型,恕我直言。

此外,您可能希望迁移到 FWIW,ConstraintLayout因为它比 is 更强大且维护得更好RelativeLayout


推荐阅读