Offline-First Reactive Android Apps (Repository Pattern + MVP + Dagger 2 + RxJava + ContentProvider)

What we will be Learning?

  • Architecture an Android app to support Offline caching of data using RxJava, SQLite and ContentProvider
  • Use Repository Architecture to decouple Local and Remote Data Store
  • Use dagger 2 to provide dependency
  • Use MVP design pattern to architecture app in a clean way and decouple business logic
  • Our Local Datastore will be maintained using SQlite and we will be using Retrofit and OkHttp and Gson to request data from remote RESTful API service
  • A content provider will be used to fetch data from SQLite database
  • On top of ContentProvider we will be using StorIO to add Reactivity to our Database
  • Use RxJava and its awesome operators to observe changes in our data store and update the UI.

Step 1: Create a project with a Blank Activity template

Step 2: Add the necessary dependency

  • We will be using Retrofit, OkHttp, Gson, RxJava, RxAndroid Dagger 2 and StorIO to your apps build.gradle file
//Retrofit
compile 'com.squareup.retrofit2:retrofit:2.0.2'
//OkHttp
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.squareup.okio:okio:1.7.0'

//Gson
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.1'

//RxJava
compile 'io.reactivex:rxjava:1.1.2'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.1'

//StorIO
compile "com.pushtorefresh.storio:sqlite:1.9.0"
compile "com.pushtorefresh.storio:content-resolver:1.9.0"
compile "com.pushtorefresh.storio:sqlite-annotations:1.9.0"
compile "com.pushtorefresh.storio:content-resolver-annotations:1.9.0"
apt "com.pushtorefresh.storio:sqlite-annotations-processor:1.9.0"
apt "com.pushtorefresh.storio:content-resolver-annotations-processor:1.9.0"

Android Studio by default will not recognize a lot of generated Dagger 2 code as legitimate classes, but adding the android-apt plugin will add these files into the IDE class path and enable you to have more visibility. Add this line to your rootbuild.gradle:

dependencies {
     // other classpath definitions here
     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 }

Then make sure to apply the plugin in your app/build.gradle:

// add after applying plugin: 'com.android.application'  
apply plugin: 'com.neenbedankt.android-apt'

Add these three lines to your app/build.gradle file after this apply statement:

dependencies {
    // apt command comes from the android-apt plugin
    apt 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'javax.annotation:jsr250-api:1.0'
}

In dependencies I added:

  • dagger library
  • dagger-compiler for code generation
  • javax.annotation for additional annotations required outside Dagger

After updating Dagger’s configuration, you can synchronize the project with the gradle files by clicking the button at the top.

dagger-2-gradle-sync.jpg

Also add INTERNET permission to your manifest

<uses-permission android:name="android.permission.INTERNET" />

Step 3: Create packages to architecture our app in a clean way

  • At top level com.ladwa.aditya.offlinefirstapp we have App , BasePresenter , BaseView files
  • Also, I have 3 packages dagger, data, and mainscreen
  • Inside mainscreen I have MainscreenContract, Presenter and Activity class
  • Inside dager package I have component and module as packages that have the respective module and component files
  • Our data package is more complex at first glance however if we take a close look at and scrutinize it become self-explanatory.
  • Inside data package, we have an interface AppDataStore that has abstract methods of our Repository.
  • Further, I have defined two more packages local and remote that have classes AppLocalDataStore and AppRemoteDataStore that implement methods from AppDataStore interface
  • Inside local package there is another package models that holds all the POJO classes.
  • There is another file in data AppRepository that implements methods from AppDataStore interface. This class will be used by the Presenter to make request to data and this is the essence of Repository Architecture

This is how my app structure looks

 

Step 4: Create BasePresenter and BaseView

  • BaseView will be inherited by every Activity or Fragment of our app
  • BasePresenter will be inherited by every Presenter of our app

 

public interface BasePresenter {
    void subscribe();

    void unsubscribe();
}
public interface BaseView {
    void setPresenter(T presenter);
}
  • Note that the BaseView takes a Presenter of a Generic type and has a method setPresenter that will be called in the View that implements it.

Step 5: In mainscreen package create an interface called MainScreenContract

  • This interface will have two more inner interface called View and Presenter
  • View interface holds all the methods which we will implement in our MainScreen View (i.e in our case MainActivity)
  • Presenter interface has all the methods that we will implement in ourMainScreenPresenter
public class MainScreenContract {

    interface View extends BaseView {

        void showPosts(List posts);

        void showError(String message);

        void showComplete();
    }

    interface Presenter extends BasePresenter {
        void loadPost();

        void loadPostFromRemoteDatatore();
    }
}

Step 6: Define the methods that our Repository provides

public interface AppDataStore {
    Observable getPost();
}
  • We have only one method getPost that returns an Observable of List of Post
  • This interface will be implemented by AppLocalDataStore, AppRemoteDataStore, and AppRepository

Step 7: Define the Database Contract

  • We have a table “post” that has 4 columns ID, USER_ID, TITLE and BODY
  • This is the JSON response we will be receiving
public class DatabaseContract {

    public static final String CONTENT_AUTHORITY = "com.ladwa.aditya.offlinefirstapp";
    private static final String CONTENT_SCHEME = "content://";
    public static final Uri BASE_CONTENT_URI = Uri.parse(CONTENT_SCHEME + CONTENT_AUTHORITY);

    public static final String PATH_POST = "post";

    public DatabaseContract() {
    }

    public static abstract class Post implements BaseColumns {
        @NonNull
        public static final String CONTENT_URI_STRING = "content://" + CONTENT_AUTHORITY + "/" + PATH_POST;
        public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING);

        public static final String CONTENT_USER_TYPE = "vnd.android.cursor.dir/" + CONTENT_AUTHORITY + "/" + PATH_POST;
        public static final String CONTENT_USER_ITEM_TYPE = "vnd.android.cursor.item/" + CONTENT_AUTHORITY + "/" + PATH_POST;

        public static final String TABLE_NAME = "post";

        public static final String COLUMN_ID = "id";
        public static final String COLUMN_USER_ID = "user_id";
        public static final String COLUMN_TITLE = "title";
        public static final String COLUMN_BODY = "body";

        public static String getPostCreateQuery() {
            return "CREATE TABLE " + TABLE_NAME + " (" +
                    COLUMN_ID + " LONG NOT NULL PRIMARY KEY, " +
                    COLUMN_USER_ID + " LONG , " +
                    COLUMN_TITLE + " TEXT NOT NULL, " +
                    COLUMN_BODY + " TEXT NOT NULL" + ");";
        }

        public static String getUserDeleteQuery() {
            return "DROP TABLE IF EXISTS " + TABLE_NAME;
        }


        public static Uri buildUserUri(long id) {
            return ContentUris.withAppendedId(CONTENT_URI, id);
        }
    }
}

Step 8: Create a Database helper class

  • This class will be used to initialize the database in our Content Provider
public class DatabaseHelper extends SQLiteOpenHelper {

