引言

Android开发是一个充满活力的领域,从智能手机到智能手表、电视和汽车,Android系统无处不在。对于初学者来说,Android开发可能看起来很复杂,但通过系统的学习和实践,任何人都可以掌握它。本指南将从零基础开始,通过详细的实例分析,逐步引导你成为一名合格的Android开发者,并涵盖在实战开发中常见的问题及其解决方案。

第一部分:Android开发基础

1.1 Android开发环境搭建

在开始编写代码之前,我们需要搭建一个完整的开发环境。Android Studio是Google官方推荐的集成开发环境(IDE),它提供了代码编辑、调试、性能分析和模拟器管理等所有必要的工具。

安装步骤:

  1. 访问Android Studio官网(https://developer.android.com/studio)下载最新版本。
  2. 运行安装程序,按照向导完成安装。
  3. 首次启动时,Android Studio会引导你安装Android SDK和其他必要组件。
  4. 配置模拟器或连接真实设备进行测试。

验证安装: 创建一个新项目,选择”Empty Activity”模板,运行应用。如果看到”Hello World!“界面,说明环境搭建成功。

1.2 Android项目结构解析

一个典型的Android项目包含以下关键目录和文件:

MyApp/
├── app/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/com/example/myapp/
│   │   │   │   └── MainActivity.java
│   │   │   ├── res/
│   │   │   │   ├── layout/
│   │   │   │   │   └── activity_main.xml
│   │   │   │   ├── values/
│   │   │   │   │   ├── strings.xml
│   │   │   │   │   └── colors.xml
│   │   │   │   └── drawable/
│   │   │   └── AndroidManifest.xml
│   │   └── test/
│   └── build.gradle
├── build.gradle
└── settings.gradle

关键文件说明:

  • MainActivity.java:主活动类,处理用户交互和业务逻辑
  • activity_main.xml:布局文件,定义界面结构
  • strings.xml:字符串资源文件,用于国际化
  • AndroidManifest.xml:应用清单文件,声明应用组件和权限
  • build.gradle:项目构建配置文件

1.3 Android四大组件

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

  1. Activity:用户交互界面,一个Activity通常对应一个屏幕
  2. Service:后台服务,执行长时间运行的操作
  3. BroadcastReceiver:接收和响应系统广播
  4. ContentProvider:管理应用间的数据共享

示例:创建一个简单的Activity

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

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);
        
        TextView textView = findViewById(R.id.textView);
        textView.setText("欢迎使用我的第一个Android应用!");
    }
}
<!-- activity_main.xml -->
<?xml version="1.0" 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:padding="16dp">

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

</LinearLayout>

第二部分:Android UI开发

2.1 布局系统详解

Android提供了多种布局管理器来组织UI元素:

  1. LinearLayout:线性布局,元素按水平或垂直方向排列
  2. RelativeLayout:相对布局,元素相对于其他元素或父容器定位
  3. ConstraintLayout:约束布局,最灵活的布局方式,支持扁平化视图层次
  4. FrameLayout:帧布局,元素堆叠显示

示例:使用ConstraintLayout创建复杂界面

<!-- activity_constraint_example.xml -->
<?xml version="1.0" 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="wrap_content"
        android:layout_height="wrap_content"
        android:text="用户登录"
        android:textSize="24sp"
        android:textStyle="bold"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="50dp" />

    <EditText
        android:id="@+id/username"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="请输入用户名"
        android:inputType="text"
        android:padding="12dp"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="30dp"
        android:layout_marginHorizontal="20dp" />

    <EditText
        android:id="@+id/password"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="请输入密码"
        android:inputType="textPassword"
        android:padding="12dp"
        app:layout_constraintTop_toBottomOf="@id/username"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="15dp"
        android:layout_marginHorizontal="20dp" />

    <Button
        android:id="@+id/loginButton"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="登录"
        android:textSize="16sp"
        app:layout_constraintTop_toBottomOf="@id/password"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="30dp"
        android:layout_marginHorizontal="20dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

2.2 常用UI组件

1. TextView和EditText

// 在Activity中使用TextView和EditText
TextView textView = findViewById(R.id.textView);
EditText editText = findViewById(R.id.editText);

// 设置文本
textView.setText("新文本");

// 获取输入内容
String input = editText.getText().toString();

// 设置文本变化监听器
editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
    
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // 实时响应输入变化
        if (s.length() > 10) {
            textView.setText("输入过长!");
        }
    }
    
    @Override
    public void afterTextChanged(Editable s) {}
});

2. Button和点击事件

Button button = findViewById(R.id.myButton);

// 方式1:使用匿名内部类
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(MainActivity.this, "按钮被点击了", Toast.LENGTH_SHORT).show();
    }
});

// 方式2:使用Lambda表达式(Android 8.0+)
button.setOnClickListener(v -> {
    Toast.makeText(MainActivity.this, "按钮被点击了", Toast.LENGTH_SHORT).show();
});

// 方式3:在XML中指定onClick方法
// 在XML中:android:onClick="onButtonClick"
// 在Activity中:
public void onButtonClick(View view) {
    // 处理点击事件
}

3. RecyclerView(列表显示)

// 1. 创建数据模型
public class User {
    private String name;
    private String email;
    
    // 构造函数、getter和setter省略
}

// 2. 创建适配器
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
    private List<User> userList;
    
    public UserAdapter(List<User> userList) {
        this.userList = userList;
    }
    
    @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.nameTextView.setText(user.getName());
        holder.emailTextView.setText(user.getEmail());
        
        // 设置点击事件
        holder.itemView.setOnClickListener(v -> {
            // 处理列表项点击
            Toast.makeText(v.getContext(), "点击了: " + user.getName(), Toast.LENGTH_SHORT).show();
        });
    }
    
    @Override
    public int getItemCount() {
        return userList.size();
    }
    
    static class UserViewHolder extends RecyclerView.ViewHolder {
        TextView nameTextView;
        TextView emailTextView;
        
        public UserViewHolder(@NonNull View itemView) {
            super(itemView);
            nameTextView = itemView.findViewById(R.id.nameTextView);
            emailTextView = itemView.findViewById(R.id.emailTextView);
        }
    }
}

