说到Android开发,很多人第一反应是“好难”、“语言多”、“配置繁琐”。但说实话,我刚入行那会儿也是这样想的。直到我自己动手跑通了第一个HelloWorld,那种成就感真的没法形容——就像你第一次亲手点亮了一盏灯。今天咱们不整那些虚的,我就当你是站在门口的那个好奇小孩,我牵着你,一步一步把这扇门推开,最后还能把整个房子盖起来。

先给你吃颗定心丸:现在的Android开发环境比五年前友好太多了。Google用Kotlin作为官方首选语言,Jetpack Compose让UI开发像搭积木一样简单,Android Studio更是直接集成了模拟器、性能分析、代码检查等全套工具。你只需要一台电脑(Windows、Mac、Linux都行),就能开启这段旅程。

第一站:把工具准备好

开发Android应用,核心工具只有一个:Android Studio。它是Google官方推出的集成开发环境(IDE),基于IntelliJ IDEA,专为Android设计。别被名字吓到,它其实很智能,会自动帮你下载依赖、配置环境变量。

下载安装很简单。去androidstudio.google.cn(国内镜像,速度快)或者直接去developer.android.com。下载完安装包,一路“下一步”就行。安装时,记得勾选“Android Virtual Device”(AVD),这是后面要用到的模拟器。

安装完成后,打开Android Studio,你会看到一个欢迎界面。第一次启动,它会问你要不要导入之前的设置,直接点“Do not import settings”就行。然后它会让你配置JDK(Java开发工具包),新版Android Studio自带JDK,所以直接点“Next”跳过即可。最后,它会让你选择UI主题,喜欢深色就选“Dark”,喜欢浅色就选“Light”,看个人喜好。

点“Finish”后,你就正式进入Android Studio的主界面了。这时候,你可能会有点懵——满屏幕的按钮和菜单,从哪下手?别急,我们先创建一个项目。

第二站:第一个应用HelloWorld

在Android Studio主界面,点击“Start a new Android Studio project”。接下来的一连串选项,是新手最容易卡住的地方。我来一步步教你怎么选。

1. 选择模板 第一个界面是“Select a Project Template”。这里有很多模板:Empty Activity、Chat Application、Gallery、Navigation Drawer Activity等。对于初学者,我们选最基础的Empty Activity。点击它,然后点“Next”。

2. 配置项目 接下来是“Configure your project”界面,这里有几个关键字段:

  • Name: 项目名称,我建议你写MyFirstApp,简单直接。
  • Package name: 包名,这是应用的唯一标识。格式通常是com.你的名字.应用名,比如com.agnes.myfirstapp。注意,包名一旦确定就不能改,所以想好点。用反域名格式最安全,比如com.example.myfirstapp
  • Save location: 项目保存路径,选一个你记得住的地方,比如D:\AndroidProjects\MyFirstApp
  • Language: 语言选择。强烈建议选择Kotlin。Java虽然也能用,但Kotlin是Google力推的,代码更简洁,空指针安全,是现代Android开发的事实标准。
  • Minimum SDK: 最低SDK版本。这是指你的应用能支持多老的Android版本。新手建议选API 24: Android 7.0 (Nougat)。这个版本覆盖了95%以上的活跃设备,而且API 24以下的系统已经非常少了,没必要为了那几百万用户增加复杂度。

选好后,点“Finish”。Android Studio会开始下载必要的依赖,这个过程可能需要几分钟,取决于你的网络。如果卡住,可以试试切换镜像源或者重启。

3. 理解项目结构 项目创建成功后,你会看到左侧的“Project”面板。默认显示的是“Android”视图,但这对于新手不太友好,因为很多文件被折叠了。建议切换到“Project Files”视图,这样你能看到真实的文件结构。

关键目录和文件如下:

MyFirstApp/
├── app/
│   ├── src/main/
│   │   ├── java/com/agnes/myfirstapp/
│   │   │   └── MainActivity.kt          // 主Activity,应用的入口
│   │   ├── res/
│   │   │   ├── layout/
│   │   │   │   └── activity_main.xml    // UI布局文件(XML方式)
│   │   │   ├── values/
│   │   │   │   ├── colors.xml           // 颜色定义
│   │   │   │   ├── strings.xml          // 字符串资源
│   │   │   │   └── themes.xml           // 主题样式
│   │   │   └── mipmap-*                 // 图标资源
│   │   ├── AndroidManifest.xml          // 应用清单,配置权限、组件等
│   │   └── res/drawable/                // 可绘制资源(图片、形状等)
│   ├── build.gradle.kts                 // 模块级构建脚本,定义依赖
│   └── proguard-rules.pro               // 代码混淆规则
├── build.gradle.kts                     // 项目级构建脚本
├── settings.gradle.kts                  // 模块设置
└── gradle/                              // Gradle Wrapper配置

