引言

Android作为全球最流行的移动操作系统,拥有庞大的开发者社区和丰富的应用场景。从简单的工具应用到复杂的游戏和企业级应用,Android开发涵盖了广泛的技术领域。本文将通过一系列实例分析,从基础入门到高级技巧,帮助读者系统掌握Android编程的核心技术,并通过实战案例加深理解。

第一部分:Android开发基础

1.1 Android开发环境搭建

在开始Android开发之前,需要搭建完整的开发环境。主要工具包括:

  • Android Studio:官方集成开发环境(IDE),基于IntelliJ IDEA
  • Android SDK:软件开发工具包,包含API库和开发工具
  • 模拟器或真机:用于测试应用

安装步骤

  1. 下载并安装Android Studio(官网:developer.android.com/studio)
  2. 配置SDK路径和模拟器
  3. 创建第一个项目(Hello World)

示例代码:创建一个简单的Activity显示”Hello World”

// MainActivity.java
package com.example.helloworld;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 通过ID获取TextView
        TextView textView = findViewById(R.id.textView);
        textView.setText("Hello, Android!");
    }
}
<!-- activity_main.xml -->
<?xml version="1.1" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:text="Hello World!" />

</LinearLayout>

1.2 Android应用基本结构

Android应用由四个核心组件构成:

  1. Activity:用户界面的单个屏幕
  2. Service:后台运行的服务
  3. Broadcast Receiver:响应系统广播事件
  4. Content Provider:管理共享数据

示例:创建一个简单的Service

// MyService.java
package com.example.myservice;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
    private static final String TAG = "MyService";
    
    @Override
    public IBinder onBind(Intent intent) {
        return null; // 不绑定时返回null
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "Service started");
        // 执行后台任务
        performBackgroundTask();
        return START_STICKY; // 如果被杀死,尝试重启
    }
    
    private void performBackgroundTask() {
        // 模拟后台任务
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                Log.d(TAG, "Task progress: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Service destroyed");
    }
}

第二部分:UI设计与交互

2.1 布局系统详解

Android提供多种布局方式,包括LinearLayout、RelativeLayout、ConstraintLayout等。

ConstraintLayout示例:创建一个响应式布局

<!-- activity_constraint.xml -->
<?xml version="1.1" encoding="utf-8"?>
<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/title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="欢迎使用"
        android:textSize="24sp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="32dp" />

    <Button
        android:id="@+id/btn_login"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="登录"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintWidth_percent="0.6" />

    <Button
        android:id="@+id/btn_register"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="注册"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@id/btn_login"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintWidth_percent="0.6" />

</androidx.constraintlayout.widget.ConstraintLayout>

2.2 RecyclerView高级用法

RecyclerView是现代Android开发中最重要的列表组件,支持高效的数据展示和复杂交互。

示例:创建一个带点击事件的RecyclerView

// 1. 创建数据模型
public class User {
    private String name;
    private String email;
    private int avatarResId;
    
    public User(String name, String email, int avatarResId) {
        this.name = name;
        this.email = email;
        this.avatarResId = avatarResId;
    }
    
    // Getters and setters...
}

// 2. 创建Adapter
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
    private List<User> userList;
    private OnItemClickListener listener;
    
    public interface OnItemClickListener {
        void onItemClick(User user);
    }
    
    public UserAdapter(List<User> userList, OnItemClickListener listener) {
        this.userList = userList;
        this.listener = listener;
    }
    
    @NonNull
    @Override
    public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_user, parent, false);
        return new UserViewHolder(view);
    }
    
    @Override
    public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
        User user = userList.get(position);
        holder.tvName.setText(user.getName());
        holder.tvEmail.setText(user.getEmail());
        holder.itemView.setOnClickListener(v -> {
            if (listener != null) {
                listener.onItemClick(user);
            }
        });
    }
    
    @Override
    public int getItemCount() {
        return userList.size();
    }
    
    static class UserViewHolder extends RecyclerView.ViewHolder {
        TextView tvName, tvEmail;
        
        public UserViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tv_name);
            tvEmail = itemView.findViewById(R.id.tv_email);
        }
    }
}