// 3. 在Activity中使用
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

List<User> users = new ArrayList<>();
users.add(new User("张三", "zhangsan@example.com"));
users.add(new User("李四", "lisi@example.com"));
users.add(new User("王五", "wangwu@example.com"));

UserAdapter adapter = new UserAdapter(users);
recyclerView.setAdapter(adapter);

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

3.1 SharedPreferences存储

SharedPreferences是Android中最简单的数据存储方式,适合存储少量简单的数据(如用户设置)。

示例:保存和读取用户偏好设置

public class SharedPreferencesExample {
    private static final String PREF_NAME = "MyAppPrefs";
    private static final String KEY_USERNAME = "username";
    private static final String KEY_THEME = "theme";
    
    // 保存数据
    public static void saveUsername(Context context, String username) {
        SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putString(KEY_USERNAME, username);
        editor.apply(); // 异步提交
    }
    
    // 读取数据
    public static String getUsername(Context context) {
        SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        return prefs.getString(KEY_USERNAME, "默认用户");
    }
    
    // 保存主题设置
    public static void saveTheme(Context context, String theme) {
        SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putString(KEY_THEME, theme);
        editor.apply();
    }
    
    // 读取主题设置
    public static String getTheme(Context context) {
        SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        return prefs.getString(KEY_THEME, "light");
    }
    
    // 删除数据
    public static void clearPreferences(Context context) {
        SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        editor.clear();
        editor.apply();
    }
}

3.2 SQLite数据库

SQLite是Android内置的轻量级数据库,适合存储结构化数据。

示例:创建数据库和表

// 1. 创建数据库帮助类
public class DatabaseHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "MyApp.db";
    private static final int DATABASE_VERSION = 1;
    
    // 表名和列名常量
    public static final String TABLE_USERS = "users";
    public static final String COLUMN_ID = "_id";
    public static final String COLUMN_NAME = "name";
    public static final String COLUMN_EMAIL = "email";
    public static final String COLUMN_AGE = "age";
    
    // 创建表的SQL语句
    private static final String CREATE_TABLE_USERS = 
        "CREATE TABLE " + TABLE_USERS + " (" +
        COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
        COLUMN_NAME + " TEXT NOT NULL, " +
        COLUMN_EMAIL + " TEXT, " +
        COLUMN_AGE + " INTEGER)";
    
    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE_USERS);
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 数据库升级时的操作
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_USERS);
        onCreate(db);
    }
}

// 2. 数据访问对象(DAO)
public class UserDAO {
    private DatabaseHelper dbHelper;
    
    public UserDAO(Context context) {
        dbHelper = new DatabaseHelper(context);
    }
    
    // 插入用户
    public long insertUser(String name, String email, int age) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(DatabaseHelper.COLUMN_NAME, name);
        values.put(DatabaseHelper.COLUMN_EMAIL, email);
        values.put(DatabaseHelper.COLUMN_AGE, age);
        
        long id = db.insert(DatabaseHelper.TABLE_USERS, null, values);
        db.close();
        return id;
    }
    
    // 查询所有用户
    public List<User> getAllUsers() {
        List<User> users = new ArrayList<>();
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        
        Cursor cursor = db.query(
            DatabaseHelper.TABLE_USERS,
            null, // 所有列
            null, // WHERE子句
            null, // WHERE参数
            null, // GROUP BY
            null, // HAVING
            null  // ORDER BY
        );
        
        if (cursor != null && cursor.moveToFirst()) {
            do {
                User user = new User();
                user.setId(cursor.getLong(cursor.getColumnIndex(DatabaseHelper.COLUMN_ID)));
                user.setName(cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_NAME)));
                user.setEmail(cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_EMAIL)));
                user.setAge(cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_AGE)));
                users.add(user);
            } while (cursor.moveToNext());
            cursor.close();
        }
        db.close();
        return users;
    }
    
    // 根据ID查询用户
    public User getUserById(long id) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = db.query(
            DatabaseHelper.TABLE_USERS,
            null,
            DatabaseHelper.COLUMN_ID + " = ?",
            new String[]{String.valueOf(id)},
            null, null, null
        );
        
        User user = null;
        if (cursor != null && cursor.moveToFirst()) {
            user = new User();
            user.setId(cursor.getLong(cursor.getColumnIndex(DatabaseHelper.COLUMN_ID)));
            user.setName(cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_NAME)));
            user.setEmail(cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_EMAIL)));
            user.setAge(cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_AGE)));
            cursor.close();
        }
        db.close();
        return user;
    }
    
    // 更新用户
    public int updateUser(long id, String name, String email, int age) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(DatabaseHelper.COLUMN_NAME, name);
        values.put(DatabaseHelper.COLUMN_EMAIL, email);
        values.put(DatabaseHelper.COLUMN_AGE, age);
        
        int rowsAffected = db.update(
            DatabaseHelper.TABLE_USERS,
            values,
            DatabaseHelper.COLUMN_ID + " = ?",
            new String[]{String.valueOf(id)}
        );
        db.close();
        return rowsAffected;
    }
    
    // 删除用户
    public int deleteUser(long id) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int rowsAffected = db.delete(
            DatabaseHelper.TABLE_USERS,
            DatabaseHelper.COLUMN_ID + " = ?",
            new String[]{String.valueOf(id)}
        );
        db.close();
        return rowsAffected;
    }
}

