Retrofit – Generic Error Handling and custom exception messages

Retrofit is an HTTP client for Android and Java written by Square Inc.

Today I want to expose a way to handle network exceptions for all your service calls and creating your generic error  messages in a simple way when using Retrofit.

 

GenericException class

We will create a GenericException class where we will put all the types which we want to catch and their corresponding error messages we want to display. SocketException, and I have created a custom exception: InternalServerError. Please find below the code for the GenericException class:

public class GenericException extends IOException {
    Context context;
    String message;

    public GenericException(Context context, Exception e) {
        this.context = context;
        message = context.getString(R.string.str_internet_connection_error);

        if (e instanceof SocketException) {
            message = context.getString(R.string.str_internet_connection_error);

        } else if (e instanceof InternalServerError) {
            message = context.getString(R.string.str_internal_server_error);
        }
    }

    @Override
    public String getMessage() {
        return message;
    }
}

class InternalServerError extends IOException {}

OkHttpClient – Request Interceptor

Retrofit uses OkHttpClient which allows you to add an “interceptor” method. This will allow you to intercept all requests made on your retrofit services. As a result, you can make (common) changes to your requests (e.g add Auth Token) and also inspect the corresponding response and status code.

Below is a code snippet is interceptor method on the Network Module. Notice that we are throwing error using instance of our GenericException class. These exceptions will be propagated to the Activities (or presenter) where the services requests were made.

I checked for 500 status code on the response to catch Internal Server Error and to throw our custom Internal Server Error which will use the custom Exception Message. We also have SocketException to handle timeout exceptions for no internet connection scenarios.

@Singleton
@Provides
public APIService getService(Context context, OkHttpClient.Builder httpClient, Retrofit.Builder retrofit) {
    httpClient.addInterceptor((Interceptor.Chain chain) -> {

        if (!NetworkUtils.isNetworkAvailable(context)) {     // Check if there is internet connection
            throw new GenericException(context, new SocketException());
        }

        Request originalRequest = chain.request();
        // set OAuth token
        Request.Builder newRequest = originalRequest.newBuilder();
        String accessToken = SharedPreferencesRepository.getAuthToken(context);

        newRequest.header("Content-Type", "application/json");
        newRequest.header("Accept", "application/json");
        newRequest.header("Authorization", "Bearer " + accessToken).method(originalRequest.method(), originalRequest.body());

        originalRequest = newRequest.build();

        Response response = chain.proceed(originalRequest); //perform request, here original request will be executed
        int responseCode = response.code();

        if (responseCode == 500) {   // Check for Internal Server errors
            throw new GenericException(context, new InternalServerError());    // Internal Server Error
        }

        if (responseCode == 401) { . // Expired or invalid Auth Token

            // Unauthorized. E.g, Token Expired
            // refreshToken service call
            .
            .
            .
        }

        return response;
    });

    retrofit
        .baseUrl(baseUrl)
        .client(httpClient.build())
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();

    return retrofit.build().create(APIService.class);
}

 

Propagating exception message to the UI

If you are using Rx Java, you will have something as below. The method (Throwable e) will get called whenever there is an exception thrown from the apiService. Exception messages which were defined on the GenericException class are thrown to (Throwable e) method. The exception message “e.getMessage()” can now be displayed to the users, e.g, on an AlertDialog.

Subscription s = apiService.loginRequest(userLoginRequest)
    .compose(RxUtils.applySchedulers())
    .subscribe(
        (UserLoginResponse userLoginResponse) -> {  // on Success
            mView.hideLoading();
            if (userLoginResponse.isSuccess()) {
                mView.onLoginSuccessful();

            } else
                new Throwable(context.getString(R.string.str_login_invalid_credentials));
        },
        (Throwable e) -> { // on Fail

            mView.onLoginFailed(e.getMessage()); // Display exception on an AlertDialog
            mView.hideLoading();
        },
        () -> { // on Complete
            mView.hideLoading();
        });

 

Looking forward for next posts. Let me know if you have any queries or if you want me to share additional codes.

Cheers,

Android Design Patterns – MVP, DI using Dagger and Unit Testing

Design Patterns

Various Android Design Patterns can be used to enhance your android code base: among those MVC, MVVM, Reactive, MVP.

The goal of design pattern is to make your code and project more:

  1. Scalable
  2. Maintainable
  3. Testable

Using Standards Design patterns will make you more valuable as a developer. It will help you to work better in a team. Learning and applying patterns will also make you more adaptable to other frameworks.

