Android Activities

Activity is the main android component which displays user interface, handles user events and provide behavior. Each screen of an app is associated with an activity. An activity can exist in different states and has life cycle. Android system calls callback methods of an activity depending on the state change.

When an app is launched there is an activity in app which is called main or launcher activity that handles launch intent and displays the main screen.

Because of the way activities work and its lifecycle, users can start an app thru multiple entry points. For example, launch icon of an app takes user to main screen, clicking a notification can open a different screen or an option in a different app can start another activity.

You can create activity by extending Activity or its sub classes. All the activities in your app need to be defined in android manifest file by providing configuration such as intents, permissions, etc. You can see manifest activity element for its attributes and child elements.

Table of Contents

Activity Lifecycle

Depending on user navigation, an activity can transition thru different states. If user is interacting with an activity meaning it is at the top of the stack, then the state of the activity is active. If an activity is visible and is not focused meaning user interacting with another activity which is transparent or non-full sized, then the activity is in paused state. The difference between active and paused activities lies in priority or order in which they are destroyed to free up memory if memory is required. System removes paused activity first in low memory situations. If an activity is not visible and completely hidden, then the activity is stopped.

Android system calls callback methods of Activity when activity transitions thru different states. By implementing proper behavior in these callback methods, you can prevent app crashes. But you don’t need to implement all callback methods as using few callback methods you can provide good user experience for simple apps. Here is the list of callback methods and details about when each one is called.

Callback method onCreate() is called when system creates activity. In this method, you can set user interface layout by calling setContentView method, instantiate member variables, bind data to views and add events listeners. This method is passed Bundle object parameter which contains activity’s saved state if this activity has existed before. Callback method onStart() is called when the activity is in started state and after the call to onStart method, activity will be visible to user Callback method onResume() is called when the activity is visible or in foreground. The call to this method makes the activity ready for user interactions. This resumed state continues until the focus is on the activity. This call back method is also called when the activity is resumed from paused state. Callback method onPause() is called when user is leaving the activity to close the app, to work on different screen in multi-window mode, or to open semi transparent activity. Callback method onStop() is called when activity is not visible due to different activity occupying complete screen. This method is also called when activity is about to be destroyed. Callback method onDestroy is called before activity is destroyed. This is last callback method in the activity lifecycle.

Starting an Activity from Different Activity

You can start an activity from another activity by calling startActivity() or the startActivityForResult() methods of Activity class. You can use startActivity to just send user to the next screen in the flow of a task. You need to use startActivityForResult method if the activity depends on another activity for doing its work. In the case of startActivityForResult, the calling activity will receive results.

Intent i = new Intent(); i.setClass(this, SomeActivity.class); startActivity(i);

Below code shows how to call startActivityForResult method and handle results.

public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final int USER_REQ_CODE = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void getUser(View v){ Intent i = new Intent(); i.setClass(this, UserActivity.class); startActivityForResult(i, USER_REQ_CODE); } protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == USER_REQ_CODE) { if (resultCode == RESULT_OK) { Toast.makeText(this, "user "+data.getStringExtra("user"), Toast.LENGTH_SHORT).show(); } } } }

Below code shows how to send result back to first activity from second activity.

public class UserActivity extends AppCompatActivity { private static final String TAG = "UserActivity"; private EditText user; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_user); user = findViewById(R.id.user); findViewById(R.id.submit).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { sendUser(); } }); } private void sendUser(){ Intent userIntent = new Intent(); userIntent.putExtra("user", user.getText().toString()); setResult(RESULT_OK, userIntent); finish(); } }

When an activity starts another activity, the activity is paused, then the second activity’s onCreate, onStart and onResume are executed and then if first activity is not visible, its onStop method will be called.

Intent Filters

In the above startActivity example, intent is created and targeted to start a specific activity by specifying the target activity class. This type of intent is called explicit intent.

You can start an activity by creating general intent without specifying target activity. You create general intent by specifying type of request, category and data that target activity handles. Android system chooses and starts the right activity using intent filters defined for the activities in manifest file. This type of intent is called implicit intent.

Below is an example of intent filter declaration.

<activity android:name=".SettingsActivity"> <intent-filter> <action android:name="android.intent.action.CALL"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>

Below code show how to start an activity with implicit intent.

Intent callIntent = new Intent(); callIntent.setAction(Intent.ACTION_CALL); if (callIntent.resolveActivity(getPackageManager()) != null) { startActivity(callIntent); }

