Skip to main content
All subjects

Mobile App Development FRQ practice

12 problems · Worked solutions · 9-point rubrics · Android Studio · Java / XML

Code Scholars practice problems. These are not official exam prompts.

Use Android Studio with Java and XML Views. Android snippets are focused implementation fragments; use the named layouts, classes, imports, and AndroidX dependencies in your project. Each topic includes emulator or device checks.

Problem 1 · Android Studio and the First App · 9 points

Launch a greeting screen

Use Android Studio with a Java Activity and XML Views. A layout activity_main contains greetingText (TextView) and greetButton (Button). Assume AppCompat and resources are configured.

  1. A. Write the Activity body that inflates the layout and handles the button.
  2. B. Describe how to run and debug it in the Android Studio emulator.
  3. C. Give two likely causes of a crash before the click handler is reached and how to locate them.
Show worked solution and rubric
// Inside MainActivity extends AppCompatActivity.
@Override protected void onCreate(Bundle state) {
    super.onCreate(state);
    setContentView(R.layout.activity_main);
    TextView greeting = findViewById(R.id.greetingText);
    Button button = findViewById(R.id.greetButton);
    button.setOnClickListener(v -> greeting.setText("Hello, Code Scholars!"));
}
  1. A. Call setContentView before findViewById. Bind the click listener to the button and update the TextView in its UI-thread callback.
  2. B. Select an emulator in Device Manager, run the app, set a breakpoint in the listener, and tap the button. Inspect the view references and step through setText.
  3. C. An incorrect layout can make findViewById return null; an invalid Activity configuration can fail launch. Read the first relevant application frame and underlying cause in Logcat, then verify resource IDs and the manifest.

Scoring rubric

  • 3 points: Call setContentView before findViewById
  • 3 points: Select an emulator in Device Manager, run the app, set a breakpoint in the listener, and tap the button
  • 3 points: An incorrect layout can make findViewById return null; an invalid Activity configuration can fail launch

Problem 2 · UI/UX Design and Interactive Widgets · 9 points

Validate a study-minute input

An EditText minutesInput, Button saveButton, and TextView resultText are already bound. Accept whole minutes from 1 through 180. Invalid input must show a field error and must not save.

  1. A. Write the Java click listener with validation.
  2. B. Specify useful keyboard and accessibility choices in the XML layout.
  3. C. Give boundary and usability tests.
Show worked solution and rubric
saveButton.setOnClickListener(v -> {
    try {
        int minutes = Integer.parseInt(minutesInput.getText().toString().trim());
        if (minutes < 1 || minutes > 180) {
            minutesInput.setError("Enter 1 to 180 minutes");
            return;
        }
        minutesInput.setError(null);
        resultText.setText("Validated minutes: " + minutes);
        // Pass validated minutes to the ViewModel/repository to save.
    } catch (NumberFormatException error) {
        minutesInput.setError("Enter a whole number from 1 to 180");
    }
});
  1. A. Trim input and catch NumberFormatException, which includes empty text and integer overflow. Reject out-of-range values before presenting a success state.
  2. B. Use inputType="number" as a keyboard hint, a visible TextView label with android:labelFor pointing to minutesInput, string resources, and at least a 48dp touch target for Save. Keyboard choice is not validation.
  3. C. Test blank, spaces, letters pasted in, 0,1,180,181, and a very long number. Verify error announcement, readable large text, and that invalid input does not change stored data.

Scoring rubric

  • 3 points: Trim input and catch NumberFormatException, which includes empty text and integer overflow
  • 3 points: Use inputType="number" as a keyboard hint, a visible TextView label with android:labelFor pointing to minutesInput, string resources, and at least a 48dp touch target for Save
  • 3 points: Test blank, spaces, letters pasted in, 0,1,180,181, and a very long number

Problem 3 · Activities, Intents, and the Android Lifecycle · 9 points

Restore a counter after rotation

A counter Activity owns int count and TextView countText. It must survive normal saved-state recreation. The layout contains countText and addButton.

Activitysaved countnew ActivityRepository
Rotation recreates the Activity. Small saved state restores the screen; durable tasks reload from the repository.
  1. A. Show restoring count and wiring the button in onCreate, plus saving count.
  2. B. Explain when a ViewModel is useful and what it does not survive by itself.
  3. C. Pass a task ID to an explicit detail Activity; say what belongs in persistent storage.