// 3. 在Activity中使用
public class UserListActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private UserAdapter adapter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_list);
        
        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        
        // 模拟数据
        List<User> users = new ArrayList<>();
        users.add(new User("张三", "zhangsan@example.com", R.drawable.avatar1));
        users.add(new User("李四", "lisi@example.com", R.drawable.avatar2));
        users.add(new User("王五", "wangwu@example.com", R.drawable.avatar3));
        
        adapter = new UserAdapter(users, user -> {
            Toast.makeText(this, "点击了: " + user.getName(), Toast.LENGTH_SHORT).show();
        });
        
        recyclerView.setAdapter(adapter);
    }
}

2.3 Material Design组件

Material Design是Google推出的设计语言,提供了丰富的UI组件。

示例:使用Material Components

<!-- activity_material.xml -->
<?xml version="1.1" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:padding="16dp">

    <!-- Material Button -->
    <com.google.android.material.button.MaterialButton
        android:id="@+id/btn_primary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="主要按钮"
        app:icon="@drawable/ic_add"
        app:iconGravity="textStart"
        app:cornerRadius="8dp" />

    <!-- Material CardView -->
    <com.google.android.material.card.MaterialCardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        app:cardCornerRadius="8dp"
        app:cardElevation="4dp">

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

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="卡片标题"
                android:textSize="18sp"
                android:textStyle="bold" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="这是卡片的内容描述,可以包含多行文本。"
                android:layout_marginTop="8dp" />

        </LinearLayout>
    </com.google.android.material.card.MaterialCardView>

    <!-- Material Text Field -->
    <com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        app:hintEnabled="true"
        app:hintAnimationEnabled="true">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/et_email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="邮箱地址"
            android:inputType="textEmailAddress" />

    </com.google.android.material.textfield.TextInputLayout>

    <!-- Material Floating Action Button -->
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:src="@drawable/ic_add"
        app:backgroundTint="@color/colorPrimary" />

</LinearLayout>

第三部分:数据存储与网络通信

3.1 SharedPreferences存储

SharedPreferences是Android中轻量级的键值对存储方式,适合存储少量数据。

示例:使用SharedPreferences保存用户设置

// SharedPreferencesHelper.java
public class SharedPreferencesHelper {
    private static final String PREF_NAME = "app_settings";
    private static final String KEY_USER_NAME = "user_name";
    private static final String KEY_IS_LOGGED_IN = "is_logged_in";
    private static final String KEY_THEME = "theme";
    
    private SharedPreferences sharedPreferences;
    
    public SharedPreferencesHelper(Context context) {
        sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
    }
    
    // 保存用户名
    public void saveUserName(String userName) {
        sharedPreferences.edit().putString(KEY_USER_NAME, userName).apply();
    }
    
    // 获取用户名
    public String getUserName() {
        return sharedPreferences.getString(KEY_USER_NAME, "Guest");
    }
    
    // 保存登录状态
    public void saveLoginStatus(boolean isLoggedIn) {
        sharedPreferences.edit().putBoolean(KEY_IS_LOGGED_IN, isLoggedIn).apply();
    }
    
    // 获取登录状态
    public boolean isLoggedIn() {
        return sharedPreferences.getBoolean(KEY_IS_LOGGED_IN, false);
    }
    
    // 保存主题设置
    public void saveTheme(String theme) {
        sharedPreferences.edit().putString(KEY_THEME, theme).apply();
    }
    
    // 获取主题设置
    public String getTheme() {
        return sharedPreferences.getString(KEY_THEME, "light");
    }
    
    // 清除所有数据
    public void clearAll() {
        sharedPreferences.edit().clear().apply();
    }
}

