Introducing MVVM (Model-View-ViewModel) Architecture in Phimpme Android App
Introducing MVVM in Phimpme

Introducing MVVM (Model-View-ViewModel) Architecture in Phimpme Android App

Phimpme Android App an image editor app that aims to replace proprietary photographing and image apps on smartphones. It offers features such as taking photos, adding filters, editing images and uploading them to social networks. The app was using MVP(Model-View-Presenter) architecture and is now being ported to MVVM(Model-View-ViewModel) architecture.

Advantages of MVVM over MVP?

  1. The view model is lifecycle aware and only updates the UI based on the lifecycle of the activity/fragment.
  2. Separation of concerns – Not all the code under one single activity
  3. Loose coupling – Activity depends on ViewModel and ViewModel depends on the Repository and not the other way around.

MVVM?

  1. Model – Model represents the data and business logic of the app. The repository can be seen as a model in an MVVM architecture which contains login to fetch the data from an API or a remote API
  2. ViewModel – The view model creates a reference with Model/Repository and gets the data for the UI. It delivers the data to UI via observers of LiveData and also the ViewModel is lifecycle aware and respects the lifecycle of the activity such as screen rotations that don’t cause the ViewModel to be created again.
  3. View – The Activity/Fragment is the view where the data is shown to the user, the View creates a reference to the ViewModel via ViewModel provider class. Hence it listens to the ViewModel callbacks via LiveData.

Process for inclusion

  1. Add ViewModel and LiveData

    implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.lifecycleVersion"

  2. Now create a class AccountViewModel – it will perform all the functioning that will drive the UI of the Account Activity. We will use LiveData for observing the data in the activity

    public class AccountViewModel extends ViewModel {
    private AccountRepository accountRepository

    = new AccountRepository();
    MutableLiveData<RealmQuery<AccountDatabase>>accountDetails = new MutableLiveData<>();//live data 

    }

  3. Create a class AccountRepository – Used to perform the DB related operations and the ViewModel will hold the instance of this repository.

    class AccountRepository {
    private Realm realm = Realm.getDefaultInstance();
    private DatabaseHelper databaseHelper = new DatabaseHelper(realm);// Fetches the details of all accounts present in database
    RealmQuery<AccountDatabase> fetchAllAccounts() {
    return databaseHelper.fetchAccountDetails();
     }
    }


  4. Now we will add the functionality in AccountViewModel to fetch accounts for the UI

    public class AccountViewModel extends ViewModel {
     final int RESULT_OK = 1;
    private AccountRepository accountRepository = new AccountRepository();
    MutableLiveData<Boolean> error = new MutableLiveData<>();
    MutableLiveData<RealmQuery<AccountDatabase>> accountDetails = new MutableLiveData<>();
    public AccountViewModel() {}
    // Used to fetch all the current logged in accounts
    void fetchAccountDetails() {
       RealmQuery<AccountDatabase> accountDetails = accountRepository.fetchAllAccounts();
    if (accountDetails.findAll().size() > 0) {
         this.accountDetails.postValue(accountDetails);
    } else {
     error.postValue(true);
    }
    }


  5. Now in the AccountActivity, we will have the reference of ViewModel and then observe the liveData error and accountDetails

    public class AccountActivity extends ThemedActivityimplements RecyclerItemClickListner.OnItemClickListener {

    private AccountViewModel accountViewModel;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ButterKnife.bind(this);
    ActivitySwitchHelper.setContext(this);
    setSupportActionBar(toolbar);
    //fetching the viewmodel from ViewModelProviders
    accountViewModel = ViewModelProviders.of(this).get(AccountViewModel.class);
    initObserver();
    }

    private void initObserver() {
    accountViewModel.error.observe(this, value -> {
    if (value) {
     SnackBarHandler.create(coordinatorLayout, getString(no_account_signed_in)).show();
    showComplete();
    }
     });
    accountViewModel.accountDetails.observe(this, this::setUpAdapter);
    }


Hence, this completes the implementation of MVVM Architecture in the Phimpme app.

Resources 

  1. Guide to App Architecture – Android Developers Blog
  2. ViewModel Overview – Android Developers Blog
  3. LiveData Overview – Android Developers Blog

Link to the Issue: https://github.com/fossasia/phimpme-android/issues/2889
Link to the PR: https://github.com/fossasia/phimpme-android/pull/2890

Continue Reading Introducing MVVM (Model-View-ViewModel) Architecture in Phimpme Android App

Chrome Custom Tabs Integration – SUSI.AI Android App

Earlier, we have seen the apps having external links that opens and navigates the user to the phone browser when clicked, then we came up with something called WebView for Android, but nowadays we have shifted to something called In-App browsers. The main drawback of the system/ phone browsers are they caused heavy transition. To overcome this drawback “Chrome Custom Tabs” were invented which allowed users to walk through the web content seamlessly.

SUSI.AI Android App earlier used the system browser to open any link present in the app.

This can be implemented easily by

Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(browserIntent);

This lead to a huge transition between the context of the app and the web browser.

Then, to reduce all the clutter Chrome Custom tabs by Google was evolved which drastically increased the loading speed and the heavy context switch was also not taking place due to the integration and adaptability of custom tabs within the app.

Chrome custom tabs also are very secured like Chrome Browser and uses the same feature and give developers a more control on the custom actions, user interface within the app.

                                comparing the load time of the above mentioned techniques

Ref : Android Dev – Chrome Custom Tabs

Integration of Chrome Custom Tabs

  • Adding the dependency in build.gradle(app-level) in the project
dependencies {
    //Other dependencies 
    compile 'com.android.support:customtabs:23.4.0'
}
  • Now instantiating a CustomTabsIntent Builder

    String url = “https://www.fossasia.org” // can be any link
    
    CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); //custom tabs intent builder
    
    CustomTabsIntent customTabsIntent = builder.build();
  • We can also add animation or customize the color of the toolbar or add action buttons.

    builder.setColor(Color.RED) //for setting the color of the toolbar 
    builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left); //for start animation
    builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right); //for exit animation
  • Finally, we have have achieved everything with a little code. Final launch the web page

    Uri webpage = Uri.parse(url); //We have to pass an URI
    
    customTabsIntent.launchUrl(context, webpage); //launching through custom tabs