别慌,我们一个一个看。

AndroidManifest.xml 是应用的“身份证”。它告诉系统这个应用叫什么名字、需要哪些权限、有哪些Activity等。打开它,你会看到:

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyFirstApp">
        <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>

重点看<activity>部分。android:name=".MainActivity"指向我们的主Activity。<intent-filter>里的MAINLAUNCHER意味着这个Activity是应用的入口,点击图标时启动的就是它。

MainActivity.kt 是应用的“大脑”。打开它,你会看到:

package com.agnes.myfirstapp

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.agnes.myfirstapp.ui.theme.MyFirstAppTheme

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyFirstAppTheme {
                Greeting("Android")
            }
        }
    }

    @Composable
    fun Greeting(name: String) {
        Text(text = "Hello $name!")
    }

    @Preview(showBackground = true)
    @Composable
    fun GreetingPreview() {
        MyFirstAppTheme {
            Greeting("Android")
        }
    }
}

这段代码用到了Jetpack Compose,这是Google推出的现代化UI toolkit。如果你之前做过iOS开发,会觉得它很像SwiftUI;如果你做过Web开发,会觉得它有点像React。核心思想是:UI是状态的函数,状态变化时,UI自动刷新。

setContent 是Composable函数的入口,告诉系统“这个Activity用Compose来构建UI”。Greeting 是一个简单的Composable函数,它接收一个字符串参数,然后显示出来。@Preview 注解让这段代码能在Android Studio的预览窗口中直接渲染,不用跑模拟器就能看到效果。

activity_main.xml 是传统XML布局文件。如果你选的是“Empty Activity (XML)”,你会看到这个文件。但新版模板默认用Compose,所以这个文件可能不存在,或者已经被弃用。我们这里先讲Compose,因为它是未来。

4. 运行你的第一个应用 现在,我们要让这个应用跑起来。有两种方式:模拟器真机

用模拟器: 在Android Studio顶部工具栏,找到设备选择下拉框(默认可能是“Pixel 3a”),点击它。如果列表为空,点“Create New Device”。选择“Phone”类别,挑一个你喜欢的手机型号(比如Pixel 6),点“Next”。然后选择系统镜像,推荐选“API 34: Android 14.0”的“x86_64”镜像(如果你用Intel/AMD CPU)或“arm64-v8a”镜像(如果你用Apple Silicon M系列芯片)。下载完镜像后,点“Finish”。

模拟器创建好后,点工具栏的“Run”按钮(绿色三角形),或者按 Shift + F10。Android Studio会编译项目,启动模拟器,然后安装并运行你的应用。

几秒钟后,模拟器屏幕亮起,你会看到一行字:“Hello Android!”。这就是你的第一个Android应用!

用真机: 如果你想用真机测试,步骤稍微多一步:

  1. 打开手机“设置” -> “关于手机”,连续点击“版本号”7次,开启“开发者选项”。
  2. 返回“设置” -> “系统” -> “开发者选项”,开启“USB调试”。
  3. 用数据线连接手机和电脑。首次连接时,手机会弹出“允许USB调试吗?”的提示,勾选“始终允许”,点“确定”。
  4. 在Android Studio的设备选择下拉框中,你应该能看到你的手机型号。选中它,点“Run”。

真机运行和模拟器效果一样,但更流畅,也能测试真实性能。

第三站:理解核心概念

HelloWorld跑通了,但你可能还是一头雾水:这到底是怎么工作的?为什么有点按钮就能跳页面?为什么UI能自动更新?我们来拆解几个核心概念。

1. Activity:屏幕的容器 Activity是Android应用的“页面”。每个Activity对应一个屏幕,用户看到的界面、进行的交互,都发生在一个Activity里。一个应用可以有多个Activity,比如登录页、主页、设置页,每个都是一个Activity。

Activity的生命周期是Android开发的基础。当你创建、启动、暂停、恢复、销毁一个Activity时,系统会回调一系列方法:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Activity被创建时调用,只调用一次
        // 在这里初始化UI、绑定数据
        setContentView(R.layout.activity_main) // XML方式
        // 或 setContent { ... } // Compose方式
    }

    override fun onStart() {
        super.onStart()
        // Activity即将可见时调用
    }

    override fun onResume() {
        super.onResume()
        // Activity获得焦点,用户可与它交互时调用
    }

    override fun onPause() {
        super.onPause()
        // 系统准备启动另一个Activity时调用
    }

    override fun onStop() {
        super.onStop()
        // Activity完全不可见时调用
    }

    override fun onDestroy() {
        super.onDestroy()
        // Activity被销毁前调用
    }
}

新手常犯的错误是在onDestroy里做耗时操作,或者在onPause里保存重要数据(因为系统可能不一定会调用onPause,而是直接杀进程)。正确的做法是用onSaveInstanceState来保存瞬态UI状态,用ViewModel来保存业务数据。

