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 interfacesmodel
: the Java object representations (entitites) for the data structures offered via the REST APIrepo
: the repositories that provide all the CRUD methods for the REST API; one per entityprofile
: 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.
In the api
package, create a new class ApiFactory
/**
* 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);
}
}
In the repo
package, create the UserProfileRepository
class.
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;
}
}
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.
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);
}
}
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.
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);
}
});
}
}