tutorial, no_image, testing,

Testing - no_image

Upendra Upendra Follow Jan 23, 2025 · 10 mins read
Testing - no_image
Share this

Table of Contents

  1. Activities
  2. Fragments
  3. Services
  4. Broadcast Receivers
  5. Content Providers
  6. UI Design
  7. Data Storage
  8. Networking
  9. Concurrency
  10. Dependency Injection
  11. Testing
  12. Performance Optimization
  13. Security
  14. Jetpack Libraries

Advanced Android Interview Guide

Deep Technical Breakdown for Senior Engineers


1. Activities

🔄 Lifecycle Deep Dive

class Activity : ComponentActivity() {
    // Creation Phase
    override fun onCreate() { /* Initialize UI, bind ViewModel */ }
    override fun onStart() { /* Visible but not interactive (e.g., behind dialog) */ }
    override fun onResume() { /* Interactive state - start animations/sensors */ }

    // Destruction Phase
    override fun onPause() { /* Release exclusive resources (e.g., camera) */ }
    override fun onStop() { /* Heavy cleanup (DB connections) */ }
    override fun onDestroy() { /* Final cleanup (unregister receivers) */ }

    // Configuration Changes
    override fun onSaveInstanceState(bundle: Bundle) { /* Save transient UI state */ }
    override fun onRestoreInstanceState(bundle: Bundle) { /* Restore UI state */ }
}

Flowchart:

onCreate() → onStart() → onResume() → [Running]  
      ↑          |            |  
      └── onRestart() ← onStop() ← onPause()  
                        |  
                        └── onDestroy()  

🎯 Launch Modes & Intent Flags

  • singleTop: Reuse existing instance if it’s at the top of the stack.
    intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
    
  • **singleTask**: Creates a new task and becomes the root.
    <activity android:launchMode="singleTask" />
    
  • **FLAG_ACTIVITY_CLEAR_TOP**: Destroys all activities above the target in the stack.

Interview Q:
Q: What happens if Activity A (launchMode=singleTask) starts Activity B, which starts Activity A again?
A: A’s existing instance is brought to the front. The back stack becomes: A (root) → B → A (top).


2. Fragments

🧩 Lifecycle & Back Stack

Lifecycle Methods:

onAttach()  onCreate()  onCreateView()  onViewCreated()  onStart()  onResume()  
onPause()  onStop()  onDestroyView()  onDestroy()  onDetach()  

Back Stack Behavior:

  • addToBackStack("tag"): Saves fragment state. Popping uses popBackStackImmediate("tag", POP_BACK_STACK_INCLUSIVE).
  • FragmentStateAdapter vs FragmentPagerAdapter:
    • StateAdapter: Destroys non-adjacent fragments (better for memory).
    • PagerAdapter: Keeps fragments in memory (faster tab switching).

Interview Q:
Q: Why does onDestroyView() get called during a fragment transaction?
A: The fragment’s view hierarchy is destroyed, but the fragment instance remains. Always nullify view references here.


3. Services

🎵 Foreground Service Pitfalls

class MusicService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification = buildNotification() // Required for Android 8+
        startForeground(NOTIFICATION_ID, notification)
        return START_NOT_STICKY
    }

    // Binder for activity binding
    inner class LocalBinder : Binder() { fun getService(): MusicService = this@MusicService }
}

Flowchart:

[startService()] → onStartCommand()  
[bindService()] → onBind() → IBinder  
[stopSelf()/stopService()] → onDestroy()  

Interview Q:
Q: How to handle ANR in a started service?
A: Offload work to a background thread. Avoid blocking onStartCommand().


4. WorkManager

⏰ Chained Periodic Work

val constraints = Constraints.Builder()  
    .setRequiredNetworkType(NetworkType.CONNECTED)  
    .setRequiresBatteryNotLow(true)  
    .build()  

val request = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS)  
    .setConstraints(constraints)  
    .build()  

WorkManager.getInstance(context).enqueueUniquePeriodicWork(  
    "sync",  
    ExistingPeriodicWorkPolicy.KEEP, // Replace vs. keep existing  
    request  
)  

Execution Flow:

Worker.doWork() → [Background Thread]  
      │  
      ├── Success → Result.success()  
      └── Failure → Result.retry()  

Interview Q:
Q: When would WorkManager NOT execute a worker?
A: If constraints aren’t met (e.g., no network) or OS battery optimizations (Doze mode) block execution.


5. Room Database

🚀 Migration Strategies

Schema Migration:

Room.databaseBuilder(appContext, AppDatabase::class.java, "mydb")  
    .addMigrations(object : Migration(1, 2) {  
        override fun migrate(database: SupportSQLiteDatabase) {  
            database.execSQL("ALTER TABLE User ADD COLUMN age INTEGER")  
        }  
    })  
    .build()  

DAO Optimization:

@Dao  
interface UserDao {  
    @Transaction  
    @Query("SELECT * FROM User")  
    fun getUsersWithPosts(): Flow<List<UserWithPosts>> // Uses @Relation  