Benefits of Chrome Custom Tabs

  1. UI Customization are easily available and can be implemented with very few lines of code. 
  2. Faster page loading and in-app access of the external link 
  3. Animations for start/exit  
  4. Has security and uses the same permission model as in chrome browser.

Resources

  1. Chrome Custom Tabs:  https://developer.chrome.com/multidevice/android/customtabs
  2. Chrome Custom Tabs Github Repo: GitHub – GoogleChrome/custom-tabs-client: Chrome custom tabs
  3. Android Blog: Android Developers Blog: Chrome custom tabs smooth the transition
  4. Video: Chrome Custom Tabs: Displaying 3rd party content in your Android

 

Continue Reading Chrome Custom Tabs Integration – SUSI.AI Android App

Leak Canary in Phimpme Android

Leak Canary is a memory detection library for Android and Java. A memory leak occurs when you hold an object for too long after its purpose has been served. If some object is holding another object then the Garbage collector will not be able to collect and this is known as Memory Leak. These memory leaks can be minor (in KB’s) or can lead to an app in ANR state with OutOfMemoryError.Hence to recover and capture this memory leak, Leak Canary is used for Android and Java.

For every functioning done in android, the system needs resource such as memory. Hence in Java the Garbage Collector(GC) plays a major role in deallocating the memory. The GC is mainly used to reclaim memory. Now the question arises, why do we need a memory leak detection library when GC is already present. The answer is sometimes the developers makes programming mistakes and that leads to inhibit the GC to collect the objects that are of no use and mark them as useful objects.

The GC starts from one point(root) and marks active to all the objects that holds references from GC root and the objects which are not marked are wiped out of memory.Hence when some unuseful objects is marked active, memory leak occurs.Hence to eliminate these problems  of memory leaks, we have employed the use of Leak Canary in our project.

The Phimpme project and every related project has possible memory leaks, like for instance we have used fragments in settings activity and to catch that memory leak we have added the refwatcher instance. Hence if any memory leaks occur we get the error such as ‘org.fossasia.phimpme’  leaked 40kb.This can also be checked by Leaks App in the android phone, which has features of showing and sharing the heap dump and info.

To add the Leak Canary in your android app, follow these steps:

  • Add the dependencies in build.gradle(app level)

    dependencies {
       debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.4'
       releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
     }
  • Add the following code to your Application class

    public class MyApplication extends Application{
    @Override
    public void onCreate() {
       if (LeakCanary.isInAnalyzerProcess(this)) {
           // This process is dedicated to LeakCanary for heap analysis.
           // You should not init your app in this process.
           return;
       }
       LeakCanary.install(this);
    }
    
  • Leak canary now will automatically detect the memory leaks  from all activities.

For fragment a refwatcher is needed, hence

public class MYFragment extends Fragment {
    ...

    @Override
    public void onDestroy() {
        super.onDestroy();
        MainApplication.getRefWatcher(getActivity()).watch(this);
    }
}

Hence LeakCanary is setup finally, and now the memory leaks will be detected.

Resources

 

Continue Reading Leak Canary in Phimpme Android