Show worked solution and rubric
// Inside CounterActivity extends AppCompatActivity; fields: int count; TextView countText.
@Override protected void onCreate(Bundle state) {
    super.onCreate(state);
    setContentView(R.layout.activity_counter);
    count = state == null ? 0 : state.getInt("count", 0);
    countText = findViewById(R.id.countText);
    countText.setText(String.valueOf(count));
    findViewById(R.id.addButton).setOnClickListener(v -> {
        count++;
        countText.setText(String.valueOf(count));
    });
}
@Override protected void onSaveInstanceState(Bundle out) {
    out.putInt("count", count);
    super.onSaveInstanceState(out);
}
// In a separate click handler, to open a task:
// Intent intent = new Intent(this, DetailActivity.class);
// intent.putExtra("task_id", taskId);
// startActivity(intent);
  1. A. Read count from the saved Bundle after setContentView and render it before wiring clicks. Save the count under the same key in onSaveInstanceState.
  2. B. A ViewModel retains in-memory screen/business state across configuration changes. It alone does not survive process death; SavedStateHandle can hold small restoration values, while a repository reloads durable data.
  3. C. Use an explicit Intent with a stable task ID extra, validate it in the receiving Activity, and load the record from storage. Saved instance state is restoration support, not a substitute for saving a user record.
Activitysaved countnew ActivityRepository
Rotation recreates the Activity. Small saved state restores the screen; durable tasks reload from the repository.

Scoring rubric

  • 3 points: Read count from the saved Bundle after setContentView and render it before wiring clicks
  • 3 points: A ViewModel retains in-memory screen/business state across configuration changes
  • 3 points: Use an explicit Intent with a stable task ID extra, validate it in the receiving Activity, and load the record from storage

Problem 4 · Fragments and Reusable Screen Components · 9 points

Observe data only while the view exists

A Fragment uses generated FragmentTasksBinding and a TasksViewModel exposing LiveData<List<Task>> tasks. Assume TasksAdapter.submitList and dependencies exist.

  1. A. Show binding initialization, view-lifecycle observation, and cleanup.
  2. B. Explain why the Fragment lifecycle is different from the view lifecycle.
  3. C. Test navigation away and back while data updates.
Show worked solution and rubric
// Fragment fields: FragmentTasksBinding binding; TasksViewModel viewModel;
// TasksAdapter adapter is initialized separately.
@Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle state) {
    binding = FragmentTasksBinding.inflate(inflater, parent, false);
    return binding.getRoot();
}
@Override public void onViewCreated(View view, Bundle state) {
    super.onViewCreated(view, state);
    viewModel = new ViewModelProvider(this).get(TasksViewModel.class);
    binding.tasksList.setAdapter(adapter);
    viewModel.tasks.observe(getViewLifecycleOwner(), adapter::submitList);
}
@Override public void onDestroyView() {
    super.onDestroyView();
    binding = null;
}
  1. A. Create binding in onCreateView, observe with getViewLifecycleOwner in onViewCreated, and clear binding in onDestroyView. The observer is removed when that view lifecycle is destroyed.
  2. B. A Fragment on the back stack can outlive its destroyed view. Observing with the Fragment itself risks a callback accessing an obsolete binding.
  3. C. Navigate away, update repository data, and return. The new view should render current data once, with no old-view updates or retained binding. Also rotate and check for duplicate observers.

Scoring rubric

  • 3 points: Create binding in onCreateView, observe with getViewLifecycleOwner in onViewCreated, and clear binding in onDestroyView
  • 3 points: A Fragment on the back stack can outlive its destroyed view
  • 3 points: Navigate away, update repository data, and return

Problem 5 · Adapters, Collections, and List Interfaces · 9 points

Fix a recycled-row checkbox bug

A RecyclerView row has a title and completed CheckBox. During scrolling, checking one task accidentally changes another. Each Task has a stable long id and boolean completed.

  1. A. Describe a correct onBindViewHolder sequence and click-ID handling.
  2. B. Define DiffUtil identity and content comparisons.
  3. C. Give a regression test for recycling, sorting, and filtering.
Show worked solution and rubric
// Within onBindViewHolder, with holder fields title and completed.
Task item = getItem(position);
holder.completed.setOnCheckedChangeListener(null);
holder.title.setText(item.title);
holder.completed.setChecked(item.completed);
holder.completed.setOnCheckedChangeListener((button, checked) ->
    onCompletionChanged.accept(item.id, checked));
