Zum Inhalt

Android Studio Cheat Sheet

Android Studio - The Official IDE for Android Development

Android Studio ist die offizielle Integrierte Entwicklungsumgebung (IDE) für Google's Android-Betriebssystem, aufgebaut auf JetBrains' IntelliJ IDEA Software und speziell für Android-Entwicklung konzipiert.

[No text to translate]

# Windows
# 64-bit Microsoft Windows 8/10/11
# x86_64 CPU architecture; 2nd generation Intel Core or newer, or AMD CPU with support for Windows Hypervisor
# 8 GB RAM or more
# 8 GB of available disk space minimum (IDE + Android SDK + Android Emulator)
# 1280 x 800 minimum screen resolution

# macOS
# macOS 10.14 (Mojave) or higher
# ARM-based chips, or 2nd generation Intel Core or newer with support for Hypervisor.Framework
# 8 GB RAM or more
# 8 GB of available disk space minimum (IDE + Android SDK + Android Emulator)
# 1280 x 800 minimum screen resolution

# Linux
# Any 64-bit Linux distribution that supports Gnome, KDE, or Unity DE; GNU C Library (glibc) 2.31 or later
# x86_64 CPU architecture; 2nd generation Intel Core or newer, or AMD processor with support for AMD Virtualization (AMD-V) and SSSE3
# 8 GB RAM or more
# 8 GB of available disk space minimum (IDE + Android SDK + Android Emulator)
# 1280 x 800 minimum screen resolution
```[No text to translate - would need specific system requirements text]
```bash
# Download from https://developer.android.com/studio
# Run the installer and follow the setup wizard

# Verify installation
android --version
adb --version
```[No text to translate - would need specific download and install instructions]
```bash
# Open Android Studio
# Go to Tools > SDK Manager
# Install required SDK platforms and tools

# Command line SDK manager
sdkmanager --list
sdkmanager "platforms;android-33"
sdkmanager "build-tools;33.0.0"
sdkmanager "system-images;android-33;google_apis;x86_64"
```[No text to translate - would need specific SDK setup instructions]
```bash
# File > New > New Project
# Choose a template (Empty Activity, Basic Activity, etc.)
# Configure project settings:
# - Name: MyApp
# - Package name: com.example.myapp
# - Save location: /path/to/project
# - Language: Java/Kotlin
# - Minimum SDK: API 21 (Android 5.0)
```[No text to translate - would need specific project creation instructions]
```kotlin
// Empty Activity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

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

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.container, MainFragment.newInstance())
                .commitNow()
        }
    }
}

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

        val navView: BottomNavigationView = findViewById(R.id.nav_view)
        val navController = findNavController(R.id.nav_host_fragment)
        navView.setupWithNavController(navController)
    }
}
```[No text to translate - would need specific project template details]
MyApp/ ├── app/ │ ├── src/ │ │ ├── main/ │ │ │ ├── java/com/example/myapp/ │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── fragments/ │ │ │ │ ├── adapters/ │ │ │ │ ├── models/ │ │ │ │ └── utils/ │ │ │ ├── res/ │ │ │ │ ├── layout/ │ │ │ │ ├── values/ │ │ │ │ ├── drawable/ │ │ │ │ ├── mipmap/ │ │ │ │ └── menu/ │ │ │ └── AndroidManifest.xml │ │ ├── test/ │ │ └── androidTest/ │ ├── build.gradle │ └── proguard-rules.pro ├── gradle/ ├── build.gradle ├── settings.gradle └── gradle.properties ```[No text to translate - would need specific project structure description]
// 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.button)
        button.setOnClickListener {
            // Handle click
            Toast.makeText(this, "Button clicked!", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onStart() {
        super.onStart()
        // Activity is becoming visible
    }

    override fun onResume() {
        super.onResume()
        // Activity is now visible and interactive
    }

    override fun onPause() {
        super.onPause()
        // Activity is partially obscured
    }

    override fun onStop() {
        super.onStop()
        // Activity is no longer visible
    }

    override fun onDestroy() {
        super.onDestroy()
        // Activity is being destroyed
    }
}

// Activity with Intent
class SecondActivity : AppCompatActivity() {
    companion object {
        const val EXTRA_MESSAGE = "extra_message"

        fun newIntent(context: Context, message: String): Intent {
            return Intent(context, SecondActivity::class.java).apply {
                putExtra(EXTRA_MESSAGE, message)
            }
        }
    }

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

        val message = intent.getStringExtra(EXTRA_MESSAGE)
        findViewById<TextView>(R.id.textView).text = message
    }
}

