Decoupling Android Components via Intent and Fragment Factories
Maintaining loose coupling between components is a fundamental principle in robust Android architecture. When navigating between screens or instantiating fragments, callers should remain unaware of the internal implementation details required by the target component. This separation ensures that changes to data transmission mechanisms do not ripple through the entire codebase.
Consider a scenario where a list item click triggers a detail view. A common anti-pattern involves constructing the Intent directly within the ViewHolder, requiring knowledge of specific extra keys. Enstead, the target Activity should expose a static factory method.
public void onListItemClick(View view) {
UUID recordUuid = adapter.getItemId(view.getAdapterPosition());
Intent navigationIntent = DetailActivity.generateIntent(view.getContext(), recordUuid);
view.getContext().startActivity(navigationIntent);
}
By delegating Intent creation to the target Activity, the ViewHolder remains decoupled. The Activity defines the contract for how data is passed internally.
private static final String KEY_RECORD_UUID = "com.example.app.extra.record_uuid";
public static Intent generateIntent(Context context, UUID id) {
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra(KEY_RECORD_UUID, id);
return intent;
}
This approach encapsulates the key constant KEY_RECORD_UUID within the Activity. If the storage mechanism changes later, only this class requires modification. The retrieval logic also resides securely within the lifecycle methods of the Activity.
@Override
protected DetailFragment initFragment() {
UUID id = (UUID) getIntent().getSerializableExtra(KEY_RECORD_UUID);
return DetailFragment.createInstance(id);
}
The same encapsulation strategy applies to Fragment instantiation. Fragmants should not be created using the default constructor when arguments are required. Instead, a static factory method handles Bundle construction.
public static DetailFragment createInstance(UUID recordId) {
DetailFragment fragment = new DetailFragment();
Bundle arguments = new Bundle();
arguments.putSerializable(ARG_RECORD_ID, recordId);
fragment.setArguments(arguments);
return fragment;
}
Inside the Fragment, argument are retrieved during initialization. The fragment relies on the bundle structure defined in its own factory method, keeping the logic self-contained.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UUID id = (UUID) getArguments().getSerializable(ARG_RECORD_ID);
Record record = Repository.getInstance(getActivity()).fetchRecord(id);
}