Exploring Activity Management and Data Transfer
Activities serve as the primary entry point for user interaction. Moving between activities often requires passing data using the Intent system.
Basic Activity Navigation
The following example demonstrates how to capture user input and pass it to a secondary activity using Intent.putExtra().
public class InputActivity extends AppCompatActivity {
private EditText nameField;
private RadioGroup selectionGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_input);
nameField = findViewById(R.id.name_input);
selectionGroup = findViewById(R.id.options_group);
}
public void processSubmission(View view) {
String inputName = nameField.getText().toString().trim();
int categoryId = -1;
int selectedId = selectionGroup.getCheckedRadioButtonId();
if (selectedId == R.id.opt_1) categoryId = 101;
else if (selectedId == R.id.opt_2) categoryId = 102;
if (TextUtils.isEmpty(inputName)) {
Toast.makeText(this, "Input required", Toast.LENGTH_SHORT).show();
return;
}
Intent nextScreen = new Intent(this, DetailActivity.class);
nextScreen.putExtra("USER_NAME", inputName);
nextScreen.putExtra("CATEGORY_ID", categoryId);
startActivity(nextScreen);
}
}
Handling Results from Sub-Activities
When an activity needs to return data to its caller, use the startActivityForResult pattern (or the modern Activity Result API). Below is the traditional approach:
// In the calling activity
public void pickContact(View v) {
Intent intent = new Intent(this, SelectionActivity.class);
startActivityForResult(intent, 200);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 200 && data != null) {
String pickedValue = data.getStringExtra("SELECTED_VAL");
// Update UI
}
}
// In the sub-activity
private void finishWithData(String value) {
Intent output = new Intent();
output.putExtra("SELECTED_VAL", value);
setResult(RESULT_OK, output);
finish();
}
Android Service Lifecycle and Implementation
Services perform long-running operations in the background without a UI. They are categorized by how they are started and how they interact with the system.
Process Hierarchy and Priority
Android manages application lifecycles based on process priority:
- Foreground Process: User is currently interacting (Active Activity or Foreground Service).
- Visible Process: Activity is visible but partially obscured.
- Service Process: Running a background service started via
startService(). - Background Process: Activity is completely hidden (in the backstack).
- Empty Process: No active components, kept for caching.
Communication via Bound Services
Binding allows an activity to interact directly with a service through an interface.
public class CoreService extends Service {
private final IBinder binder = new LocalBinder();
public class LocalBinder extends Binder {
CoreService getService() {
return CoreService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public void executeTask(String params) {
Log.d("Service", "Executing: " + params);
}
}
Remote Service Commmunication (AIDL)
For cross-process communication (IPC), Android uses AIDL. Both the client and the service must share the same .aidl file structure.
// IRemoteInterface.aidl
interface IRemoteInterface {
void performRemoteAction(String data);
}
// Service Implementation
private final IRemoteInterface.Stub mBinder = new IRemoteInterface.Stub() {
public void performRemoteAction(String data) {
// Implementation
}
};
BroadcastReceiver: Responding to System Events
BroadcastReceivers allow apps to listen for system-wide announcements or custom events.
Dynamic Registration for Screen State
Certain events, like screen state changes, often require dynamic registration within an activtiy lifecycle to prevent memory leaks.
public class EventMonitor extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
// Handle screen off
}
}
}
// In Activity
@Override
protected void onStart() {
super.onStart();
registerReceiver(myMonitor, new IntentFilter(Intent.ACTION_SCREEN_OFF));
}
@Override
protected void onStop() {
unregisterReceiver(myMonitor);
super.onStop();
}
Ordered Broadcasts
Ordered broadcasts are delivered sequentially to receivers based on priority (defined in intent-filter). Receivers can modify the result or abort the broadcast.
public void triggerPriorityBroadcast() {
Intent intent = new Intent("com.example.CUSTOM_EVENT");
sendOrderedBroadcast(intent, null, new FinalReceiver(), null, 0, "Initial Data", null);
}
Data Sharing with ContentProviders
ContentProviders manage access to a structured set of data. They encapsulate data and provide mechanisms for security and cross-app data sharing.
Custom Provider Implementation
A provider uses a UriMatcher to identify incoming data requests.
public class MyDataProvider extends ContentProvider {
private static final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int USER_LIST = 1;
static {
matcher.addURI("com.example.provider", "users", USER_LIST);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] args, String sort) {
if (matcher.match(uri) == USER_LIST) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
return db.query("user_table", projection, selection, args, null, null, sort);
}
return null;
}
// Implement insert, update, delete similarly...
}
Observing Data Changes
A ContentObserver can be used to listen for changes in specific datasets, such as the system SMS database or a custom provider.
public void watchData() {
Uri targetUri = Uri.parse("content://sms/");
getContentResolver().registerContentObserver(targetUri, true, new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d("Observer", "Database updated.");
}
});
}
The Endroid Manifest: Core Configuration
The AndroidManifest.xml file is mandatory for every Android application. It declares components, permissions, and hardware requirements.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:theme="@style/AppTheme"
android:icon="@mipmap/ic_launcher">
<activity android:name=".HomeActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".BackgroundWorker" />
<provider
android:name=".LocalProvider"
android:authorities="com.example.app.data"
android:exported="false" />
</application>
</manifest>