Wednesday 25 February 2015

This is what they say(Eventbus): Less code, better quality. And you don't need to implement a single interface!

Most of us have recently started using Retrofit for REST API calls. So you all must be familiar with it. I have been doing some RnD since last few days and ended up writing a sample code(yes its completely written by me :P  there could be few issues but I made every attempt to make it easily understandable) which is a combo of Retrofit and Eventbus &  few other libs.

For further details on Events or Retrofit please follow the links at the bottom of the blog.

Here I am taking an example of a simple user login functionality. First I create an Activity LoginActivity which registers Eventbus inside onCreate method and unregisters it inside OnDestroy method of the Activity lifecycle.

/**
 * Defines the user interface for user login.
 *
 * @author kthakur
 *
 * @version 0.1
 *
 */
public class LoginActivity extends ActionBarActivity implements ProgressListener{

    EventBus bus = EventBus.getDefault();
   
    @InjectView(R.id.button1) Button start_button;
    @InjectView(R.id.textView1)TextView textView;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        ButterKnife.inject(this);
       
        //register EventBus
        bus.register(this);
    }
   
    @OnClick(R.id.button1)
    public void start() {
        // just an example
        new LoginApi(LoginActivity.this,LoginActivity.this);
    }

    @Override
    public void showProgress() {
        ProgressUtility.showProgress(LoginActivity.this, "Please wait..");
    }
   
    /**
     * Called when the bus posts an event for no Internet connection.
     * @param event
     */
    public void onEvent(NoInternetEvent  event) {
        dismissProgress();
    }
   
    /**
     * Called when the bus posts an event for successful result.
     *
     * <br>Call onEvent: Called in the same thread (default)
     * <br>Call onEventMainThread: Called in Android UI's main thread
     * <br>Call onEventBackgroundThread: Called in the background thread
     * <br>Call onEventAsync: Called in a separate thread
     *
     * @param event
     */
    public void onEvent(ApiResultEvent  event) {
        dismissProgress();
        LoginResponse response = (LoginResponse) event.getResponse();
        textView.setText("User Id: "+response.getUserLoginResult().getUserDetail()[0].getUserID());
    }
   
    /**
     * Called when the bus posts an event for service failure.
     * @param event
     */
    public void onEvent(ApiErrorEvent event) {
        dismissProgress();
       
        Log.d("ApiErrorEvent", ""+event.getError());
    }
   
    /**
     * Called when the bus posts an event for exception while GSON parsing.
     * @param event
     */
    public void onEvent(ExceptionEvent event) {
        dismissProgress();
    }
   
    /**
     * Dismiss progress dialog
     */
    public void dismissProgress(){
        if(ProgressUtility.isShowingProgress()){
            ProgressUtility.dismissProgress();
        }
    }
   
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //unregister EventBus
        bus.unregister(this);
    }
}


Here we send a request to the server, fetch response, performs Gson parsing and returns the result to our Activity as an event in the bus: I have posted 4 different events (NoInternetEvent, ApiResultEvent, ApiErrorEvent, ExceptionEvent) here which I have customized in a way that can be used throughout the App for all the Rest API calls. They are defined later below:


/**
 * This class is used to authenticate user via web service and read
 * the results returned in a web service and communicate it back to the Activity.
*/
public class LoginApi implements UrlListener{
   
    Activity activity;
    LoginRestApi loginRestApi;
    ProgressListener progressListener;
    EventBus bus = EventBus.getDefault();
   
    /**
     * Parameterized constructor.
     *
     * @param activity: Context of the Activity from which it is called.
     * @param progressListener: Progress listener
     * @param loginData: Login data object
     */
    public LoginApi(Activity activity,ProgressListener progressListener) {
       
        this.activity = activity;
        this.progressListener = progressListener;
       
        loginRequest();
    }
   
    /**
     * Create login request.
     *
     * @param loginData
     * @param resultListner
     * @param retrofitListner
     */
    public void loginRequest() {
       
        //check for Internet
        if(!Utilities.checkInternet(activity)){
            //posts no Internet status back to the Activity
            NoInternetEvent event = new NoInternetEvent(false);
            bus.post(event);
            return;
        }
       
        //start the progress bar
        progressListener.showProgress();
       
        LoginData loginData = new LoginData();
        loginData.setEmailID("abc@xyz.com");
        loginData.setPassword("aaaaaa");
           
        getApi().login(loginData, new Callback<Response>() {
           
            @Override
            public void failure(RetrofitError error) {
                ApiErrorEvent event=new ApiErrorEvent(error);
                // Post the event
                bus.post(event);
            }

            @Override
            public void success(Response response, Response arg1) {
                handleResponse(response);
            }
        });
    }
   
