Created
          March 30, 2020 19:46 
        
      - 
      
- 
        Save 51enra/eb228af74ec66e4f6a23e151d30ad72c to your computer and use it in GitHub Desktop. 
Revisions
- 
        51enra created this gist Mar 30, 2020 .There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,165 @@ ## Android: REST API, Repository, ViewModel, LiveData Changes are described starting from the status after the previous live coding (Android Restcall with Retrofit) Package Structure below ```de.telekom.mayo.frontend.android.mayo```: - ```api```: the Retrofit interfaces and the Retrofit factory that also creates the implementations of the interfaces - ```model```: the Java object representations (entitites) for the data structures offered via the REST API - ```repo```: the repositories that provide all the CRUD methods for the REST API; one per entity - ```profile```: user interface related classes around the user profile, e.g. activities, viewmodels, adapters The package structure is likely to evolve further in the course of the project. Move the Interface ```UserProfileApi``` into the ```api``` package. The changes described below make use of the Android concept of LiveData. These are wrappers around datastructures that are often used when the content of the data structures is retrieved in background tasks, e.g. from a database or via Internet access. LiveData can be "observed" in order to detect when new content is available. ### Create the Retrofit factory In the ```api``` package, create a new class ```ApiFactory``` ```java /** * Creates API instances for performing REST calls. */ public class ApiFactory { private static final String BACKEND_BASE_URL = "http://10.0.2.2:8080"; private final Retrofit retrofit; private static ApiFactory instance; private ApiFactory() { Gson gson = new GsonBuilder() .setLenient() .create(); retrofit = new Retrofit.Builder() .baseUrl(BACKEND_BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); } /** * @return the only ApiService instance, never null */ public static synchronized ApiFactory getInstance() { if (instance == null) { instance = new ApiFactory(); } return instance; } /** * @param retrofitApiInterface defines the REST interface, must not be null * @param <S> * @return API instance for performing REST calls, never null */ public <S> S createApi(Class<S> retrofitApiInterface) { return retrofit.create(retrofitApiInterface); } } ``` ### Create the repository In the ```repo``` package, create the ```UserProfileRepository``` class. ```java public class UserProfileRepository { private static final String LOG_TAG = "UserProfileRepository"; private static UserProfileRepository instance; private final UserProfileApi userProfileApi; private UserProfileRepository() { userProfileApi = ApiFactory.getInstance().createApi(UserProfileApi.class); } public static synchronized UserProfileRepository getInstance() { if (instance == null) { instance = new UserProfileRepository(); } return instance; } public LiveData<UserProfile> getById(Long id) { final MutableLiveData<UserProfile> userProfileMutableLiveData = new MutableLiveData<>(); Call call = userProfileApi.getById(id); call.enqueue(new Callback<UserProfile>() { @Override public void onResponse(Call<UserProfile> call, Response<UserProfile> response) { if (response.isSuccessful()) { Log.d(LOG_TAG, "UserProfile " + response.body()); userProfileMutableLiveData.setValue(response.body()); } } @Override public void onFailure(Call<UserProfile> call, Throwable t) { Log.d(LOG_TAG, "UserProfile Error:" + t); userProfileMutableLiveData.setValue(null); } }); return userProfileMutableLiveData; } } ``` ### Create the viewmodel A viewmodel is paired with an activity. Therefore we put both in the same package. The viewmodel provides the data holder that survives activity lifecycle state changes. It exposes the LiveData to the activity. In the ```profile``` package, create the ```ShowUserProfileVieModel``` class. ```java public class ShowUserProfileViewModel extends AndroidViewModel { private final UserProfileRepository repository = UserProfileRepository.getInstance(); private final MutableLiveData<UserProfile> userProfile = new MutableLiveData<>(); public ShowUserProfileViewModel(@NonNull Application application) { super(application); } public MutableLiveData<UserProfile> getUserProfile() { return userProfile; } public LiveData<UserProfile> fetchUserProfileById(Long id) { return repository.getById(id); } } ``` ### Adjust the activity to make use of the viewmodel To provide the ViewModel object in the ```ShowUserProfileActivity``` class, add one dependencies to build.gradle (app module): ```implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'```. The ```ShowUserProfileActivity``` class is in the ```profile``` package. ``` java public class ShowUserProfileActivity extends AppCompatActivity { private static final String LOG_TAG = "ShowUserProfileActivity"; private ShowUserProfileViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show_user_profile); Log.d(LOG_TAG, "onCreate, activity=" + this); viewModel = new ViewModelProvider(this).get(ShowUserProfileViewModel.class); viewModel.getUserProfile().observe(this, new Observer<UserProfile>() { @Override public void onChanged(UserProfile userProfile) { if (userProfile != null) { TextView tFirstName = findViewById(R.id.text_first_name); TextView tLastName = findViewById(R.id.text_last_name); tFirstName.setText(userProfile.getFirstName()); tLastName.setText(userProfile.getLastName()); } } }); Log.d(LOG_TAG, "onCreate, viewModel=" + viewModel); } public void loadUserProfile(View view) { viewModel.fetchUserProfileById(1L).observe(this, new Observer<UserProfile>() { @Override public void onChanged(UserProfile userProfile) { viewModel.getUserProfile().setValue(userProfile); } }); } } ```