백그라운드 서비스 및 업데이트 UI에서 ViewModel의 LiveData를 업데이트하는 방법


97

최근에 구글에서 소개 한 안드로이드 아키텍처를 탐구하고 있습니다. 문서 에서 다음 을 찾았습니다.

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

    private void loadUsers() {
        // do async operation to fetch users
    }
}

활동은 다음과 같이이 목록에 액세스 할 수 있습니다.

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

내 질문은 이렇게 할 것입니다.

  1. loadUsers()기능 내가 먼저 데이터에 대한 데이터베이스 (방)을 확인합니다 경우 비동기 적으로 데이터를 가져 오는 있어요

  2. 거기에서 데이터를 얻지 못하면 웹 서버에서 데이터를 가져 오기 위해 API 호출을 할 것입니다.

  3. 가져온 데이터를 데이터베이스 (Room)에 삽입하고 데이터에 따라 UI를 업데이트하겠습니다.

이를 위해 권장되는 접근 방식은 무엇입니까?

메서드 Service에서 API를 호출 하기 위해 a 를 시작하면 loadUsers()어떻게 그 MutableLiveData<List<User>> users변수를 업데이트 할 수 Service있습니까?


9
우선, 리포지토리가 없습니다. ViewModel은 데이터로드 작업을 수행하지 않아야합니다. 그 외에는 Room을 사용하기 때문에 서비스가 ViewModel에서 LiveData를 직접 업데이트 할 필요가 없습니다. Service는 Room에 데이터 만 삽입 할 수 있지만 ViewModelData는 Room에만 연결해야하며 Room에서 업데이트를 가져와야합니다 (Service가 데이터를 삽입 한 후). 그러나 최고의 아키텍처를 위해이 페이지 하단의 NetworkBoundResource 클래스 구현을 살펴보십시오. developer.android.com/topic/libraries/architecture/guide.html
Marko Gajić

제안 해 주셔서 감사합니다 :)
CodeCameo

1
Repositor 클래스는 ROOM 또는 Android 아키텍처 구성 요소를 설명하는 공식 문서에 언급되지 않았습니다
Jonathan

2
리포지토리는 코드 분리 및 아키텍처를위한 권장 모범 사례입니다. 다음 예제를보십시오. codelabs.developers.google.com/codelabs/…
glisu

1
이 기능은 loadUsers()기본적으로 사용자 정보를 얻을 수있는 REPO를 호출합니다
CodeCameo

답변:


97

나는 당신이 안드로이드 아키텍처 구성 요소를 사용하고 있다고 가정하고 있습니다 . 실제로 service, asynctask or handler데이터를 업데이트하기 위해 전화 하는 곳은 중요하지 않습니다 . method를 사용하여 서비스 또는 asynctask에서 데이터를 삽입 할 수 있습니다 postValue(..). 수업은 다음과 같습니다.

private void loadUsers() {
    // do async operation to fetch users and use postValue() method
    users.postValue(listOfData)
}

(가)로 users입니다 LiveData, Room데이터베이스가 삽입 된 위치에 관계없이 사용자가 데이터를 제공 할 책임이있다.

Note: 아키텍처와 같은 MVVM에서 저장소는 주로 로컬 데이터 및 원격 데이터를 확인하고 가져 오는 역할을합니다.


3
위와 같이 내 db 메서드를 호출 할 때 "java.lang.IllegalStateException : Cannot access database on the main thread since it"이 발생합니다. 무엇이 잘못되었는지 알 수 있습니까?
Prashant

UI에있는 동안 백그라운드에서 데이터베이스를 업데이트하는 evernote-job을 사용하고 있습니다. 그러나 LiveData업데이트되지 않는다
하기 Akshay Chordiya

users.postValue(mUsers);->하지만 MutableLiveData의 postValue 메소드는 LiveData를받을 수 있습니까 ???
Cheok Yan Cheng

2
내 실수는 value대신 postValue. 답변 해 주셔서 감사합니다.
Hesam jul.

1
@pcj 스레드에서 룸 작업을 수행하거나 기본 스레드에서 작업을 활성화해야합니다.
kilokahn

46

MutableLiveData<T>.postValue(T value)백그라운드 스레드에서 메서드를 사용할 수 있습니다 .

private void loadUsers() {
    // do async operation to fetch users and use postValue() method
   users.postValue(listOfData)
}

19
생각 나게하기 위해, postValue는 () MutableLiveData에 LiveData 보호하지만, 공개
롱 레인저

이 작업은 백그라운드 작업 및 .setValue()허용되지 않는 상황에서도 작동합니다.
davejoem

18