    public static final String DATABASE_NAME = "OfflineFirstApp.db";
    public static final int DATABASE_VERSION = 1;

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(DatabaseContract.Post.getPostCreateQuery());

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
        sqLiteDatabase.execSQL(DatabaseContract.Post.getUserDeleteQuery());
        onCreate(sqLiteDatabase);
    }
}

Step 9: Create the ContentProvider of the app

  • This may seem a little intimidating to grasp but ContentProviders are awesome once you master them
  • We have two URI matcher POST_ITEM and POST_DIR

Note : Dont forget to declare the ContentProvider in the AppMainfest file

<provider
    android:name=".data.local.Provider"
    android:authorities="com.ladwa.aditya.offlinefirstapp"
    android:exported="false"
    android:syncable="true" />
public class Provider extends ContentProvider {

    private static final int POST_ITEM = 100;
    private static final int POST_DIR = 101;

    private static final UriMatcher sUriMatcher = buildUriMatcher();
    private DatabaseHelper mDbHelper;


    private static UriMatcher buildUriMatcher() {
        final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
        final String authority = DatabaseContract.CONTENT_AUTHORITY;

        matcher.addURI(authority, DatabaseContract.PATH_POST + "/#", POST_ITEM);
        matcher.addURI(authority, DatabaseContract.PATH_POST, POST_DIR);

        return matcher;
    }

    @Override
    public boolean onCreate() {
        mDbHelper = new DatabaseHelper(getContext());
        return true;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor retCursor;
        switch (sUriMatcher.match(uri)) {
            case POST_ITEM:
                retCursor = mDbHelper.getReadableDatabase().query(
                        DatabaseContract.Post.TABLE_NAME,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder
                );
                break;
            case POST_DIR:
                retCursor = mDbHelper.getReadableDatabase().query(
                        DatabaseContract.Post.TABLE_NAME,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder
                );
                break;
            default:
                throw new UnsupportedOperationException("Unknown Uri " + uri);
        }
        retCursor.setNotificationUri(getContext().getContentResolver(), uri);
        return retCursor;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        final int match = sUriMatcher.match(uri);
        switch (match) {
            //Case for user
            case POST_ITEM:
                return DatabaseContract.Post.CONTENT_USER_ITEM_TYPE;
            case POST_DIR:
                return DatabaseContract.Post.CONTENT_USER_TYPE;
            default:
                throw new UnsupportedOperationException("Unknown URI " + uri);
        }
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues contentValues) {
        final SQLiteDatabase db = mDbHelper.getWritableDatabase();
        Uri returnUri;
        switch (sUriMatcher.match(uri)) {
            //Case for Post
            case POST_DIR:
                long _id = db.insert(DatabaseContract.Post.TABLE_NAME, null, contentValues);
                if (_id > 0)
                    returnUri = DatabaseContract.Post.buildUserUri(_id);
                else
                    throw new SQLException("Failed to insert row " + uri);
                break;
            default:
                throw new UnsupportedOperationException("Unknown URI " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return returnUri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        final SQLiteDatabase db = mDbHelper.getWritableDatabase();
        int rowsDeleted;
        switch (sUriMatcher.match(uri)) {
            case POST_DIR:
                rowsDeleted = db.delete(DatabaseContract.Post.TABLE_NAME, selection, selectionArgs);
                break;
            default:
                throw new UnsupportedOperationException("Unknown URI " + uri);
        }
        if (selection == null || 0 != rowsDeleted)
            getContext().getContentResolver().notifyChange(uri, null);
        return rowsDeleted;
    }

    @Override
    public int update(Uri uri,  ContentValues values, String selection, String[] selectionArgs) {
        final SQLiteDatabase db = mDbHelper.getWritableDatabase();
        int update;
        switch (sUriMatcher.match(uri)) {
            //Case for User
            case POST_DIR:
                update = db.update(DatabaseContract.Post.TABLE_NAME, values, selection, selectionArgs);
                break;
            default:
                throw new UnsupportedOperationException("Unknown URI " + uri);
        }
        if (update > 0)
            getContext().getContentResolver().notifyChange(uri, null);
        return update;
    }
}

Step 10: Create the POJO class for Post

  • Notice the Annotations that I’m using provided by StorIO library
  • These annotations are used to create Get, Put and Delete resolvers to Content Provider that. Refer StorIO documentation for more info
@StorIOSQLiteType(table = DatabaseContract.Post.TABLE_NAME)
@StorIOContentResolverType(uri = DatabaseContract.Post.CONTENT_URI_STRING)
public class Post {


    @StorIOSQLiteColumn(name = DatabaseContract.Post.COLUMN_ID, key = true)
    @StorIOContentResolverColumn(name = DatabaseContract.Post.COLUMN_ID, key = true)
    public  Integer id;

    @StorIOSQLiteColumn(name = DatabaseContract.Post.COLUMN_USER_ID)
    @StorIOContentResolverColumn(name = DatabaseContract.Post.COLUMN_USER_ID)
    public  Integer userId;

    @StorIOSQLiteColumn(name = DatabaseContract.Post.COLUMN_TITLE)
    @StorIOContentResolverColumn(name = DatabaseContract.Post.COLUMN_TITLE)
    public  String title;

    @StorIOSQLiteColumn(name = DatabaseContract.Post.COLUMN_BODY)
    @StorIOContentResolverColumn(name = DatabaseContract.Post.COLUMN_BODY)
    public  String body;

    public Post(Integer id, Integer userId, String title, String body) {
        this.id = id;
        this.userId = userId;
        this.title = title;
        this.body = body;
    }

    public Post() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }
}

Step 11:Use dagger 2 to provide dependency

  • AppModule provides context of the Application
  • DatModule provides depencency such as Retrofit, LocalRepository, RemoteRepository
  • Component
@Module
public class AppModule {
    Application mApplication;

    public AppModule(Application mApplication) {
        this.mApplication = mApplication;
    }

    @Provides
    @Singleton
    Application provideApplication() {
        return mApplication;
    }
}
@Module
public class DataModule {
    String mBaseUrl;

    public DataModule(String mBaseUrl) {
        this.mBaseUrl = mBaseUrl;
    }

    @Provides
    @Singleton
    SharedPreferences providesSharedPreferences(Application application) {
        return PreferenceManager.getDefaultSharedPreferences(application);
    }

    @Provides
    @Singleton
    Cache provideHttpCache(Application application) {
        int cacheSize = 10 * 1024 * 1024;
        Cache cache = new Cache(application.getCacheDir(), cacheSize);
        return cache;
    }

    @Provides
    @Singleton
    Gson provideGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
        return gsonBuilder.create();
    }

    @Provides
    @Singleton
    OkHttpClient provideOkhttpClient(Cache cache) {
        OkHttpClient.Builder client = new OkHttpClient.Builder();
        client.cache(cache);
        return client.build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(mBaseUrl)
                .client(okHttpClient)
                .build();
        return retrofit;
    }


    @Provides
    @Singleton
    AppLocalDataStore porvidesAppLocalDataStore(Application context) {
        return new AppLocalDataStore(context);
    }

