android - MVVM 注入(inject) Activity 字符串资源读取器

标签 android memory-leaks android-resources android-mvvm

因此,每个人 都知道将上下文引用(不是应用程序)传递给 ViewModel 是一件坏事。在我的例子中,有些项目要使用 android 字符串资源表示按字母顺序排序(因此,要阅读它,我需要一个 Activity Context)。

推荐的方法是什么?将项目列表从 ViewModel 传递到 Activity,读取这些字符串并返回给 ViewModel 确实看起来有点不像 MVVM,并且使用字符串资源读取器注入(inject) ViewModel 会泄漏 Context..

对此有什么想法吗?

最佳答案

一种选择是从 AndroidViewModel 扩展相反,它引用了应用程序上下文。然后你可以使用那个 Context加载字符串资源并将它们传送回您的 Activity .

public class MyViewModel extends AndroidViewModel {
    private final LiveData<String> stringResource = new MutableLiveData<>();

    public MyViewModel(Application context) {
        super(context);
        statusLabel.setValue(context.getString(R.string.labelString));
    }

    public LiveData<String> getStringResource() {
        return stringResource;
    }
}

但是,正如本 Android Developers Medium Post by Jose Alcerreca 中指出的那样,这不是推荐的做法,因为例如,如果 Locale更改并且 Activity 被重建,ViewModel 将不会对此配置更改使用react,并将继续提供过时的字符串(来自以前的 Locale )。

因此,建议的方法是仅从 ViewModel 返回资源 ID 并获取 Activity 上的字符串。

public class MyViewModel extends ViewModel {
    public final LiveData<Integer> stringResource = new MutableLiveData<>();

    public MyViewModel(Application context) {
        super(context);
        stringResource.setValue(R.string.labelString);
    }

    public LiveData<Integer> getStringResource() {
        return stringResource;
    }
}

更新

因为您必须从 Activity 中获取字符串资源但在 ViewModel 中应用排序逻辑, 我认为你无法避免通过 List<String>回到你的ViewModel待排序:

public class MyViewModel extends ViewModel {
    public final MutableLiveData<Integer> stringArrayId = new MutableLiveData<>();

    public MyViewModel(Application context) {
        super(context);
        stringArrayId.setValue(R.array.string_array_id);
    }

    public LiveData<Integer> getStringArrayId() {
        return stringArrayId;
    }
}


public class MyActivity extends AppCompatActivity {

    public void onCreate(Bundle savedInstanceState) {
        MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);

        viewModel.getStringArrayId().observe(this, strArrayId -> {
            String[] resolvedStrings = getResources().getStringArray(strArrayId);
            List<String> sortedStrings = viewModel.sortList(Arrays.asList(resolvedStrings));
            updateUi(sortedStrings);
        });
    }
}

如果您认为这还不够 MVVM,也许您可​​以继续解决 List<String>在你的ViewModel还有一个额外的 LiveData使用排序列表,每次 LiveData 时都会更新保留原始字符串列表更改。

public class MyViewModel extends ViewModel {
    public final MutableLiveData<Integer> stringArrayId = new MutableLiveData<>();
    public final MutableLiveData<List<String>> stringsList = new MutableLiveData<>();
    public final LiveData<List<String>> sortedStringList;


    public MyViewModel(Application context) {
        super(context);
        stringArrayId.setValue(R.array.string_array_id);

        sortedStringList = Transformations.map(stringsList, l -> {
            Collections.sort(l);
            return l; 
        });
    }

    public LiveData<Integer> getStringArrayId() {
        return stringArrayId;
    }

    public LiveData<List<String>> sortedStringList() {
        return sortedStringList;
    }

    public void setStringsList(List<String> resolvedStrings) {
        stringsList.setValue(resolvedStrings);
    }
}


public class MyActivity extends AppCompatActivity {

    public void onCreate(Bundle savedInstanceState) {
        MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);

        viewModel.getStringArrayId().observe(this, strArrayId -> {
            String[] resolvedStrings = getResources().getStringArray(strArrayId);
            viewModel.setStringsList(Arrays.asList(resolvedStrings));
        });

        viewModel.sortedStringList().observe(this, sortedStrings -> updateUi(sortedStrings));
    }
}

我觉得设计过度了,你仍然必须发送 List<String>回到你的ViewModel .但是,如果排序顺序取决于可以在运行时更改的过滤器,那么采用这种方式可能会有所帮助。然后,您可以添加 MediatorLiveData在过滤器更改或字符串列表更改时使用react,那么您的 View 只需将这些更改通知给 ViewModel并将观察排序后的列表。

关于android - MVVM 注入(inject) Activity 字符串资源读取器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57760742/

相关文章:

java - Valgrind 和 Java

android - 在 ListView (Android Studio) 的自定义适配器中从 URL 加载图像

java - "Result of method is ignored"- 这意味着什么?

c++ - 你能给我一个内存泄漏的真实工作示例吗?

c - getline 和 strsep 的内存泄漏

android - 支持多个 Android 屏幕 : Advantages vs. 具有所有密度的抽屉文件夹(图像)的缺点?

android - Vue 使用 Android Chrome 观察意外响应

android - 使用 ActionBarSherlock 的 setShareIntent 上的 NullPointerException

android - 如何获取不同资源引用的资源的ID?

android - 删除未使用的资源