// Starting an Activity
val intent = SecondActivity.newIntent(this, "Hello from MainActivity!")
startActivity(intent)

// Starting Activity for Result
private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val data = result.data?.getStringExtra("result_data")
        // Handle result
    }
}

launcher.launch(intent)
```[No text to translate - would need specific activities description]
```kotlin
// Basic Fragment
class MainFragment : Fragment() {

    companion object {
        fun newInstance() = MainFragment()
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

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

// Fragment with Arguments
class DetailFragment : Fragment() {

    companion object {
        private const val ARG_ITEM_ID = "item_id"

        fun newInstance(itemId: String) = DetailFragment().apply {
            arguments = Bundle().apply {
                putString(ARG_ITEM_ID, itemId)
            }
        }
    }

    private val itemId: String by lazy {
        arguments?.getString(ARG_ITEM_ID) ?: ""
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_detail, container, false)
    }
}

// Fragment Transaction
supportFragmentManager.beginTransaction()
    .replace(R.id.container, DetailFragment.newInstance("123"))
    .addToBackStack(null)
    .commit()
```[No text to translate - would need specific fragments description]
```xml
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    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/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Click Me" />

</LinearLayout>
```[No text to translate - would need specific linear layout details]
```xml
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>
```[No text to translate - would need specific constraint layout details]
```kotlin
// Adapter
class MyAdapter(private val items: List<String>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView = view.findViewById(R.id.textView)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_layout, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.textView.text = items[position]
    }

    override fun getItemCount() = items.size
}

// Setup RecyclerView
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = MyAdapter(listOf("Item 1", "Item 2", "Item 3"))
```[No text to translate - would need specific RecyclerView details]
```kotlin
// Fragment Adapter
class ViewPagerAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {

    override fun getItemCount(): Int = 3

    override fun createFragment(position: Int): Fragment {
        return when (position) {
            0 -> FirstFragment()
            1 -> SecondFragment()
            2 -> ThirdFragment()
            else -> FirstFragment()
        }
    }
}

// Setup ViewPager2
val viewPager = findViewById<ViewPager2>(R.id.viewPager)
viewPager.adapter = ViewPagerAdapter(this)

// With TabLayout
val tabLayout = findViewById<TabLayout>(R.id.tabLayout)
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
    tab.text = "Tab ${position + 1}"
}.attach()
```[No text to translate - would need specific ViewPager2 with Fragments details]
```xml
<!-- res/values/strings.xml -->
<resources>
    <string name="app_name">My App</string>
    <string name="hello_world">Hello World!</string>
    <string name="welcome_message">Welcome, %1$s!</string>

    <plurals name="numberOfItems">
        <item quantity="one">%d item</item>
        <item quantity="other">%d items</item>
    </plurals>
</resources>

<!-- Usage in Kotlin -->
val appName = getString(R.string.app_name)
val welcomeMessage = getString(R.string.welcome_message, "John")
val itemCount = resources.getQuantityString(R.plurals.numberOfItems, count, count)
```[No text to translate - would need specific strings resource details]
```xml
<!-- res/values/colors.xml -->
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
</resources>

<!-- Usage in XML -->
<TextView
    android:textColor="@color/purple_500"
    android:background="@color/teal_200" />

<!-- Usage in Kotlin -->
val color = ContextCompat.getColor(this, R.color.purple_500)
textView.setTextColor(color)
```[No text to translate - would need specific colors resource details]
```xml
<!-- res/values/dimens.xml -->
<resources>
    <dimen name="margin_small">8dp</dimen>
    <dimen name="margin_medium">16dp</dimen>
    <dimen name="margin_large">24dp</dimen>
    <dimen name="text_size_small">12sp</dimen>
    <dimen name="text_size_medium">16sp</dimen>
    <dimen name="text_size_large">20sp</dimen>
</resources>

<!-- Usage in XML -->
<TextView
    android:layout_margin="@dimen/margin_medium"
    android:textSize="@dimen/text_size_large" />