    @Provides
    @Singleton
    AppRemoteDataStore providesRepository() {
        return new AppRemoteDataStore();
    }

}
  • The methods will be injected in MainActivity and RemoteDatastore
@Singleton
@Component(modules = {AppModule.class, DataModule.class})
public interface AppComponent {
    void inject(MainActivity activity);
    void inject(AppRemoteDataStore appRemoteDataStore);
}

Step 12: Implement AppDataStore in AppLocalDataStore class

  • We use @Inject in the Constructor so that dagger provides the context
  • We have a member variable of type StorIOContentResolver
  • We add the Type maping for Post POJO class and the Put, Get, Delete resolvers that were generated by StorIO
  • Note : Rebuild your project to generate these class
public class AppLocalDataStore implements AppDataStore {

    private StorIOContentResolver mStorIOContentResolver;

    @Inject
    public AppLocalDataStore(@NonNull Context context) {
        mStorIOContentResolver = DefaultStorIOContentResolver.builder()
                .contentResolver(context.getContentResolver())
                .addTypeMapping(Post.class, ContentResolverTypeMapping.builder()
                        .putResolver(new PostStorIOContentResolverPutResolver())
                        .getResolver(new PostStorIOContentResolverGetResolver())
                        .deleteResolver(new PostStorIOContentResolverDeleteResolver())
                        .build()
                ).build();
    }


    @Override
    public Observable getPost() {
        Log.d("LOCAL","Loaded from local");
        return mStorIOContentResolver.get()
                .listOfObjects(Post.class)
                .withQuery(Query.builder().uri(DatabaseContract.Post.CONTENT_URI).build())
                .prepare()
                .asRxObservable();
    }

    public void savePostToDatabase(List posts) {
        mStorIOContentResolver.put().objects(posts).prepare().executeAsBlocking();
    }
}

Step 13: Implement AppDataStore in AppRemoteDataStore class

  • We have 2 member variables injected
  • In getPost() we make a request to our remote RESTful API and when we get the result we use RxJava’s doOnNext() operator to save the posts in the database
public class AppRemoteDataStore implements AppDataStore {

    @Inject
    Retrofit retrofit;

    @Inject
    AppLocalDataStore appLocalDataStore;

    public AppRemoteDataStore() {
        App.getAppComponent().inject(this);
    }


    @Override
    public Observable getPost() {
        Log.d("REMOTE","Loaded from remote");

        return retrofit.create(PostService.class).getPostList().doOnNext(new Action1() {
            @Override
            public void call(List posts) {
                appLocalDataStore.savePostToDatabase(posts);
            }
        });
    }


    private interface PostService {
        @GET("/posts")
        Observable getPostList();
    }
}

Step 14: Implement AppDataStore in AppRepository class

  • We use @Inject so that dagger provides the Local and Remote repository
  • In getPost() we use RxJava’s concat operator to concat local and remote repository
  • The operator first() will return the observable from the repository that has posts
  • So if we have posts in local database they will be returned first. If not a GET request will be made to the Remote RESTful data service
public class AppRepository implements AppDataStore {

    private AppLocalDataStore mAppLocalDataStore;
    private AppRemoteDataStore mAppRemoteDataStore;


    @Inject
    public AppRepository(AppLocalDataStore mAppLocalDataStore, AppRemoteDataStore mAppRemoteDataStore) {
        this.mAppLocalDataStore = mAppLocalDataStore;
        this.mAppRemoteDataStore = mAppRemoteDataStore;
    }

    @Override
    public Observable getPost() {
        return Observable.concat(mAppLocalDataStore.getPost(), mAppRemoteDataStore.getPost())
                .first(new Func1<List, Boolean>() {
                    @Override
                    public Boolean call(List posts) {
                        return posts != null;
                    }
                });
    }
}

Step 15: Create an App class that extends Application and add it to manifest

  • We create the Dagger component in onCreate()
  • getAppComponent() will return the AppComponent wherever we need variables to be injected
public class App extends Application {

    private static AppComponent mAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        mAppComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .dataModule(new DataModule("http://jsonplaceholder.typicode.com/"))
                .build();
    }


    public static AppComponent getAppComponent() {
        return mAppComponent;
    }

}
  • Add this class name in the tag of AppMainfest
<application
    android:allowBackup="true"
    android:name=".App"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

Step 16: Implement the Presenter

  • The presenter calls loadPost() immediately when the view subscribes to the Presenter
  • loadPostFromRemote() is called when the user whats the date to be explicitly loaded from the remote REST API, which automaticlly updates the data in our database also
  • in unsubscrive() the Subscriptions are unsubscribed to avoid memory leak
public class MainScreenPresenter implements MainScreenContract.Presenter {

    private static final String TAG = MainScreenPresenter.class.getSimpleName();
    private Subscription mSubscription;
    private AppRepository mAppRepository;
    private MainScreenContract.View mView;

    public MainScreenPresenter(AppRepository mAppRepository, MainScreenContract.View mView) {
        this.mAppRepository = mAppRepository;
        this.mView = mView;
        mView.setPresenter(this);
    }

    @Override
    public void loadPost() {
        mSubscription = mAppRepository.getPost()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.newThread())
                .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        Log.d(TAG, "Complete");
                        mView.showComplete();
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d(TAG, e.toString());
                        mView.showError(e.toString());
                    }

                    @Override
                    public void onNext(List posts) {
                        mView.showPosts(posts);
                    }
                });
    }

    @Override
    public void loadPostFromRemoteDatatore() {
        new AppRemoteDataStore().getPost().observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.newThread())
                .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        Log.d(TAG, "Complete");
                        mView.showComplete();
                        loadPost();
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d(TAG, e.toString());
                        mView.showError(e.toString());
                    }

                    @Override
                    public void onNext(List posts) {

                    }
                });
    }

    @Override
    public void subscribe() {
        loadPost();
    }

    @Override
    public void unsubscribe() {
        //Unsubscribe Rx subscription
        if (mSubscription != null && mSubscription.isUnsubscribed())
            mSubscription.unsubscribe();
    }
}

Step 17: Create activity_main layout

  • I have a Listview inside a SwipeRefreshLayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.ladwa.aditya.offlinefirstapp.mainscreen.MainActivity">


    <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/swipeContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ListView
            android:id="@+id/my_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>

 

Step 18: Code MainActivity

  • MainActivity implements MainScreenContract.View interface
  • AppRepository is injected by using @Inject annotation
  • We override onResume() and onStop() where we  subscribe and unsubscribe respectively to the Presenter
  • When we receive the data showPost() is called by presenter and the result is displayed
  • When there is an error onError() is called and a Toast is show to user
public class MainActivity extends AppCompatActivity implements MainScreenContract.View, SwipeRefreshLayout.OnRefreshListener {

    private MainScreenContract.Presenter mPresenter;
    private ListView listView;
    private ArrayList list;
    private ArrayAdapter adapter;

    @Inject
    AppRepository repository;
    SwipeRefreshLayout swipeContainer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Inject dependency
        App.getAppComponent().inject(this);