// Contract: onCompletionChanged persists by ID and emits a fresh immutable list.
  1. A. Detach the old checkbox listener, set text and checked state from the bound item, then attach a new listener that updates by that item’s stable ID. Never treat adapter position as a permanent record ID.
  2. B. areItemsTheSame compares IDs; areContentsTheSame compares every displayed field. Submit a new immutable list of updated objects to ListAdapter so previous and new snapshots differ.
  3. C. Create more items than fit on screen, toggle one, scroll away and back, then sort and filter. Only that record should change. A listener that captures an old position or an unreset checkbox can corrupt a different row.

Scoring rubric

  • 3 points: Detach the old checkbox listener, set text and checked state from the bound item, then attach a new listener that updates by that item’s stable ID
  • 3 points: areItemsTheSame compares IDs; areContentsTheSame compares every displayed field
  • 3 points: Create more items than fit on screen, toggle one, scroll away and back, then sort and filter

Problem 6 · Local Storage and App Data · 9 points

Persist tasks with Room

A task has generated long id, non-null title, and completed flag. Design Room storage for task insertion and observing tasks. Assume Room dependencies, a configured database, and a repository executor.

FragmentViewModelRepositoryRoom
Data flow: Fragment sends an action to ViewModel; repository writes Room. Observed results return to the screen.
  1. A. Write the entity and DAO contract.
  2. B. Explain where insertion runs and how data reaches a Fragment.
  3. C. Describe a persistence and migration test, and distinguish storage from saved UI state.
Show worked solution and rubric
// Task.java (separate file)
@Entity(tableName = "tasks")
public class Task {
    @PrimaryKey(autoGenerate = true) public long id;
    @NonNull public String title = "";
    public boolean completed;
}
// TaskDao.java (separate file)
@Dao
public interface TaskDao {
    @Insert long insert(Task task);
    @Query("SELECT * FROM tasks ORDER BY id")
    LiveData<List<Task>> observeAll();
}
// Repository fragment, called with a non-null validated title:
// io.execute(() -> dao.insert(task));
  1. A. Use @Entity with @PrimaryKey(autoGenerate=true), and a DAO with @Insert and a deterministic ORDER BY query returning LiveData. Put each public Java type in its own file.
  2. B. Run the synchronous insert on a repository-owned worker executor. Room dispatches observable queries off the main thread; expose results through a ViewModel and observe with the Fragment view lifecycle.
  3. C. Insert, close and reopen the database, and verify the record survives. Test migrations from each supported schema instead of destructive reset for user data. A saved counter or selected ID is small UI restoration state, not durable task storage.
FragmentViewModelRepositoryRoom
Data flow: Fragment sends an action to ViewModel; repository writes Room. Observed results return to the screen.

Scoring rubric

  • 3 points: Use @Entity with @PrimaryKey(autoGenerate=true), and a DAO with @Insert and a deterministic ORDER BY query returning LiveData
  • 3 points: Run the synchronous insert on a repository-owned worker executor
  • 3 points: Insert, close and reopen the database, and verify the record survives

Problem 7 · REST APIs and Connected Applications · 9 points

Handle an out-of-order search response

A user searches cat, then dog. The dog response arrives first, but the later cat response overwrites the screen. Design a Java ViewModel/repository solution; no real API key is needed.

  1. A. Describe a request-generation or cancellation mechanism.
  2. B. Define loading, success, empty, and error UI states.
  3. C. Give deterministic tests, including network errors and rotation.
Show worked solution and rubric
// Pseudocode for a main-thread ViewModel coordinator:
// search(query):
//   id = ++currentRequest
//   state = Loading(query)
//   repository.searchAsync(query, result -> mainThread.post(() -> {
//     if (id != currentRequest) return;
//     state = result.isError ? Error(query) :
//             result.items.isEmpty ? Empty(query) : Success(query, result.items);
//   }));
// Repository does network and JSON work off the main thread.
  1. A. Assign an increasing request ID when launching on the main thread. Capture it with the request and accept completion only when it still matches the current ID; publish UI state on the main thread. Cancellation saves work but retain the ID guard for races.
  2. B. Loading identifies an active request; success holds nonempty data; empty holds a successful empty result; error carries a safe message and retry action. Preserve the latest query and avoid showing credentials or raw server errors.
  3. C. Use a fake repository with manually controlled responses: complete dog before cat and assert dog remains. Test timeout, malformed JSON, empty data, and rotation without duplicate requests. Use HTTPS, declare INTERNET permission, and keep privileged secrets on a server.