```[No text to translate - would need specific dimensions resource details]
```xml
<!-- res/values/styles.xml -->
<resources>
    <style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
    </style>

    <style name="CustomButton" parent="Widget.MaterialComponents.Button">
        <item name="android:textColor">@color/white</item>
        <item name="backgroundTint">@color/purple_500</item>
        <item name="cornerRadius">8dp</item>
    </style>
</resources>

<!-- Usage in XML -->
<Button
    style="@style/CustomButton"
    android:text="Custom Button" />
```[No text to translate - would need specific styles and themes details]
```kotlin
// Save data
val sharedPref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
with(sharedPref.edit()) {
    putString("username", "john_doe")
    putInt("user_id", 123)
    putBoolean("is_logged_in", true)
    apply()
}

// Read data
val username = sharedPref.getString("username", "")
val userId = sharedPref.getInt("user_id", 0)
val isLoggedIn = sharedPref.getBoolean("is_logged_in", false)
```[No text to translate - would need specific SharedPreferences details]

Note: For entries 4-20, no translation was provided as no specific text was given. If you provide the specific text for those sections, I can translate them following the same guidelines.```kotlin
// Entity
@Entity(tableName = "users")
data class User(
    @PrimaryKey val id: Int,
    val name: String,
    val email: String
)

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

    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: Int): User?

    @Insert
    suspend fun insertUser(user: User)

    @Update
    suspend fun updateUser(user: User)

    @Delete
    suspend fun deleteUser(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
            }
        }
    }
}

// Usage
class UserRepository(private val userDao: UserDao) {
    val allUsers: LiveData<List<User>> = userDao.getAllUsers()

    suspend fun insert(user: User) {
        userDao.insertUser(user)
    }
}

File Storage

// Internal storage
val filename = "myfile.txt"
val fileContents = "Hello, Android!"
openFileOutput(filename, Context.MODE_PRIVATE).use {
    it.write(fileContents.toByteArray())
}

// Read from internal storage
val content = openFileInput(filename).bufferedReader().useLines { lines ->
    lines.fold("") { some, text ->
        "$some\n$text"
    }
}

// External storage
if (isExternalStorageWritable()) {
    val file = File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "myfile.txt")
    file.writeText("Hello, External Storage!")
}

private fun isExternalStorageWritable(): Boolean {
    return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}

Networking

Retrofit

// Add dependencies to build.gradle
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

// Data model
data class Post(
    val id: Int,
    val title: String,
    val body: String,
    val userId: Int
)

// API interface
interface ApiService {
    @GET("posts")
    suspend fun getPosts(): List<Post>

    @GET("posts/{id}")
    suspend fun getPost(@Path("id") id: Int): Post

    @POST("posts")
    suspend fun createPost(@Body post: Post): Post

    @PUT("posts/{id}")
    suspend fun updatePost(@Path("id") id: Int, @Body post: Post): Post

    @DELETE("posts/{id}")
    suspend fun deletePost(@Path("id") id: Int): Response<Unit>
}

// Retrofit instance
object RetrofitInstance {
    private const val BASE_URL = "https://jsonplaceholder.typicode.com/"

    val api: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// Usage in ViewModel
class PostViewModel : ViewModel() {
    private val _posts = MutableLiveData<List<Post>>()
    val posts: LiveData<List<Post>> = _posts

    fun fetchPosts() {
        viewModelScope.launch {
            try {
                val response = RetrofitInstance.api.getPosts()
                _posts.value = response
            } catch (e: Exception) {
                // Handle error
            }
        }
    }
}

OkHttp

// Add dependency
implementation 'com.squareup.okhttp3:okhttp:4.10.0'

// Basic usage
val client = OkHttpClient()

val request = Request.Builder()
    .url("https://api.example.com/data")
    .build()

client.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        // Handle failure
    }

    override fun onResponse(call: Call, response: Response) {
        response.use {
            if (!response.isSuccessful) throw IOException("Unexpected code $response")

            val responseBody = response.body?.string()
            // Handle response
        }
    }
})

// With interceptors
val client = OkHttpClient.Builder()
    .addInterceptor { chain ->
        val request = chain.request().newBuilder()
            .addHeader("Authorization", "Bearer $token")
            .build()
        chain.proceed(request)
    }
    .build()