        listView = (ListView) findViewById(R.id.my_list);
        swipeContainer = (SwipeRefreshLayout) findViewById(R.id.swipeContainer);
        swipeContainer.setOnRefreshListener(this);
        list = new ArrayList<>();

        new MainScreenPresenter(repository, this);
    }

    @Override
    public void showPosts(List posts) {
        for (int i = 0; i < posts.size(); i++) {
            list.add(posts.get(i).getTitle());
        }
        //Create the array adapter and set it to list view
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
        listView.setAdapter(adapter);
    }

    @Override
    public void showError(String message) {
        Toast.makeText(this, "Error loading post", Toast.LENGTH_SHORT).show();
        if (swipeContainer != null)
            swipeContainer.post(new Runnable() {
                @Override
                public void run() {
                    swipeContainer.setRefreshing(false);
                }
            });
    }

    @Override
    public void showComplete() {
        Toast.makeText(this, "Completed loading", Toast.LENGTH_SHORT).show();

        if (swipeContainer != null)
            swipeContainer.post(new Runnable() {
                @Override
                public void run() {
                    swipeContainer.setRefreshing(false);
                }
            });
    }

    @Override
    protected void onResume() {
        super.onResume();
        mPresenter.subscribe();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mPresenter.unsubscribe();
    }

    @Override
    public void setPresenter(MainScreenContract.Presenter presenter) {
        mPresenter = presenter;
    }

    @Override
    public void onRefresh() {
        mPresenter.loadPostFromRemoteDatatore();
    }
}

ScreenShots

Screenshot_20161025-190810.png

Github

Conclusion

  • We used Repository architecture to make your apps Offline first
  • We used MVP design pattern to decouple business logic from implementation
  • We used Dagger 2 for dependency injection
  • We also created a local cache i.e SQLite database and a ContentProvider and wrapped it with StorIO and its Reactive features
  • We learnt a few RxJava operator.

 

Advertisements

Android NDK (HelloJNI)

How will this post help you?

  • You’ll learn how to setup Android studioand use Android NDKand use Native code (C++)in your Android Apps.
  • I will be using Android Studio 2.2 RC

Step 1: Setup Android Studio and Android NDK

Before we can start writing native code we need to download Android NDK using Android SDK Manager as shown in the figure.

Download the Android NDK package and CMake

ndk.pngcmake.png

 

Step 2: Create a new Project in Android studio

Please pay close attention as this is different from other normal android project

  • In this window check the box that says include C++ Support

project_create.png

  • Choose your target Android Devices

create1.png

  • Select the Activity type you want to add

create2.png

  • I’m leaving this to default

create3.png

  • Choose the default toolchain and click Finish

create4.png

Step 3: Run the project

run.png

Screenshot_20160903-194330.png

Step 4: Let’s change the code

  • Open the file native-lib.cpp and add the following code

open.png

  • Java_com_example_aditya_hellondk must match your package
  • Next MainActivity is the name of activity where you’ll use this code
  • stringAnother is the method name
extern "C"
jstring
Java_com_example_aditya_hellondk_MainActivity_stringAnother(JNIEnv *env, jobject) {
    std::string anotherString = "The code is changed now";
    return env->NewStringUTF(anotherString.c_str());
}
  • Now change MainActivity.java as follows
public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringAnother());
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
    public native String stringAnother();
}

Step 4: Run it again

You should see this as your output

Screenshot_20160903-202131.png

Conclusion

  • We learned to set up and run a helloJNI project

Github’s link for code

Testing android MVP

What this post is about?

  • Using Gradle and Android Studio to run Android tests
  • Writing unit tests for Android using JUnit4
  • Writing UI tests with Espresso and the Android Testing Support Library
  • We will be using our last project so if you are new please check out Dagger 2 and MVP Architecture  first

Let’s get started

Step 1: Add the necessary dependency

The Android Testing Support library (ATSL) provides a great framework for testing your Android app. It has a JUnit 4-compatible test runner (AndroidJUnitRunner) and functional UI testing through Espresso and UI Automator. You can run tests for these APIs directly in the Android Studio IDE or from the command line, which makes it very easy to integrate them into your development flow.

 

// Dependencies for local unit tests
testCompile "junit:junit:4.12"
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support:support-annotations:24.1.1'
compile 'com.android.support.test.espresso:espresso-idling-resource:2.2.2'
compile 'com.android.support.test.espresso:espresso-contrib:2.2.2'

 

Add this

android {
    ...
    ...
    ...
    ...
    ...
 
    testOptions.unitTests.all {
        testLogging {
            events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
        }
    }

    packagingOptions {
        exclude 'META-INF/maven/com.google.dagger/dagger/pom.properties'
        exclude 'META-INF/maven/com.google.dagger/dagger/pom.xml'
    }


}

And add this line in the defaultConfig  block

defaultConfig {
    ....

    testInstrumentationRunner ""
}

If you perform a sync there will be a conflict with the minSdk version for test source set and main source set, so create AndroidManifest.xml  in  AndroidTest source set

 2016-08-18_00h10_01.png

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="${applicationId}">
    <uses-sdk tools:overrideLibrary="android.app, android.support.test, android.support.test.rule, android.support.test.espresso, android.support.test.espresso.idling, android.support.test.uiautomator.v18"/>
</manifest>

Step 2: Create an IdlingResource file to idle resource when the application makes call to Network

 

public class EspressoIdlingResource {

    private static final String RESOURCE = "GLOBAL";

    private static CountingIdlingResource mCountingIdlingResource =
            new CountingIdlingResource(RESOURCE);

    public static void increment() {
        mCountingIdlingResource.increment();
    }

    public static void decrement() {
        mCountingIdlingResource.decrement();
    }

    public static IdlingResource getIdlingResource() {
        return mCountingIdlingResource;
    }
}

Step 3: Add the following static method in MainActivity.java

@VisibleForTesting
public IdlingResource getCountingIdlingResource() {
    return EspressoIdlingResource.getIdlingResource();
}

Step 4: Add Idling status when network request is made

The CountingIdlingResource is incremented when we make a NetworkRequest and Decrement after the network request finishes.

Note:. When the counter is 0 – it is considered to be idle, when it is non-zero it is not idle.

So in onCreate we increment the counter before calling the loadPost in our Presenter

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    listView = (ListView) findViewById(R.id.my_list);
    list = new ArrayList<>();

    DaggerMainScreenComponent.builder()
            .netComponent(((App) getApplicationContext()).getNetComponent())
            .mainScreenModule(new MainScreenModule(this))
            .build().inject(this);


    //Increment the counter before making a network request
    EspressoIdlingResource.increment();

    //Call the method in MainPresenter to make Network Request
    mainPresenter.loadPost();
}

Similarly,  we decrement the counter after the posts are fetched

@Override
public void showPosts(List<Post> posts) {
    //Loop through the posts and get the title of the post and add it to our list object
    for (int i = 0; i < posts.size(); i++) {
        list.add(posts.get(i).getTitle());
    }
    //Create the array adapter and set it to list view
    adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
    listView.setAdapter(adapter);
    //Decrement after loading the posts
    EspressoIdlingResource.decrement();
}