// 在Activity中使用
public class SettingsActivity extends AppCompatActivity {
    private SharedPreferencesHelper helper;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_settings);
        
        helper = new SharedPreferencesHelper(this);
        
        // 保存数据
        helper.saveUserName("张三");
        helper.saveLoginStatus(true);
        helper.saveTheme("dark");
        
        // 读取数据
        String userName = helper.getUserName();
        boolean isLoggedIn = helper.isLoggedIn();
        String theme = helper.getTheme();
        
        Log.d("Settings", "User: " + userName + ", Logged in: " + isLoggedIn + ", Theme: " + theme);
    }
}

3.2 Room数据库操作

Room是Google推荐的SQLite数据库框架,提供编译时SQL验证和简化数据库操作。

示例:使用Room实现简单的待办事项应用

// 1. 定义实体类
@Entity(tableName = "todo_items")
public class TodoItem {
    @PrimaryKey(autoGenerate = true)
    private int id;
    
    private String title;
    private String description;
    private boolean completed;
    private long createdAt;
    
    // 构造函数、getters和setters...
    
    public TodoItem(String title, String description, boolean completed, long createdAt) {
        this.title = title;
        this.description = description;
        this.completed = completed;
        this.createdAt = createdAt;
    }
    
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
    public boolean isCompleted() { return completed; }
    public void setCompleted(boolean completed) { this.completed = completed; }
    public long getCreatedAt() { return createdAt; }
    public void setCreatedAt(long createdAt) { this.createdAt = createdAt; }
}

// 2. 定义DAO(数据访问对象)
@Dao
public interface TodoDao {
    @Insert
    void insert(TodoItem item);
    
    @Update
    void update(TodoItem item);
    
    @Delete
    void delete(TodoItem item);
    
    @Query("SELECT * FROM todo_items ORDER BY createdAt DESC")
    LiveData<List<TodoItem>> getAllItems();
    
    @Query("SELECT * FROM todo_items WHERE completed = 0 ORDER BY createdAt DESC")
    LiveData<List<TodoItem>> getActiveItems();
    
    @Query("SELECT * FROM todo_items WHERE completed = 1 ORDER BY createdAt DESC")
    LiveData<List<TodoItem>> getCompletedItems();
}

// 3. 定义数据库
@Database(entities = {TodoItem.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    public abstract TodoDao todoDao();
    
    private static volatile AppDatabase INSTANCE;
    
    public static AppDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            AppDatabase.class, "todo_database")
                            .build();
                }
            }
        }
        return INSTANCE;
    }
}

// 4. 在ViewModel中使用
public class TodoViewModel extends AndroidViewModel {
    private TodoDao todoDao;
    private LiveData<List<TodoItem>> allItems;
    
    public TodoViewModel(@NonNull Application application) {
        super(application);
        AppDatabase db = AppDatabase.getDatabase(application);
        todoDao = db.todoDao();
        allItems = todoDao.getAllItems();
    }
    
    public LiveData<List<TodoItem>> getAllItems() {
        return allItems;
    }
    
    public void insert(TodoItem item) {
        AppDatabase.databaseWriteExecutor.execute(() -> {
            todoDao.insert(item);
        });
    }
    
    public void update(TodoItem item) {
        AppDatabase.databaseWriteExecutor.execute(() -> {
            todoDao.update(item);
        });
    }
    
    public void delete(TodoItem item) {
        AppDatabase.databaseWriteExecutor.execute(() -> {
            todoDao.delete(item);
        });
    }
}

3.3 Retrofit网络请求

Retrofit是Square公司开发的HTTP客户端,简化了REST API的调用。

示例:使用Retrofit获取GitHub用户信息

// 1. 定义API接口
public interface GitHubApiService {
    @GET("users/{username}")
    Call<GithubUser> getUser(@Path("username") String username);
    
    @GET("users/{username}/repos")
    Call<List<Repository>> getRepositories(@Path("username") String username);
}