To make android system display chooser incase if an implicit intent can be handled by multiple activities, the calling activity should create implicit intent using Intent.createChooser.

Intent callIntent = new Intent(); callIntent.setAction(Intent.ACTION_CALL); Intent chooser = Intent.createChooser(callIntent, title);

Maintaining Activity State

If an activity is destroyed to release system resources required for foreground activity, the activity will be recreated if user presses back button. To create the activity with same state that it was before, android system uses the saved state of activity. The activity state is saved in Bundle object. By default system saves state data about each view in layout. You don’t need to do anything to restore view’s state when an activity is recreated after it has been destroyed to release system resources.

But to save member variables or state data of the activity, you need to use callback methods and save it either in the Bundle or in database (persistent local storage) depending on the complexity of data.

To save activity state, you need to use onSaveInstanceState() callback method which is called when activity is about to stop. In this callback method, you can add member variable state to Bundle object passed to it. To have default behavior of saving view’s state, you need to call super. onSaveInstanceState() in the callback method.

public class UserActivity extends AppCompatActivity { private static final String COUPON_COUNTER = "COUPON_COUNTER"; private int couponCounter; ......... @Override public void onSaveInstanceState(Bundle savedInstanceState) { savedInstanceState.putInt(COUPON_COUNTER, couponCounter); super.onSaveInstanceState(savedInstanceState); } }

To restore the state, you can use either onCreate method or onRestoreInstanceState callback methods as both of them receive saved state as Bundle object parameter. The only difference is that onCreate method is called two situations, when the new instance activity is created and when it is restored if it has existed before. Bundle object will be null if new instance of activity is created.

public void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); couponCounter = savedInstanceState.getInt(COUPON_COUNTER); }

Configuration Changes

Android system destroys the instance of current activity and recreates instance of it due to device configuration changes such as orientation or language. This feature makes it possible to provide different user experience for different device configurations. For example, you can provide different layout for landscape and portrait orientation or if user change locale, content of the screen can be shown in local language.

When android system recreates instance of current activity because of configuration change, it uses saved state to restore state of the previous instance of the activity if it was in foreground or visible.

If in some situations, like performance reasons due to restart, you don’t want android system to restart an activity, then you can inform about it by adding configuration in manifest file. You can use android:configChanges attribute of activity element and list configuration changes for which you want to handle the change instead of system restarting the activity. In this case, android calls onConfigurationChanged() callback method of the activity instead of restarting it.

Some of the configuration changes are orientation, keyboardHidden, mcc, mnc, locale, touchscreen, keyboard, screenLayout, fontScale, density and screenSize.

<activity android:name=".UserActivity" android:configChanges="orientation|screenSize|keyboardHidden|locale"></activity>

In the example, when screen orientation, keyboard availability or local changes, system will not restart the activity instead it calls onConfigurationChanged() callback method of the activity and runs the behavior defined in the onConfigurationChanged() method for the configuration change. Since this method is passed Configuration object which can be used to find out new configuration.

@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { user.setVisibility(View.GONE); } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ user.setVisibility(View.VISIBLE); } if (newConfig.keyboardHidden == Configuration.KEYBOARD_QWERTY){ notes.setVisibility(View.VISIBLE); }else{ notes.setVisibility(View.GONE); } }

Permissions

If an activity requires certain permissions to perform its work, you can enforce the permission using android:permission attribute of activity as shown below.

<activity android:name=".SettingsActivity" android:permission="android.permission.READ_CONTACTS"></activity>

And request for the permission in the code if it is not granted.

if (ContextCompat.checkSelfPermission(this, "android.permission.READ_CONTACTS") != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{"android.permission.READ_CONTACTS"}, 2); }

The app which starts the activity should declare uses-permission element for the permission in its manifest.

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="zoftino.com.databinding"> <uses-permission android:name="android.permission.READ_CONTACTS" />. . .

Sending Data between Activities

Data can be passed between activities using Bundle object and adding it to intent as shown below.

Intent i = new Intent(); i.setClass(this, StoreActivity.class); Bundle data = new Bundle(); data.putString("store", "zyd store"); data.putInt("noCoupons", 24); data.putString("maxCashback", "20%"); i.putExtra("couponBundle", data); startActivity(i);

Below code shows retrieving data from intent in the target activity.

Bundle data = getIntent().getBundleExtra("couponBundle");

If you need to send complex object between activities, then you need to create Pracelable object by implementing writeToParcel method.