Testing

Unit Testing

// Add dependencies
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:4.6.1'

// Test class
class CalculatorTest {

    private lateinit var calculator: Calculator

    @Before
    fun setUp() {
        calculator = Calculator()
    }

    @Test
    fun addition_isCorrect() {
        val result = calculator.add(2, 2)
        assertEquals(4, result)
    }

    @Test
    fun division_byZero_throwsException() {
        assertThrows(ArithmeticException::class.java) {
            calculator.divide(10, 0)
        }
    }
}

// Testing with Mockito
class UserRepositoryTest {

    @Mock
    private lateinit var apiService: ApiService

    @Mock
    private lateinit var userDao: UserDao

    private lateinit var repository: UserRepository

    @Before
    fun setUp() {
        MockitoAnnotations.openMocks(this)
        repository = UserRepository(apiService, userDao)
    }

    @Test
    fun getUser_returnsUser() = runTest {
        val user = User(1, "John", "john@example.com")
        `when`(apiService.getUser(1)).thenReturn(user)

        val result = repository.getUser(1)

        assertEquals(user, result)
        verify(apiService).getUser(1)
    }
}

Instrumented Testing

// Add dependencies
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

// Test class
@RunWith(AndroidJUnit4::class)
class MainActivityTest {

    @get:Rule
    val activityRule = ActivityScenarioRule(MainActivity::class.java)

    @Test
    fun button_click_showsToast() {
        onView(withId(R.id.button))
            .perform(click())

        onView(withText("Button clicked!"))
            .inRoot(withDecorView(not(activityRule.scenario.onActivity { it.window.decorView })))
            .check(matches(isDisplayed()))
    }

    @Test
    fun recyclerView_displaysItems() {
        onView(withId(R.id.recyclerView))
            .check(matches(isDisplayed()))

        onView(withId(R.id.recyclerView))
            .perform(RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(2))

        onView(withText("Item 3"))
            .check(matches(isDisplayed()))
    }
}

Debugging

Logging

// Log levels
Log.v(TAG, "Verbose message")
Log.d(TAG, "Debug message")
Log.i(TAG, "Info message")
Log.w(TAG, "Warning message")
Log.e(TAG, "Error message")

// With exception
try {
    // Some code
} catch (e: Exception) {
    Log.e(TAG, "Error occurred", e)
}

// Timber (recommended logging library)
implementation 'com.jakewharton.timber:timber:5.0.1'

// Initialize in Application class
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            Timber.plant(Timber.DebugTree())
        }
    }
}

// Usage
Timber.d("Debug message")
Timber.e(exception, "Error occurred")

Debugging Tools

// Breakpoints
// Set breakpoints by clicking on the line number gutter
// Conditional breakpoints: right-click on breakpoint

// Debug console
// Use "Evaluate Expression" (Alt+F8) to execute code during debugging

// Layout Inspector
// Tools > Layout Inspector
// Inspect view hierarchy and properties

// Database Inspector
// View > Tool Windows > Database Inspector
// Inspect Room database contents

// Network Inspector
// View > Tool Windows > Network Inspector
// Monitor network requests and responses

Performance

Memory Profiling

// Memory Profiler
// View > Tool Windows > Profiler
// Select your app and start a memory profiling session

// Detecting memory leaks
class MainActivity : AppCompatActivity() {
    private var leakyHandler: Handler? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Bad: This creates a memory leak
        leakyHandler = Handler(Looper.getMainLooper())
        leakyHandler?.postDelayed({
            // This runnable holds a reference to the activity
        }, 60000)
    }

    override fun onDestroy() {
        super.onDestroy()
        // Good: Clean up to prevent memory leaks
        leakyHandler?.removeCallbacksAndMessages(null)
        leakyHandler = null
    }
}

// Using WeakReference
class MyHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
    private val activityRef = WeakReference(activity)

    override fun handleMessage(msg: Message) {
        val activity = activityRef.get()
        if (activity != null && !activity.isFinishing) {
            // Handle message
        }
    }
}

CPU Profiling

// CPU Profiler
// View > Tool Windows > Profiler
// Select your app and start a CPU profiling session