// 2. 定义数据模型
public class GithubUser {
    private String login;
    private String name;
    private String avatar_url;
    private String bio;
    private int public_repos;
    private int followers;
    private int following;
    
    // Getters and setters...
}

public class Repository {
    private String name;
    private String description;
    private String language;
    private int stargazers_count;
    
    // Getters and setters...
}

// 3. 创建Retrofit实例
public class RetrofitClient {
    private static final String BASE_URL = "https://api.github.com/";
    private static Retrofit retrofit = null;
    
    public static Retrofit getClient() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

// 4. 在Activity中使用
public class GitHubUserActivity extends AppCompatActivity {
    private TextView tvResult;
    private ProgressBar progressBar;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_github_user);
        
        tvResult = findViewById(R.id.tv_result);
        progressBar = findViewById(R.id.progressBar);
        
        fetchGitHubUser("google");
    }
    
    private void fetchGitHubUser(String username) {
        progressBar.setVisibility(View.VISIBLE);
        
        GitHubApiService service = RetrofitClient.getClient().create(GitHubApiService.class);
        Call<GithubUser> call = service.getUser(username);
        
        call.enqueue(new Callback<GithubUser>() {
            @Override
            public void onResponse(Call<GithubUser> call, Response<GithubUser> response) {
                progressBar.setVisibility(View.GONE);
                
                if (response.isSuccessful() && response.body() != null) {
                    GithubUser user = response.body();
                    String result = String.format(
                        "用户名: %s\n姓名: %s\nBio: %s\n仓库数: %d\n粉丝数: %d\n关注数: %d",
                        user.getLogin(),
                        user.getName(),
                        user.getBio(),
                        user.getPublic_repos(),
                        user.getFollowers(),
                        user.getFollowing()
                    );
                    tvResult.setText(result);
                } else {
                    tvResult.setText("请求失败: " + response.code());
                }
            }
            
            @Override
            public void onFailure(Call<GithubUser> call, Throwable t) {
                progressBar.setVisibility(View.GONE);
                tvResult.setText("网络错误: " + t.getMessage());
            }
        });
    }
}

第四部分:高级技巧与性能优化

4.1 多线程与异步处理

Android中处理耗时操作需要使用异步机制,避免阻塞主线程。

示例:使用Kotlin协程处理异步任务

// 1. 添加依赖(build.gradle)
dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
}

// 2. 在ViewModel中使用协程
class UserViewModel : ViewModel() {
    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> get() = _users
    
    private val _isLoading = MutableLiveData<Boolean>()
    val isLoading: LiveData<Boolean> get() = _isLoading
    
    fun loadUsers() {
        viewModelScope.launch {
            _isLoading.value = true
            try {
                // 模拟网络请求
                val result = withContext(Dispatchers.IO) {
                    delay(2000) // 模拟耗时操作
                    listOf(
                        User("张三", "zhangsan@example.com"),
                        User("李四", "lisi@example.com"),
                        User("王五", "wangwu@example.com")
                    )
                }
                _users.value = result
            } catch (e: Exception) {
                // 处理错误
                Log.e("UserViewModel", "Error loading users", e)
            } finally {
                _isLoading.value = false
            }
        }
    }
}

// 3. 在Activity中观察数据
class UserListActivity : AppCompatActivity() {
    private lateinit var viewModel: UserViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user_list)
        
        viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        
        // 观察数据变化
        viewModel.users.observe(this) { users ->
            // 更新UI
            updateUI(users)
        }
        
        viewModel.isLoading.observe(this) { isLoading ->
            progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
        }
        
        // 触发数据加载
        viewModel.loadUsers()
    }
    
    private fun updateUI(users: List<User>) {
        // 更新RecyclerView或其他UI组件
    }
}

4.2 内存优化技巧

Android应用内存管理至关重要,以下是一些优化技巧:

示例:避免内存泄漏