Also, decrement the counter if there is an error (No active Internet or Server error)

@Override
public void showError(String message) {
    //Show error message Toast
    Toast.makeText(getApplicationContext(), "Error" + message, Toast.LENGTH_SHORT).show();
    
    // If there is no network connection we get an error and decrement the counter because the call has finished
    EspressoIdlingResource.decrement();
}

Step 5: Write the test case

Create a file MainActivityTest.java in AndroidTest/java source set

@RunWith(AndroidJUnit4.class)
@SmallTest
public class MainActivityTest {

    @Rule
    public ActivityTestRule<MainActivity> mainActivityActivityTestRule = new ActivityTestRule<>(MainActivity.class);


    @Test
    public void checkRecyclerView() throws Exception {
        Espresso.registerIdlingResources(mainActivityActivityTestRule.getActivity().getCountingIdlingResource());
        Espresso.onView(ViewMatchers.withId(R.id.my_list)).perform(ViewActions.click());
    }
}

Step 6: Run the Test by creating a new Run Configuration

2016-08-18_00h28_47.png

Then click on the plus icon and select Android Tests

2016-08-18_00h30_26.png

Configure the test as follows
Name : Test (Can be anything)

Module : app

Specific instrumentation runner : android.support.test.runner.AndroidJUnitRunner

and then click Ok

2016-08-18_00h31_16.png

Now, select the newly create Run Configuration for the Run Menu

2016-08-18_00h31_16.png

Select the device to run the Instrumentation test

2016-08-18_00h42_55.png

2016-08-18_00h43_06.png

2016-08-18_00h43_49.png

Finally, you should see the Test pass. If the test takes too long to finish stop and rerun.

Conclusion

  • We learnt to set up Android Testing Support library
  • We used Espresso’s Idling resources feature to wait till the network call happen
  • We created a custom run configuration to run our tests

Github’s link for code

https://github.com/LadwaAditya/DaggerMVP-Tutorial

Reference

Dagger 2 and MVP Architecture

What we will be Learning?

  • Use dagger2 with RetrofitOkHttpGson and RxJava and RxAndroid
  • Use Model View Presenter(MVP) Architecture to decouple the Business logic and the underlying implementation of the logic to produce more cleaner code
  • Use RxJava to make request to our REST API
  • Use Retrofit to make call to a API and display the result
  • Learn about dependent components in dagger and creating custom scope for dependent components
  • We will be using Android studio and the code will be hosted on github

Lets get started

Step 1: Create a project with a Blank Activity

This slideshow requires JavaScript.

Step 2: Add the necessary dependency

Android Studio by default will not recognize a lot of generated Dagger 2 code as legitimate classes, but adding the android-apt plugin will add these files into the IDE class path and enable you to have more visibility. Add this line to your rootbuild.gradle:

dependencies {
     // other classpath definitions here
     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 }

Then make sure to apply the plugin in your app/build.gradle:

// add after applying plugin: 'com.android.application'  
apply plugin: 'com.neenbedankt.android-apt'

Add these three lines to your app/build.gradle file after this apply statement:

dependencies {
    // apt command comes from the android-apt plugin
    apt 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'javax.annotation:jsr250-api:1.0'
}

In dependencies I added:

  • dagger library
  • dagger-compiler for code generation
  • javax.annotation for additional annotations required outside Dagger

After updating Dagger’s configuration, you can synchronize the project with the gradle files by clicking the button at the top.

dagger-2-gradle-sync.jpg

 

Step 3: Create packages to architecture our app in a clean way

  • We will be creating three packages called dataand mainscreen and util that we add the respective classes in these packages
  • We create two more packages inside dagger packages called asmodule and component
  • We will create all the files related to our mainscreen in the mainscreen package (MainActivity MainScreenContract)

This is how my app structure looks

2016-05-11_16h52_04.png

Step 4: Add Retrofit, OkHttp, Gson, RxJava libraries to your app/gradle file

 

This is how my build.gradle file looks after i add the dependency

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'

    //Retrofit
    compile 'com.squareup.retrofit2:retrofit:2.0.2'
    //OkHttp
    compile 'com.squareup.okhttp3:okhttp:3.2.0'
    compile 'com.squareup.okio:okio:1.7.0'

    //Gson
    compile 'com.google.code.gson:gson:2.6.2'
    compile 'com.squareup.retrofit2:converter-gson:2.0.1'

    //RxJava
    compile 'io.reactivex:rxjava:1.1.2'
    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.1'

    //Dagger
    apt 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'javax.annotation:jsr250-api:1.0'
}

Also add INTERNET permission to your manifest

<uses-permission android:name="android.permission.INTERNET" />

Step 5: Create an AppModule that will provide context to other modules

  • Now because Retrofit and OkHttp will be used through the application we will created an AppModule that will be instantiated when the Application starts so that the app context is provided to Retrofit and OkHttp library
@Module
public class AppModule {
    Application mApplication;

    public AppModule(Application mApplication) {
        this.mApplication = mApplication;
    }

    @Provides
    @Singleton
    Application provideApplication() {
        return mApplication;
    }
}

Step 6: Create a NetModule that will provide Retrofit and OkHttp

@Module
public class NetModule {
    String mBaseUrl;

    public NetModule(String mBaseUrl) {
        this.mBaseUrl = mBaseUrl;
    }

    @Provides
    @Singleton
    SharedPreferences providesSharedPreferences(Application application) {
        return PreferenceManager.getDefaultSharedPreferences(application);
    }

    @Provides
    @Singleton
    Cache provideHttpCache(Application application) {
        int cacheSize = 10 * 1024 * 1024;
        Cache cache = new Cache(application.getCacheDir(), cacheSize);
        return cache;
    }

    @Provides
    @Singleton
    Gson provideGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
        return gsonBuilder.create();
    }

    @Provides
    @Singleton
    OkHttpClient provideOkhttpClient(Cache cache) {
        OkHttpClient.Builder client = new OkHttpClient.Builder();
        client.cache(cache);
        return client.build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(mBaseUrl)
                .client(okHttpClient)
                .build();
        return retrofit;
    }
}

Step 7: Create NetComponent Interface

  • We create an Interface called NetComponent with AppModule and NetModule as its modules
  • Note that we will be using this Component as a dependency for our MainScreenComponent so we need retrofit as a dependency. Hence we expose its return type so that when MainScreen injects in any of the activities or fragments the MainScreenComponent can find and Inject Retrofit from this Module(May be confusing , continue reading to understand how this works)
@Singleton
@Component(modules = {AppModule.class, NetModule.class})
public interface NetComponent {
    // downstream components need these exposed with the return type
    // method name does not really matter
    Retrofit retrofit();
}

Step 8: Create a class App that extendApplication

  • Create a class App, extend it from Application class
  • Override onCreate method
public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
    }
}
  • Because we are overriding the default Application class, we also modify the application name to launch App. This way your application will use this application class to handle the initial instantiation.
<application
      android:allowBackup="true"
      android:name=".App">
  • Create a private variable of type NetComponent and in onCreate build theNetComponent
  • Create a public method called getNetComponent() that will return the netcomponent variable whenever we require this in our App