Scoring rubric

  • 3 points: Assign an increasing request ID when launching on the main thread
  • 3 points: Loading identifies an active request; success holds nonempty data; empty holds a successful empty result; error carries a safe message and retry action
  • 3 points: Use a fake repository with manually controlled responses: complete dog before cat and assert dog remains

Problem 8 · Multithreading and Responsive Apps · 9 points

Choose the right background mechanism

An app must parse a large local file while a screen is open, and separately sync pending edits eventually even after the app leaves the foreground.

  1. A. Choose mechanisms for both jobs and justify their lifetimes.
  2. B. Explain thread-safe result delivery and lifecycle cleanup.
  3. C. Specify tests for responsiveness, cancellation, and retries.
Show worked solution and rubric
  1. A. Use a repository-owned Executor for immediate parsing coordinated by a ViewModel. Use WorkManager for deferrable persistent sync, with constraints such as connected network. It is not an exact-time alarm.
  2. B. Avoid reading Views from worker threads. Publish immutable results via lifecycle-aware state and render on the main thread. Cancel screen-only work when its owner no longer needs it; retain application-scoped sync independently of a destroyed Activity.
  3. C. Use a large fixture to verify scrolling remains responsive. Navigate away during parsing, simulate offline sync then reconnect, and retry a failed request. Make server writes idempotent so a retried sync does not duplicate records.

Scoring rubric

  • 3 points: Use a repository-owned Executor for immediate parsing coordinated by a ViewModel
  • 3 points: Avoid reading Views from worker threads
  • 3 points: Use a large fixture to verify scrolling remains responsive

Problem 9 · Services, Broadcasts, and App Widgets · 9 points

Cancel a scheduled synchronization

A task app enqueues one background sync whenever edits are pending. Repeated taps must not create duplicate pending sync jobs. The user may cancel pending sync.

  1. A. Show named unique WorkManager work and cancellation.
  2. B. Explain why neither a broadcast receiver nor a foreground service is a generic substitute.
  3. C. Explain cancellation limits and design a widget update path.
Show worked solution and rubric
Constraints constraints = new Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED).build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(SyncWorker.class)
    .setConstraints(constraints).build();
WorkManager manager = WorkManager.getInstance(context);
manager.enqueueUniqueWork("pending-task-sync", ExistingWorkPolicy.KEEP, request);
// In the separate Cancel handler:
// manager.cancelUniqueWork("pending-task-sync");
// SyncWorker is an application-defined Worker with retry and idempotency logic.
  1. A. Enqueue unique work with KEEP, using a stable name and a network constraint. Cancel by the same name; worker code should cooperate with stop requests.
  2. B. A receiver handles a brief event and should hand longer work to an appropriate scheduler. Foreground services serve permitted user-visible ongoing work and have platform restrictions; they are not an unrestricted scheduler.
  3. C. Cancellation cannot undo a server write already committed. Store local sync status and reconcile idempotently. A widget should read persisted data and request scheduled refresh without holding an Activity reference.

Scoring rubric

  • 3 points: Enqueue unique work with KEEP, using a stable name and a network constraint
  • 3 points: A receiver handles a brief event and should hand longer work to an appropriate scheduler
  • 3 points: Cancellation cannot undo a server write already committed

Problem 10 · Mobile Sensors and Device Capabilities · 9 points

Release a sensor listener

An Activity implements SensorEventListener, owns SensorManager manager and Sensor accelerometer, and displays a tilt experiment. Assume manager and accelerometer are initialized in onCreate; the sensor may be null.

  1. A. Register and unregister the listener with the visible lifecycle.
  2. B. Explain handling unavailable hardware, noisy data, and permissions.
  3. C. Define emulator and physical-device tests.