// Optimizing RecyclerView
class OptimizedAdapter : RecyclerView.Adapter<OptimizedAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        // Reuse view holders
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_layout, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        // Minimize work in onBindViewHolder
        holder.bind(items[position])
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val textView: TextView = itemView.findViewById(R.id.textView)

        fun bind(item: String) {
            textView.text = item
        }
    }
}

// Using DiffUtil for efficient updates
class MyDiffCallback(
    private val oldList: List<String>,
    private val newList: List<String>
) : DiffUtil.Callback() {

    override fun getOldListSize() = oldList.size
    override fun getNewListSize() = newList.size

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition] == newList[newItemPosition]
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition] == newList[newItemPosition]
    }
}

// Update adapter with DiffUtil
fun updateItems(newItems: List<String>) {
    val diffCallback = MyDiffCallback(items, newItems)
    val diffResult = DiffUtil.calculateDiff(diffCallback)
    items.clear()
    items.addAll(newItems)
    diffResult.dispatchUpdatesTo(this)
}

Build System

Gradle Configuration

// app/build.gradle
android {
    compileSdk 33

    defaultConfig {
        applicationId "com.example.myapp"
        minSdk 21
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }
    }

    productFlavors {
        free {
            dimension "version"
            applicationIdSuffix ".free"
        }
        paid {
            dimension "version"
            applicationIdSuffix ".paid"
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    buildFeatures {
        viewBinding true
        dataBinding true
    }
}

dependencies {
    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.8.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

Build Variants

# Build debug APK
./gradlew assembleDebug

# Build release APK
./gradlew assembleRelease

# Build specific flavor
./gradlew assembleFreeDebug
./gradlew assemblePaidRelease

# Install on device
./gradlew installDebug

# Run tests
./gradlew test
./gradlew connectedAndroidTest

# Clean build
./gradlew clean

Publishing

Signing Configuration

// app/build.gradle
android {
    signingConfigs {
        release {
            storeFile file('path/to/keystore.jks')
            storePassword 'store_password'
            keyAlias 'key_alias'
            keyPassword 'key_password'
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

Generate Signed APK

# Using Android Studio
# Build > Generate Signed Bundle / APK
# Choose APK or Android App Bundle
# Select keystore and enter passwords
# Choose build variant
# Click Finish

# Using command line
./gradlew assembleRelease

# Generate App Bundle (recommended for Play Store)
./gradlew bundleRelease

ProGuard/R8 Configuration

# proguard-rules.pro

# Keep model classes
-keep class com.example.myapp.model.** { *; }

# Keep Retrofit interfaces
-keep interface com.example.myapp.api.** { *; }

# Keep Gson annotations
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.google.gson.** { *; }

# Keep Parcelable classes
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}

# Remove logging in release builds
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
}

Keyboard Shortcuts

# General
Ctrl+Shift+A    # Find Action
Ctrl+Shift+N    # Find File
Ctrl+Alt+Shift+N # Find Symbol
Ctrl+E          # Recent Files
Ctrl+Shift+E    # Recent Locations

# Code Navigation
Ctrl+B          # Go to Declaration
Ctrl+Alt+B      # Go to Implementation
Ctrl+U          # Go to Super Method
Ctrl+H          # Type Hierarchy
Ctrl+Alt+H      # Call Hierarchy

# Search and Replace
Ctrl+F          # Find
Ctrl+R          # Replace
Ctrl+Shift+F    # Find in Path
Ctrl+Shift+R    # Replace in Path

Editing

# Code Generation
Alt+Insert      # Generate Code
Ctrl+O          # Override Methods
Ctrl+I          # Implement Methods
Ctrl+Alt+T      # Surround With

# Refactoring
Shift+F6        # Rename
Ctrl+Alt+M      # Extract Method
Ctrl+Alt+V      # Extract Variable
Ctrl+Alt+F      # Extract Field
Ctrl+Alt+C      # Extract Constant

# Code Formatting
Ctrl+Alt+L      # Reformat Code
Ctrl+Alt+O      # Optimize Imports
Ctrl+Shift+Up/Down # Move Line Up/Down

Debugging

# Debug Actions
F8              # Step Over
F7              # Step Into
Shift+F8        # Step Out
F9              # Resume Program
Ctrl+F8         # Toggle Breakpoint
Ctrl+Shift+F8   # View Breakpoints

# Run Actions
Shift+F10       # Run
Shift+F9        # Debug
Ctrl+F2         # Stop

Best Practices

Architecture

// MVVM Architecture
class UserViewModel(private val repository: UserRepository) : ViewModel() {
    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> = _users

    private val _loading = MutableLiveData<Boolean>()
    val loading: LiveData<Boolean> = _loading

    fun loadUsers() {
        viewModelScope.launch {
            _loading.value = true
            try {
                val userList = repository.getUsers()
                _users.value = userList
            } catch (e: Exception) {
                // Handle error
            } finally {
                _loading.value = false
            }
        }
    }
}

// Repository Pattern
class UserRepository(
    private val apiService: ApiService,
    private val userDao: UserDao
) {
    suspend fun getUsers(): List<User> {
        return try {
            val users = apiService.getUsers()
            userDao.insertUsers(users)
            users
        } catch (e: Exception) {
            userDao.getAllUsers()
        }
    }
}

// Dependency Injection with Hilt
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var userRepository: UserRepository
}

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideApiService(): ApiService {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

Performance

// Use ViewBinding instead of findViewById
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // Handle click
        }
    }
}