public class App extends Application {
    private NetComponent mNetComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mNetComponent = DaggerNetComponent.builder()
                .appModule(new AppModule(this))
                .netModule(new NetModule("http://jsonplaceholder.typicode.com/"))
                .build();
    }

    public NetComponent getNetComponent() {
        return mNetComponent;
    }
}

Make sure to rebuild the project (in Android Studio, select Build > Rebuild Project) if you cannot reference the Dagger component.

Step 8: Create a POJO class and interface to make GET request to a REST API

  • http://www.jsonplaceholder.typicode.com is a fake REST api that providesJSON data
  • Create a POJO class called Post with title and body as String variables
  • Also add getters and setters using Android Studio
public class Post {
    private final Integer userId;
    private final Integer id;
    private final String title;
    private final String body;

    public Post(Integer userId, Integer id, String title, String body) {
        this.userId = userId;
        this.id = id;
        this.title = title;
        this.body = body;
    }

    public Integer getUserId() {
        return userId;
    }

    public Integer getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getBody() {
        return body;
    }
}

Step 9: In mainscreen package create an interface called MainScreenContract

  • This interface will have two more inner interface called View and Presenter
  • View interface holds all the methods which we will implement in our MainScreen View (i.e in our case MainActivity)
  • Presenter interface has all the methods that we will implement in our MainScreenPresenter (i.e only one method loadPost())
public interface MainScreenContract {
    interface View {
        void showPosts(List<Post> posts);

        void showError(String message);

        void showComplete();
    }

    interface Presenter {
        void loadPost();
    }
}

Step 10: Create MainScreenModule and MainScreenComponent

  • The purpose of creating MainScreenModule is to provide the View ( in our case MainActivity) to our Presenter when it is injected. The Presenter uses this view as a reference to our MainAvtivity to call the methods in the View (showError(),showComplete()) whenever the network operation are performed
  • It is a means through which the View and the Presenter Communicate with each other
  • In Dagger, two dependent components cannot share the same scope i.e (Our NetComponent  and MainScreenComponent cannot share the scope of @Singlton). So we create a custom  scope  called @CustomeScope to be used by our MainScreenComponent and MainScreenModule 

 

This is our MainScreenModule class

@Module
public class MainScreenModule {
    private final MainScreenContract.View mView;


    public MainScreenModule(MainScreenContract.View mView) {
        this.mView = mView;
    }

    @Provides
    @CustomScope
    MainScreenContract.View providesMainScreenContractView() {
        return mView;
    }
}

This is our MainScreenComponent

@CustomScope
@Component(dependencies = NetComponent.class, modules = MainScreenModule.class)
public interface MainScreenComponent {
    void inject(MainActivity activity);
}

This is our CustomeScope interface in util package

@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomScope {
}

Step 9: Create MainPresenter which will implement the methods to make network calls(Your apps logic)

  • MainPresenter implements methods from MainScreenContract.Presenter
public class MainScreenPresenter implements MainScreenContract.Presenter {

    @Override
    public void loadPost() {

    }
}
  • We create an interface called PostService that makes a GET request at /posts and get a Observable of List of  Post objects as return type
private interface PostService {
    @GET("/posts")
    Observable<List<Post>> getPostList();
}
  • Our MainPresenter requires retrofit to make the network calls and the View to which it will display the results of the network calls. So we provide the Retrofit and View (MainActivity) by injecting them in the constructor.
  • So whenever a new object of MainPresenter is created Dagger will inject the dependency.
  • The retrofit dependency will be provided by NetComponent and the MainScreenContract.View dependency will be provided by the MainScreenComponent whenever we inject it in our Views(in our case we will inject it in MainActivity)
Retrofit retrofit;
MainScreenContract.View mView;

@Inject
public MainScreenPresenter(Retrofit retrofit, MainScreenContract.View mView) {
    this.retrofit = retrofit;
    this.mView = mView;
}
  • Now in our MainPresenter we implement the method loadPost() to make NetworkRequest using the retrofit object and PostService
  • As we will be using RxJava we get an Observable of a List of Post Objects
@Override
public void loadPost() {
    retrofit.create(PostService.class).getPostList().subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .unsubscribeOn(Schedulers.io())
            .subscribe(new Observer<List<Post>>() {
                @Override
                public void onCompleted() {
                    mView.showComplete();
                }

                @Override
                public void onError(Throwable e) {
                    mView.showError(e.getMessage());
                }

                @Override
                public void onNext(List<Post> posts) {
                    mView.showPosts(posts);
                }
            });
}
  • RxJavahas 3 methods for us
  • onNext() is called when we recieve the data
  • onError() is called obviously when we some error occures
  • onCompleted() is called when the network call is finished
  • We use the reference object mView and pass the results to the View via this reference variable which was injected in the View (i.e MainActivity)

This is how our MainPresenter class should look after all this

public class MainScreenPresenter implements MainScreenContract.Presenter {

    Retrofit retrofit;
    MainScreenContract.View mView;

    @Inject
    public MainScreenPresenter(Retrofit retrofit, MainScreenContract.View mView) {
        this.retrofit = retrofit;
        this.mView = mView;
    }

    @Override
    public void loadPost() {
        retrofit.create(PostService.class).getPostList().subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .unsubscribeOn(Schedulers.io())
                .subscribe(new Observer<List<Post>>() {
                    @Override
                    public void onCompleted() {
                        mView.showComplete();
                    }

                    @Override
                    public void onError(Throwable e) {
                        mView.showError(e.getMessage());
                    }

                    @Override
                    public void onNext(List<Post> posts) {
                        mView.showPosts(posts);
                    }
                });
    }

    private interface PostService {
        @GET("/posts")
        Observable<List<Post>> getPostList();
    }
}

Step 10: Create a Listview in activity_main.xml to display the list we receive

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".mainscreen.MainActivity">

    <ListView
        android:id="@+id/my_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

Step 11: Implement MainScreenContract.View in our MainActivity

  • We implement the 3 methods in our MainActivity

    public class MainActivity extends AppCompatActivity implements MainScreenContract.View {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        @Override
        public void showPosts(List<Post> posts) {
            
        }
    
        @Override
        public void showError(String message) {
    
        }
    
        @Override
        public void showComplete() {
    
        }
    }

 

  • We create the necessary variables to deal with the listview

ListView listView;
ArrayList<String> list;
ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    listView = (ListView) findViewById(R.id.my_list);
    list = new ArrayList<>();
}
  • We create a variable for our MainScreenPresenter and use @Inject annotation to inject it. We will use this variable to call the loadPost() method which is implemented in MainPresenter

@Inject
MainScreenPresenter mainPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    listView = (ListView) findViewById(R.id.my_list);
    list = new ArrayList<>();

    DaggerMainScreenComponent.builder()
            .netComponent(((App) getApplicationContext()).getNetComponent())
            .mainScreenModule(new MainScreenModule(this))
            .build().inject(this);

    //Call the method in MainPresenter to make Network Request
    mainPresenter.loadPost();
}
  • Now we write the logic inside the methods  showPosts(List<Post> posts)  , showError(String message),showComplete()
  • These methods will be called by our MainPresenter using the mViewreference whenever the Network calls are made and the results will be passed to our MainActivity