Show worked solution and rubric
@Override protected void onResume() {
    super.onResume();
    if (accelerometer != null)
        manager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
    // Otherwise render an unavailable-hardware message.
}
@Override protected void onPause() {
    manager.unregisterListener(this);
    super.onPause();
}
// SensorEventListener callbacks are implemented elsewhere in this Activity.
  1. A. Register in onResume only if the sensor exists. Unregister in onPause so an inactive screen does not keep receiving events or consuming battery.
  2. B. Show a disabled-feature message if hardware is absent. Use smoothing and documented thresholds rather than treating every noisy sample as a gesture. Ordinary accelerometer use at a normal rate does not require a location permission; check requirements for the actual sensor, rate, and Android target.
  3. C. Test missing-sensor behavior, portrait/landscape axes, pause/resume, rapid motion, and noise on a real device. Emulator controls can simulate some motion but do not establish real-world battery cost or sensor quality.

Scoring rubric

  • 3 points: Register in onResume only if the sensor exists
  • 3 points: Show a disabled-feature message if hardware is absent
  • 3 points: Test missing-sensor behavior, portrait/landscape axes, pause/resume, rapid motion, and noise on a real device

Problem 11 · An Introduction to Mobile Game Development · 9 points

Make a frame-rate-independent game step

An Android Studio game prototype moves a sprite horizontally at 120 pixels per second. x is its left edge, width is the viewport width, spriteWidth is positive and <=width, and dt is nonnegative elapsed seconds. Implement a pure Java clamp step.

  1. A. Implement movement and clamping to the visible viewport.
  2. B. Compare one 0.1-second step with two 0.05-second steps away from a boundary.
  3. C. Explain pause/resume timing and collision-test limits.
Show worked solution and rubric
public static double step(double x, double dt, double width, double spriteWidth) {
    double next = x + 120.0 * dt;
    return Math.max(0.0, Math.min(width - spriteWidth, next));
}
  1. A. Advance by speed*dt, then clamp the left edge to [0,width-spriteWidth]. Keeping simulation separate from drawing permits local JVM tests.
  2. B. Both advance by 12 pixels if neither reaches the boundary. Updating by a fixed amount per frame would move faster on high-frame-rate devices.
  3. C. Pause updates when backgrounded and reset the timestamp on resume to avoid a huge dt. Clamping handles viewport edges only; fast movement can tunnel through obstacles, requiring substeps or swept collision tests.

Scoring rubric

  • 3 points: Advance by speed*dt, then clamp the left edge to [0,width-spriteWidth]
  • 3 points: Both advance by 12 pixels if neither reaches the boundary
  • 3 points: Pause updates when backgrounded and reset the timestamp on resume to avoid a huge dt

Problem 12 · Capstone: Design, Test, and Present · 9 points

Plan an offline study planner

Build an Android Studio planner with a task list, details, completion state, and local persistence. The app must work without network access. You may add optional sync, but it cannot be required for saving.

UIViewModelRepositoryRoomoptional sync
Offline-first planner: UI to ViewModel to repository to local Room database. Optional sync is a separate repository operation.
  1. A. Define a small architecture and data flow for editing a task.
  2. B. Specify three measurable acceptance tests and a privacy rule.
  3. C. Describe the Android Studio delivery artifacts and one honest limitation.
Show worked solution and rubric
  1. A. Use Activity/Fragments for rendering, ViewModel for UI state/actions, a repository for data operations, and Room as the durable source of truth. Save locally first; optional remote sync can follow independently.
  2. B. Tests: an offline edit survives relaunch; rotating a detail screen preserves the selected task and unsaved restoration state; toggling a filtered list row updates only its stable ID. Collect only necessary task data and never log private notes or credentials.
  3. C. Deliver the Android Studio project with Gradle configuration, a README with build/run steps, test reports, and a short emulator/device demonstration. State a real limitation such as no cross-device sync; distinguish planned features from implemented ones.
UIViewModelRepositoryRoomoptional sync
Offline-first planner: UI to ViewModel to repository to local Room database. Optional sync is a separate repository operation.

Scoring rubric

  • 3 points: Use Activity/Fragments for rendering, ViewModel for UI state/actions, a repository for data operations, and Room as the durable source of truth
  • 3 points: Tests: an offline edit survives relaunch; rotating a detail screen preserves the selected task and unsaved restoration state; toggling a filtered list row updates only its stable ID
  • 3 points: Deliver the Android Studio project with Gradle configuration, a README with build/run steps, test reports, and a short emulator/device demonstration

Android documentation

Need help with this topic?

Work through questions with a tutor and get help with the steps you find difficult.

Get help with this topic