Android

OnCreate 중복호출 방지 및 ViewModel

Dean83 2022. 6. 2. 17:04

1. OnCreate 중복호출 막기
    - 해당 함수는 딱 한번만 호출되는지 알았는데 그렇지가 않았다. 키보드가 생겼다 사라진다던지, Landscape, Portrait로 변경시

        중복 호출된다. 

    - 이를 막기 위해서는, Manifest에 다음을 추가 해야한다. 그러나 추천하지 않는다. 

<activity
.....
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
......

</activity>

 

2. Orientation 전환등 onCreate가 호출시 기존 항목 저장, 복원
    - 기본적으로 onCreate가 호출되면서 onSaveInstance() 등 시스템에서 자동으로 처리되나, 데이터가 많을경우에는

      수동으로 처리해 주어야 한다. 

    - onSaveInstance, viewmodel을 이용한 저장방법이 있고, 아예 수동으로 저장하는 방법이 있다. 
       onSaveInstance의 경우에는 작은 데이터에만 유용하므로 여기선 다루지 않는다.

3. ViewModel
     - UI에서 사용하는 데이터 들을 저장, 복원하는 클래스를 생성하고,  액티비티 혹은 프래그먼트에서 이 클래스를 이용한다.
     - 액티비티나 프래그먼트가 소멸될때 ViewModel의 onCleared가 호출된다.      
     - ViewModel은 생명주기가 뷰 보다 길기 때문에, 내부에 View 를 직접 참조하면 안된다. 
     - ViewModel 클래스 생성 부분

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}


   - 액티비티에서 해당 뷰모델 접근

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle 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.

        MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}


3.1. 한 Activity에 속한 2개의 프래그먼트 간 데이터 공유 (ViewModel 이용)
       - 한 Activity에서 2개의 프래그먼트를 사용하고, 두개가 밀접한 연관이 있을경우 ViewModel을 통해 데이터 공유가 가능하다.

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class ListFragment extends Fragment {
    private SharedViewModel model;

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        model.getSelected().observe(getViewLifecycleOwner(), item -> {
           // Update the UI.
        });
    }
}

 ** UI의 데이터와 DB를 연결하는 로더를 대체할 수 있다고 하나 이는 다루지 않는다. (추후에)

참조
 - https://developer.android.com/guide/topics/resources/runtime-changes?hl=ko

 

구성 변경 처리  |  Android 개발자  |  Android Developers

구성 변경 처리 일부 기기 구성은 런타임에 변경될 수 있습니다(예: 화면 방향, 키보드 가용성 및 사용자가 다중 창 모드를 활성화할 경우). 그러한 변경이 일어나는 경우 Android는 실행 중인 Activi

developer.android.com

- https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ko#loaders