// 1. 避免静态Context引用
public class Utils {
    private static Context context; // 错误做法
    
    public static void init(Context context) {
        Utils.context = context; // 可能导致内存泄漏
    }
    
    // 正确做法:使用Application Context
    public static void init(Context context) {
        Utils.context = context.getApplicationContext();
    }
}

// 2. 避免非静态内部类持有外部类引用
public class MainActivity extends AppCompatActivity {
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 这个内部类会持有MainActivity的引用
            // 如果Handler有延迟消息,可能导致内存泄漏
        }
    };
    
    // 正确做法:使用静态内部类
    private static class MyHandler extends Handler {
        private final WeakReference<MainActivity> activityRef;
        
        public MyHandler(MainActivity activity) {
            activityRef = new WeakReference<>(activity);
        }
        
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = activityRef.get();
            if (activity != null) {
                // 处理消息
            }
        }
    }
}

// 3. 使用LeakCanary检测内存泄漏
// 在build.gradle中添加依赖
dependencies {
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
}

4.3 性能优化工具

Android Studio提供了多种性能分析工具:

示例:使用Profiler分析应用性能

  1. CPU Profiler:分析方法执行时间和调用栈
  2. Memory Profiler:监控内存使用情况
  3. Network Profiler:分析网络请求
  4. Energy Profiler:监控电池消耗

使用步骤

  1. 连接设备或启动模拟器
  2. 在Android Studio中点击”Profiler”标签
  3. 选择要分析的进程
  4. 运行应用并观察性能数据

第五部分:实战案例分析

5.1 案例一:天气预报应用

项目结构

WeatherApp/
├── app/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/com/example/weatherapp/
│   │   │   │   ├── data/
│   │   │   │   │   ├── model/
│   │   │   │   │   │   ├── WeatherData.java
│   │   │   │   │   │   └── Forecast.java
│   │   │   │   │   ├── repository/
│   │   │   │   │   │   └── WeatherRepository.java
│   │   │   │   │   └── api/
│   │   │   │   │       └── WeatherApiService.java
│   │   │   │   ├── ui/
│   │   │   │   │   ├── viewmodel/
│   │   │   │   │   │   └── WeatherViewModel.java
│   │   │   │   │   └── activity/
│   │   │   │   │       ├── MainActivity.java
│   │   │   │   │       └── DetailActivity.java
│   │   │   │   └── utils/
│   │   │   │       └── NetworkUtils.java
│   │   │   └── res/
│   │   │       ├── layout/
│   │   │       │   ├── activity_main.xml
│   │   │       │   └── activity_detail.xml
│   │   │       └── values/
│   │   │           └── strings.xml

核心代码实现

// 1. 数据模型
public class WeatherData {
    private String city;
    private double temperature;
    private String condition;
    private double humidity;
    private double windSpeed;
    private List<Forecast> forecasts;
    
    // Getters and setters...
}

// 2. API接口
public interface WeatherApiService {
    @GET("weather")
    Call<WeatherData> getCurrentWeather(
        @Query("q") String city,
        @Query("appid") String apiKey
    );
    
    @GET("forecast")
    Call<ForecastResponse> getForecast(
        @Query("q") String city,
        @Query("appid") String apiKey
    );
}

// 3. Repository
public class WeatherRepository {
    private WeatherApiService apiService;
    private WeatherDao weatherDao;
    
    public WeatherRepository(WeatherApiService apiService, WeatherDao weatherDao) {
        this.apiService = apiService;
        this.weatherDao = weatherDao;
    }
    
