├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── RetroRX.iml ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── captech │ │ └── retrorx │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── captech │ │ └── retrorx │ │ ├── ActivityView.java │ │ ├── FriendResponse.java │ │ ├── NetworkService.java │ │ ├── PresenterInteractor.java │ │ ├── PresenterLayer.java │ │ └── RxApplication.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | RetroRX-public -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RetroRx 2 | Demo application of an MVP approach to RxJava and Retrofit 2.0 3 | 4 | Check out the associated blog here: 5 | 6 | http://www.captechconsulting.com/blogs/a-mvp-approach-to-lifecycle-safe-requests-with-retrofit-20-and-rxjava 7 | 8 | This application is meant solely for demonstration purposes based on the blog topics discussed. 9 | -------------------------------------------------------------------------------- /RetroRX.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.captech.retrorx" 9 | minSdkVersion 14 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:23.1.1' 25 | compile 'com.squareup.retrofit2:retrofit:2.0.1' 26 | compile 'com.squareup.retrofit2:converter-gson:2.0.1' 27 | compile 'com.squareup.retrofit2:adapter-rxjava:2.0.1' 28 | compile 'io.reactivex:rxandroid:1.1.0' 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/cteegarden/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/captech/retrorx/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.captech.retrorx; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/captech/retrorx/ActivityView.java: -------------------------------------------------------------------------------- 1 | package com.captech.retrorx; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.view.View.OnClickListener; 8 | import android.widget.Button; 9 | import android.widget.ProgressBar; 10 | import android.widget.TextView; 11 | 12 | import retrofit2.Response; 13 | 14 | 15 | public class ActivityView extends AppCompatActivity implements OnClickListener { 16 | 17 | private static final String EXTRA_RX = "EXTRA_RX"; 18 | private Button rxCall, retroCall; 19 | private TextView rxResponse, retroResponse; 20 | private ProgressBar progressBar; 21 | private NetworkService service; 22 | private boolean rxCallInWorks = false; 23 | private PresenterInteractor presenter; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | rxCall = (Button)findViewById(R.id.rxCall); 30 | retroCall = (Button)findViewById(R.id.retroCall); 31 | rxResponse = (TextView)findViewById(R.id.rxResponse); 32 | retroResponse = (TextView)findViewById(R.id.retroResponse); 33 | progressBar = (ProgressBar)findViewById(R.id.progressBar); 34 | rxCall.setOnClickListener(this); 35 | retroCall.setOnClickListener(this); 36 | service = ((RxApplication)getApplication()).getNetworkService(); 37 | presenter = new PresenterLayer(this, service); 38 | if(savedInstanceState!=null){ 39 | rxCallInWorks = savedInstanceState.getBoolean(EXTRA_RX); 40 | } 41 | } 42 | 43 | 44 | @Override 45 | public void onClick(View v) { 46 | switch (v.getId()){ 47 | case R.id.retroCall: 48 | presenter.loadRetroData(); 49 | break; 50 | case R.id.rxCall: 51 | rxCallInWorks = true; 52 | presenter.loadRxData(); 53 | break; 54 | } 55 | } 56 | 57 | @Override 58 | protected void onPause() { 59 | super.onPause(); 60 | presenter.rxUnSubscribe(); 61 | 62 | } 63 | 64 | @Override 65 | protected void onSaveInstanceState(Bundle outState) { 66 | super.onSaveInstanceState(outState); 67 | outState.putBoolean(EXTRA_RX, rxCallInWorks); 68 | } 69 | 70 | @Override 71 | protected void onResume() { 72 | super.onResume(); 73 | if(rxCallInWorks) 74 | presenter.loadRxData(); 75 | } 76 | 77 | 78 | protected void showRxResults(FriendResponse response){ 79 | rxResponse.setText(response.friendLocations.data.friend.get(0).friendName); 80 | rxResponse.setVisibility(View.VISIBLE); 81 | rxCall.setEnabled(true); 82 | retroCall.setEnabled(true); 83 | progressBar.setVisibility(View.GONE); 84 | } 85 | 86 | protected void showRxFailure(Throwable throwable){ 87 | Log.d("TAG", throwable.toString()); 88 | rxResponse.setText("ERROR"); 89 | rxResponse.setVisibility(View.VISIBLE); 90 | rxCall.setEnabled(true); 91 | retroCall.setEnabled(true); 92 | progressBar.setVisibility(View.GONE); 93 | } 94 | 95 | protected void showRetroResults(Response response){ 96 | retroResponse.setText(response.body().friendLocations.data.friend.get(0).friendName); 97 | retroResponse.setVisibility(View.VISIBLE); 98 | retroCall.setEnabled(true); 99 | rxCall.setEnabled(true); 100 | progressBar.setVisibility(View.GONE); 101 | } 102 | 103 | protected void showRetroFailure(Throwable throwable){ 104 | Log.d("TAG", throwable.toString()); 105 | retroResponse.setText("ERROR"); 106 | retroResponse.setVisibility(View.VISIBLE); 107 | retroCall.setEnabled(true); 108 | rxCall.setEnabled(true); 109 | progressBar.setVisibility(View.GONE); 110 | } 111 | 112 | protected void showRxInProcess(){ 113 | rxResponse.setVisibility(View.INVISIBLE); 114 | progressBar.setVisibility(View.VISIBLE); 115 | retroCall.setEnabled(false); 116 | rxCall.setEnabled(false); 117 | } 118 | 119 | protected void showRetroInProcess(){ 120 | retroResponse.setVisibility(View.INVISIBLE); 121 | progressBar.setVisibility(View.VISIBLE); 122 | retroCall.setEnabled(false); 123 | rxCall.setEnabled(false); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/com/captech/retrorx/FriendResponse.java: -------------------------------------------------------------------------------- 1 | package com.captech.retrorx; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by cteegarden on 1/25/16. 7 | */ 8 | public class FriendResponse { 9 | 10 | public FriendLocations friendLocations; 11 | 12 | public FriendResponse(){} 13 | 14 | public class FriendLocations { 15 | public Data data; 16 | public class Data{ 17 | public ArrayList friend = new ArrayList<>(); 18 | public class Friend{ 19 | public String friendName; 20 | public String friendType; 21 | public String lat; 22 | public String lon; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/captech/retrorx/NetworkService.java: -------------------------------------------------------------------------------- 1 | package com.captech.retrorx; 2 | 3 | 4 | import android.support.v4.util.LruCache; 5 | 6 | import java.io.IOException; 7 | 8 | import okhttp3.Interceptor; 9 | import okhttp3.OkHttpClient; 10 | import okhttp3.Request; 11 | import okhttp3.Response; 12 | import retrofit2.Call; 13 | import retrofit2.Retrofit; 14 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 15 | import retrofit2.converter.gson.GsonConverterFactory; 16 | import retrofit2.http.GET; 17 | import rx.Observable; 18 | import rx.android.schedulers.AndroidSchedulers; 19 | import rx.schedulers.Schedulers; 20 | 21 | /** 22 | * Created by cteegarden on 1/25/16. 23 | */ 24 | 25 | public class NetworkService{ 26 | 27 | private static String baseUrl ="https://dl.dropboxusercontent.com/u/57707756/"; 28 | private NetworkAPI networkAPI; 29 | private OkHttpClient okHttpClient; 30 | private LruCache, Observable> apiObservables; 31 | 32 | public NetworkService(){ 33 | this(baseUrl); 34 | } 35 | 36 | public NetworkService(String baseUrl){ 37 | okHttpClient = buildClient(); 38 | apiObservables = new LruCache<>(10); 39 | Retrofit retrofit = new Retrofit.Builder() 40 | .baseUrl(baseUrl) 41 | .addConverterFactory(GsonConverterFactory.create()) 42 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 43 | .client(okHttpClient) 44 | .build(); 45 | 46 | networkAPI = retrofit.create(NetworkAPI.class); 47 | } 48 | 49 | /** 50 | * Method to return the API interface. 51 | * @return 52 | */ 53 | public NetworkAPI getAPI(){ 54 | return networkAPI; 55 | } 56 | 57 | 58 | /** 59 | * Method to build and return an OkHttpClient so we can set/get 60 | * headers quickly and efficiently. 61 | * @return 62 | */ 63 | public OkHttpClient buildClient(){ 64 | 65 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 66 | 67 | builder.addInterceptor(new Interceptor() { 68 | @Override 69 | public Response intercept(Chain chain) throws IOException { 70 | Response response = chain.proceed(chain.request()); 71 | // Do anything with response here 72 | //if we ant to grab a specific cookie or something.. 73 | return response; 74 | } 75 | }); 76 | 77 | builder.addInterceptor(new Interceptor() { 78 | @Override 79 | public Response intercept(Chain chain) throws IOException { 80 | //this is where we will add whatever we want to our request headers. 81 | Request request = chain.request().newBuilder().addHeader("Accept", "application/json").build(); 82 | return chain.proceed(request); 83 | } 84 | }); 85 | 86 | return builder.build(); 87 | } 88 | 89 | /** 90 | * Method to clear the entire cache of observables 91 | */ 92 | public void clearCache(){ 93 | apiObservables.evictAll(); 94 | } 95 | 96 | 97 | /** 98 | * Method to either return a cached observable or prepare a new one. 99 | * 100 | * @param unPreparedObservable 101 | * @param clazz 102 | * @param cacheObservable 103 | * @param useCache 104 | * @return Observable ready to be subscribed to 105 | */ 106 | public Observable getPreparedObservable(Observable unPreparedObservable, Class clazz, boolean cacheObservable, boolean useCache){ 107 | 108 | Observable preparedObservable = null; 109 | 110 | if(useCache)//this way we don't reset anything in the cache if this is the only instance of us not wanting to use it. 111 | preparedObservable = apiObservables.get(clazz); 112 | 113 | if(preparedObservable!=null) 114 | return preparedObservable; 115 | 116 | 117 | 118 | //we are here because we have never created this observable before or we didn't want to use the cache... 119 | 120 | preparedObservable = unPreparedObservable.subscribeOn(Schedulers.newThread()) 121 | .observeOn(AndroidSchedulers.mainThread()); 122 | 123 | if(cacheObservable){ 124 | preparedObservable = preparedObservable.cache(); 125 | apiObservables.put(clazz, preparedObservable); 126 | } 127 | 128 | 129 | return preparedObservable; 130 | } 131 | 132 | 133 | /** 134 | * all the Service alls to use for the retrofit requests. 135 | */ 136 | public interface NetworkAPI { 137 | 138 | 139 | @GET("FriendLocations.json")//real endpoint 140 | Call getFriends(); 141 | 142 | 143 | @GET("FriendLocations.json") //real endpoint 144 | Observable getFriendsObservable(); 145 | 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/com/captech/retrorx/PresenterInteractor.java: -------------------------------------------------------------------------------- 1 | package com.captech.retrorx; 2 | 3 | /** 4 | * Created by cteegarden on 2/1/16. 5 | */ 6 | public interface PresenterInteractor { 7 | void loadRxData(); 8 | void loadRetroData(); 9 | void rxUnSubscribe(); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/captech/retrorx/PresenterLayer.java: -------------------------------------------------------------------------------- 1 | package com.captech.retrorx; 2 | 3 | import retrofit2.Call; 4 | import retrofit2.Callback; 5 | import retrofit2.Response; 6 | import rx.Observable; 7 | import rx.Observer; 8 | import rx.Subscription; 9 | 10 | /** 11 | * Created by cteegarden on 1/28/16. 12 | */ 13 | @SuppressWarnings("unchecked") 14 | public class PresenterLayer implements PresenterInteractor { 15 | 16 | private ActivityView view; 17 | private NetworkService service; 18 | private Subscription subscription; 19 | 20 | public PresenterLayer(ActivityView view, NetworkService service){ 21 | this.view = view; 22 | this.service = service; 23 | } 24 | 25 | public void loadRxData(){ 26 | view.showRxInProcess(); 27 | Observable friendResponseObservable = (Observable) 28 | service.getPreparedObservable(service.getAPI().getFriendsObservable(), FriendResponse.class, true, true); 29 | subscription = friendResponseObservable.subscribe(new Observer() { 30 | @Override 31 | public void onCompleted() { 32 | 33 | } 34 | 35 | @Override 36 | public void onError(Throwable e) { 37 | view.showRxFailure(e); 38 | } 39 | 40 | @Override 41 | public void onNext(FriendResponse response) { 42 | view.showRxResults(response); 43 | } 44 | }); 45 | } 46 | 47 | public void loadRetroData(){ 48 | view.showRetroInProcess(); 49 | Call call = service.getAPI().getFriends(); 50 | call.enqueue(new Callback() { 51 | @Override 52 | public void onResponse(Response response) { 53 | view.showRetroResults(response); 54 | } 55 | 56 | @Override 57 | public void onFailure(Throwable t) { 58 | view.showRetroFailure(t); 59 | } 60 | }); 61 | } 62 | 63 | public void rxUnSubscribe(){ 64 | if(subscription!=null && !subscription.isUnsubscribed()) 65 | subscription.unsubscribe(); 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/captech/retrorx/RxApplication.java: -------------------------------------------------------------------------------- 1 | package com.captech.retrorx; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * Created by cteegarden on 1/26/16. 7 | */ 8 | public class RxApplication extends Application { 9 | 10 | private NetworkService networkService; 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | 15 | networkService = new NetworkService(); 16 | 17 | } 18 | 19 | public NetworkService getNetworkService(){ 20 | return networkService; 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 |