3.3 网络通信

Android中常见的网络通信方式包括HttpURLConnection、OkHttp和Retrofit。

示例:使用Retrofit进行网络请求

  1. 添加依赖build.gradle文件中添加:
dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.google.code.gson:gson:2.8.9'
}
  1. 定义数据模型
// 用户数据模型
public class UserResponse {
    private int id;
    private String name;
    private String email;
    private String phone;
    
    // getter和setter省略
}

// API响应模型
public class ApiResponse<T> {
    private boolean success;
    private String message;
    private T data;
    
    // getter和setter省略
}
  1. 定义API接口
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;

public interface ApiService {
    // 获取单个用户
    @GET("users/{id}")
    Call<ApiResponse<UserResponse>> getUser(@Path("id") int userId);
    
    // 获取所有用户
    @GET("users")
    Call<ApiResponse<List<UserResponse>>> getAllUsers();
}
  1. 创建Retrofit实例
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {
    private static final String BASE_URL = "https://api.example.com/";
    private static Retrofit retrofit = null;
    
    public static Retrofit getInstance() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        }
        return retrofit;
    }
}
  1. 在Activity中使用
public class NetworkActivity extends AppCompatActivity {
    private TextView resultTextView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_network);
        
        resultTextView = findViewById(R.id.resultTextView);
        Button fetchButton = findViewById(R.id.fetchButton);
        
        fetchButton.setOnClickListener(v -> fetchUserData());
    }
    
    private void fetchUserData() {
        ApiService apiService = RetrofitClient.getInstance().create(ApiService.class);
        Call<ApiResponse<UserResponse>> call = apiService.getUser(1);
        
        call.enqueue(new Callback<ApiResponse<UserResponse>>() {
            @Override
            public void onResponse(Call<ApiResponse<UserResponse>> call, 
                                 Response<ApiResponse<UserResponse>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    ApiResponse<UserResponse> apiResponse = response.body();
                    if (apiResponse.isSuccess()) {
                        UserResponse user = apiResponse.getData();
                        String result = "用户ID: " + user.getId() + "\n" +
                                      "姓名: " + user.getName() + "\n" +
                                      "邮箱: " + user.getEmail();
                        resultTextView.setText(result);
                    } else {
                        resultTextView.setText("错误: " + apiResponse.getMessage());
                    }
                } else {
                    resultTextView.setText("网络请求失败: " + response.code());
                }
            }
            
            @Override
            public void onFailure(Call<ApiResponse<UserResponse>> call, Throwable t) {
                resultTextView.setText("请求失败: " + t.getMessage());
            }
        });
    }
}

第四部分:高级主题与实战技巧

4.1 多线程与异步处理

Android不允许在主线程(UI线程)中执行耗时操作,否则会导致应用无响应(ANR)。

示例:使用AsyncTask(已弃用,但了解原理)

// AsyncTask示例(Android 11+已弃用,建议使用其他方式)
private class DownloadTask extends AsyncTask<String, Integer, String> {
    @Override
    protected String doInBackground(String... urls) {
        // 在后台线程执行耗时操作
        try {
            URL url = new URL(urls[0]);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.connect();
            
            InputStream inputStream = connection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder result = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
            return result.toString();
        } catch (Exception e) {
            return "下载失败: " + e.getMessage();
        }
    }
    
    @Override
    protected void onPostExecute(String result) {
        // 在主线程更新UI
        resultTextView.setText(result);
    }
}

示例:使用ExecutorService(推荐)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AsyncExample {
    private ExecutorService executor = Executors.newFixedThreadPool(4);
    
    public void performTask() {
        executor.submit(() -> {
            // 后台任务
            try {
                Thread.sleep(2000); // 模拟耗时操作
                // 更新UI需要切换到主线程
                runOnUiThread(() -> {
                    // 在主线程更新UI
                    Toast.makeText(this, "任务完成", Toast.LENGTH_SHORT).show();
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

示例:使用Kotlin协程(现代推荐方式)

// Kotlin代码示例
class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
        
        // 观察LiveData
        viewModel.userData.observe(this, Observer { user ->
            // 更新UI
            textView.text = "用户: ${user.name}"
        })
        
        // 启动协程
        lifecycleScope.launch {
            // 在IO线程执行网络请求
            val result = withContext(Dispatchers.IO) {
                // 网络请求
                apiService.getUser(1)
            }
            // 自动切换到主线程更新UI
            textView.text = "结果: ${result}"
        }
    }
}

4.2 ViewModel和LiveData

ViewModel和LiveData是Android架构组件的一部分,用于实现MVVM架构模式。

示例:创建ViewModel

// 1. 添加依赖
dependencies {
    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1'
    implementation 'androidx.lifecycle:lifecycle-livedata:2.5.1'
    implementation 'androidx.lifecycle:lifecycle-runtime:2.5.1'
}

// 2. 创建ViewModel
public class UserViewModel extends ViewModel {
    private MutableLiveData<User> userLiveData = new MutableLiveData<>();
    private UserRepository userRepository;
    
    public UserViewModel() {
        userRepository = new UserRepository();
    }
    
    public LiveData<User> getUser() {
        return userLiveData;
    }
    
    public void loadUser(int userId) {
        // 在后台线程加载数据
        new Thread(() -> {
            User user = userRepository.getUser(userId);
            userLiveData.postValue(user);
        }).start();
    }
}

// 3. 在Activity中使用
public class UserActivity extends AppCompatActivity {
    private UserViewModel viewModel;
    private TextView nameTextView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);
        
        nameTextView = findViewById(R.id.nameTextView);
        
        // 获取ViewModel
        viewModel = new ViewModelProvider(this).get(UserViewModel.class);
        
        // 观察LiveData
        viewModel.getUser().observe(this, user -> {
            if (user != null) {
                nameTextView.setText(user.getName());
            }
        });
        
        // 加载数据
        viewModel.loadUser(1);
    }
}

4.3 Jetpack Compose(现代UI开发)

Jetpack Compose是Android的现代UI工具包,使用声明式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 MyApp() {
    var text by remember { mutableStateOf("Hello, World!") }
    
    MaterialTheme {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = text,
                style = MaterialTheme.typography.h4,
                modifier = Modifier.padding(bottom = 16.dp)
            )
            
            Button(onClick = { text = "按钮被点击了!" }) {
                Text("点击我")
            }
            
            TextField(
                value = text,
                onValueChange = { text = it },
                label = { Text("输入文本") },
                modifier = Modifier.padding(top = 16.dp)
            )
        }
    }
}

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

第五部分:常见问题与解决方案

5.1 内存泄漏问题

问题描述: 内存泄漏是Android开发中最常见的问题之一,会导致应用占用内存不断增加,最终导致应用崩溃。

常见原因:

  1. 非静态内部类持有外部类引用
  2. 未取消的监听器或回调
  3. 静态Context引用
  4. 未关闭的资源(如数据库连接、文件流)

解决方案:

// 1. 避免非静态内部类持有外部类引用
// 错误示例:
public class MyActivity extends AppCompatActivity {
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 这里持有MyActivity的隐式引用
        }
    };
}

