1.CLEAN UP THE ONCREATE METHOD
If you have been doing Android development for some time, most likely you are cramming in excessive amounts of code into the onCreate method. This is not good practice because it slows down your application, makes the activity less clean and harder to maintain.
Let me propose two simple but effective solutions to this problem.
Create inner classes to implement interfaces
We can overcome this problem by implementing interfaces in separate classes. It will help to decouple our methods and provide a more efficient way of testing our code. Take a look at the FloatingActionButton below.
public class MainActivity extends AppCompatActivity { private FloatingActionButton fab; //Global variable declared @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); fab = findViewById(R.id.FloatingActionButton); fab.setOnClickListener(new FabButtonClick()); } private void fabButtonClicked() { //Where the magic happens... } private class FabButtonClick implements View.OnClickListener{ //Implement the interface @Override public void onClick(final View v) { //Overide the onClick method here, instead of inside onCreate fabButtonClicked(); } } }
Here we are declaring a class called FabButtonClick which implements the OnClickListener. This allows us to pass a new instance of the class when we call setOnClickListener. We override onClick inside that class and call a private method in MainActivity class.
The code is also more organized and easy to read. The method can be easily referenced in other areas of the app. It also makes it quite simple to disable this method if required.
Use a template design pattern
A template design pattern allows us to define behaviors in the base class and let subclasses override them without changing the overall algorithm structure. The pattern is quite easy to use and is very effective.
We start by creating a base class that extends AppCompatActivity.
public class BaseActivity extends AppCompatActivity { protected void callMethods() { runA(); runB(); runC(); runD(); } protected void runA() { //does nothing by default } protected void runB() { //does nothing by default } protected void runC() { //does nothing by default } protected void runD() { //does nothing by default } }
In the above code, we have defined callMethods which is responsible for calling other methods in the order they are written. It does nothing by default but the subclasses can now override them. See below.
public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); callMethods(); //Call this method } //ALT + INSERT + Override Methods @Override protected void runA() { setContentView(R.layout.activity_main); } @Override protected void runB() { //Do something meaningful } @Override protected void runC() { //Do something meaningful } @Override protected void runD() { //Do something meaningful } }
2. Using data binding to handle events
Data binding allows us to develop apps faster and substantially reduces boilerplate code. The necessary classes are generated in the background automatically making them easy to implement. Forget about writing mind-numbing code like findViewById and other long code declarations.
Firstly, make sure that you have enabled data binding in your build.gradle file:
dataBinding { enabled = true }
We then simply put our XML code inside the <layout> and declare some variables to initiate the binding.
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="clicks" type="com.example.activty.BusinessDetailActivity.ClickHandler" /> </data> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".activities.DataBindPractice"> <Button android:id="@+id/button" android:text="SAVE AND SHOW" android:onClick="@{clicks::buttonClicked}" android:layout_width="match_parent" android:layout_height="85dp"> </Button> </LinearLayout> </ScrollView> </layout>
@{clicks::buttonClicked} registers our button and points to buttonClicked method in our ClickHandler inner class.
public class BusinessDetailActivity extends AppCompatActivity { private ActivityClientDetailsBinding binding; private ClickHandler mClickHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(BusinessDetailActivity.this,R.layout.activity_client_details); mClickHandler = new ClickHandler(this); binding.setClicks(mClickHandler); } public class ClickHandler{ Context mContext; public ClickHandler(Context context) { mContext = context; } public void buttonClicked(View view){ //Where the magic happens... } } }
Here we simply bind our XML layout using DataBindingUtil and set the ClickHandler class for binding.
You can then apply more event handlers to new classes, or alternatively to the same class as so:
public class ClickHandler{ Context mContext; public ClickHandler(Context context) { mContext = context; } public void buttonClicked(View view){ //Where the magic happens... } public void floatingActionButtonClicked(View view){ //Where the magic happens... } public void imageViewClicked(View view){ //Where the magic happens... } }
For more information and guidance on how to use data binding please follow the link: https://developer.android.com/topic/libraries/data-binding/expressions
3. Using a ViewModel class to communicate between multiple fragments in one activity
Suppose you have 3 fragments hosted inside one activity, which you navigate with a viewPager:

How would the fragments know when the FloatingActionButton is clicked? We can simply use a ViewModel and declare a MutableLiveData variable allowing the fragments to observe for any clicks and act accordingly.
public class BusinessViewModel extends AndroidViewModel { //Create a live data object public MutableLiveData<Boolean> click = new MutableLiveData<>(); public BusinessViewModel(Application application) { super(application); } //This method will be observed from our fragments and will be set from our Main Activity which hosts the fragments public void saveClick(Boolean isClicked){ click.setValue(isClicked); } }
In our main activity, we instantiate the ViewModel class and set the click method as true when the FloatingActionButton is clicked…
public class MainActivity extends AppCompatActivity { private FloatingActionButton fab; private BusinessViewModel mBusinessViewModel; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Init the view model: mBusinessViewModel = ViewModelProviders.of(this).get(BusinessViewModel.class); fab = findViewById(R.id.FloatingActionButton); fab.setOnClickListener(new FabButtonClick()); } private void fabButtonClicked() { //Set the click as true in the view model class mBusinessViewModel.saveClick(true); } private class FabButtonClick implements View.OnClickListener{ @Override public void onClick(final View v) { fabButtonClicked(); } } }
Now we can easily add an observable method in our fragments which will listen for the click from our main activity…
public class BusinessInfo1_Fragment extends Fragment { private FragmentBusinessInfo1Binding info1Binding; private BusinessViewModel mBusinessViewModel; public BusinessInfo1_Fragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { info1Binding = DataBindingUtil.inflate(inflater,R.layout.fragment_business_info1,container,false); return info1Binding.getRoot(); } @Override public void onActivityCreated(@Nullable final Bundle savedInstanceState) { //Lifecycle method super.onActivityCreated(savedInstanceState); //Init the view model mBusinessViewModel = ViewModelProviders.of(getActivity()).get(BusinessViewModel.class); //Observe the click from main activity. mBusinessViewModel.click.observe(this, new Observer<Boolean>() { @Override public void onChanged(final Boolean isClicked) { //Perform some action when Floating action button is clicked... } }); }
As you can see this method of communication is much easier than the standard way using transaction methods. Moreover, the data is managed in a life-cycle conscious way because the class survives configuration changes and can be used as a storage of view state and data.

4. Follow commonly used architecture patternS
Consider this as a foundation for writing better code. Not only will it improve testability and development speed, but it will also make your code easier to understand and refactor later in the future. The reason for this is simple… Architecture patterns encourage you to break your code apart into manageable, bite-size pieces. Real-world applications of which demand large amounts code to develop, benefit from these structure patterns enormously. Some companies even go about developing their own architecture patterns.
Let us briefly explore the most commonly used patterns below…
- MVVM (Model-View-ViewModel)
- MVP (Model-View-Presenter)
- MVC (Model-View-Controller)

MVC
- User input is handled by a controller.
- The controller has a one-way relationship with the View and the View does not have any knowledge or reference to the controller.
- The controller passes back the Model, so there is the knowledge between the View .and the expected Model being passed into it, but not the controller serving it up.
MVP
- User input directed to the View.
- One to one mapping exists between the View and Presenter.
- View has a reference to the presenter and the Presenter is aware of the View.
- Presenter updates the View based on the requested actions it performs on the Model, but the View is not Model aware.
MVVM
- Input begins at the View and not the View Model.
- View Model has no reference to the View itself. This allows for one-to-many mapping between various Views and yet only one View Model.
For more information about these architecture patterns follow the links below: