Android Studio

Development Tool

Android Studio

Overview

Android Studio is an Android-specific integrated development environment (IDE) developed by Google. Specialized for Android application development, it is used by Android developers worldwide as the official development tool.

Details

Android Studio is an Android-specific IDE released by Google in 2013, based on IntelliJ IDEA. The current latest version is Android Studio Koala (2024.1.1), based on the IntelliJ 2024.1 platform. It provides an environment optimized for Android development, including complete support for Java and Kotlin, deep integration with Android SDK, fast emulator, and rich debugging tools.

The 2024 version introduces numerous important new features. Generative AI integration enables AI-powered app development using the Gemini API template with Google AI SDK. Enhanced Running Devices window allows simultaneous display of multiple devices, along with new terminal and "sticky lines" functionality in the editor.

Android Studio's greatest strength lies in its comprehensive feature set specialized for Android development. It covers the entire Android development workflow with smart code editor, automatic code completion, real-time auditing, Gradle integration, layout editor, profiling tools, Firebase integration, and Google Play Console connectivity. The AVD emulator achieves fast startup within 6 seconds, streamlining testing across various devices.

Advantages and Disadvantages

Advantages

  • Official Android IDE: Official support from Google with complete Android SDK integration
  • Lightweight and Fast: Lightweight design enables fast startup and memory-efficient operation
  • Intuitive UI: User-friendly interface accessible from beginners to advanced users
  • Rich Library Support: More library integration than other IDEs
  • Fast Emulator: Startup within 6 seconds enables diverse device testing
  • Team Development Support: Excellent support for code sharing, change merging, and version control
  • Completely Free: All features available at no cost

Disadvantages

  • High System Requirements: Requires minimum 4GB RAM and 64-bit OS
  • Installation Time: Initial installation takes considerable time
  • Heavy RAM Consumption: Uses large amounts of memory, making it slow on older PCs
  • Plugin Impact: Performance degradation when using many plugins
  • Android Only: Cannot be used for non-Android development
  • Learning Cost: Initial mastery takes time due to rich feature set
  • Build Time: Long build times for large projects

Key Links

Code Examples

Project Configuration Example

// MainActivity.kt - Basic Kotlin Activity Structure
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.myapp.ui.theme.MyAppTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    MainScreen()
                }
            }
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(viewModel: MainViewModel = viewModel()) {
    var text by remember { mutableStateOf("") }
    val items by viewModel.items.collectAsState()
    
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        TopAppBar(
            title = { Text("My Android App") }
        )
        
        Spacer(modifier = Modifier.height(16.dp))
        
        OutlinedTextField(
            value = text,
            onValueChange = { text = it },
            label = { Text("Enter item") },
            modifier = Modifier.fillMaxWidth()
        )
        
        Spacer(modifier = Modifier.height(8.dp))
        
        Button(
            onClick = {
                if (text.isNotEmpty()) {
                    viewModel.addItem(text)
                    text = ""
                }
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Add Item")
        }
        
        Spacer(modifier = Modifier.height(16.dp))
        
        LazyColumn {
            items(items) { item ->
                ItemCard(
                    item = item,
                    onDelete = { viewModel.removeItem(it) }
                )
            }
        }
    }
}

@Composable
fun ItemCard(
    item: String,
    onDelete: (String) -> Unit
) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 4.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = item,
                style = MaterialTheme.typography.bodyLarge
            )
            IconButton(onClick = { onDelete(item) }) {
                Icon(
                    imageVector = Icons.Default.Delete,
                    contentDescription = "Delete"
                )
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MyAppTheme {
        MainScreen()
    }
}

Gradle Build Configuration

// build.gradle.kts (Module: app)
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("kotlin-kapt")
    id("dagger.hilt.android.plugin")
    id("androidx.navigation.safeargs.kotlin")
}

android {
    namespace = "com.example.myapp"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.myapp"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        debug {
            isDebuggable = true
            applicationIdSuffix = ".debug"
        }
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }

    buildFeatures {
        compose = true
        viewBinding = true
        dataBinding = true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.8"
    }

    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {
    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
    implementation("androidx.activity:activity-compose:1.8.2")
    implementation("androidx.compose.ui:ui:1.5.8")
    implementation("androidx.compose.ui:ui-tooling-preview:1.5.8")
    implementation("androidx.compose.material3:material3:1.1.2")
    implementation("androidx.navigation:navigation-compose:2.7.6")
    
    // Hilt Dependency Injection
    implementation("com.google.dagger:hilt-android:2.48")
    kapt("com.google.dagger:hilt-compiler:2.48")
    
    // Network
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
    
    // Database
    implementation("androidx.room:room-runtime:2.6.1")
    implementation("androidx.room:room-ktx:2.6.1")
    kapt("androidx.room:room-compiler:2.6.1")
    
    // Testing
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.5.8")
    debugImplementation("androidx.compose.ui:ui-tooling:1.5.8")
    debugImplementation("androidx.compose.ui:ui-test-manifest:1.5.8")
}