    public LiveData<WeatherData> getWeather(String city) {
        MutableLiveData<WeatherData> result = new MutableLiveData<>();
        
        apiService.getCurrentWeather(city, BuildConfig.API_KEY)
            .enqueue(new Callback<WeatherData>() {
                @Override
                public void onResponse(Call<WeatherData> call, Response<WeatherData> response) {
                    if (response.isSuccessful() && response.body() != null) {
                        WeatherData data = response.body();
                        result.setValue(data);
                        
                        // 保存到本地数据库
                        new Thread(() -> {
                            weatherDao.insertWeather(data);
                        }).start();
                    }
                }
                
                @Override
                public void onFailure(Call<WeatherData> call, Throwable t) {
                    // 从本地数据库加载
                    new Thread(() -> {
                        WeatherData localData = weatherDao.getWeather(city);
                        result.postValue(localData);
                    }).start();
                }
            });
        
        return result;
    }
}

// 4. ViewModel
public class WeatherViewModel extends ViewModel {
    private WeatherRepository repository;
    private MutableLiveData<WeatherData> currentWeather = new MutableLiveData<>();
    private MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
    
    public WeatherViewModel(WeatherRepository repository) {
        this.repository = repository;
    }
    
    public void loadWeather(String city) {
        isLoading.setValue(true);
        
        repository.getWeather(city).observeForever(new Observer<WeatherData>() {
            @Override
            public void onChanged(WeatherData data) {
                currentWeather.setValue(data);
                isLoading.setValue(false);
                repository.getWeather(city).removeObserver(this);
            }
        });
    }
    
    public LiveData<WeatherData> getCurrentWeather() {
        return currentWeather;
    }
    
    public LiveData<Boolean> getIsLoading() {
        return isLoading;
    }
}

// 5. MainActivity
public class MainActivity extends AppCompatActivity {
    private WeatherViewModel viewModel;
    private TextView tvCity, tvTemperature, tvCondition;
    private EditText etCity;
    private Button btnSearch;
    private ProgressBar progressBar;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        initViews();
        setupViewModel();
        setupListeners();
    }
    
    private void initViews() {
        tvCity = findViewById(R.id.tv_city);
        tvTemperature = findViewById(R.id.tv_temperature);
        tvCondition = findViewById(R.id.tv_condition);
        etCity = findViewById(R.id.et_city);
        btnSearch = findViewById(R.id.btn_search);
        progressBar = findViewById(R.id.progressBar);
    }
    
    private void setupViewModel() {
        WeatherApiService apiService = RetrofitClient.getClient().create(WeatherApiService.class);
        WeatherDao weatherDao = AppDatabase.getDatabase(this).weatherDao();
        WeatherRepository repository = new WeatherRepository(apiService, weatherDao);
        
        viewModel = new ViewModelProvider(this, new ViewModelProvider.Factory() {
            @NonNull
            @Override
            public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
                return (T) new WeatherViewModel(repository);
            }
        }).get(WeatherViewModel.class);
        
        // 观察数据
        viewModel.getCurrentWeather().observe(this, weatherData -> {
            if (weatherData != null) {
                updateUI(weatherData);
            }
        });
        
        viewModel.getIsLoading().observe(this, isLoading -> {
            progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE);
        });
    }
    
    private void setupListeners() {
        btnSearch.setOnClickListener(v -> {
            String city = etCity.getText().toString().trim();
            if (!city.isEmpty()) {
                viewModel.loadWeather(city);
            } else {
                Toast.makeText(this, "请输入城市名称", Toast.LENGTH_SHORT).show();
            }
        });
    }
    
    private void updateUI(WeatherData data) {
        tvCity.setText(data.getCity());
        tvTemperature.setText(String.format("%.1f°C", data.getTemperature()));
        tvCondition.setText(data.getCondition());
    }
}

5.2 案例二:新闻阅读器应用

技术栈

  • MVVM架构
  • Room数据库(离线缓存)
  • Retrofit + Gson(网络请求)
  • RecyclerView + DiffUtil(高效列表更新)
  • WorkManager(定时更新)

关键实现