// 正确示例:
public class MyActivity extends AppCompatActivity {
    private static class MyHandler extends Handler {
        private final WeakReference<MyActivity> activityRef;
        
        public MyHandler(MyActivity activity) {
            activityRef = new WeakReference<>(activity);
        }
        
        @Override
        public void handleMessage(Message msg) {
            MyActivity activity = activityRef.get();
            if (activity != null) {
                // 处理消息
            }
        }
    }
    
    private MyHandler handler = new MyHandler(this);
}

// 2. 及时取消监听器
@Override
protected void onDestroy() {
    super.onDestroy();
    // 取消所有监听器
    if (myListener != null) {
        unregisterListener(myListener);
    }
    // 取消Handler的回调
    if (handler != null) {
        handler.removeCallbacksAndMessages(null);
    }
}

// 3. 使用WeakReference
private static class MyCallback implements Callback {
    private final WeakReference<Activity> activityRef;
    
    public MyCallback(Activity activity) {
        activityRef = new WeakReference<>(activity);
    }
    
    @Override
    public void onSuccess() {
        Activity activity = activityRef.get();
        if (activity != null) {
            // 更新UI
        }
    }
}

5.2 ANR(应用无响应)

问题描述: ANR(Application Not Responding)发生在主线程执行耗时操作超过5秒时。

常见原因:

  1. 主线程执行网络请求
  2. 主线程执行数据库操作
  3. 主线程执行文件读写
  4. 死锁或无限循环

解决方案:

// 1. 将耗时操作移到后台线程
// 错误示例:
public void onClick(View view) {
    // 在主线程执行网络请求
    String result = performNetworkRequest(); // 耗时操作
    textView.setText(result);
}

// 正确示例:
public void onClick(View view) {
    // 使用ExecutorService
    executor.submit(() -> {
        String result = performNetworkRequest();
        runOnUiThread(() -> textView.setText(result));
    });
}

// 2. 使用AsyncTask(已弃用,但了解原理)
private class NetworkTask extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... params) {
        return performNetworkRequest();
    }
    
    @Override
    protected void onPostExecute(String result) {
        textView.setText(result);
    }
}

// 3. 使用Kotlin协程(推荐)
fun onClick() {
    lifecycleScope.launch {
        val result = withContext(Dispatchers.IO) {
            performNetworkRequest()
        }
        textView.text = result
    }
}

5.3 权限管理问题

问题描述: Android 6.0+引入了运行时权限,需要在运行时请求敏感权限。

解决方案:

// 1. 在AndroidManifest.xml中声明权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

// 2. 检查和请求权限
public class PermissionManager {
    private static final int PERMISSION_REQUEST_CODE = 100;
    
    public static void requestCameraPermission(Activity activity) {
        if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) 
            != PackageManager.PERMISSION_GRANTED) {
            
            ActivityCompat.requestPermissions(
                activity,
                new String[]{Manifest.permission.CAMERA},
                PERMISSION_REQUEST_CODE
            );
        } else {
            // 权限已授予,执行相机操作
            openCamera();
        }
    }
    
    // 处理权限请求结果
    @Override
    public void onRequestPermissionsResult(int requestCode, 
                                         @NonNull String[] permissions, 
                                         @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        
        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限已授予
                openCamera();
            } else {
                // 权限被拒绝
                showPermissionDeniedDialog();
            }
        }
    }
    
    // 3. 使用第三方库简化权限管理(推荐)
    // 添加依赖:implementation 'com.github.PauloCereda:permissions:1.0.0'
    // 使用示例:
    public void requestPermissions() {
        PermissionsManager.requestPermissions(
            this,
            new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
            new PermissionsManager.PermissionCallback() {
                @Override
                public void onPermissionGranted() {
                    // 权限全部授予
                    openCamera();
                }
                
                @Override
                public void onPermissionDenied(String[] deniedPermissions) {
                    // 部分权限被拒绝
                    showPermissionDeniedDialog();
                }
            }
        );
    }
}

5.4 屏幕适配问题

问题: 不同设备的屏幕尺寸、密度和方向各不相同,如何确保应用在所有设备上都能正常显示。

解决方案:

  1. 使用ConstraintLayout:自动适应不同屏幕尺寸
  2. 使用dp和sp单位:dp用于布局,sp用于字体大小
  3. 创建多个布局文件:为不同屏幕尺寸创建不同的布局
  4. 使用百分比布局:使用百分比控制元素大小

