Fundamentals of Core Android Components

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>

Tags: Android Activity Service BroadcastReceiver contentprovider

Posted on Mon, 03 Aug 2026 16:05:37 +0000 by wipe