// 1. 使用DiffUtil优化RecyclerView更新
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.NewsViewHolder> {
    private List<NewsItem> newsList = new ArrayList<>();
    
    public void setNewsList(List<NewsItem> newList) {
        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
            new NewsDiffCallback(newsList, newList)
        );
        newsList = new ArrayList<>(newList);
        diffResult.dispatchUpdatesTo(this);
    }
    
    // DiffUtil.Callback实现
    static class NewsDiffCallback extends DiffUtil.Callback {
        private final List<NewsItem> oldList;
        private final List<NewsItem> newList;
        
        public NewsDiffCallback(List<NewsItem> oldList, List<NewsItem> newList) {
            this.oldList = oldList;
            this.newList = newList;
        }
        
        @Override
        public int getOldListSize() {
            return oldList.size();
        }
        
        @Override
        public int getNewListSize() {
            return newList.size();
        }
        
        @Override
        public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
            return oldList.get(oldItemPosition).getId() == newList.get(newItemPosition).getId();
        }
        
        @Override
        public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
            return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
        }
    }
}

// 2. WorkManager定时更新
public class NewsUpdateWorker extends Worker {
    public NewsUpdateWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }
    
    @NonNull
    @Override
    public Result doWork() {
        // 执行后台更新任务
        try {
            NewsRepository repository = new NewsRepository(getApplicationContext());
            repository.fetchLatestNews();
            return Result.success();
        } catch (Exception e) {
            return Result.failure();
        }
    }
}

// 3. 在Application中调度WorkManager
public class NewsApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        // 每6小时更新一次新闻
        PeriodicWorkRequest newsUpdateWork = new PeriodicWorkRequest.Builder(
            NewsUpdateWorker.class, 6, TimeUnit.HOURS)
            .setConstraints(new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .setRequiresBatteryNotLow(true)
                .build())
            .build();
        
        WorkManager.getInstance(this).enqueueUniquePeriodicWork(
            "news_update",
            ExistingPeriodicWorkPolicy.KEEP,
            newsUpdateWork
        );
    }
}

第六部分:发布与测试

6.1 应用测试

单元测试示例

// 1. 添加测试依赖
dependencies {
    testImplementation 'junit:junit:4.13.2'
    testImplementation 'org.mockito:mockito-core:4.8.0'
    testImplementation 'androidx.arch.core:core-testing:2.1.0'
}

// 2. 编写单元测试
@RunWith(MockitoJUnitRunner.class)
public class WeatherViewModelTest {
    @Mock
    private WeatherRepository repository;
    
    @Mock
    private Observer<WeatherData> observer;
    
    @Mock
    private Observer<Boolean> loadingObserver;
    
    private WeatherViewModel viewModel;
    
    @Before
    public void setUp() {
        viewModel = new WeatherViewModel(repository);
    }
    
    @Test
    public void testLoadWeather_Success() {
        // 准备测试数据
        WeatherData testData = new WeatherData();
        testData.setCity("Beijing");
        testData.setTemperature(25.0);
        
        MutableLiveData<WeatherData> liveData = new MutableLiveData<>();
        liveData.setValue(testData);
        
        when(repository.getWeather("Beijing")).thenReturn(liveData);
        
        // 观察数据
        viewModel.getCurrentWeather().observeForever(observer);
        viewModel.getIsLoading().observeForever(loadingObserver);
        
        // 执行测试
        viewModel.loadWeather("Beijing");
        
        // 验证结果
        verify(observer).onChanged(testData);
        verify(loadingObserver).onChanged(true);
        verify(loadingObserver).onChanged(false);
    }
    
    @Test
    public void testLoadWeather_Error() {
        MutableLiveData<WeatherData> liveData = new MutableLiveData<>();
        liveData.setValue(null);
        
        when(repository.getWeather("Beijing")).thenReturn(liveData);
        
        viewModel.getCurrentWeather().observeForever(observer);
        viewModel.loadWeather("Beijing");
        
        verify(observer).onChanged(null);
    }
}

6.2 应用发布