示例:创建多个布局文件

res/
├── layout/
│   ├── activity_main.xml          // 默认布局
│   └── activity_main_land.xml     // 横屏布局
├── layout-sw600dp/                // 7英寸平板
│   └── activity_main.xml
├── layout-sw720dp/                // 10英寸平板
│   └── activity_main.xml
└── values/
    ├── strings.xml
    └── dimens.xml                 // 尺寸资源

dimens.xml示例:

<!-- values/dimens.xml -->
<resources>
    <dimen name="padding_medium">16dp</dimen>
    <dimen name="text_size_large">18sp</dimen>
</resources>

<!-- values-sw600dp/dimens.xml -->
<resources>
    <dimen name="padding_medium">24dp</dimen>
    <dimen name="text_size_large">22sp</dimen>
</resources>

5.5 性能优化问题

问题: 应用卡顿、内存占用高、耗电快等性能问题。

解决方案:

1. 布局优化:

<!-- 避免过度嵌套 -->
<!-- 错误示例:嵌套过多 -->
<LinearLayout>
    <LinearLayout>
        <LinearLayout>
            <TextView />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

<!-- 正确示例:使用ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout>
    <TextView
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

2. 列表优化(RecyclerView):

// 1. 使用DiffUtil优化列表更新
public class UserDiffCallback extends DiffUtil.Callback {
    private List<User> oldList;
    private List<User> newList;
    
    public UserDiffCallback(List<User> oldList, List<User> 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. 在适配器中使用
public void updateList(List<User> newList) {
    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
        new UserDiffCallback(this.userList, newList)
    );
    this.userList.clear();
    this.userList.addAll(newList);
    diffResult.dispatchUpdatesTo(this);
}

// 3. 使用ViewHolder缓存
static class UserViewHolder extends RecyclerView.ViewHolder {
    // 缓存视图引用
    TextView nameTextView;
    TextView emailTextView;
    
    public UserViewHolder(@NonNull View itemView) {
        super(itemView);
        nameTextView = itemView.findViewById(R.id.nameTextView);
        emailTextView = itemView.findViewById(R.id.emailTextView);
    }
}

3. 内存优化:

// 1. 及时释放Bitmap内存
public void loadBitmap(ImageView imageView, String url) {
    // 使用Glide或Picasso等库自动管理内存
    Glide.with(this)
        .load(url)
        .into(imageView);
}

// 2. 避免在onDraw中创建对象
// 错误示例:
@Override
protected void onDraw(Canvas canvas) {
    Paint paint = new Paint(); // 每次绘制都创建新对象
    // ...
}

// 正确示例:
private Paint paint = new Paint(); // 作为成员变量

@Override
protected void onDraw(Canvas canvas) {
    // 重用paint对象
    // ...
}

// 3. 使用LruCache缓存图片
public class BitmapCache {
    private LruCache<String, Bitmap> memoryCache;
    
    public BitmapCache() {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8; // 使用1/8的内存
        
        memoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount() / 1024;
            }
        };
    }
    
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            memoryCache.put(key, bitmap);
        }
    }
    
    public Bitmap getBitmapFromMemCache(String key) {
        return memoryCache.get(key);
    }
}

5.6 兼容性问题

问题: 不同Android版本、不同厂商定制系统之间的兼容性问题。

解决方案:

1. 版本检查:

// 检查Android版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    // Android 8.0及以上版本的代码
    NotificationChannel channel = new NotificationChannel(
        "channel_id",
        "Channel Name",
        NotificationManager.IMPORTANCE_DEFAULT
    );
    notificationManager.createNotificationChannel(channel);
} else {
    // 旧版本的处理方式
    Notification notification = new Notification.Builder(this)
        .setContentTitle("标题")
        .setContentText("内容")
        .build();
}

2. 使用兼容库:

// 使用AndroidX库确保兼容性
dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'androidx.fragment:fragment-ktx:1.5.5'
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}

3. 处理厂商定制系统:

// 检查是否为特定厂商设备
public boolean isXiaomiDevice() {
    return Build.MANUFACTURER.equalsIgnoreCase("Xiaomi");
}

public boolean isHuaweiDevice() {
    return Build.MANUFACTURER.equalsIgnoreCase("Huawei");
}

// 针对不同厂商的特殊处理
public void handleManufacturerSpecificIssue() {
    if (isXiaomiDevice()) {
        // 小米设备的特殊处理
        // 例如:小米的后台限制较严格,需要特殊处理
    } else if (isHuaweiDevice()) {
        // 华为设备的特殊处理
    }
}

5.7 测试与调试

问题: 如何确保应用质量,快速定位和修复问题。

解决方案:

1. 单元测试:

// 1. 添加测试依赖
dependencies {
    testImplementation 'junit:junit:4.13.2'
    testImplementation 'org.mockito:mockito-core:4.8.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

// 2. 创建单元测试
public class UserViewModelTest {
    private UserViewModel viewModel;
    private UserRepository mockRepository;
    
    @Before
    public void setUp() {
        mockRepository = mock(UserRepository.class);
        viewModel = new UserViewModel(mockRepository);
    }
    
    @Test
    public void testLoadUser_Success() {
        // 准备测试数据
        User expectedUser = new User(1, "Test User", "test@example.com");
        when(mockRepository.getUser(1)).thenReturn(expectedUser);
        
        // 执行测试
        viewModel.loadUser(1);
        
        // 验证结果
        LiveData<User> liveData = viewModel.getUser();
        User actualUser = liveData.getValue();
        
        assertNotNull(actualUser);
        assertEquals(expectedUser.getName(), actualUser.getName());
    }
    
    @Test
    public void testLoadUser_Failure() {
        // 模拟异常
        when(mockRepository.getUser(1)).thenThrow(new RuntimeException("Network error"));
        
        // 执行测试
        viewModel.loadUser(1);
        
        // 验证错误处理
        // ...
    }
}

2. UI测试:

// 使用Espresso进行UI测试
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
    
    @Rule
    public ActivityScenarioRule<MainActivity> activityRule = 
        new ActivityScenarioRule<>(MainActivity.class);
    
    @Test
    public void testButtonClick() {
        // 查找按钮并点击
        onView(withId(R.id.myButton))
            .perform(click());
        
        // 验证结果
        onView(withId(R.id.resultTextView))
            .check(matches(withText("按钮被点击了")));
    }
    
    @Test
    public void testEditTextInput() {
        // 输入文本
        onView(withId(R.id.editText))
            .perform(typeText("Hello"), closeSoftKeyboard());
        
        // 验证文本
        onView(withId(R.id.editText))
            .check(matches(withText("Hello")));
    }
}