2. Intent:组件间的信使 Intent是Android中用于在组件之间传递消息的对象。它可以启动Activity、启动Service、发送Broadcast。

启动另一个Activity:

// 显式Intent:指定目标Activity
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("key", "value") // 传递数据
startActivity(intent)

// 隐式Intent:不指定目标,让系统匹配
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.example.com")
startActivity(intent)

在第二个Activity中接收数据:

class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val value = intent.getStringExtra("key") ?: "default"
        // 使用value
    }
}

Intent还有startActivityForResult(已废弃,用Activity Result API替代)和sendBroadcast等用法,但新手阶段,掌握显式和隐式Intent就够用了。

3. View和Compose:UI的两种范式 传统Android UI用XML布局文件定义,代码逻辑写在Kotlin/Java里。这种方式直观,但容易碎片化——UI和业务逻辑分离,更新时需手动刷新。

Jetpack Compose是声明式UI框架,UI和逻辑写在同一个地方,用Kotlin代码描述UI结构。比如,一个简单的按钮:

Button(onClick = { /* 点击处理 */ }) {
    Text("Click me")
}

是不是很像HTML?但Compose更强大,它能自动处理状态更新。当状态变化时,Compose只会重新绘制发生变化的部分,而不是整个页面。

对于新手,我建议先学Compose,因为它是趋势,而且代码更简洁。但也要了解XML,因为很多老项目还在用,而且理解底层原理有助于调试。

4. ViewModel:数据的守护者 ViewModel是Android Architecture Components的一部分,用于存储和管理UI相关的数据。它的核心特点是:生命周期感知。当Activity或Fragment因配置变更(如屏幕旋转)而重建时,ViewModel中的数据不会丢失。

用法:

class MyViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> = _count

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }
}

class MainActivity : AppCompatActivity() {
    private val viewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyFirstAppTheme {
                val count by viewModel.count.observeAsState()
                Column(modifier = Modifier.padding(16.dp)) {
                    Text(text = "Count: $count")
                    Button(onClick = { viewModel.increment() }) {
                        Text("Increment")
                    }
                }
            }
        }
    }
}

这里,_countMutableLiveDatacountLiveDataobserveAsState是Compose中观察LiveData的方式,当数据变化时,UI自动刷新。by viewModels()是委托属性,自动创建ViewModel实例。

5. Repository:数据的提供者 Repository是数据访问的抽象层,它整合多个数据源(本地数据库、网络API等),为ViewModel提供统一的数据接口。这样,ViewModel不需要关心数据从哪里来,只需要调用Repository的方法。

class MyRepository {
    suspend fun fetchData(): Result<Data> {
        // 从网络或数据库获取数据
    }
}

第四站:搭建一个完整的小项目

光说不练假把式。我们来做一个稍微有点实际意义的小应用:待办事项列表(Todo List)。这个应用会用到Activity、ViewModel、Compose、本地存储等核心概念。

需求分析:

  1. 显示待办事项列表。
  2. 能添加新事项。
  3. 能删除已事项。
  4. 能标记事项为完成/未完成。
  5. 数据持久化,重启应用后数据不丢失。

步骤1:创建项目 按照前面的方法,创建一个新的Kotlin Compose项目,命名为TodoApp。包名用com.agnes.todoapp,最低SDK选24。

步骤2:设计数据模型data包下创建TodoItem.kt

data class TodoItem(
    val id: Long,
    val title: String,
    val completed: Boolean = false
)

步骤3:创建本地存储 用Room数据库,这是Google推荐的本地存储方案。先在build.gradle.kts中添加依赖:

dependencies {
    val roomVersion = "2.6.1"
    implementation("androidx.room:room-runtime:$roomVersion")
    implementation("androidx.room:room-ktx:$roomVersion")
    ksp("androidx.room:room-compiler:$roomVersion") // 需要安装KSP插件
}

然后创建实体类:

@Entity(tableName = "todo_items")
data class TodoItemEntity(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    val title: String,
    val completed: Boolean = false
)

创建DAO(数据访问对象):

@Dao
interface TodoDao {
    @Query("SELECT * FROM todo_items ORDER BY id DESC")
    fun getAllTodos(): Flow<List<TodoItemEntity>>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(todo: TodoItemEntity)

    @Update
    suspend fun update(todo: TodoItemEntity)

    @Delete
    suspend fun delete(todo: TodoItemEntity)
}

创建Database类:

@Database(entities = [TodoItemEntity::class], version = 1, exportSchema = false)
abstract class TodoDatabase : RoomDatabase() {
    abstract fun todoDao(): TodoDao

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

        fun getDatabase(context: Context): TodoDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    TodoDatabase::class.java,
                    "todo_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

步骤4:创建Repositoryrepository包下创建TodoRepository.kt

”`kotlin class TodoRepository(private val dao: Todo