@Override
public void showPosts(List<Post> posts) {
    //Loop through the posts and get the title of the post and add it to our list object
    for (int i = 0; i < posts.size(); i++) {
        list.add(posts.get(i).getTitle());
    }
    //Create the array adapter and set it to list view
    adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
    listView.setAdapter(adapter);
}

@Override
public void showError(String message) {
    //Show error message Toast
    Toast.makeText(getApplicationContext(), "Error" + message, Toast.LENGTH_SHORT).show();

}

@Override
public void showComplete() {
    //Show completed message Toast
    Toast.makeText(getApplicationContext(), "Complete", Toast.LENGTH_SHORT).show();

}
  • This is how our MainActivity looks finaly
public class MainActivity extends AppCompatActivity implements MainScreenContract.View {
    ListView listView;
    ArrayList<String> list;
    ArrayAdapter<String> adapter;

    @Inject
    MainScreenPresenter mainPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.my_list);
        list = new ArrayList<>();

        DaggerMainScreenComponent.builder()
                .netComponent(((App) getApplicationContext()).getNetComponent())
                .mainScreenModule(new MainScreenModule(this))
                .build().inject(this);

        //Call the method in MainPresenter to make Network Request
        mainPresenter.loadPost();
    }

    @Override
    public void showPosts(List<Post> posts) {
        //Loop through the posts and get the title of the post and add it to our list object
        for (int i = 0; i < posts.size(); i++) {
            list.add(posts.get(i).getTitle());
        }
        //Create the array adapter and set it to list view
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
        listView.setAdapter(adapter);
    }

    @Override
    public void showError(String message) {
        //Show error message Toast
        Toast.makeText(getApplicationContext(), "Error" + message, Toast.LENGTH_SHORT).show();

    }

    @Override
    public void showComplete() {
        //Show completed message Toast
        Toast.makeText(getApplicationContext(), "Complete", Toast.LENGTH_SHORT).show();

    }
}

Final Output

Screenshot_2016-05-11-18-20-16.png

Conclusion

  • We use dagger 2 along with retrofit, OkHttp and Gson and RxJava to make a request to a REST API and display the data in the ListView
  • We used MVP architecture to decouple our Application logic with the underlining implementation details
  • Pros : MVP is great and recommended for large code base, and promotes cleaner code
  • Cons: MVP is writing too much of code to get things done (lot of boiler plate)

Github link for code

https://github.com/LadwaAditya/DaggerMVP-Tutorial

 Reference

Dagger 2 with Retrofit and OkHttp and Gson

What we will be Learning?

  • Use dagger2 with Retrofit and OkHttp and Gson
  • Inject dependency using @Inject  annotation to the Variables
  • Use Retrofit to make call to a API and display the result
  • We will be using Android studio and the code will be hosted on github

Lets get started

Step 1: Create a project with a Blank Activity

Screenshot (3).png

Step 2: Add the necessary dependency

Android Studio by default will not recognize a lot of generated Dagger 2 code as legitimate classes, but adding the android-apt plugin will add these files into the IDE class path and enable you to have more visibility. Add this line to your rootbuild.gradle:

dependencies {
     // other classpath definitions here
     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 }

Then make sure to apply the plugin in your app/build.gradle:

// add after applying plugin: 'com.android.application'  
apply plugin: 'com.neenbedankt.android-apt'

Add these three lines to your app/build.gradle file after this apply statement:

dependencies {
    // apt command comes from the android-apt plugin
    apt 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'javax.annotation:jsr250-api:1.0'
}

In dependencies I added:

  • dagger library
  • dagger-compiler for code generation
  • javax.annotation for additional annotations required outside Dagger

After updating Dagger’s configuration, you can synchronize the project with the gradle files by clicking the button at the top.

dagger-2-gradle-sync.jpg

Step 3: Create packages to architecture our app in a clean way

  • We will be creating two packages called dagger and ui so that we add the respective classes in these packages
  • We will put all the user interface related classes such as MainActivity in the ui package
  • Further we create two more packages inside dagger packages called as module and component

This is how my app structure looks

2016-05-09_13h59_55.png

Step 4: Add Retrofit, OkHttp, Gson libraries to your app/gradle file

 

This is how my build.gradle file looks after i add the dependency

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'

    //Dagger
    apt 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'javax.annotation:jsr250-api:1.0'

    //Retrofit
    compile 'com.squareup.retrofit2:retrofit:2.0.2'
    
    //OkHttp
    compile 'com.squareup.okhttp3:okhttp:3.2.0'
    compile 'com.squareup.okio:okio:1.7.0'

    //Gson
    compile 'com.google.code.gson:gson:2.6.2'
    compile 'com.squareup.retrofit2:converter-gson:2.0.1'
}

Also add INTERNET permission to your manifest

<uses-permission android:name="android.permission.INTERNET" />

Step 5: Create an AppModule that will provide context to other modules

  • Now because Retrofit and OkHttp will be used through the application we will created an AppModule that will be instantiated when the Application starts so that the app context is provided to Retrofit and OkHttp library
@Module
public class AppModule {
    Application mApplication;

    public AppModule(Application mApplication) {
        this.mApplication = mApplication;
    }

    @Provides
    @Singleton
    Application provideApplication() {
        return mApplication;
    }
}

Step 6: Create a NetModule that will provide Retrofit and OkHttp

@Module
public class NetModule {
    String mBaseUrl;

    public NetModule(String mBaseUrl) {
        this.mBaseUrl = mBaseUrl;
    }


    @Provides
    @Singleton
    Cache provideHttpCache(Application application) {
        int cacheSize = 10 * 1024 * 1024;
        Cache cache = new Cache(application.getCacheDir(), cacheSize);
        return cache;
    }

    @Provides
    @Singleton
    Gson provideGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
        return gsonBuilder.create();
    }

    @Provides
    @Singleton
    OkHttpClient provideOkhttpClient(Cache cache) {
        OkHttpClient.Builder client = new OkHttpClient.Builder();
        client.cache(cache);
        return client.build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .baseUrl(mBaseUrl)
                .client(okHttpClient)
                .build();
    }
}

Step 7: Create NetComponent Interface

  • We create an Interface called NetComponent with AppModule and NetModule as its modules
  • We create a method called inject and pass activity in which we will require retrofit in our case we will be calling it only from MainActivity.(You can add other Activity or Fragments)
@Singleton
@Component(modules = {AppModule.class, NetModule.class})
public interface NetComponent {
    void inject(MainActivity activity);
}

Step 8: Create a class App that extend Application

  • Create a class App, extend it from Application class
  • Override onCreate method
public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
    }
}
  • Because we are overriding the default Application class, we also modify the application name to launch App. This way your application will use this application class to handle the initial instantiation.