3. 调试工具:

// 1. 使用Logcat查看日志
Log.d("MyApp", "调试信息: " + someVariable);
Log.e("MyApp", "错误信息: " + error.getMessage());

// 2. 使用Android Profiler分析性能
// 在Android Studio中:View -> Tool Windows -> Profiler

// 3. 使用StrictMode检测主线程问题
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        if (BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()
                .detectDiskWrites()
                .detectNetwork()
                .penaltyLog()
                .build());
            
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectLeakedSqlLiteObjects()
                .detectLeakedClosableObjects()
                .penaltyLog()
                .penaltyDeath()
                .build());
        }
    }
}

第六部分:实战项目示例

6.1 项目1:天气预报应用

功能需求:

  1. 显示当前天气信息
  2. 搜索城市天气
  3. 保存历史搜索记录
  4. 显示未来7天天气预报

技术栈:

  • Retrofit + OkHttp:网络请求
  • Gson:JSON解析
  • Room:本地数据库
  • ViewModel + LiveData:架构组件
  • RecyclerView:列表显示

核心代码示例:

1. 数据模型:

// 天气数据模型
public class Weather {
    @SerializedName("name")
    private String cityName;
    
    @SerializedName("main")
    private Main main;
    
    @SerializedName("weather")
    private List<WeatherCondition> weather;
    
    // Getter和setter省略
}

public class Main {
    @SerializedName("temp")
    private double temperature;
    
    @SerializedName("humidity")
    private int humidity;
    
    // Getter和setter省略
}

public class WeatherCondition {
    @SerializedName("description")
    private String description;
    
    @SerializedName("icon")
    private String icon;
    
    // Getter和setter省略
}

2. API接口:

public interface WeatherApiService {
    @GET("weather")
    Call<Weather> 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(Context context) {
        apiService = RetrofitClient.getInstance().create(WeatherApiService.class);
        weatherDao = WeatherDatabase.getInstance(context).weatherDao();
    }
    
    public LiveData<Weather> getCurrentWeather(String city) {
        MutableLiveData<Weather> result = new MutableLiveData<>();
        
        apiService.getCurrentWeather(city, BuildConfig.API_KEY)
            .enqueue(new Callback<Weather>() {
                @Override
                public void onResponse(Call<Weather> call, Response<Weather> response) {
                    if (response.isSuccessful() && response.body() != null) {
                        result.setValue(response.body());
                        
                        // 保存到数据库
                        new Thread(() -> {
                            weatherDao.insertWeather(response.body());
                        }).start();
                    }
                }
                
                @Override
                public void onFailure(Call<Weather> call, Throwable t) {
                    // 从数据库加载缓存
                    new Thread(() -> {
                        Weather cached = weatherDao.getWeather(city);
                        result.postValue(cached);
                    }).start();
                }
            });
        
        return result;
    }
}

4. ViewModel:

public class WeatherViewModel extends ViewModel {
    private WeatherRepository repository;
    private MutableLiveData<String> errorMessage = new MutableLiveData<>();
    
    public WeatherViewModel() {
        repository = new WeatherRepository(getApplication());
    }
    
    public LiveData<Weather> getWeather(String city) {
        return repository.getCurrentWeather(city);
    }
    
    public LiveData<String> getErrorMessage() {
        return errorMessage;
    }
    
    public void searchWeather(String city) {
        if (city.isEmpty()) {
            errorMessage.setValue("请输入城市名称");
            return;
        }
        
        // 执行搜索
        getWeather(city);
    }
}

5. UI界面:

<!-- activity_weather.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">

    <EditText
        android:id="@+id/cityEditText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="输入城市名称"
        android:imeOptions="actionSearch"
        android:inputType="text"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_margin="16dp" />

    <TextView
        android:id="@+id/temperatureTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="48sp"
        android:textStyle="bold"
        app:layout_constraintTop_toBottomOf="@id/cityEditText"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="32dp" />

    <TextView
        android:id="@+id/descriptionTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        app:layout_constraintTop_toBottomOf="@id/temperatureTextView"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/forecastRecyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/descriptionTextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_margin="16dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

6.2 项目2:待办事项应用(Todo List)

功能需求:

  1. 添加、编辑、删除待办事项
  2. 标记事项完成状态
  3. 按优先级排序
  4. 数据持久化存储
  5. 搜索和过滤功能

技术栈:

  • Room:本地数据库
  • ViewModel + LiveData:架构组件
  • RecyclerView:列表显示
  • Material Design:UI组件

核心代码示例:

1. 数据库设计:

// 实体类
@Entity(tableName = "todos")
public class TodoItem {
    @PrimaryKey(autoGenerate = true)
    private int id;
    
    private String title;
    private String description;
    private long dueDate;
    private int priority; // 1: 高, 2: 中, 3: 低
    private boolean completed;
    private long createdAt;
    
    // 构造函数、getter和setter省略
}

