Getting Started

Android Setup

# Install Android Studio
# Download from: https://developer.android.com/studio

# Create a new project
# File → New → New Project → Choose template

# Gradle dependencies (app/build.gradle)
android {
  compileSdkVersion 33
  defaultConfig {
    applicationId "com.example.myapp"
    minSdkVersion 21
    targetSdkVersion 33
    versionCode 1
    versionName "1.0"
  }
}

dependencies {
  implementation 'androidx.core:core-ktx:1.9.0'
  implementation 'androidx.appcompat:appcompat:1.5.1'
  implementation 'com.google.android.material:material:1.7.0'
  implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
  testImplementation 'junit:junit:4.13.2'
  androidTestImplementation 'androidx.test.ext:junit:1.1.3'
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
Note: Make sure you have Java Development Kit (JDK) installed. Android Studio includes OpenJDK by default.

Project Structure

# Android Project Structure
app/
├── manifests/
│   └── AndroidManifest.xml
├── java/
│   └── com.example.myapp
│       ├── MainActivity.kt
│       ├── models/
│       ├── viewmodels/
│       └── adapters/
├── res/
│   ├── layout/
│   │   ├── activity_main.xml
│   │   └── item_view.xml
│   ├── values/
│   │   ├── strings.xml
│   │   ├── colors.xml
│   │   └── styles.xml
│   ├── drawable/
│   ├── mipmap/
│   └── menu/
└── Gradle Scripts/
    ├── build.gradle (Project)
    └── build.gradle (Module: app)

# AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Kotlin Basics

Kotlin Syntax

// Variables and constants
var mutableVariable: String = "Hello" // Mutable
val immutableVariable: Int = 42 // Immutable (read-only)

// Null safety
var nullableString: String? = null // Nullable type
val length = nullableString?.length // Safe call
val nonNullLength = nullableString!!.length // Non-null assertion
val result = nullableString ?: "default" // Elvis operator

// Functions
fun greet(name: String): String {
    return "Hello, $name"
}

// Single-expression function
fun square(x: Int) = x * x

// Higher-order functions
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
    return operation(x, y)
}

// Lambda expression
val sum = { x: Int, y: Int -> x + y }
Note: Kotlin is now the preferred language for Android development, offering null safety, extension functions, and concise syntax.

Kotlin OOP

// Class definition
class Person(val name: String, var age: Int) {
    // Property with custom getter
    val isAdult: Boolean
        get() = age >= 18

    // Method
    fun speak() {
        println("My name is $name")
    }
}

// Data class (automatically generates equals(), hashCode(), toString())
data class User(val id: Long, val name: String, val email: String)

// Singleton object
object AppConfig {
    const val API_URL = "https://api.example.com"
    fun initialize() {
        // Initialization code
    }
}

// Companion object (like static in Java)
class MyClass {
    companion object {
        fun create(): MyClass = MyClass()
    }
}

// Extension function
fun String.addExclamation() = this + "!"
Note: Kotlin provides data classes, object declarations, extension functions, and other features that reduce boilerplate code compared to Java.

Android Components

Activities & Fragments

// Basic Activity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Initialize views
        val button = findViewById<Button>(R.id.my_button)
        button.setOnClickListener {
            // Handle click
        }
    }
}

// Basic Fragment
class MyFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_my, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // Initialize views
        view.findViewById<Button>(R.id.button).setOnClickListener {
            // Handle click
        }
    }
}

// Starting an Activity
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("key", "value")
startActivity(intent)

// Starting an Activity for result
val intent = Intent(this, SecondActivity::class.java)
startActivityForResult(intent, REQUEST_CODE)

Services & Broadcasts

// Basic Service
class MyService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        return null // For non-bound service
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // Perform tasks here
        return START_STICKY
    }
}

// Starting and stopping a service
val intent = Intent(this, MyService::class.java)
startService(intent) // To start
stopService(intent) // To stop

// Broadcast Receiver
class MyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // Handle broadcast
        if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
            // Do something after boot
        }
    }
}

// Registering broadcast receiver in AndroidManifest.xml
<receiver android:name=".MyReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
Note: Services run in the background to perform long-running operations, while Broadcast Receivers respond to system-wide announcements.

UI Design

Layouts & Views

// LinearLayout (vertical)
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="18sp" />

    <Button
        android:id="@+id/my_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />

</LinearLayout>

// ConstraintLayout
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello ConstraintLayout!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

// RecyclerView item layout
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="8dp">

    <ImageView
        android:id="@+id/item_image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:src="@drawable/ic_launcher_foreground" />

    <TextView
        android:id="@+id/item_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Item Title"
        android:textSize="16sp"
        android:layout_gravity="center_vertical"
        android:paddingStart="8dp"
        android:paddingEnd="8dp" />

</LinearLayout>

RecyclerView & Adapters

// Data class for RecyclerView items
data class Item(val id: Long, val title: String, val imageRes: Int)

// ViewHolder
class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    fun bind(item: Item) {
        itemView.findViewById<TextView>(R.id.item_title).text = item.title
        itemView.findViewById<ImageView>(R.id.item_image).setImageResource(item.imageRes)
    }
}

// Adapter
class ItemAdapter(private val items: List<Item>) : RecyclerView.Adapter<ItemViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return ItemViewHolder(view)
    }

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        holder.bind(items[position])
    }

    override fun getItemCount() = items.size
}

// Using RecyclerView in Activity/Fragment
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.layoutManager = LinearLayoutManager(this)

val items = listOf(
    Item(1, "First Item", R.drawable.ic_first),
    Item(2, "Second Item", R.drawable.ic_second),
    Item(3, "Third Item", R.drawable.ic_third)
)

recyclerView.adapter = ItemAdapter(items)
Note: RecyclerView is more efficient than ListView for displaying large datasets as it recycles item views as they scroll off-screen.

Architecture & Patterns

MVVM with LiveData

// ViewModel
class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> = _data

    fun loadData() {
        viewModelScope.launch {
            // Simulate data loading
            delay(1000)
            _data.value = "Loaded data"
        }
    }
}

// Activity/Fragment using ViewModel
class MyActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my)

        // Initialize ViewModel
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

        // Observe LiveData
        viewModel.data.observe(this) { data ->
            // Update UI with new data
            findViewById<TextView>(R.id.text_view).text = data
        }

        // Load data
        findViewById<Button>(R.id.load_button).setOnClickListener {
            viewModel.loadData()
        }
    }
}

// Repository pattern
class DataRepository {
    suspend fun fetchData(): String {
        // Simulate network call
        delay(2000)
        return "Data from repository"
    }
}

Room Database

// Entity (Table)
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    @ColumnInfo(name = "first_name") val firstName: String,
    @ColumnInfo(name = "last_name") val lastName: String,
    @ColumnInfo(name = "age") val age: Int
)

// DAO (Data Access Object)
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAll(): LiveData<List<User>>

    @Query("SELECT * FROM users WHERE id = :id")
    fun getById(id: Long): LiveData<User>

    @Insert
    suspend fun insert(user: User)

    @Update
    suspend fun update(user: User)

    @Delete
    suspend fun delete(user: User)
}

// Database
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}
Note: Room is an abstraction layer over SQLite that provides compile-time checks of SQL queries and easy integration with LiveData and RxJava.