Overview
Android is Google's open-source mobile operating system powering billions of devices worldwide. This cheatsheet covers essential Android development concepts using Java, focusing on the most critical topics for technical interviews. Whether you're building native Android apps or preparing for interviews, these concepts form the foundation of Android development.
1. Android Fundamentals
Android Architecture Layers
- Linux Kernel: Core system services (drivers, power management, memory management)
- Hardware Abstraction Layer (HAL): Standard interfaces exposing device hardware capabilities
- Android Runtime (ART):
- Ahead-of-Time (AOT) compilation for better performance
- Improved garbage collection
- Better debugging support
- Native C/C++ Libraries: WebKit, OpenGL ES, SQLite, Media Framework
- Java API Framework: Content Providers, View System, Activity Manager, etc.
- System Apps: Dialer, SMS, Calendar, Camera
Four Application Components
Activities:
- Single screen with user interface
- Extends
AppCompatActivityorActivity - Example: Login screen, Settings page
Services:
- Background operations without UI
- Types: Started Service, Bound Service, Foreground Service
- Example: Music playback, file download
Broadcast Receivers:
- Responds to system-wide broadcast announcements
- Can be registered in manifest or dynamically
- Example: Battery low, WiFi connected
Content Providers:
- Manages shared app data
- Provides data to other applications
- Example: Contacts, Media Store
AndroidManifest.xml
<manifest package="com.example.app">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2. Activity Lifecycle
Lifecycle Methods
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Activity created, initialize UI
}
@Override
protected void onStart() {
super.onStart();
// Activity visible
}
@Override
protected void onResume() {
super.onResume();
// Activity interactive
}
@Override
protected void onPause() {
super.onPause();
// Activity losing focus
}
@Override
protected void onStop() {
super.onStop();
// Activity no longer visible
}
@Override
protected void onDestroy() {
super.onDestroy();
// Activity destroyed
}
}
Common State Transitions
- App Launch: onCreate() → onStart() → onResume()
- Home Button: onPause() → onStop() → onSaveInstanceState()
- Back Button: onPause() → onStop() → onDestroy()
- Phone Call Interrupt: onPause() → onResume()
- App Switch & Return: onPause() → onStop() → onRestart() → onStart() → onResume()
- Configuration Change: onPause() → onStop() → onSaveInstanceState() → onDestroy() → onCreate() → onStart() → onRestoreInstanceState() → onResume()
3. Layouts and Views
Common Layouts
<!-- LinearLayout -->
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:text="Hello" />
<Button android:text="Click" />
</LinearLayout>
<!-- ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/textView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- RecyclerView -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
View Binding
// Enable in build.gradle
android {
viewBinding {
enabled = true
}
}
// In Activity
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.button.setOnClickListener(v -> {
binding.textView.setText("Clicked!");
});
}
}
4. Fragments
Fragment Lifecycle
public class MyFragment extends Fragment {
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// Fragment attached to activity
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_my, container, false);
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// View created, initialize UI components
}
}
Fragment Transactions
// Add Fragment
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new MyFragment())
.commit();
// Replace Fragment with backstack
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new AnotherFragment())
.addToBackStack(null)
.commit();
6. Data Storage
SharedPreferences
// Write
SharedPreferences prefs = getSharedPreferences("MyPrefs", MODE_PRIVATE);
prefs.edit()
.putString("key", "value")
.putInt("count", 10)
.apply();
// Read
String value = prefs.getString("key", "default");
int count = prefs.getInt("count", 0);
Room Database
// Entity
@Entity(tableName = "users")
public class User {
@PrimaryKey(autoGenerate = true)
public int id;
@ColumnInfo(name = "user_name")
public String name;
public String email;
}
// DAO
@Dao
public interface UserDao {
@Query("SELECT * FROM users")
List<User> getAllUsers();
@Insert
void insertUser(User user);
@Update
void updateUser(User user);
@Delete
void deleteUser(User user);
}
// Database
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
// Usage
AppDatabase db = Room.databaseBuilder(context,
AppDatabase.class, "database-name").build();
7. Networking
Retrofit
// API Interface
public interface ApiService {
@GET("users/{id}")
Call<User> getUser(@Path("id") int userId);
@POST("users")
Call<User> createUser(@Body User user);
}
// Setup
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService service = retrofit.create(ApiService.class);
// Usage
service.getUser(1).enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
User user = response.body();
}
@Override
public void onFailure(Call<User> call, Throwable t) {
// Handle error
}
});
Permissions
// Check permission
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// Request permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA}, REQUEST_CODE);
}
// Handle result
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == REQUEST_CODE) {
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted
}
}
}
8. RecyclerView
Adapter Implementation
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> data;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public ViewHolder(View view) {
super(view);
textView = view.findViewById(R.id.textView);
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_layout, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.textView.setText(data.get(position));
}
@Override
public int getItemCount() {
return data.size();
}
}
// Setup RecyclerView
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new MyAdapter(dataList));
9. Services
Foreground Service
public class MyService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Create notification channel (Android O+)
createNotificationChannel();
// Create notification
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Service Running")
.setSmallIcon(R.drawable.ic_notification)
.build();
startForeground(1, notification);
// Do work
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
// Start service
Intent serviceIntent = new Intent(this, MyService.class);
ContextCompat.startForegroundService(this, serviceIntent);
10. Broadcast Receivers
Dynamic Registration
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BATTERY_LOW.equals(intent.getAction())) {
// Handle battery low
}
}
}
// Register
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_LOW);
registerReceiver(receiver, filter);
// Unregister
unregisterReceiver(receiver);
Static Registration (Manifest)
<receiver android:name=".BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
11. Architecture Components
ViewModel
public class MyViewModel extends ViewModel {
private MutableLiveData<String> data = new MutableLiveData<>();
public LiveData<String> getData() {
return data;
}
public void loadData() {
// Load data asynchronously
data.setValue("Loaded data");
}
}
// In Activity/Fragment
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.getData().observe(this, newData -> {
// Update UI
textView.setText(newData);
});
LiveData
// MutableLiveData
MutableLiveData<String> liveData = new MutableLiveData<>();
liveData.setValue("Main thread update");
liveData.postValue("Background thread update");
// Transformations
LiveData<String> upperCaseData = Transformations.map(liveData,
input -> input.toUpperCase());
Data Binding
<!-- Enable in build.gradle -->
android {
dataBinding {
enabled = true
}
}
<!-- Layout -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.User" />
</data>
<TextView
android:text="@{user.name}"
android:visibility="@{user.isVisible ? View.VISIBLE : View.GONE}" />
</layout>
12. Coroutines (Kotlin-Java Interop)
Basic Usage
// Launch coroutine from Java (using Kotlin interop)
// Requires Kotlin coroutines library
// In Kotlin helper class
class CoroutineHelper {
fun doAsyncWork(callback: (String) -> Unit) {
GlobalScope.launch {
val result = withContext(Dispatchers.IO) {
// Background work
"Result"
}
withContext(Dispatchers.Main) {
callback(result)
}
}
}
}
// In Java
new CoroutineHelper().doAsyncWork(result -> {
// Update UI with result
});
13. Threading
Handler and Looper
// Main thread handler
Handler mainHandler = new Handler(Looper.getMainLooper());
// Background thread
new Thread(() -> {
// Do background work
String result = performTask();
// Update UI on main thread
mainHandler.post(() -> {
textView.setText(result);
});
}).start();
// HandlerThread
HandlerThread handlerThread = new HandlerThread("BackgroundThread");
handlerThread.start();
Handler backgroundHandler = new Handler(handlerThread.getLooper());
AsyncTask (Deprecated but still asked)
private class MyAsyncTask extends AsyncTask<String, Integer, String> {
@Override
protected String doInBackground(String... params) {
// Background thread
publishProgress(50);
return "Result";
}
@Override
protected void onProgressUpdate(Integer... progress) {
// UI thread
progressBar.setProgress(progress[0]);
}
@Override
protected void onPostExecute(String result) {
// UI thread
textView.setText(result);
}
}
14. Content Providers
Query Content Provider
// Query contacts
Cursor cursor = getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
do {
String name = cursor.getString(
cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
} while (cursor.moveToNext());
cursor.close();
}
15. Notifications
Creating Notifications
// Create channel (Android O+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID, "Channel Name", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
// Build notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("Title")
.setContentText("Message")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true);
// Show notification
NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, builder.build());
16. Material Design Components
Common Components
<!-- TextInputLayout -->
<com.google.android.material.textfield.TextInputLayout
android:hint="Email"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<com.google.android.material.textfield.TextInputEditText
android:inputType="textEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
<!-- FloatingActionButton -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:src="@drawable/ic_add"
app:layout_anchor="@id/bottom_app_bar" />
<!-- BottomNavigationView -->
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
app:menu="@menu/bottom_navigation_menu" />
17. Performance Optimization
Best Practices & Techniques
RecyclerView Optimization:
- Implement ViewHolder pattern correctly
- Use DiffUtil for efficient updates
- Set fixed size when possible:
setHasFixedSize(true) - Use RecycledViewPool for nested RecyclerViews
Memory Management:
- Use WeakReference for Activity/Context references
- Unregister all listeners in onDestroy()
- Avoid memory leaks from static references
- Use LeakCanary in development
Image Optimization:
- Use WebP format (30% smaller than PNG)
- Implement proper image scaling
- Use Glide or Picasso for efficient loading
- Cache images appropriately
Layout Performance:
- Prefer ConstraintLayout over nested LinearLayouts
- Use
<merge>tags to reduce hierarchy - Enable hardware acceleration
- Use ViewStub for rarely used views
Code Optimization:
- Enable R8/ProGuard for release builds
- Use @Keep annotation for reflection-accessed code
- Remove unused resources with
shrinkResources - Use Android App Bundle for smaller downloads
Data Structures:
- Use SparseArray instead of HashMap<Integer, Object>
- Use ArrayMap for small collections (<1000 items)
- Prefer primitive types over boxed types
Memory Leak Prevention
public class MyActivity extends AppCompatActivity {
private static WeakReference<MyActivity> weakActivity;
// Avoid static references to activities
// Use WeakReference if necessary
@Override
protected void onDestroy() {
super.onDestroy();
// Clean up resources
handler.removeCallbacksAndMessages(null);
if (asyncTask != null) {
asyncTask.cancel(true);
}
}
}
18. Testing
Unit Testing
// JUnit test
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
// Mockito
@Test
public void testUserRepository() {
UserDao mockDao = mock(UserDao.class);
when(mockDao.getUser(1)).thenReturn(new User("Test"));
UserRepository repository = new UserRepository(mockDao);
User user = repository.getUser(1);
assertEquals("Test", user.getName());
}
Instrumentation Testing
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
@Rule
public ActivityScenarioRule<MainActivity> rule =
new ActivityScenarioRule<>(MainActivity.class);
@Test
public void testButtonClick() {
onView(withId(R.id.button)).perform(click());
onView(withId(R.id.textView)).check(matches(withText("Clicked!")));
}
}
19. Security Best Practices
- Use HTTPS for all network communications
- Store sensitive data encrypted (Android Keystore)
- Validate all inputs
- Use ProGuard to obfuscate code
- Implement certificate pinning for sensitive APIs
- Don't log sensitive information
- Use Android's permission system properly
Encryption Example
// Generate and store key in Android Keystore
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(
"MyKeyAlias",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keyGenerator.init(keyGenParameterSpec);
keyGenerator.generateKey();
20. Critical Interview Topics
Conceptual Questions
What is Context in Android?
- Interface to global information about application environment
- Two types: Application Context and Activity Context
Difference between Serializable and Parcelable?
- Parcelable: Android-specific, faster, more efficient
- Serializable: Java standard, slower, uses reflection
What is ANR?
- Application Not Responding
- Occurs when UI thread blocked for >5 seconds
Launch Modes
- standard: New instance always created
- singleTop: Reuse if on top of stack
- singleTask: Only one instance in task
- singleInstance: Only one instance in system
Memory Leaks Common Causes
- Static references to activities
- Non-static inner classes
- Unregistered listeners
- Handler references
Code Questions Often Asked
// Singleton Pattern
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
// Custom View
public class CustomView extends View {
private Paint paint;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.RED);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(getWidth()/2, getHeight()/2, 100, paint);
}
}
Quick Reference
Gradle Dependencies (Common)
dependencies {
// AndroidX
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// Material Design
implementation 'com.google.android.material:material:1.9.0'
// Architecture Components
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.1'
implementation 'androidx.lifecycle:lifecycle-livedata:2.6.1'
// Room
implementation 'androidx.room:room-runtime:2.5.2'
annotationProcessor 'androidx.room:room-compiler:2.5.2'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// Testing
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Important Files Structure
app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/app/
│ │ ├── res/
│ │ │ ├── layout/
│ │ │ ├── values/
│ │ │ ├── drawable/
│ │ │ └── mipmap/
│ │ └── AndroidManifest.xml
│ ├── test/
│ └── androidTest/
├── build.gradle
└── proguard-rules.pro
Key Takeaways for Interviews
Must-Know Topics
- Activity/Fragment Lifecycle - Be able to draw and explain state transitions
- Memory Management - Understand leaks, weak references, and garbage collection
- Threading - Main thread, AsyncTask alternatives, Handlers, Coroutines
- Architecture Patterns - MVP, MVVM, Clean Architecture
- Performance - RecyclerView optimization, image loading, APK size reduction
Common Pitfalls to Avoid
- Performing network operations on main thread
- Not handling configuration changes properly
- Memory leaks from inner classes and listeners
- Ignoring ProGuard/R8 for production builds
- Not testing on different screen sizes and API levels
Modern Android Development (2024)
- Kotlin is now preferred over Java
- Jetpack Compose for declarative UI
- Coroutines for asynchronous programming
- Hilt for dependency injection
- Navigation Component for app navigation
- WorkManager for background tasks
Interview Success Tip: Always discuss trade-offs when explaining solutions. Show understanding of performance implications, maintainability, and user experience considerations in your architectural decisions.