// DAO接口
@Dao
public interface TodoDao {
    @Insert
    void insert(TodoItem todo);
    
    @Update
    void update(TodoItem todo);
    
    @Delete
    void delete(TodoItem todo);
    
    @Query("SELECT * FROM todos ORDER BY priority ASC, dueDate ASC")
    LiveData<List<TodoItem>> getAllTodos();
    
    @Query("SELECT * FROM todos WHERE completed = 0 ORDER BY priority ASC, dueDate ASC")
    LiveData<List<TodoItem>> getActiveTodos();
    
    @Query("SELECT * FROM todos WHERE title LIKE '%' || :query || '%' OR description LIKE '%' || :query || '%'")
    LiveData<List<TodoItem>> searchTodos(String query);
    
    @Query("SELECT * FROM todos WHERE priority = :priority ORDER BY dueDate ASC")
    LiveData<List<TodoItem>> getTodosByPriority(int priority);
}

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

2. Repository:

public class TodoRepository {
    private TodoDao todoDao;
    private LiveData<List<TodoItem>> allTodos;
    
    public TodoRepository(Context context) {
        TodoDatabase db = TodoDatabase.getInstance(context);
        todoDao = db.todoDao();
        allTodos = todoDao.getAllTodos();
    }
    
    public LiveData<List<TodoItem>> getAllTodos() {
        return allTodos;
    }
    
    public LiveData<List<TodoItem>> getActiveTodos() {
        return todoDao.getActiveTodos();
    }
    
    public LiveData<List<TodoItem>> searchTodos(String query) {
        return todoDao.searchTodos(query);
    }
    
    public void insert(TodoItem todo) {
        new Thread(() -> todoDao.insert(todo)).start();
    }
    
    public void update(TodoItem todo) {
        new Thread(() -> todoDao.update(todo)).start();
    }
    
    public void delete(TodoItem todo) {
        new Thread(() -> todoDao.delete(todo)).start();
    }
}

3. ViewModel:

public class TodoViewModel extends ViewModel {
    private TodoRepository repository;
    private LiveData<List<TodoItem>> allTodos;
    
    public TodoViewModel() {
        repository = new TodoRepository(getApplication());
        allTodos = repository.getAllTodos();
    }
    
    public LiveData<List<TodoItem>> getAllTodos() {
        return allTodos;
    }
    
    public LiveData<List<TodoItem>> getActiveTodos() {
        return repository.getActiveTodos();
    }
    
    public LiveData<List<TodoItem>> searchTodos(String query) {
        return repository.searchTodos(query);
    }
    
    public void insert(TodoItem todo) {
        repository.insert(todo);
    }
    
    public void update(TodoItem todo) {
        repository.update(todo);
    }
    
    public void delete(TodoItem todo) {
        repository.delete(todo);
    }
}

4. RecyclerView适配器:

public class TodoAdapter extends RecyclerView.Adapter<TodoAdapter.TodoViewHolder> {
    private List<TodoItem> todoList = new ArrayList<>();
    private OnItemClickListener listener;
    
    public interface OnItemClickListener {
        void onItemClick(TodoItem todo);
        void onItemLongClick(TodoItem todo);
        void onCheckboxClick(TodoItem todo, boolean isChecked);
    }
    
    public TodoAdapter(OnItemClickListener listener) {
        this.listener = listener;
    }
    
    @NonNull
    @Override
    public TodoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item_todo, parent, false);
        return new TodoViewHolder(view);
    }
    
    @Override
    public void onBindViewHolder(@NonNull TodoViewHolder holder, int position) {
        TodoItem todo = todoList.get(position);
        
        holder.titleTextView.setText(todo.getTitle());
        holder.descriptionTextView.setText(todo.getDescription());
        holder.completedCheckBox.setChecked(todo.isCompleted());
        
        // 设置优先级颜色
        int priorityColor;
        switch (todo.getPriority()) {
            case 1: // 高优先级
                priorityColor = Color.RED;
                break;
            case 2: // 中优先级
                priorityColor = Color.YELLOW;
                break;
            default: // 低优先级
                priorityColor = Color.GREEN;
        }
        holder.priorityIndicator.setBackgroundColor(priorityColor);
        
        // 设置点击事件
        holder.itemView.setOnClickListener(v -> {
            if (listener != null) {
                listener.onItemClick(todo);
            }
        });
        
        holder.itemView.setOnLongClickListener(v -> {
            if (listener != null) {
                listener.onItemLongClick(todo);
            }
            return true;
        });
        
        holder.completedCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (listener != null) {
                listener.onCheckboxClick(todo, isChecked);
            }
        });
    }
    
    @Override
    public int getItemCount() {
        return todoList.size();
    }
    
    public void setTodoList(List<TodoItem> todoList) {
        this.todoList = todoList;
        notifyDataSetChanged();
    }
    
    static class TodoViewHolder extends RecyclerView.ViewHolder {
        TextView titleTextView;
        TextView descriptionTextView;
        CheckBox completedCheckBox;
        View priorityIndicator;
        
        public TodoViewHolder(@NonNull View itemView) {
            super(itemView);
            titleTextView = itemView.findViewById(R.id.titleTextView);
            descriptionTextView = itemView.findViewById(R.id.descriptionTextView);
            completedCheckBox = itemView.findViewById(R.id.completedCheckBox);
            priorityIndicator = itemView.findViewById(R.id.priorityIndicator);
        }
    }
}

5. 主界面:

public class TodoListActivity extends AppCompatActivity {
    private TodoViewModel viewModel;
    private TodoAdapter adapter;
    private RecyclerView recyclerView;
    private EditText searchEditText;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_todo_list);
        
        initViews();
        setupRecyclerView();
        setupViewModel();
        setupSearch();
    }
    
    private void initViews() {
        recyclerView = findViewById(R.id.recyclerView);
        searchEditText = findViewById(R.id.searchEditText);
        FloatingActionButton fab = findViewById(R.id.fab);
        
        fab.setOnClickListener(v -> showAddTodoDialog());
    }
    
    private void setupRecyclerView() {
        adapter = new TodoAdapter(new TodoAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(TodoItem todo) {
                showEditTodoDialog(todo);
            }
            
            @Override
            public void onItemLongClick(TodoItem todo) {
                showDeleteConfirmationDialog(todo);
            }
            
            @Override
            public void onCheckboxClick(TodoItem todo, boolean isChecked) {
                todo.setCompleted(isChecked);
                viewModel.update(todo);
            }
        });
        
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);
    }
    
    private void setupViewModel() {
        viewModel = new ViewModelProvider(this).get(TodoViewModel.class);
        
        viewModel.getAllTodos().observe(this, todos -> {
            if (todos != null) {
                adapter.setTodoList(todos);
            }
        });
    }
    
    private void setupSearch() {
        searchEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
            
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                String query = s.toString().trim();
                if (query.isEmpty()) {
                    viewModel.getAllTodos().observe(TodoListActivity.this, todos -> {
                        if (todos != null) {
                            adapter.setTodoList(todos);
                        }
                    });
                } else {
                    viewModel.searchTodos(query).observe(TodoListActivity.this, todos -> {
                        if (todos != null) {
                            adapter.setTodoList(todos);
                        }
                    });
                }
            }
            
            @Override
            public void afterTextChanged(Editable s) {}
        });
    }
    
    private void showAddTodoDialog() {
        // 创建对话框
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        View dialogView = getLayoutInflater().inflate(R.layout.dialog_add_todo, null);
        
        EditText titleEditText = dialogView.findViewById(R.id.titleEditText);
        EditText descriptionEditText = dialogView.findViewById(R.id.descriptionEditText);
        Spinner prioritySpinner = dialogView.findViewById(R.id.prioritySpinner);
        
        builder.setView(dialogView)
            .setTitle("添加待办事项")
            .setPositiveButton("添加", (dialog, which) -> {
                String title = titleEditText.getText().toString().trim();
                String description = descriptionEditText.getText().toString().trim();
                int priority = prioritySpinner.getSelectedItemPosition() + 1;
                
                if (!title.isEmpty()) {
                    TodoItem todo = new TodoItem();
                    todo.setTitle(title);
                    todo.setDescription(description);
                    todo.setPriority(priority);
                    todo.setCompleted(false);
                    todo.setDueDate(System.currentTimeMillis());
                    todo.setCreatedAt(System.currentTimeMillis());
                    
                    viewModel.insert(todo);
                }
            })
            .setNegativeButton("取消", null)
            .show();
    }
    
    private void showEditTodoDialog(TodoItem todo) {
        // 编辑对话框实现类似添加对话框
        // ...
    }
    
    private void showDeleteConfirmationDialog(TodoItem todo) {
        new AlertDialog.Builder(this)
            .setTitle("删除待办事项")
            .setMessage("确定要删除 \"" + todo.getTitle() + "\" 吗?")
            .setPositiveButton("删除", (dialog, which) -> {
                viewModel.delete(todo);
            })
            .setNegativeButton("取消", null)
            .show();
    }
}

第七部分:进阶学习与资源推荐

7.1 推荐的学习路径

  1. 基础阶段(1-2个月)

    • Java/Kotlin语言基础
    • Android基础组件(Activity、Fragment、Service)
    • UI开发(布局、常用控件)
    • 数据存储(SharedPreferences、SQLite)
  2. 进阶阶段(2-3个月)

    • 网络通信(Retrofit、OkHttp)
    • 架构组件(ViewModel、LiveData、Room)
    • Jetpack Compose(现代UI开发)
    • 多线程与异步处理
  3. 高级阶段(3-6个月)

    • 性能优化
    • 安全与加密
    • 测试与调试
    • CI/CD集成

7.2 推荐的学习资源

官方资源:

在线课程:

  • Udacity Android开发纳米学位
  • Coursera Android专项课程
  • Google官方培训课程

书籍推荐:

  • 《Android编程权威指南》
  • 《Kotlin实战》
  • 《Android开发艺术探索》

开源项目:

  • Android Architecture Components官方示例
  • Google Samples
  • Awesome Android开源项目列表

7.3 常用工具和库

开发工具:

  • Android Studio:官方IDE
  • Genymotion:第三方模拟器
  • LeakCanary:内存泄漏检测
  • Stetho:调试工具

常用库:

  • 网络:Retrofit、OkHttp、Volley
  • 图片加载:Glide、Picasso、Coil
  • 数据库:Room、GreenDAO
  • 依赖注入:Dagger、Hilt
  • 事件总线:EventBus、RxJava
  • UI组件:Material Design Components

7.4 社区与支持

技术社区:

  • Stack Overflow:问题解答
  • Reddit r/androiddev:开发者社区
  • GitHub:开源项目和代码示例
  • Medium:技术博客和文章

官方支持:

  • Android开发者论坛
  • Google Groups
  • Twitter @AndroidDev

结语

Android开发是一个持续学习和实践的过程。通过本指南的学习,你应该已经掌握了从基础到进阶的Android开发知识。记住,最好的学习方式是动手实践,尝试自己构建项目,解决实际问题。

在开发过程中,遇到问题时不要气馁,这是成长的机会。善用官方文档、社区资源和调试工具,逐步提升自己的技能水平。

最后,保持对新技术的好奇心,Android生态系统在不断演进,Jetpack Compose、Kotlin Multiplatform等新技术正在改变开发方式。持续学习,保持更新,你将成为一名优秀的Android开发者。

祝你在Android开发的道路上取得成功!