    @Query("SELECT * FROM User WHERE name LIKE :query")  
    fun searchUsers(query: String): Flow<List<User>>  
}  

Interview Q:
Q: How to handle Room CURSOR_WINDOW exceptions?
A: Paginate large queries with LIMIT/OFFSET or use PagingSource.


6. Coroutines & Flow

🌀 Structured Concurrency

viewModelScope.launch(Dispatchers.IO) {  
    val user = async { userRepo.getUser() }  
    val posts = async { postRepo.getPosts() }  
    val result = user.await() to posts.await()  
    withContext(Dispatchers.Main) { updateUI(result) }  
}  

Cancellation Rules:

  • Cooperative Cancellation: Check isActive in loops.
  • Non-cancellable Blocks:
    withContext(NonCancellable) { /* Critical cleanup */ }  
    

Flow Operators:

userFlow  
    .filter { it.isActive }  
    .map { it.name }  
    .catch { e -> emit("Error") }  
    .collect { names -> println(names) }  

Interview Q:
Q: Why does Flow.collect() suspend?
A: To backpressure streams - collector controls emission rate.


7. Security

🔒 Encrypted SharedPreferences

val masterKey = MasterKey.Builder(context)  
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)  
    .build()  

val sharedPreferences = EncryptedSharedPreferences.create(  
    context,  
    "secret_prefs",  
    masterKey,  
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,  
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM  
)  

sharedPreferences.edit().putString("API_KEY", "12345").apply()  

Key Attestation:

  • Use KeyGenParameterSpec.Builder.setAttestationChallenge() for hardware-backed keys.

Interview Q:
Q: How to prevent certificate pinning bypass in rooted devices?
A: Use obfuscation or runtime integrity checks (e.g., SafetyNet Attestation).


8. Performance Optimization

🛠️ Memory Leak Patterns

Leak 1: Static reference to Activity:

companion object {  
    var leakedActivity: Activity? = null // BAD!  
}  

Leak 2: Non-cancelled coroutines:

fun fetchData() {  
    GlobalScope.launch { /* Leak if Activity dies before completion */ }  
}  

Fix: Use viewModelScope or lifecycleScope.

Profiling Tools:

  • Memory Profiler: Track heap allocations.
  • Systrace: Identify UI jank.

Interview Q:
Q: How to debug a 120Hz UI freeze?
A: Check Choreographer logs for skipped frames. Optimize onDraw()/measure passes.


9. Dagger-Hilt

🔄 Component Hierarchies

@Module  
@InstallIn(ViewModelComponent::class)  
object ViewModelModule {  
    @Provides  
    fun provideDependency() = MyDependency()  
}  

@AndroidEntryPoint  
class MainActivity : AppCompatActivity() {  
    @Inject lateinit var viewModelFactory: MyViewModelFactory  
}  

Scoping:

  • @Singleton: App-level scope.
  • @ViewModelScoped: Lives as long as the ViewModel.

Interview Q:
Q: Why use @AssistedInject with ViewModels?
A: To inject dependencies while allowing constructor params from the ViewModelProvider.Factory.


10. Espresso Testing

🧪 Idling Resources

class MyIdlingResource : IdlingResource {  
    override fun isIdleNow(): Boolean = /* Check if background work is done */  
}  

@Before  
fun registerIdlingResource() {  
    IdlingRegistry.getInstance().register(idlingResource)  
}  

Synchronization:

  • Espresso automatically waits for UI events and AsyncTasks.
  • Custom Waits:
    onView(withId(R.id.progress)).check(matches(not(isDisplayed())))  
    

Interview Q:
Q: How to test RecyclerView item clicks?
A:

onView(withId(R.id.recycler))  
    .perform(actionOnItem<ViewHolder>(hasDescendant(withText("Item")), click()))  

Appendix: Flowchart Legend

  • Lifecycles: Use state transition diagrams (e.g., CREATED → STARTED → RESUMED).
  • Data Flow:
    UI → ViewModel → Repository → API/Database  
         ↑          ↓  
        LiveData   Coroutine  
    
  • Dependency Graph:
    AppComponent → Subcomponents (Activity, Fragment)  
         |  
         ˪─ Provides Singletons  
    

    ```

Final Notes

  • Key Focus Areas:
    • Lifecycle Awareness: Avoid leaks with LifecycleObserver.
    • Threading: Coroutine dispatchers, withContext best practices.
    • Performance: SparseArray over HashMap, RecyclerView view pooling.
  • Anti-Patterns:
    • Using runBlocking in production.
    • Ignoring StrictMode warnings.

Let me know if you need further expansion on any topic! 🔥

Join Newsletter
Get the latest news right in your inbox. We never spam!
Upendra
Written by Upendra Follow
Hi, I am Upendra, the author in Human and machine languages,I don't know to how 3 liner bio works so just Connect with me on social sites you will get to know me better.