发布前检查清单

  1. 性能优化:确保应用无内存泄漏,响应时间<200ms
  2. 安全检查:移除调试代码,保护API密钥
  3. 兼容性测试:在不同Android版本和设备上测试
  4. 权限管理:只请求必要的权限
  5. 隐私政策:如果收集用户数据,需要提供隐私政策

生成签名APK

  1. 在Android Studio中选择”Build” → “Generate Signed Bundle/APK”
  2. 创建或选择签名密钥
  3. 选择发布版本(Release)
  4. 生成APK文件

第七部分:进阶主题

7.1 Jetpack Compose

Jetpack Compose是Android的现代UI工具包,使用Kotlin声明式UI。

示例:使用Compose创建简单界面

// 1. 添加依赖
dependencies {
    implementation 'androidx.compose.ui:ui:1.3.3'
    implementation 'androidx.compose.material:material:1.3.1'
    implementation 'androidx.compose.ui:ui-tooling-preview:1.3.3'
    implementation 'androidx.activity:activity-compose:1.6.1'
}

// 2. 创建Compose界面
@Composable
fun WeatherScreen(viewModel: WeatherViewModel = viewModel()) {
    val weatherData by viewModel.currentWeather.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()
    
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        if (isLoading) {
            CircularProgressIndicator()
        } else if (weatherData != null) {
            WeatherContent(weatherData!!)
        } else {
            Text("请输入城市名称")
        }
    }
}

@Composable
fun WeatherContent(data: WeatherData) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 8.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
    ) {
        Column(
            modifier = Modifier.padding(16.dp)
        ) {
            Text(
                text = data.city,
                style = MaterialTheme.typography.h4,
                fontWeight = FontWeight.Bold
            )
            Spacer(modifier = Modifier.height(8.dp))
            Text(
                text = "${data.temperature}°C",
                style = MaterialTheme.typography.h3,
                color = MaterialTheme.colors.primary
            )
            Spacer(modifier = Modifier.height(4.dp))
            Text(
                text = data.condition,
                style = MaterialTheme.typography.body1
            )
        }
    }
}

// 3. 在Activity中使用
class ComposeActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            WeatherAppTheme {
                WeatherScreen()
            }
        }
    }
}

7.2 Kotlin多平台开发

Kotlin Multiplatform允许共享业务逻辑代码。

示例:共享数据模型和网络逻辑

// 1. 创建共享模块(shared/src/commonMain/kotlin)
expect class Platform() {
    val platformName: String
}

class SharedData {
    fun getPlatformInfo(): String {
        return "Running on ${Platform().platformName}"
    }
}

// 2. Android平台实现(shared/src/androidMain/kotlin)
actual class Platform {
    actual val platformName: String = "Android"
}

// 3. iOS平台实现(shared/src/iosMain/kotlin)
actual class Platform {
    actual val platformName: String = "iOS"
}

// 4. 在Android项目中使用
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val sharedData = SharedData()
        val info = sharedData.getPlatformInfo()
        
        Toast.makeText(this, info, Toast.LENGTH_SHORT).show()
    }
}

总结

通过本文的系统学习,读者应该能够:

  1. 掌握基础:理解Android应用的基本结构和开发环境
  2. 精通UI:熟练使用各种布局和Material Design组件
  3. 数据处理:掌握SharedPreferences、Room和Retrofit的使用
  4. 性能优化:了解多线程、内存管理和性能分析工具
  5. 实战能力:通过案例分析获得实际项目开发经验
  6. 进阶技能:了解Jetpack Compose和Kotlin多平台开发

Android开发是一个持续学习的过程,建议读者:

  • 定期阅读官方文档(developer.android.com)
  • 参与开源项目
  • 关注Android开发社区(如Stack Overflow、Reddit)
  • 实践更多项目,积累经验

记住,优秀的Android开发者不仅需要掌握技术,还需要关注用户体验、性能优化和代码质量。通过不断实践和学习,你一定能成为Android开发领域的专家。