    /**
     * Handle login response, performs GSON parsing.
     * @param response
     */
    protected void handleResponse(Response response) {
        String jsonString = Utilities.parseTypedInput(response.getBody());
        if (jsonString == null)
            return;
       
        try {
            Gson gson = new Gson();
            LoginResponse loginResponse = gson.fromJson(jsonString, LoginResponse.class);
            ApiResultEvent event = new ApiResultEvent(loginResponse);
            String UserID = loginResponse.getUserLoginResult().getUserDetail()[0].getUserID();
             // Post the event back to the Activity with login response.
            bus.post(event);
        }
        catch (Exception e) {
            ExceptionEvent event = new ExceptionEvent(e);
            // Post the event back to the activity with exception.
            bus.post(event);
        }
    }
   
    /**
     * Create rest adapter.
     * HOST_ADDRESS_URL is the web service endpoint
     */
    private LoginRestApi getApi() {
        OkHttpClient okHttpClient = new OkHttpClient();
        RestAdapter restAdapter = new RestAdapter.Builder().
                setClient(new OkClient(okHttpClient)).setEndpoint(HOST_ADDRESS_URL)
                .setLogLevel(LogLevel.BASIC).build();
        return restAdapter.create(LoginRestApi.class);
    }
}


<<---------------- Utilities method parseTyedInput used above------------------->>
/**
  * Parse Retrofit TypedInput
  *
  * @return parsed string or null
  */
    public static String parseTypedInput(TypedInput body) {
        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder();
        if (body == null)
            return null;
        try {
            reader = new BufferedReader(new InputStreamReader(body.in()));
            String line;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            }catch (IOException e) { e.printStackTrace();}
        }
        catch (IOException e) { e.printStackTrace(); }
        return sb.toString();
    }


/**
 * API calls.
 * Use your respective url, method name and params.
 */
public class UrlGenerator {
   
    interface LoginRestApi {
        @POST("/Login.svc/UserLogin")
        void login(@Body Object param_object, Callback<retrofit.client.Response> cb);
    }

    interface GetCityRestApi {
        @GET("/Common.svc/GetCity")
        void getCity(Callback<Response> cb);
    }
}
   

Now define different events that are important for us while making a Rest API call. You can define any no and kind of events as per your convenience. I have used 4 types of events: first for no internet connectivity, second for any error during API call, third for successfull result and fourth one for any exception during the complete process.

 /** 
  * Reports Internet connectivity status.   
  */ 
 public class NoInternetEvent { 
      boolean value; 
      public NoInternetEvent(boolean value) { 
           this.value = value; 
      } 
      public boolean getException() { 
           return value; 
      } 
 } 

 /** 
  * Reports error during API call. 
  */ 
 public class ApiErrorEvent { 
      RetrofitError error; 
        public ApiErrorEvent(RetrofitError error){      
          this.error = error; 
        } 
        public RetrofitError getError(){ 
          return error; 
        } 
 } 

 /** 
  * Reports result returned in the API call.
  */ 
 public class ApiResultEvent { 
      Object _class; 
      public ApiResultEvent(Object _class) { 
           this._class = _class; 
      } 
      public Object getResponse() { 
           return _class; 
      } 
 } 

 /**
   * Reports error during API call. 
   */
public class ExceptionEvent {
    Exception exception;
       public ExceptionEvent(Exception exception){         
        this.exception = exception;
    }
        public Exception getException(){
        return exception;
    }
}


I have put most of the code here, a little effort would be required to put all these classes together and make necessary declarations to have an executable code.

Library dependencies:
1.  Retrofit 1.9.0
2.  Event Bus 2.4.0
3.  Butterknife 6.1.0 (for UI annotations)
4.  Lombok (for getter setter)
5.  Gson2.2.4
6.  Okhttp 2.2.0 (url connection)
7.  Okio 1.0.1



Referneces:
http://greenrobot.github.io/EventBus/
https://github.com/greenrobot/EventBus/blob/master/HOWTO.md
http://square.github.io/retrofit/

Download source code from GithHub:
https://github.com/kamalthakur2305/RetroEventBus

Let me know if anyone needs any assistance in understanding and executing. I'd highly appreciate your thoughts, criticisms, or suggestions.

1 comment:

  1. Slot Machines in Slot Machines in New York City - JTHub
    Slot Machines in 울산광역 출장샵 Slot 진주 출장샵 Machines 나주 출장마사지 in 거제 출장샵 New York City · Casinos Near The Canal. · Slots for sale near Canal Park · Slots for sale near The 여수 출장마사지 Canal · Casino in Queens

    ReplyDelete