<application
      android:allowBackup="true"
      android:name=".App">
  • Create a private variable of type NetComponent and in onCreate build the NetComponent
  • Create a public method called getNetComponent() that will return the netcomponent variable whenever we require this in our App
public class App extends Application {

    private NetComponent mNetComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        mNetComponent = DaggerNetComponent.builder()
             .appModule(new AppModule(this))
             .netModule(new NetModule("http://www.jsonplaceholder.typicode.com/"))
             .build();
    }

    public NetComponent getNetComponent() {
        return mNetComponent;
    }
}

Make sure to rebuild the project (in Android Studio, select Build > Rebuild Project) if you cannot reference the Dagger component.

Step 8: Create a POJO class and interface to make GET request to a REST API

  • http://www.jsonplaceholder.typicode.com is a fake REST api that provides JSON data
  • Create a POJO class called Post with title and body as String variables
  • Also add getters and setters using Android Studio
public class Post {
    String title;
    String body;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }
}
  • Create an interface called Restapi that makes a GET request at /posts and get a List of  Post objects as return type
public interface Restapi {

    @GET("/posts")
    Call<List<Post>> getPosts();
}

Step 9: In MainActivity inject the retrofit dependency

  • The inject method will look for any variables annotated with @Inject and will provide dependency if there is any @Provides method that returns the same type
public class MainActivity extends AppCompatActivity {

    @Inject
    Retrofit retrofit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((App) getApplication()).getNetComponent().inject(this);
    }

}

Step 10: Make request using the Retrofit Object

public class MainActivity extends AppCompatActivity {

    @Inject
    Retrofit retrofit;
    TextView textView;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((App) getApplication()).getNetComponent().inject(this);

        //Create textview and findViewByID
        textView = (TextView) findViewById(R.id.textview_post);
        //Create a retrofit call object
        Call<List<Post>> posts = retrofit.create(Restapi.class).getPosts();

        //Enque the call
        posts.enqueue(new Callback<List<Post>>() {
            @Override
            public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {
               //Set the response to the textview
                textView.setText(response.body().get(0).getBody());

            }

            @Override
            public void onFailure(Call<List<Post>> call, Throwable t) {
                //Set the error to the textview
                textView.setText(t.toString());
            }
        });
    }
}

Final Output

Screenshot_2016-05-09-16-24-02.png

Conclusion

  • We use dagger 2 along with retrofit, OkHttp and Gson to make a request to a REST API and display the data in the textview.
  • We can see how dagger improves the architecture of the android application in making our code mode modular and clean

Github link for code

https://github.com/LadwaAditya/DaggerRetrofitOkhttp-Tutorial

 Reference

 

Dagger 2 dependency injection

What we will be Learning?

  • Use dagger 2 with SharedPreference
  • Inject dependency and code in a clean way
  • We will be using Android studio and the code will be hosted on github

What is Dagger and Why Should I use it?

  • Dagger 2 is a dependency injection tool by Google used for Java and Android Projects (In our case we will be using it for Android)
  • Dagger is used to Architecture Android Application which improves and enhances the workflow of your Application
  • For larger production level application Dagger come in handy to provide Dependency such as SharedPreference, Network client such as Retrofit, OkHttp, JSON parser such as Gson or Jackson

Dagger 2 API

Dagger has a few Annotation that we need to know before we get started

  • @Module for the classes whose methods provide dependencies
  • @Provides for the methods within @Module classes
  • @Inject to request a dependency (a constructor, a field, or a method)
  • @Component is a bridge interface between modules and injection

Lets get started

Step 1: Create a project with a Blank Activity

Screenshot (3).png

Step 2: Add the necessary dependency

Android Studio by default will not recognize a lot of generated Dagger 2 code as legitimate classes, but adding the android-apt plugin will add these files into the IDE class path and enable you to have more visibility. Add this line to your root build.gradle:

dependencies {
     // other classpath definitions here
     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 }

Then make sure to apply the plugin in your app/build.gradle:

// add after applying plugin: 'com.android.application'  
apply plugin: 'com.neenbedankt.android-apt'

Add these three lines to your app/build.gradle file after this apply statement:

dependencies {
    // apt command comes from the android-apt plugin
    apt 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'javax.annotation:jsr250-api:1.0'
}

In dependencies I added:

  • dagger library
  • dagger-compiler for code generation
  • javax.annotation for additional annotations required outside Dagger

After updating Dagger’s configuration, you can synchronize the project with the gradle files by clicking the button at the top.

dagger-2-gradle-sync.jpg

Step 3: Create @Module Class for Shared Preference

We create a class called SharedPreferenceModule.java and annotate it with @Module to signal to Dagger to search within the available methods for possible instance providers.

The methods that will actually expose available return types should also be annotated with@Provides decorator. The Singleton annotation also signals to the Dagger compiler that the instance should be created only once in the application.

@Module
public class SharedPreferenceModule {
    Application mApplication;

    public SharedPreferenceModule(Application mApplication) {
        this.mApplication = mApplication;
    }

    @Provides
    @Singleton
    Application providesApplication() {
        return mApplication;
    }

    @Provides
    @Singleton
    SharedPreferences providesSharedPreferences(Application application) {
        return PreferenceManager.getDefaultSharedPreferences(application);
    }
}

Step 4: Connecting @Modules With @Inject

The connection between the provider of dependencies, @Module, and the classes requesting them through@Inject is made using @Component, which is an interface:

@Singleton
@Component(modules = {SharedPreferenceModule.class})
public interface SharedPrerenceComponent {
    SharedPreferences getSharedPreference();
}

Next to the @Component annotation, you have to specify which modules are going to be used—in this case I use SharedPreferenceModule, which we created earlier. If you need to use more modules, then just add them using a comma as a separator.

Within the interface, add methods for every object you need and it will automatically give you one with all its dependencies satisfied. In this case, I only need a SharedPreference object, which is why there is only one method.

 

Step 5 :Using @Component Interface to Obtain Objects

An important  aspect of Dagger 2 is that the library generates code for classes annotated with the@Component interface. You can use a class prefixed with Dagger (i.e.DaggerSharedPreferenceComponent.java) that will be responsible for instantiating an instance of our dependency graph

 

Note : Make sure to rebuild the project (in Android Studio, select Build > Rebuild Project) if you cannot reference the Dagger component.

 

public class MainActivity extends AppCompatActivity {

    private SharedPreferences mSharedPreferences;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Dagger%COMPONENT_NAME%
        mSharedPreferences = DaggerSharedPrerenceComponent.builder()
                .sharedPreferenceModule(new SharedPreferenceModule(getApplication()))
                .build().getSharedPreference();

        String messgage = mSharedPreferences.getString("key", "Hello");
        Toast.makeText(this, messgage, Toast.LENGTH_SHORT).show();

    }
}

This is our final output

Screenshot_2016-05-07-13-40-35.png

Conclusion

Dependency injection is a pattern that you will have to implement sooner or later in your own applications. With Dagger 2, you have an easy-to-use library to implement it. I hope you found this tutorial useful, and don’t forget to share it if you liked it.

Github link for code

https://github.com/LadwaAditya/Dagger2-Tutorial

 

Reference