Android: an idiom for Activity parameters
These days, Android development is switching fast to use Kotlin as the standard programming language. However, there is still a lot of Java code written for Android and developers face the challenge of modifying (and hopefully improving) big codebases written in Java. In this article, I would like to show an idiom that can make the code for starting Activity
objects a little easier to to deal with, and help you cope with some old Java codebase.
As Android developers know, in order to start an Activity
, you have to create an Intent
object. In that object, you can pass parameters to the Activity
, which will be aggregated to the Intent
as extra info. For each piece of information, you need to create a tag, as a string, so that the Activity
on its onCreate()
method will be able to retrieve each parameter from the extra info. This lead to a number of variations on how to do that in the most easily understandable and modifiable way.
One way that I’ve seen many times is to simply create the tag strings as public constants belonging to the Activity
:
public class SampleActivity : AppCompatActivity { public static final String NUM_ROOMS_TAG = "com.sample.AppCompatActivity.numRoomsInfo";
public static final String COLOR_ROOMS_TAG = "com.sample.AppCompatActivity.colorRooms";}
Then, typically you would start SampleActivity
this way:
final Intent intent = new Intent(context, SampleActivity.class);
intent.putExtra(SampleActivity.NUM_ROOMS_TAG, 2);
intent.putExtra(SampleActivity.COLOR_ROOMS_TAG, "Blue");
context.startActivity(intent);
And then in the onCreate()
method you would retrieve those values using the tags defined in the Activity
itself. A variant of the above idiom is to define a static method in the Activity
in which you pass the values and the method returns a new Intent
. However, it would be nice if we could keep the “mechanics” of passing data to the Activity
separated from the code of the Activity
itself: this way, the Activity
could focus solely on the functionality it is supposed to provide.
The way I propose to do that is by creating a static inner class whose purpose is solely to deal with the “mechanics” of passing the data around, and, furthermore, it also works as a data object to keep the parameters to the Activity
separate from the rest of its data. Let me just show some sample code that implements that:
public class LivingRoomActivity extends AppCompatActivity {
private Arguments arguments;
// [...] @AutoValue
public static abstract class Arguments {
private static final String ROOM_COLOR_TAG = "roomColorTag";
private static final String NUM_OF_WALLS_TAG = "numOfWallsTag";
private static final int DEFAULT_NUM_OF_WALLS = 4;
public abstract String roomColor();
public abstract int numOfWalls();
public static Arguments createFromIntent(final Intent intent) {
return builder()
.setRoomColor(intent.getStringExtra(ROOM_COLOR_TAG))
.setNumOfWalls(intent.getIntExtra(NUM_OF_WALLS_TAG, DEFAULT_NUM_OF_WALLS))
.build();
}
public static Builder builder() {
return new AutoValue_LivingRoomActivity_Arguments.Builder();
}
public void startActivity(final Context context) {
final Intent intent = new Intent(context, LivingRoomActivity.class);
intent.putExtra(ROOM_COLOR_TAG, roomColor());
intent.putExtra(NUM_OF_WALLS_TAG, numOfWalls());
context.startActivity(intent);
}
@AutoValue.Builder
public static abstract class Builder {
public abstract Builder setRoomColor(String value);
public abstract Builder setNumOfWalls(int value);
public abstract Arguments build();
} // Builder class
} // Arguments class
} // LivingRoomActivity class
In this case, we have a LivingRoomActivity
that requires a couple of parameters: the number of walls of the room and the room color (this is just an example, typically you would employ this idiom on Activity
classes that require a lot of parameters). Then, inside that class, you define a static inner class called Arguments
which will encapsulate how those parameters will be passed to the Activity
and how they are going to be retrieved. Additionally, the Arguments
class is set up as an AutoValue
class and makes use of the Builder
pattern offered by it: this way, it minimizes the amount of lines of code to define each individual parameter and also allows us to use the very handy Builder pattern to create an instance of Arguments
. Furthermore, the constants used as tags can live as private constants inside the Arguments
class and nobody else needs to know about them.
Now, let’s look how some external code can start an instance of LivingRoomActivity
:
final LivingRoomActivity.Arguments arguments = LivingRoomActivity.Arguments.builder()
.setRoomColor(roomColorEdittext.getText().toString())
.setNumOfWalls(Integer.parseInt(numWallsEdittext.getText().toString()))
.build();
arguments.startActivity(this);
As you can see, there is no need to find out what are the tags for each parameter, all parameters are set individually by the Builder
and you don’t even need to specify the class when starting the Activity
. Then, when it comes the time for the LivingRoomActivity
to retrieve those parameters, it can do the following:
public class LivingRoomActivity extends AppCompatActivity {
private Arguments arguments;
@BindView(R.id.wall_colors_text_view) TextView wallColorsTextView;
@BindView(R.id.num_walls_text_view) TextView numWallsTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_living_room);
ButterKnife.bind(this);
arguments = Arguments.createFromIntent(getIntent());
wallColorsTextView.setText("Room color: " + arguments.roomColor());
numWallsTextView.setText(String.format("Number of walls: %d", arguments.numOfWalls()));
} // [...]} // LivingRoomActivity class
Pretty easy: just call Arguments.createFromIntent(getIntent())
and you’re good to go. Following that, you can just refer to the parameters by invoking arguments.roomColor()
for example.
The whole mechanism is hidden from everybody and concentrated in the LivingRoomActivity.Arguments
class. This has the additional benefit that, if you need to add (or remove) parameters for this Activity
, you just need to go to one place to do so. Furthermore, if additionally you need to pass data to a Fragment
, you can reuse the above class to put the data in a Bundle
object and then create a static method in Arguments
to create an instance of it based upon a Bundle
, again keeping all the code and tags for it hidden from everybody else.
You can check a fully working example of the use of the above idiom in a sample app that I put on GitHub: https://github.com/Discoverer98/living-room. The code snippets I put above were taken from that project.
As I said at the beginning of this article, Android is quickly moving towards Kotlin: so, I intend to publish a follow up to this article describing this idiom in Kotlin. Nevertheless, for all those that still have to deal with Java code for Android, I hope this idiom can help make the code you’re dealing with a little cleaner and your job a little easier. Good luck in your coding!
Update: I just published the follow up to this article in which I describe the same idiom above but written in Kotlin. You can take a look at it here.