// Lazy initialization
class MyActivity : AppCompatActivity() {
    private val adapter by lazy { MyAdapter() }
    private val viewModel by viewModels<MyViewModel>()
}

// Use coroutines for background work
class DataRepository {
    suspend fun fetchData(): List<Data> = withContext(Dispatchers.IO) {
        // Network call or database operation
        apiService.getData()
    }
}

Security

// Network Security Config
// res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">api.example.com</domain>
    </domain-config>
</network-security-config>

// AndroidManifest.xml
<application
    android:networkSecurityConfig="@xml/network_security_config">

// Certificate Pinning
val certificatePinner = CertificatePinner.Builder()
    .add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
    .build()

val client = OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .build()

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

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

Fehlerbehebung

Häufige Probleme

# Gradle sync issues
# File > Invalidate Caches and Restart
# Delete .gradle folder and rebuild

# Build errors
./gradlew clean
./gradlew build --stacktrace

# Emulator issues
# AVD Manager > Wipe Data
# Cold Boot Now

# Memory issues
# Increase heap size in gradle.properties
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=512m

# Dependency conflicts
./gradlew app:dependencies
# Check for version conflicts and exclude transitive dependencies

Debugging-Tipps

// Use meaningful log tags
companion object {
    private const val TAG = "MainActivity"
}

// Log method entry and exit
override fun onCreate(savedInstanceState: Bundle?) {
    Log.d(TAG, "onCreate() called")
    super.onCreate(savedInstanceState)
    // ...
    Log.d(TAG, "onCreate() finished")
}

// Use assertions for debugging
assert(user != null) { "User should not be null at this point" }

// Use TODO() for unimplemented code
fun notImplementedYet() {
    TODO("This function is not implemented yet")
}

Zusammenfassung

Android Studio ist eine leistungsstarke IDE, die umfassende Tools für die Android-Entwicklung bereitstellt. Zu den Schlüsselfunktionen gehören:

  • Intelligenter Code-Editor: Erweiterte Codevervollständigung, Refactoring und Analyse
  • Visueller Layout-Editor: Drag-and-Drop-Schnittstellendesign mit Constraint-Layout
  • APK-Analyzer: Analyse von APK-Größe und -Zusammensetzung
  • Schneller Emulator: Schneller und funktionsreicher Android-Emulator
  • Instant Run: Änderungen sofort sehen ohne komplette App-Neuerstellung
  • Profiling-Tools: Speicher-, CPU- und Netzwerk-Profiler für Leistungsoptimierung
  • Testunterstützung: Unit-Testing, Instrumentiertes Testing und UI-Testing-Frameworks
  • Versionskontrolle: Integrierte Git-Unterstützung und Integration mit gängigen VCS
  • Gradle-Buildsystem: Flexibles und leistungsstarkes Build-Automatisierungssystem
  • Plugin-Ökosystem: Umfangreiche Plugin-Unterstützung für erweiterte Funktionalität

Android Studio optimiert den gesamten Android-Entwicklungs-Workflow von Projekterstellen bis zur Bereitstellung und ist damit das unverzichtbare Tool für Android-Entwickler.