... loadUsers () 함수에서 데이터를 비동기 적으로 가져오고 있습니다. loadUsers () 메서드에서 API를 호출하는 서비스를 시작하는 경우 해당 서비스에서 MutableLiveData> users 변수를 어떻게 업데이트 할 수 있습니까?

앱이 백그라운드 스레드에서 사용자 데이터를 가져 오는 경우 postValue ( setValue 가 아닌 )가 유용합니다.

loadData 메서드에는 MutableLiveData "users"개체에 대한 참조가 있습니다. loadData 메소드는 또한 어딘가 (예 : 저장소)에서 일부 새로운 사용자 데이터를 가져옵니다.

이제 실행이 백그라운드 스레드에서 이루어지면 MutableLiveData.postValue ()는 MutableLiveData 객체의 관찰자 외부에서 업데이트됩니다.

아마도 다음과 같습니다.

private MutableLiveData<List<User>> users;

.
.
.

private void loadUsers() {
    // do async operation to fetch users
    ExecutorService service =  Executors.newSingleThreadExecutor();
    service.submit(new Runnable() {
        @Override
        public void run() {
            // on background thread, obtain a fresh list of users
            List<String> freshUserList = aRepositorySomewhere.getUsers();

            // now that you have the fresh user data in freshUserList, 
            // make it available to outside observers of the "users" 
            // MutableLiveData object
            users.postValue(freshUserList);        
        }
    });

}

리포지토리의 getUsers()메서드는 비동기 데이터에 대해 API를 호출 할 수 있습니다 (이 경우 서비스 또는 비동기 작업을 시작할 수 있음).이 경우 반환 문에서 List를 어떻게 반환 할 수 있습니까?
CodeCameo 2017-06-09

아마도 LiveData 개체를 인수로 사용할 수 있습니다. (repository.getUsers (users)와 같은 것). 그런 다음 저장소 메서드는 users.postValue 자체를 호출합니다. 그리고이 경우 loadUsers 메서드에는 백그라운드 스레드가 필요하지 않습니다.
albert c braun

1
대답 해 주셔서 감사합니다 .... 그러나 저는 저장을 위해 Room DB를 사용하고 있고 DAO는 LiveData <Object>를 반환합니다 ... LiveData를 MutableLiveData <Object>로 어떻게 변환합니까?
kilokahn

Room의 DAO가 실제로 MutableLiveData 개체를 처리하기위한 것이 아니라고 생각합니다. DAO는 기본 db에 대한 변경을 알리지 만 DB의 값을 변경하려면 DAO의 메서드를 호출합니다. 또한 여기에서 토론하는 것이 유용 할 수 있습니다. stackoverflow.com/questions/50943919/...
알버트 C의 브라운

3

상기 살펴보세요 안드로이드 아키텍처 가이드 와 같은 새로운 아키텍처 모듈을 함께 LiveDataViewModel. 그들은이 정확한 문제를 심도있게 논의합니다.

그들의 예에서 그들은 그것을 서비스에 넣지 않습니다. "리포지토리"모듈과 Retrofit을 사용하여 어떻게 해결하는지 살펴보십시오. 하단의 부록에는 네트워크 상태 전달, 오류보고 등 더 완전한 예가 포함되어 있습니다.


2

Repository에서 API를 호출하는 경우

에서 저장소 :

public MutableLiveData<LoginResponseModel> checkLogin(LoginRequestModel loginRequestModel) {
    final MutableLiveData<LoginResponseModel> data = new MutableLiveData<>();
    apiService.checkLogin(loginRequestModel)
            .enqueue(new Callback<LoginResponseModel>() {
                @Override
                public void onResponse(@NonNull Call<LoginResponseModel> call, @Nullable Response<LoginResponseModel> response) {
                    if (response != null && response.isSuccessful()) {
                        data.postValue(response.body());
                        Log.i("Response ", response.body().getMessage());
                    }
                }

                @Override
                public void onFailure(@NonNull Call<LoginResponseModel> call, Throwable t) {
                    data.postValue(null);
                }
            });
    return data;
}

에서 의 ViewModel

public LiveData<LoginResponseModel> getUser() {
    loginResponseModelMutableLiveData = repository.checkLogin(loginRequestModel);
    return loginResponseModelMutableLiveData;
}

에서 활동 / 조각

loginViewModel.getUser().observe(LoginActivity.this, loginResponseModel -> {
        if (loginResponseModel != null) {
            Toast.makeText(LoginActivity.this, loginResponseModel.getUser().getType(), Toast.LENGTH_SHORT).show();
        }
    });

참고 : 여기에서 JAVA_1.8 람다를 사용하면 없이도 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.