ภาพรวมของ ViewModel (มุมมอง)

แนวคิดและการใช้งาน Jetpack Compose

คลาส ViewModel เป็นตัวเก็บสถานะระดับตรรกะทางธุรกิจหรือระดับหน้าจอ โดยจะแสดงสถานะต่อ UI และห่อหุ้มตรรกะทางธุรกิจที่เกี่ยวข้อง ข้อดีหลักคือการแคชสถานะและเก็บสถานะไว้เมื่อมีการเปลี่ยนแปลงการกำหนดค่า ซึ่งหมายความว่า UI ไม่จำเป็นต้องดึงข้อมูลอีกครั้งเมื่อมีการนำทางระหว่างกิจกรรม หรือเมื่อมีการเปลี่ยนแปลงการกำหนดค่า เช่น เมื่อหมุนหน้าจอ

สิทธิประโยชน์ของ ViewModel

ทางเลือกแทน ViewModel คือคลาสธรรมดาที่เก็บข้อมูลที่คุณแสดงใน UI ซึ่งอาจกลายเป็นปัญหาเมื่อมีการนำทางระหว่างกิจกรรมหรือปลายทางการนำทาง เนื่องจากข้อมูลจะถูกทำลายหากคุณไม่จัดเก็บโดยใช้ กลไกการทำงานของสถานะอินสแตนซ์ที่บันทึกไว้ ViewModel มี API ที่สะดวกสำหรับการเก็บข้อมูลไว้ ซึ่งช่วยแก้ปัญหานี้ได้

ประโยชน์หลักของคลาส ViewModel มี 2 ข้อ ได้แก่

  • ช่วยให้คุณเก็บสถานะ UI ไว้ได้
  • ให้สิทธิ์เข้าถึงตรรกะทางธุรกิจ

ขอบเขต

เมื่อสร้างอินสแตนซ์ ViewModel คุณจะส่งออบเจ็กต์ที่ใช้ ViewModelStoreOwner อินเทอร์เฟซ ซึ่งอาจเป็นปลายทางการนำทาง กราฟการนำทาง กิจกรรม Fragment หรือประเภทอื่นๆ ที่ใช้ อินเทอร์เฟซ จากนั้น ViewModel จะมีขอบเขตเป็น Lifecycle ของ ViewModelStoreOwner และจะยังคงอยู่ในหน่วยความจำจนกว่า ViewModelStoreOwner จะหายไปอย่างถาวร

คลาสหลายคลาสเป็นคลาสย่อยโดยตรงหรือโดยอ้อมของอินเทอร์เฟซ ViewModelStoreOwner คลาสย่อยโดยตรง ได้แก่ ComponentActivity, Fragment และ NavBackStackEntry ดูรายการคลาสย่อยโดยอ้อมทั้งหมดได้ที่ข้อมูลอ้างอิง ViewModelStoreOwner

ใช้ ViewModel

ต่อไปนี้เป็นตัวอย่างการใช้งาน ViewModel สำหรับหน้าจอที่อนุญาตให้ผู้ใช้ทอยลูกเต๋า

Kotlin

data class DiceUiState(
    val firstDieValue: Int? = null,
    val secondDieValue: Int? = null,
    val numberOfRolls: Int = 0,
)

class DiceRollViewModel : ViewModel() {

    // Expose screen UI state
    private val _uiState = MutableStateFlow(DiceUiState())
    val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()

    // Handle business logic
    fun rollDice() {
        _uiState.update { currentState ->
            currentState.copy(
                firstDieValue = Random.nextInt(from = 1, until = 7),
                secondDieValue = Random.nextInt(from = 1, until = 7),
                numberOfRolls = currentState.numberOfRolls + 1,
            )
        }
    }
}

Java

public class DiceUiState {
    private final Integer firstDieValue;
    private final Integer secondDieValue;
    private final int numberOfRolls;

    // ...
}

public class DiceRollViewModel extends ViewModel {

    private final MutableLiveData<DiceUiState> uiState =
        new MutableLiveData(new DiceUiState(null, null, 0));
    public LiveData<DiceUiState> getUiState() {
        return uiState;
    }

    public void rollDice() {
        Random random = new Random();
        uiState.setValue(
            new DiceUiState(
                random.nextInt(7) + 1,
                random.nextInt(7) + 1,
                uiState.getValue().getNumberOfRolls() + 1
            )
        );
    }
}

จากนั้นคุณจะเข้าถึง ViewModel จากกิจกรรมได้ดังนี้

Kotlin

import androidx.activity.viewModels

class DiceRollActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same DiceRollViewModel instance created by the first activity.

        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        val viewModel: DiceRollViewModel by viewModels()
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

Java

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.
        DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
        model.getUiState().observe(this, uiState -> {
            // update UI
        });
    }
}