Clean architecture/MVP pattern and unit testing are practices which helped me improve my code quality and to develop maintainable apps. In the process of applying a new pattern, you will be learning new techniques which you may apply in other places of your code base as well. The MVP pattern can have a slower learning curve than using “classic” android architecture where you have Activities/Fragments which act like a “ViewController” and contain mix of UI and Business logics. But in long run, using classic android does not bring much Pros compared to other design patterns.

Intro to MVP Pattern – with Example

MVP stands for Model-View-Presenter architectural pattern. It is a derivative of Model View Controller and is a more popular architecture.

Model-View-Presenter, the View are our Activities or Fragments which will contain only UI update logics. All logics which depends on Android SDK and related to UI will remain in View (Fragment or Activity). The “Presenter” will contain all business logics. Presenter will communicate to the “View” through interfaces (e.g to send data for the View to display on the User Interface). The Presenter layer should not include any Android SDK specifics implementations. The Presenter logic can be fully tested with JUnit tests. Model will include repositories and all data types we have to manipulate on the app.

Let’s look at simple Logic example to get basic concern. The example includes usage Dagger 2 and Rx Java.

(For more on Dagger 2, please check my blog post: https://devanshramen.com/2017/08/23/explaining-dependency-injection-with-dagger-2-android/ )

 

First thing, we need define a “Contract” which is basically an Interface which will define all the required methods on the View and the Presenter.

LoginContract

public interface LoginContract {

    interface View extends BaseContract.BaseView {

        void onLoginSuccessful();

        void onLoginFailed(String strError);

        void onErrorEmptyEmail();

        void onErrorInvalidEmail();

        void onErrorEmptyPassword();

    }

    interface Presenter extends BaseContract.BasePresenter {

        void callLoginAPI(UserLoginRequest userLogin);

        boolean validateLoginInputs(UserLoginRequest userLogin);
    }
}

 

In this example, we have a client-side validation to check for empty/invalid email, and password and finally and API call to Login Service. The validation logic, will be implemented inside the “validateLoginInputs(UserLoginRequest userLogin)” method which will be implemented by Presenter.

The Fragment will implement the View so as to implement the display error or success UI updates. The Presenter will be trigger UI update by calling the corresponding “View” methods. E.g, onErrorInvalidEmail() will display a hint message stating user’s have input an invalid email.

Below are implementations for LoginPresenter and LoginFragment:

LoginPresenter

/**
 * Created by devanshramen on 4/17/17.
 * @author devansh
 */

public class LoginPresenter extends BasePresenter implements LoginContract.Presenter {

    @NonNull
    LoginContract.View mView;

    public LoginPresenter(@NonNull LoginContract.View mView) {
        super(mView);
        this.mView = mView;
    }
    
    // Client validation
    @Override
    public boolean validateLoginInputs(UserLoginRequest userLogin) {

        boolean isValid = true;

        if (userLogin.getEmail().length() <= 0) {
            mView.onErrorEmptyEmail();
            isValid = false;
        }

        if (!EmailUtils.isValidEmail(userLogin.getEmail())) {
            mView.onErrorInvalidEmail();
            isValid = false;
        }

        if (userLogin.getPassword().length() <= 0) {
            mView.onErrorEmptyPassword();
            isValid = false;
        }
        return isValid;
    }

    @Override
    public void callLoginAPI(UserLoginRequest userLoginRequest) {

        mView.showLoading();

        Subscription s = apiService.loginRequest(userLoginRequest)
            .compose(RxUtils.applySchedulers())
            .subscribe(
                (UserLoginResponse userLoginResponse) -> {  // on Success

                    mView.hideLoading();
                    LogUtils.showLogInfo(userLoginResponse.toString());

                    if (userLoginResponse.isSuccess()) {
                        appSessionRepository.setFirstTime(true);
                        appSessionRepository.setLogin(true);

                        mView.onLoginSuccessful();

                    } else
                        new Throwable(context.getString(R.string.str_login_invalid_credentials));
                },
                (Throwable e) -> { // on Fail

                    if (e instanceof GenericException)
                        mView.onLoginFailed(e.getMessage());
                    else
                        mView.onLoginFailed(context.getString(R.string.str_login_invalid_credentials));

                    mView.hideLoading();
                },
                () -> { // on Complete
                    mView.hideLoading();
                });

        mSubscriptions.add(s);
    }
}

LoginFragment

/**
 * Created by devanshramen on 4/17/17.
 * @author devansh
 */

public class LoginFragment extends BaseFragment implements LoginContract.View {

    @Bind(R.id.edt_username)
    EditText edtUsername;

    @Bind(R.id.edt_password)
    EditText edtPassword;

    @Bind(R.id.txt_no_username_error)
    TextView txtInvalidUsername;

    @Bind(R.id.txt_invalid_password)
    TextView txtInvalidPassword;

    LoginActivity activity;

    private LoginContract.Presenter mPresenter;

    public LoginFragment() {
        // Required empty public constructor
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    protected int getResourceLayout() {
        return R.layout.fragment_login;
    }

    @Override
    protected void onViewReady(Bundle savedInstanceState) {
        initViews();
    }

    private void initViews() {
        mPresenter = new LoginPresenter(this);
        activity = (LoginActivity) getActivity();
    }

    @OnClick(R.id.btn_login)
    public void onClickBtnLogin() {

        UserLoginRequest userLogin = new UserLoginRequest();
        userLogin.setEmail(edtUsername.getText().toString());
        userLogin.setPassword(edtPassword.getText().toString());

        txtInvalidUsername.setVisibility(View.INVISIBLE);
        txtInvalidPassword.setVisibility(View.INVISIBLE);

        if (mPresenter.validateLoginInputs(userLogin)) {
            KeyboardUtils.hideKeyboard(getActivity());
            mPresenter.callLoginAPI(userLogin);
        }
    }

    @OnClick(R.id.txt_forget_password)
    public void onClickTxtForgetPassword() {
        ActivityUtils.openActivity(getContext(), ResetPasswordActivity.class,"");
    }

    @Override
    public void onLoginSuccessful() {
        ActivityUtils.openActivityAndFinish(getActivity(), MainDrawerActivity.class, "login");
    }

    @Override
    public void onLoginFailed(String strError) {
        dialogUtils.showDialogBox(getString(R.string.str_error_dialog), strError);
    }


    @Override
    public void onErrorEmptyEmail() {
        txtInvalidUsername.setText(getString(R.string.str_login_username_error_empty));
        txtInvalidUsername.setVisibility(View.VISIBLE);
    }

    @Override
    public void onErrorInvalidEmail() {
        txtInvalidUsername.setText(getString(R.string.str_login_username_error_invalid));
        txtInvalidUsername.setVisibility(View.VISIBLE);
    }

    @Override
    public void onErrorEmptyPassword() {
        txtInvalidPassword.setVisibility(View.VISIBLE);
    }
}

BasePresenter & Dependency Injection with Dagger 2

You may have noticed that our LoginPresenter is extending BasePresenter. In these Base classes, we have common initialization methods e.g Database, RxJava stuffs we will put in itself BasePresenter.

public class BasePresenter implements BaseContract.AppBasePresenter {

    @Nonnull
    BaseContract.AppBaseView mView;

    @NonNull
    protected CompositeSubscription mSubscriptions;

    @Inject
    protected APIService apiService;

    @Inject
    protected Context context;

    @Inject
    protected ProfileRepository profileRepository;

    public BasePresenter(@Nonnull BaseContract.AppBaseView mView) {
        this.mView = mView;

        MApplication.getAppComponent().inject(this); // DI using Dagger

        mSubscriptions = new CompositeSubscription();

        LogUtils.showLogDebug("subscribe");
    }

    @Override
    public void subscribe() {

    }

    @Override
    public void unSubscribe() {
        mSubscriptions.clear();
    }
}

We are using Dagger 2 here as our Dependency Injection framework. (For more about Dagger 2: Please check http://www.vogella.com/tutorials/Dagger/article.html) which describes it in enough details.

Unit Testing

We will also want to have Unit Tests on the Presenter such that we can check if we are implementing the right/expected business logic and also the right “UI” method is being called by the Presenter:

@RunWith(MockitoJUnitRunner.class)
public class LoginPresenterTest extends TestCase{

    @Mock
    private LoginPresenter mPresenter;

    @Mock
    private LoginContract.View mView;

    @Mock
    SessionRepository appSessionRepository;

    private UserLoginRequest userLogin;

    @Before
    public void setUpTesting() throws Exception{
        MockitoAnnotations.initMocks(this);

        mPresenter = new LoginPresenter(mView);
    }

    @Test
    public void testEmptyUsername() throws Exception {
        //test case passed - Data missing
        userLogin = new UserLoginRequest("", "xxx");
        mPresenter.validateLoginInputs(userLogin);
        verify(mView).onErrorEmptyEmail();
    }


    @Test
    public void testEmptyPassword() throws Exception {
        //test case passed - Data missing
        userLogin = new UserLoginRequest("xxx@xxxx.com", "");
        mPresenter.validateLoginInputs(userLogin);
        verify(mView).onErrorEmptyPassword();
    }

    @Test
    public void testValidUsernamePassword() throws Exception {
        //test case passed - Data missing
        userLogin = new UserLoginRequest("xxx@xxxx.com", "xxxx");
        assertTrue(mPresenter.validateLoginInputs(userLogin));
    }

}

We are using Mockito to mock the Views. (Please check this link for more about Mockito: http://www.vogella.com/tutorials/Mockito/article.html). Note that we are only testing if the appropriate View methods are called, we can’t really check if it is correctly implemented on the View since the implementation contains Android SDK specific implementations and will not be testable using JUnit framework.

That’s all for today. Catch you soon with a new topic. Please leave a comments should this be helpful to you.

Best Regards

 

Refs:

– Google’s Sample

Sharing Printer in Ubuntu

Sharing printer in a LAN (to Windows machine or virtualbox) from a Ubuntu host machine.
1. Setup Samba:
sudo apt-get install system-config-samba.
Launch “Samba” from the launcher
Open “Preferences” > “Server Settings” and enter the work group name used in Windows.

2. Share printer:
Go to http://localhost:631/admin/ and check the box “Share printers connected to this system”.
Go to http://localhost:631/printers/ and check that the expected printer is listed.
Click the printer and check that it is listed as “Idle, Accepting Jobs, Shared, Server Default”.

3. Connect the printer:
Either:
On the Windows computer or (e.g VirtualBox), open Windows Explorer and go to the “Network” item. There’s an icon for the Ubuntu computer; open it. There’s an icon for the printer shared from Ubuntu. Right-click that one and select “Connect…”.

Or:
On the Windows computer, select “Add new printer” then “Connect to a printer on the Internet or network” and specify the full path of “http://{computername}:631/printers/{printername}”

Java RMI Simple Bank System run on ubuntu

This is a simple Java RMI application where the server manages the bank accounts which are stored in a database and responds to the client requests. The system permits multiple clients to be able to access the server from remote PCs. The client, after he has log in, is able to check his account details, withdraw and and deposit money into his account. It may also help to compile/run Java RMI system on ubuntu

github link: https://github.com/2vansh/RMISimpleBankSystem

 

To run Java RMI server/client on ubuntu, run the following commands on terminal:

cd ‘/directory’

//compile .java files

javac BankServer.java

javac BankClient.java

javac BankAccount.java

javac BankInterface.java

javac DbConnection.java

rmic BankAccount

rmiregistry 2008

//run the server with mysql dependency

java -cp “/mysqldirectory/mysql-connector-java-5.1.23:.”BankServer

//run the client

java BankClient

A simple TCP Socket calculator

A simple TCP Socket calculator

 

//Server Class

public class SimpleServer {
public static void main(String args[]) throws IOException {
// Register service on port 1254
ServerSocket s = new ServerSocket(1251);
Socket s1=s.accept(); // Wait and accept a connection
// Get a communication stream associated with the socket

InputStream s1In = s1.getInputStream();
DataInputStream dis = new DataInputStream(s1In);
int num1 = (dis.readInt());
int num2 = (dis.readInt());
String operation = (dis.readUTF());
int ans = 0;
if (operation.equals(“add”) == true){
ans = num1 + num2;
}
else if (operation.equals(“sub”)){
ans = num1 – num2;
}
// Close the connection, but not the server socket
dos.close();
s1out.close();
s1.close();
}
}

 

//Client Class

public class SimpleClient {
public static void main(String args[]) throws IOException {
// Open your connection to a server, at port 1254
Socket s1 = new Socket(“localhost”,1251);
// Get an input file handle from the socket and read the input
Scanner scanln = new Scanner(System.in);

//System.out.println(“Enter number1: “);
//int num1 = scanln.nextInt();

//System.out.println(“Enter number2: “);
//int num2 = scanln.nextInt();

System.out.println(“Search Movie: “);
String searchmovie = scanln.next();

OutputStream s1out = s1.getOutputStream();
DataOutputStream dos = new DataOutputStream (s1out);
//dos.writeInt(num1); //send to server6
//dos.writeInt(num2); //send to server6
dos.writeUTF(searchmovie);
//dos.writeInt(operation); //send to server6

//GUI.HomeMenu.homeMenu=new GUI.HomeMenu();
InputStream s1In = s1.getInputStream();
DataInputStream dis = new DataInputStream(s1In);
String st = new String (dis.readUTF());

System.out.println(“” + st);
// When done, just close the connection and exit
dis.close();
s1In.close();
s1.close();
}
}