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

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

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

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

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

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

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

ขอบเขต

เมื่อสร้างอินสแตนซ์ ViewModel คุณจะส่งออบเจ็กต์ที่ใช้ อินเทอร์เฟซ ViewModelStoreOwner ไปยัง ViewModel ซึ่งอาจเป็นปลายทางการนำทาง กราฟการนำทาง กิจกรรม 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
        });
    }
}