引言
Android作为全球最流行的移动操作系统,拥有超过25亿活跃设备。掌握Android开发技能对于程序员来说至关重要。本指南将通过实战案例,从基础到高级,系统性地讲解Android开发的核心知识和技巧。
第一部分:Android开发环境搭建
1.1 开发工具选择
Android开发主要使用Android Studio,这是Google官方推荐的集成开发环境(IDE)。它基于IntelliJ IDEA,提供了代码编辑、调试、性能分析等完整功能。
安装步骤:
- 访问Android开发者官网
- 下载对应操作系统的安装包
- 运行安装程序,按照向导完成安装
- 首次启动时,会引导下载Android SDK和相关组件
1.2 创建第一个项目
在Android Studio中创建新项目:
// 选择Empty Activity模板
// 项目结构:
// - app/
// - src/
// - main/
// - java/ (或 kotlin/)
// - com.example.myapp/
// - MainActivity.kt
// - res/
// - layout/
// - activity_main.xml
// - values/
// - strings.xml
// - drawable/
// - AndroidManifest.xml
// - build.gradle
// - build.gradle (项目级)
关键文件说明:
AndroidManifest.xml:应用的配置文件,声明Activity、权限等MainActivity.kt:主Activity,应用入口activity_main.xml:界面布局文件build.gradle:项目构建配置
第二部分:Android基础组件
2.1 Activity生命周期
Activity是Android应用的基本组件,负责管理用户界面和交互。理解生命周期是开发的关键。
class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d(TAG, "onCreate: Activity被创建")
}
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart: Activity开始可见")
}
override fun onResume() {
super.onResume()
Log.d(TAG, "onResume: Activity获得焦点")
}
override fun onPause() {
super.onPause()
Log.d(TAG, "onPause: Activity失去焦点")
}
override fun onStop() {
super.onStop()
Log.d(TAG, "onStop: Activity不可见")
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy: Activity被销毁")
}
}
生命周期图解:
onCreate() → onStart() → onResume() → [运行状态] → onPause() → onStop() → onDestroy()
2.2 Fragment使用
Fragment是可重用的UI模块,可以在Activity中动态添加和移除。
// 创建Fragment
class MyFragment : Fragment() {
private lateinit var textView: TextView
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 加载布局
val view = inflater.inflate(R.layout.fragment_my, container, false)
textView = view.findViewById(R.id.textView)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 设置数据
textView.text = "Hello from Fragment!"
}
}
// 在Activity中使用Fragment
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 动态添加Fragment
val fragment = MyFragment()
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit()
}
}
2.3 Intent和数据传递
Intent是Android组件间通信的桥梁,可以启动Activity、Service等。
// 显式Intent启动Activity
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("username", "张三")
intent.putExtra("age", 25)
startActivity(intent)
// 隐式Intent
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com"))
startActivity(intent)
// 接收数据
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val username = intent.getStringExtra("username")
val age = intent.getIntExtra("age", 0)
val textView = findViewById<TextView>(R.id.textView)
textView.text = "欢迎 $username,你今年 $age 岁"
}
}
第三部分:UI开发与布局
3.1 常用布局
Android提供多种布局管理器,用于组织UI元素。
LinearLayout(线性布局):
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="标题"
android:textSize="20sp"
android:gravity="center"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击我"/>
</LinearLayout>
ConstraintLayout(约束布局):
<androidx.constraintlayout.widget.ConstraintLayout
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_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK"
app:layout_constraintTop_toBottomOf="@id/textView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
3.2 RecyclerView使用
RecyclerView是现代Android开发中用于显示列表数据的首选组件。
// 1. 创建数据模型
data class User(
val id: Int,
val name: String,
val email: String
)
// 2. 创建Adapter
class UserAdapter(private val userList: List<User>) :
RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val nameTextView: TextView = itemView.findViewById(R.id.nameTextView)
val emailTextView: TextView = itemView.findViewById(R.id.emailTextView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_user, parent, false)
return UserViewHolder(view)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val user = userList[position]
holder.nameTextView.text = user.name
holder.emailTextView.text = user.email
}
override fun getItemCount(): Int = userList.size
}
// 3. 在Activity中使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
val userList = listOf(
User(1, "张三", "zhangsan@example.com"),
User(2, "李四", "lisi@example.com"),
User(3, "王五", "wangwu@example.com")
)
val adapter = UserAdapter(userList)
recyclerView.adapter = adapter
}
}
3.3 自定义View
当标准组件无法满足需求时,可以创建自定义View。
// 创建自定义View:圆形进度条
class CircleProgressView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var progress = 0
private var maxProgress = 100
private var progressColor = Color.BLUE
private var backgroundColor = Color.LTGRAY
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val rectF = RectF()
init {
// 读取自定义属性
val typedArray = context.obtainStyledAttributes(
attrs,
R.styleable.CircleProgressView,
defStyleAttr,
0
)
progress = typedArray.getInteger(R.styleable.CircleProgressView_progress, 0)
maxProgress = typedArray.getInteger(R.styleable.CircleProgressView_maxProgress, 100)
progressColor = typedArray.getColor(R.styleable.CircleProgressView_progressColor, Color.BLUE)
backgroundColor = typedArray.getColor(R.styleable.CircleProgressView_backgroundColor, Color.LTGRAY)
typedArray.recycle()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val centerX = width / 2f
val centerY = height / 2f
val radius = min(centerX, centerY) - paint.strokeWidth / 2
// 绘制背景圆
paint.color = backgroundColor
paint.style = Paint.Style.STROKE
paint.strokeWidth = 20f
canvas.drawCircle(centerX, centerY, radius, paint)
// 绘制进度圆弧
paint.color = progressColor
val sweepAngle = (progress.toFloat() / maxProgress) * 360f
rectF.set(
centerX - radius,
centerY - radius,
centerX + radius,
centerY + radius
)
canvas.drawArc(rectF, -90f, sweepAngle, false, paint)
// 绘制中心文字
paint.style = Paint.Style.FILL
paint.textSize = 40f
paint.textAlign = Paint.Align.CENTER
val text = "$progress%"
canvas.drawText(text, centerX, centerY + paint.textSize / 3, paint)
}
fun setProgress(newProgress: Int) {
progress = newProgress.coerceIn(0, maxProgress)
invalidate() // 触发重绘
}
}
第四部分:数据存储与网络
4.1 SharedPreferences存储
SharedPreferences是Android中轻量级的键值对存储方案。
class SharedPreferencesManager(context: Context) {
private val prefs = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
companion object {
private const val KEY_USER_NAME = "user_name"
private const val KEY_USER_AGE = "user_age"
private const val KEY_IS_LOGGED_IN = "is_logged_in"
}
fun saveUserName(name: String) {
prefs.edit().putString(KEY_USER_NAME, name).apply()
}
fun getUserName(): String {
return prefs.getString(KEY_USER_NAME, "") ?: ""
}
fun saveUserAge(age: Int) {
prefs.edit().putInt(KEY_USER_AGE, age).apply()
}
fun getUserAge(): Int {
return prefs.getInt(KEY_USER_AGE, 0)
}
fun setLoggedIn(isLoggedIn: Boolean) {
prefs.edit().putBoolean(KEY_IS_LOGGED_IN, isLoggedIn).apply()
}
fun isLoggedIn(): Boolean {
return prefs.getBoolean(KEY_IS_LOGGED_IN, false)
}
fun clearAll() {
prefs.edit().clear().apply()
}
}
4.2 Room数据库
Room是Google推荐的SQLite封装库,提供了编译时SQL验证和类型安全。
// 1. 定义实体类
@Entity(tableName = "users")
data class UserEntity(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val name: String,
val email: String,
@ColumnInfo(name = "created_at")
val createdAt: Long = System.currentTimeMillis()
)
// 2. 定义DAO(数据访问对象)
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAll(): List<UserEntity>
@Query("SELECT * FROM users WHERE id = :userId")
fun getUserById(userId: Int): UserEntity?
@Insert
fun insert(user: UserEntity)
@Update
fun update(user: UserEntity)
@Delete
fun delete(user: UserEntity)
@Query("DELETE FROM users")
fun deleteAll()
}
// 3. 定义数据库
@Database(entities = [UserEntity::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
}
}
}
}
// 4. 在ViewModel中使用
class UserViewModel(application: Application) : AndroidViewModel(application) {
private val userDao = AppDatabase.getDatabase(application).userDao()
val allUsers: LiveData<List<UserEntity>> = userDao.getAll().asLiveData()
fun insert(user: UserEntity) {
viewModelScope.launch(Dispatchers.IO) {
userDao.insert(user)
}
}
}
4.3 Retrofit网络请求
Retrofit是目前最流行的Android网络请求库,基于OkHttp。
// 1. 定义API接口
interface ApiService {
@GET("users/{id}")
suspend fun getUserById(@Path("id") userId: Int): Response<User>
@POST("users")
suspend fun createUser(@Body user: User): Response<User>
@GET("posts")
suspend fun getPosts(): List<Post>
}
// 2. 创建Retrofit实例
object RetrofitClient {
private const val BASE_URL = "https://jsonplaceholder.typicode.com/"
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
val instance: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
// 3. 在协程中使用
class UserRepository {
suspend fun getUser(userId: Int): Result<User> {
return try {
val response = RetrofitClient.instance.getUserById(userId)
if (response.isSuccessful) {
response.body()?.let {
Result.success(it)
} ?: Result.failure(Exception("Empty response"))
} else {
Result.failure(Exception("HTTP error: ${response.code()}"))
}
} catch (e: Exception) {
Result.failure(e)
}
}
}
// 4. 在ViewModel中调用
class UserViewModel : ViewModel() {
private val repository = UserRepository()
private val _userState = MutableStateFlow<UserState>(UserState.Loading)
val userState: StateFlow<UserState> = _userState
fun fetchUser(userId: Int) {
viewModelScope.launch {
_userState.value = UserState.Loading
val result = repository.getUser(userId)
_userState.value = when (result) {
is Result.Success -> UserState.Success(result.data)
is Result.Failure -> UserState.Error(result.exception.message ?: "Unknown error")
}
}
}
}
sealed class UserState {
object Loading : UserState()
data class Success(val user: User) : UserState()
data class Error(val message: String) : UserState()
}
第五部分:高级主题
5.1 Jetpack Compose
Jetpack Compose是Android的现代UI工具包,使用Kotlin声明式编程。
// 1. 基本组件
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
// 2. 布局组件
@Composable
fun UserProfile(user: User) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
text = user.name,
style = MaterialTheme.typography.h4,
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = user.email,
style = MaterialTheme.typography.body1,
color = Color.Gray
)
Button(
onClick = { /* 处理点击 */ },
modifier = Modifier.padding(top = 16.dp)
) {
Text("编辑资料")
}
}
}
// 3. 状态管理
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = "计数: $count",
style = MaterialTheme.typography.h5,
modifier = Modifier.padding(16.dp)
)
Row(
modifier = Modifier.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Button(onClick = { count++ }) {
Text("增加")
}
Button(onClick = { count-- }) {
Text("减少")
}
Button(onClick = { count = 0 }) {
Text("重置")
}
}
}
}
// 4. 列表显示
@Composable
fun UserList(users: List<User>) {
LazyColumn {
items(users) { user ->
UserProfile(user = user)
Divider()
}
}
}
// 5. 状态提升(State Hoisting)
@Composable
fun EditableText(
text: String,
onTextChange: (String) -> Unit
) {
TextField(
value = text,
onValueChange = onTextChange,
label = { Text("输入文本") },
modifier = Modifier.fillMaxWidth()
)
}
@Composable
fun ParentComponent() {
var text by remember { mutableStateOf("") }
Column {
EditableText(
text = text,
onTextChange = { newText -> text = newText }
)
Text("当前文本: $text")
}
}
5.2 依赖注入(Dagger Hilt)
依赖注入是管理复杂应用依赖关系的优秀模式。
// 1. 定义模块
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideApiService(): ApiService {
return RetrofitClient.instance
}
@Provides
@Singleton
fun provideUserRepository(apiService: ApiService): UserRepository {
return UserRepository(apiService)
}
}
// 2. 定义Repository
class UserRepository @Inject constructor(
private val apiService: ApiService
) {
suspend fun getUser(userId: Int): User {
return apiService.getUserById(userId)
}
}
// 3. 在ViewModel中使用
@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _user = MutableStateFlow<User?>(null)
val user: StateFlow<User?> = _user
fun loadUser(userId: Int) {
viewModelScope.launch {
_user.value = userRepository.getUser(userId)
}
}
}
// 4. 在Activity中使用
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var userRepository: UserRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 使用注入的依赖
lifecycleScope.launch {
val user = userRepository.getUser(1)
// 处理用户数据
}
}
}
5.3 性能优化
5.3.1 内存泄漏检测
// 1. 使用LeakCanary检测内存泄漏
// 在build.gradle中添加依赖
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
// 2. 避免常见内存泄漏模式
class MyActivity : AppCompatActivity() {
private val handler = Handler(Looper.getMainLooper())
private val runnable = object : Runnable {
override fun run() {
// 避免持有Activity引用
// 使用弱引用或静态内部类
handler.postDelayed(this, 1000)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 错误做法:持有Activity引用
// handler.postDelayed(runnable, 1000)
// 正确做法:使用弱引用
val weakActivity = WeakReference(this)
handler.postDelayed(object : Runnable {
override fun run() {
weakActivity.get()?.let { activity ->
// 使用activity
}
handler.postDelayed(this, 1000)
}
}, 1000)
}
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacks(runnable)
}
}
5.3.2 布局优化
// 1. 使用ConstraintLayout减少嵌套
// 2. 使用ViewStub延迟加载
// 3. 使用RecyclerView的优化技巧
// RecyclerView优化示例
class OptimizedAdapter : RecyclerView.Adapter<OptimizedAdapter.ViewHolder>() {
// 使用DiffUtil进行高效更新
private val diffCallback = object : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem == newItem
}
}
private val differ = AsyncListDiffer(this, diffCallback)
fun submitList(list: List<User>) {
differ.submitList(list)
}
override fun getItemCount(): Int = differ.currentList.size
// 其他方法...
}
第六部分:实战项目案例
6.1 天气应用开发
6.1.1 项目结构
WeatherApp/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/example/weatherapp/
│ │ │ │ ├── data/
│ │ │ │ │ ├── model/
│ │ │ │ │ │ ├── Weather.kt
│ │ │ │ │ │ └── Forecast.kt
│ │ │ │ │ ├── repository/
│ │ │ │ │ │ └── WeatherRepository.kt
│ │ │ │ │ └── api/
│ │ │ │ │ └── WeatherApi.kt
│ │ │ │ ├── ui/
│ │ │ │ │ ├── viewmodel/
│ │ │ │ │ │ └── WeatherViewModel.kt
│ │ │ │ │ └── screen/
│ │ │ │ │ ├── WeatherScreen.kt
│ │ │ │ │ └── ForecastScreen.kt
│ │ │ │ └── MainActivity.kt
│ │ │ └── res/
│ │ └── test/
│ └── build.gradle
└── build.gradle
6.1.2 核心代码实现
// 1. 数据模型
data class Weather(
val city: String,
val temperature: Double,
val condition: String,
val humidity: Int,
val windSpeed: Double
)
// 2. API接口
interface WeatherApi {
@GET("weather")
suspend fun getCurrentWeather(
@Query("q") city: String,
@Query("appid") apiKey: String
): Response<WeatherResponse>
}
// 3. Repository
class WeatherRepository(private val api: WeatherApi) {
suspend fun getWeather(city: String): Result<Weather> {
return try {
val response = api.getCurrentWeather(city, "YOUR_API_KEY")
if (response.isSuccessful) {
response.body()?.let { weatherResponse ->
Result.success(weatherResponse.toWeather())
} ?: Result.failure(Exception("Empty response"))
} else {
Result.failure(Exception("API error: ${response.code()}"))
}
} catch (e: Exception) {
Result.failure(e)
}
}
}
// 4. ViewModel
@HiltViewModel
class WeatherViewModel @Inject constructor(
private val repository: WeatherRepository
) : ViewModel() {
private val _weatherState = MutableStateFlow<WeatherState>(WeatherState.Loading)
val weatherState: StateFlow<WeatherState> = _weatherState
fun fetchWeather(city: String) {
viewModelScope.launch {
_weatherState.value = WeatherState.Loading
val result = repository.getWeather(city)
_weatherState.value = when (result) {
is Result.Success -> WeatherState.Success(result.data)
is Result.Failure -> WeatherState.Error(result.exception.message ?: "Unknown error")
}
}
}
}
// 5. UI组件(Jetpack Compose)
@Composable
fun WeatherScreen(viewModel: WeatherViewModel) {
val weatherState by viewModel.weatherState.collectAsState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
when (val state = weatherState) {
is WeatherState.Loading -> {
CircularProgressIndicator()
}
is WeatherState.Success -> {
WeatherDisplay(weather = state.weather)
}
is WeatherState.Error -> {
Text(
text = "Error: ${state.message}",
color = Color.Red,
style = MaterialTheme.typography.body1
)
}
}
}
}
@Composable
fun WeatherDisplay(weather: Weather) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = weather.city,
style = MaterialTheme.typography.h4,
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = "${weather.temperature}°C",
style = MaterialTheme.typography.h2,
color = if (weather.temperature > 25) Color.Red else Color.Blue
)
Text(
text = weather.condition,
style = MaterialTheme.typography.h5,
modifier = Modifier.padding(vertical = 8.dp)
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
WeatherInfoItem("湿度", "${weather.humidity}%")
WeatherInfoItem("风速", "${weather.windSpeed} m/s")
}
}
}
}
@Composable
fun WeatherInfoItem(label: String, value: String) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = label,
style = MaterialTheme.typography.caption,
color = Color.Gray
)
Text(
text = value,
style = MaterialTheme.typography.body1,
fontWeight = FontWeight.Bold
)
}
}
6.2 社交媒体应用开发
6.2.1 功能模块
- 用户认证(登录/注册)
- 帖子发布与展示
- 点赞/评论功能
- 实时消息通知
6.2.2 关键技术点
// 1. Firebase集成
class FirebaseManager {
private val auth = Firebase.auth
private val firestore = Firebase.firestore
// 用户认证
suspend fun login(email: String, password: String): Result<User> {
return try {
val authResult = auth.signInWithEmailAndPassword(email, password).await()
val user = authResult.user
if (user != null) {
Result.success(User(user.uid, user.email ?: ""))
} else {
Result.failure(Exception("User not found"))
}
} catch (e: Exception) {
Result.failure(e)
}
}
// 发布帖子
suspend fun createPost(post: Post): Result<String> {
return try {
val documentRef = firestore.collection("posts").document()
documentRef.set(post).await()
Result.success(documentRef.id)
} catch (e: Exception) {
Result.failure(e)
}
}
// 实时监听帖子更新
fun listenToPosts(callback: (List<Post>) -> Unit) {
firestore.collection("posts")
.orderBy("timestamp", Query.Direction.DESCENDING)
.addSnapshotListener { snapshot, error ->
if (error != null) {
Log.e("Firebase", "Error listening to posts", error)
return@addSnapshotListener
}
val posts = snapshot?.documents?.mapNotNull { doc ->
doc.toObject(Post::class.java)?.copy(id = doc.id)
} ?: emptyList()
callback(posts)
}
}
}
// 2. 通知系统
class NotificationManager(private val context: Context) {
private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
fun showNotification(title: String, message: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
"social_app_channel",
"Social App Notifications",
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(context, "social_app_channel")
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
.build()
notificationManager.notify(System.currentTimeMillis().toInt(), notification)
}
}
第七部分:测试与调试
7.1 单元测试
// 1. 测试Repository
class UserRepositoryTest {
private lateinit var userRepository: UserRepository
private lateinit var mockApi: ApiService
@Before
fun setup() {
mockApi = mockk()
userRepository = UserRepository(mockApi)
}
@Test
fun `getUser should return success when API call is successful`() = runTest {
// Given
val expectedUser = User(1, "Test User", "test@example.com")
coEvery { mockApi.getUserById(1) } returns expectedUser
// When
val result = userRepository.getUser(1)
// Then
assertTrue(result.isSuccess)
assertEquals(expectedUser, result.getOrNull())
}
@Test
fun `getUser should return failure when API call fails`() = runTest {
// Given
coEvery { mockApi.getUserById(1) } throws Exception("Network error")
// When
val result = userRepository.getUser(1)
// Then
assertTrue(result.isFailure)
assertEquals("Network error", result.exceptionOrNull()?.message)
}
}
// 2. 测试ViewModel
class UserViewModelTest {
private lateinit var viewModel: UserViewModel
private lateinit var mockRepository: UserRepository
@Before
fun setup() {
mockRepository = mockk()
viewModel = UserViewModel(mockRepository)
}
@Test
fun `fetchUser should update state to success`() = runTest {
// Given
val expectedUser = User(1, "Test", "test@example.com")
coEvery { mockRepository.getUser(1) } returns Result.success(expectedUser)
// When
viewModel.fetchUser(1)
// Then
val state = viewModel.userState.first()
assertTrue(state is UserState.Success)
assertEquals(expectedUser, (state as UserState.Success).user)
}
}
7.2 UI测试
// 1. Espresso测试
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
@Test
fun testLoginButton() {
// 点击登录按钮
onView(withId(R.id.loginButton)).perform(click())
// 验证是否跳转到登录页面
onView(withId(R.id.loginScreen)).check(matches(isDisplayed()))
}
@Test
fun testRecyclerViewDisplay() {
// 验证RecyclerView是否显示
onView(withId(R.id.recyclerView)).check(matches(isDisplayed()))
// 验证RecyclerView是否有项目
onView(withId(R.id.recyclerView)).check(matches(hasChildCount(greaterThan(0))))
}
}
// 2. Compose UI测试
@HiltAndroidTest
class WeatherScreenTest {
@get:Rule
val hiltRule = HiltAndroidRule(this)
@Test
fun testWeatherScreenLoading() {
val viewModel = WeatherViewModel(mockRepository)
viewModel._weatherState.value = WeatherState.Loading
composeTestRule.setContent {
WeatherScreen(viewModel = viewModel)
}
composeTestRule.onNodeWithText("Loading...").assertIsDisplayed()
}
}
第八部分:发布与优化
8.1 应用签名与发布
// 1. 生成签名密钥
// 使用Android Studio的Generate Signed Bundle/APK向导
// 或使用命令行:
// keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-key-alias
// 2. 配置build.gradle
android {
signingConfigs {
release {
storeFile file("my-release-key.jks")
storePassword "your-password"
keyAlias "my-key-alias"
keyPassword "your-key-password"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
8.2 性能监控
// 1. 使用Firebase Performance Monitoring
class PerformanceMonitor {
fun trackScreenLoad(screenName: String) {
val trace = Firebase.performance.newTrace("screen_load_$screenName")
trace.start()
// 模拟屏幕加载
Handler(Looper.getMainLooper()).postDelayed({
trace.stop()
}, 1000)
}
fun trackNetworkRequest(url: String) {
val trace = Firebase.performance.newTrace("network_request")
trace.putAttribute("url", url)
trace.start()
// 网络请求完成后停止
// trace.stop()
}
}
// 2. 使用Android Profiler
// 在Android Studio中,点击View > Tool Windows > Profiler
// 可以查看CPU、内存、网络使用情况
第九部分:常见问题与解决方案
9.1 内存泄漏
问题: Activity被持有导致无法回收
解决方案:
// 使用弱引用
class MyActivity : AppCompatActivity() {
private val weakActivity = WeakReference(this)
private val callback = object : SomeCallback {
override fun onEvent() {
weakActivity.get()?.let { activity ->
// 安全使用activity
}
}
}
// 或者在onDestroy中清理
override fun onDestroy() {
super.onDestroy()
// 移除所有监听器和回调
someManager.removeListener(callback)
}
}
9.2 网络请求超时
问题: 网络请求在弱网络环境下超时
解决方案:
// 1. 设置合理的超时时间
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()
// 2. 添加重试机制
class RetryInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
var response: Response? = null
var exception: IOException? = null
for (i in 0 until 3) { // 最多重试3次
try {
response = chain.proceed(request)
if (response.isSuccessful) {
return response
}
} catch (e: IOException) {
exception = e
if (i < 2) {
// 等待一段时间后重试
Thread.sleep(1000 * (i + 1).toLong())
}
}
}
throw exception ?: IOException("Max retries exceeded")
}
}
9.3 界面卡顿
问题: 主线程执行耗时操作导致ANR
解决方案:
// 1. 使用协程处理异步操作
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch(Dispatchers.IO) {
// 耗时操作在后台线程执行
val data = repository.getData()
// 更新UI在主线程
withContext(Dispatchers.Main) {
_uiState.value = UiState.Success(data)
}
}
}
}
// 2. 使用WorkManager处理后台任务
class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// 执行耗时操作
return Result.success()
}
}
// 安排工作
val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build()
WorkManager.getInstance(context).enqueue(workRequest)
第十部分:进阶学习路径
10.1 推荐学习资源
- 官方文档: Android开发者官网
- Kotlin官方文档: Kotlin Docs
- Jetpack Compose官方教程: Compose Tutorial
- Android Architecture Components: 官方指南
10.2 开源项目参考
- Android Architecture Blueprints: Google官方的架构示例
- Now in Android: Google的Jetpack Compose示例应用
- Sunflower: Google的园艺应用示例
- iosched: Google I/O会议应用
10.3 社区与论坛
- Stack Overflow: 问题解答
- Reddit r/androiddev: 社区讨论
- Android Developers Discord: 实时交流
- GitHub Issues: 开源项目问题跟踪
总结
本指南通过系统性的讲解和丰富的实战案例,涵盖了Android开发从入门到精通的完整知识体系。关键要点包括:
- 基础扎实: 深入理解Activity生命周期、Fragment、Intent等核心概念
- 现代开发: 掌握Jetpack Compose、Kotlin协程、依赖注入等现代技术
- 架构清晰: 采用MVVM架构,分离关注点,提高代码可维护性
- 性能优化: 关注内存管理、布局优化、网络请求效率
- 测试驱动: 编写单元测试和UI测试,保证代码质量
- 持续学习: 关注Android生态发展,不断更新知识体系
Android开发是一个持续学习的过程,建议通过实际项目不断实践,参与开源社区,关注技术动态,逐步成长为一名优秀的Android开发者。