ViewModel Example

// MainViewModel.kt - MVVM Architecture
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class MainViewModel @Inject constructor(
    private val repository: ItemRepository
) : ViewModel() {
    
    private val _items = MutableStateFlow<List<String>>(emptyList())
    val items: StateFlow<List<String>> = _items.asStateFlow()
    
    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
    
    private val _error = MutableStateFlow<String?>(null)
    val error: StateFlow<String?> = _error.asStateFlow()
    
    init {
        loadItems()
    }
    
    fun addItem(item: String) {
        viewModelScope.launch {
            try {
                _isLoading.value = true
                repository.addItem(item)
                loadItems()
            } catch (e: Exception) {
                _error.value = e.message
            } finally {
                _isLoading.value = false
            }
        }
    }
    
    fun removeItem(item: String) {
        viewModelScope.launch {
            try {
                repository.removeItem(item)
                loadItems()
            } catch (e: Exception) {
                _error.value = e.message
            }
        }
    }
    
    private fun loadItems() {
        viewModelScope.launch {
            try {
                _isLoading.value = true
                _items.value = repository.getAllItems()
                _error.value = null
            } catch (e: Exception) {
                _error.value = e.message
            } finally {
                _isLoading.value = false
            }
        }
    }
}

Room Database Configuration

// AppDatabase.kt - Room Database Configuration
import androidx.room.*
import androidx.room.Room

@Entity(tableName = "items")
data class ItemEntity(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    @ColumnInfo(name = "name")
    val name: String,
    @ColumnInfo(name = "created_at")
    val createdAt: Long = System.currentTimeMillis()
)

@Dao
interface ItemDao {
    @Query("SELECT * FROM items ORDER BY created_at DESC")
    suspend fun getAllItems(): List<ItemEntity>
    
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertItem(item: ItemEntity)
    
    @Query("DELETE FROM items WHERE name = :name")
    suspend fun deleteItemByName(name: String)
    
    @Query("DELETE FROM items")
    suspend fun deleteAllItems()
}

@Database(
    entities = [ItemEntity::class],
    version = 1,
    exportSchema = false
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun itemDao(): ItemDao
    
    companion object {
        const val DATABASE_NAME = "app_database"
    }
}

// Database Provider (Hilt)
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
    
    @Provides
    @Singleton
    fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
        return Room.databaseBuilder(
            context,
            AppDatabase::class.java,
            AppDatabase.DATABASE_NAME
        ).build()
    }
    
    @Provides
    fun provideItemDao(database: AppDatabase): ItemDao {
        return database.itemDao()
    }
}

Android Manifest Configuration

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

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApp"
        android:usesCleartextTraffic="true"
        tools:targetApi="31">

        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.MyApp"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".DetailActivity"
            android:exported="false"
            android:parentActivityName=".MainActivity" />

        <service
            android:name=".BackgroundService"
            android:enabled="true"
            android:exported="false" />

        <receiver
            android:name=".NotificationReceiver"
            android:enabled="true"
            android:exported="false" />
        
    </application>
</manifest>

Unit Test Example

// MainViewModelTest.kt - Unit Testing
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.*
import org.junit.*
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.junit.MockitoJUnitRunner

@ExperimentalCoroutinesApi
@RunWith(MockitoJUnitRunner::class)
class MainViewModelTest {
    
    @get:Rule
    val instantTaskExecutorRule = InstantTaskExecutorRule()
    
    private val testDispatcher = UnconfinedTestDispatcher()
    
    @Mock
    private lateinit var repository: ItemRepository
    
    private lateinit var viewModel: MainViewModel
    
    @Before
    fun setup() {
        Dispatchers.setMain(testDispatcher)
        viewModel = MainViewModel(repository)
    }
    
    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }
    
    @Test
    fun `addItem should update items list`() = runTest {
        // Given
        val newItem = "Test Item"
        val expectedItems = listOf(newItem)
        `when`(repository.getAllItems()).thenReturn(expectedItems)
        
        // When
        viewModel.addItem(newItem)
        
        // Then
        verify(repository).addItem(newItem)
        verify(repository, times(2)).getAllItems() // During initialization and after addItem
        assert(viewModel.items.value == expectedItems)
    }
    
    @Test
    fun `removeItem should remove item from list`() = runTest {
        // Given
        val itemToRemove = "Item to remove"
        val updatedItems = emptyList<String>()
        `when`(repository.getAllItems()).thenReturn(updatedItems)
        
        // When
        viewModel.removeItem(itemToRemove)
        
        // Then
        verify(repository).removeItem(itemToRemove)
        verify(repository, times(2)).getAllItems()
        assert(viewModel.items.value == updatedItems)
    }
}