引言

Android作为全球最流行的移动操作系统,拥有超过25亿活跃设备。对于开发者而言,掌握Android编程不仅意味着能够开发应用,更是进入移动开发领域的关键技能。本文将通过一系列实例,从基础概念到高级实战,全面解析Android开发的完整流程。

第一部分:Android开发基础

1.1 Android开发环境搭建

在开始编程之前,我们需要搭建完整的开发环境。Android Studio是Google官方推荐的集成开发环境(IDE)。

安装步骤:

  1. 下载Android Studio(最新版本为2023.1.1)
  2. 安装JDK(Java Development Kit)11或更高版本
  3. 配置Android SDK和模拟器

验证安装:

# 在终端中运行以下命令验证安装
adb version
# 应该显示类似:Android Debug Bridge version 1.0.41

1.2 Android应用基本结构

每个Android应用都由以下核心组件构成:

  • Activity:用户交互界面
  • Service:后台服务
  • Broadcast Receiver:广播接收器
  • Content Provider:数据共享

示例:创建第一个Activity

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

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("Hello, 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: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>

第二部分:核心组件深入解析

2.1 Activity生命周期管理

Activity的生命周期是Android开发的核心概念。理解生命周期可以避免内存泄漏和状态丢失。

生命周期方法:

public class LifecycleActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lifecycle);
        Log.d("Lifecycle", "onCreate called");
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        Log.d("Lifecycle", "onStart called");
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        Log.d("Lifecycle", "onResume called");
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        Log.d("Lifecycle", "onPause called");
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        Log.d("Lifecycle", "onStop called");
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("Lifecycle", "onDestroy called");
    }
}

生命周期状态图:

onCreate() → onStart() → onResume() → [运行状态] → onPause() → onStop() → onDestroy()

2.2 Fragment的使用

Fragment是可重用的UI组件,特别适合在不同屏幕尺寸的设备上提供灵活的界面。

创建Fragment示例:

// MyFragment.java
public class MyFragment extends Fragment {
    
    private TextView textView;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 加载布局
        View view = inflater.inflate(R.layout.fragment_my, container, false);
        
        // 绑定视图
        textView = view.findViewById(R.id.fragmentTextView);
        textView.setText("This is a Fragment");
        
        // 设置点击事件
        view.findViewById(R.id.button).setOnClickListener(v -> {
            textView.setText("Button clicked!");
        });
        
        return view;
    }
}

在Activity中使用Fragment:

// MainActivity.java
public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 动态添加Fragment
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                .add(R.id.fragment_container, new MyFragment())
                .commit();
        }
    }
}

第三部分:UI设计与交互

3.1 常用布局管理器

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

LinearLayout(线性布局):

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="用户名"
        android:textSize="16sp" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入用户名"
        android:inputType="text" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="登录"
        android:layout_marginTop="16dp" />

</LinearLayout>

RelativeLayout(相对布局):

<RelativeLayout
    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:layout_centerHorizontal="true" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮1"
        android:layout_below="@id/title"
        android:layout_centerHorizontal="true" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮2"
        android:layout_below="@id/button1"
        android:layout_centerHorizontal="true" />

</RelativeLayout>

3.2 RecyclerView的使用

RecyclerView是现代Android开发中用于显示列表数据的首选组件。

创建RecyclerView适配器:

// MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    
    private List<String> dataList;
    
    public MyAdapter(List<String> dataList) {
        this.dataList = dataList;
    }
    
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item_layout, parent, false);
        return new ViewHolder(view);
    }
    
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        String item = dataList.get(position);
        holder.textView.setText(item);
        
        // 设置点击事件
        holder.itemView.setOnClickListener(v -> {
            Toast.makeText(v.getContext(), "点击了: " + item, Toast.LENGTH_SHORT).show();
        });
    }
    
    @Override
    public int getItemCount() {
        return dataList.size();
    }
    
    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.itemTextView);
        }
    }
}

在Activity中使用:

// RecyclerViewActivity.java
public class RecyclerViewActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_view);
        
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        
        // 创建数据
        List<String> data = new ArrayList<>();
        for (int i = 1; i <= 20; i++) {
            data.add("项目 " + i);
        }
        
        // 设置布局管理器
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        
        // 设置适配器
        MyAdapter adapter = new MyAdapter(data);
        recyclerView.setAdapter(adapter);
    }
}

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

4.1 SharedPreferences存储

SharedPreferences是Android中用于存储简单键值对数据的轻量级方案。

使用示例:

// SharedPreferencesHelper.java
public class SharedPreferencesHelper {
    
    private static final String PREF_NAME = "MyAppPrefs";
    private static final String KEY_USERNAME = "username";
    private static final String KEY_IS_LOGGED_IN = "is_logged_in";
    
    private SharedPreferences sharedPreferences;
    
    public SharedPreferencesHelper(Context context) {
        sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
    }
    
    // 保存用户名
    public void saveUsername(String username) {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString(KEY_USERNAME, username);
        editor.apply(); // 异步提交
    }
    
    // 获取用户名
    public String getUsername() {
        return sharedPreferences.getString(KEY_USERNAME, "Guest");
    }
    
    // 保存登录状态
    public void setLoggedIn(boolean isLoggedIn) {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putBoolean(KEY_IS_LOGGED_IN, isLoggedIn);
        editor.apply();
    }
    
    // 检查登录状态
    public boolean isLoggedIn() {
        return sharedPreferences.getBoolean(KEY_IS_LOGGED_IN, false);
    }
    
    // 清除所有数据
    public void clearAll() {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.clear();
        editor.apply();
    }
}

4.2 Room数据库操作

Room是Google推荐的SQLite数据库抽象层,提供了编译时验证和简化数据库操作。

实体类定义:

// User.java
@Entity(tableName = "users")
public class User {
    
    @PrimaryKey(autoGenerate = true)
    public int id;
    
    @ColumnInfo(name = "user_name")
    public String userName;
    
    @ColumnInfo(name = "email")
    public String email;
    
    @ColumnInfo(name = "created_at")
    public long createdAt;
    
    // 构造函数
    public User(String userName, String email, long createdAt) {
        this.userName = userName;
        this.email = email;
        this.createdAt = createdAt;
    }
}

DAO(数据访问对象):

// UserDao.java
@Dao
public interface UserDao {
    
    @Insert
    void insert(User user);
    
    @Insert
    void insertAll(List<User> users);
    
    @Update
    void update(User user);
    
    @Delete
    void delete(User user);
    
    @Query("SELECT * FROM users ORDER BY created_at DESC")
    List<User> getAllUsers();
    
    @Query("SELECT * FROM users WHERE user_name = :userName")
    User getUserByName(String userName);
    
    @Query("DELETE FROM users WHERE id = :userId")
    void deleteUserById(int userId);
}

数据库类:

// AppDatabase.java
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    
    public abstract UserDao userDao();
    
    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, "app_database")
                            .build();
                }
            }
        }
        return INSTANCE;
    }
}

使用示例:

// DatabaseActivity.java
public class DatabaseActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_database);
        
        // 在后台线程中执行数据库操作
        new Thread(() -> {
            AppDatabase db = AppDatabase.getDatabase(this);
            
            // 插入用户
            User user = new User("张三", "zhangsan@example.com", System.currentTimeMillis());
            db.userDao().insert(user);
            
            // 查询所有用户
            List<User> users = db.userDao().getAllUsers();
            
            // 在UI线程中更新UI
            runOnUiThread(() -> {
                // 更新UI显示用户列表
                updateUI(users);
            });
        }).start();
    }
    
    private void updateUI(List<User> users) {
        // 更新UI代码
    }
}

4.3 Retrofit网络请求

Retrofit是Android中最流行的HTTP客户端库,用于与RESTful API通信。

定义API接口:

// ApiService.java
public interface ApiService {
    
    @GET("users/{id}")
    Call<User> getUserById(@Path("id") int userId);
    
    @GET("users")
    Call<List<User>> getAllUsers();
    
    @POST("users")
    Call<User> createUser(@Body User user);
    
    @PUT("users/{id}")
    Call<User> updateUser(@Path("id") int userId, @Body User user);
    
    @DELETE("users/{id}")
    Call<Void> deleteUser(@Path("id") int userId);
}

创建Retrofit实例:

// RetrofitClient.java
public class RetrofitClient {
    
    private static final String BASE_URL = "https://api.example.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;
    }
    
    public static ApiService getApiService() {
        return getClient().create(ApiService.class);
    }
}

使用示例:

// NetworkActivity.java
public class NetworkActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_network);
        
        // 获取用户数据
        fetchUserData();
    }
    
    private void fetchUserData() {
        ApiService apiService = RetrofitClient.getApiService();
        
        Call<User> call = apiService.getUserById(1);
        
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccessful() && response.body() != null) {
                    User user = response.body();
                    // 更新UI
                    updateUI(user);
                }
            }
            
            @Override
            public void onFailure(Call<User> call, Throwable t) {
                // 处理错误
                Toast.makeText(NetworkActivity.this, 
                    "请求失败: " + t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }
    
    private void updateUI(User user) {
        // 更新UI代码
    }
}

第五部分:高级主题与实战项目

5.1 Jetpack Compose基础

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

基本组件示例:

// MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyTheme {
                Surface(color = MaterialTheme.colors.background) {
                    Greeting("Android")
                }
            }
        }
    }
}

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

复杂UI示例:

// UserListScreen.kt
@Composable
fun UserListScreen() {
    val viewModel: UserViewModel = viewModel()
    val users by viewModel.users.collectAsState()
    
    LazyColumn {
        items(users) { user ->
            UserItem(user = user, onClick = {
                // 处理点击事件
                viewModel.selectUser(user)
            })
        }
    }
}

@Composable
fun UserItem(user: User, onClick: () -> Unit) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp)
            .clickable { onClick() },
        elevation = 4.dp
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(
                text = user.userName,
                style = MaterialTheme.typography.h6
            )
            Text(
                text = user.email,
                style = MaterialTheme.typography.body2
            )
        }
    }
}

5.2 实战项目:待办事项应用

让我们创建一个完整的待办事项应用,整合前面学到的所有知识。

项目结构:

TodoApp/
├── app/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/com/example/todoapp/
│   │   │   │   ├── data/
│   │   │   │   │   ├── TodoItem.kt
│   │   │   │   │   ├── TodoDao.kt
│   │   │   │   │   └── AppDatabase.kt
│   │   │   │   ├── ui/
│   │   │   │   │   ├── MainActivity.kt
│   │   │   │   │   ├── TodoAdapter.kt
│   │   │   │   │   └── TodoViewModel.kt
│   │   │   │   └── repository/
│   │   │   │       └── TodoRepository.kt
│   │   │   └── res/
│   │   │       ├── layout/
│   │   │       │   └── activity_main.xml
│   │   │       └── values/
│   │   │           └── strings.xml

数据模型:

// TodoItem.kt
@Entity(tableName = "todo_items")
data class TodoItem(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val title: String,
    val description: String,
    val completed: Boolean = false,
    val createdAt: Long = System.currentTimeMillis()
)

Repository模式:

// TodoRepository.kt
class TodoRepository(private val todoDao: TodoDao) {
    
    val allTodos: LiveData<List<TodoItem>> = todoDao.getAllTodos()
    
    suspend fun insert(todo: TodoItem) {
        todoDao.insert(todo)
    }
    
    suspend fun update(todo: TodoItem) {
        todoDao.update(todo)
    }
    
    suspend fun delete(todo: TodoItem) {
        todoDao.delete(todo)
    }
}

ViewModel:

// TodoViewModel.kt
class TodoViewModel(application: Application) : AndroidViewModel(application) {
    
    private val repository: TodoRepository
    val allTodos: LiveData<List<TodoItem>>
    
    init {
        val todoDao = AppDatabase.getDatabase(application).todoDao()
        repository = TodoRepository(todoDao)
        allTodos = repository.allTodos
    }
    
    fun insert(todo: TodoItem) = viewModelScope.launch {
        repository.insert(todo)
    }
    
    fun update(todo: TodoItem) = viewModelScope.launch {
        repository.update(todo)
    }
    
    fun delete(todo: TodoItem) = viewModelScope.launch {
        repository.delete(todo)
    }
}

Adapter:

// TodoAdapter.kt
class TodoAdapter(
    private val onItemClicked: (TodoItem) -> Unit,
    private val onItemDeleted: (TodoItem) -> Unit
) : RecyclerView.Adapter<TodoAdapter.ViewHolder>() {
    
    private var todoItems = emptyList<TodoItem>()
    
    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val title: TextView = itemView.findViewById(R.id.todoTitle)
        val description: TextView = itemView.findViewById(R.id.todoDescription)
        val checkbox: CheckBox = itemView.findViewById(R.id.todoCheckbox)
        val deleteButton: ImageButton = itemView.findViewById(R.id.deleteButton)
    }
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_todo, parent, false)
        return ViewHolder(view)
    }
    
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val current = todoItems[position]
        
        holder.title.text = current.title
        holder.description.text = current.description
        holder.checkbox.isChecked = current.completed
        
        holder.itemView.setOnClickListener {
            onItemClicked(current)
        }
        
        holder.deleteButton.setOnClickListener {
            onItemDeleted(current)
        }
        
        holder.checkbox.setOnCheckedChangeListener { _, isChecked ->
            val updated = current.copy(completed = isChecked)
            onItemClicked(updated)
        }
    }
    
    override fun getItemCount() = todoItems.size
    
    fun setTodos(todos: List<TodoItem>) {
        todoItems = todos
        notifyDataSetChanged()
    }
}

MainActivity:

// MainActivity.kt
class MainActivity : AppCompatActivity() {
    
    private lateinit var viewModel: TodoViewModel
    private lateinit var adapter: TodoAdapter
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        setupViewModel()
        setupRecyclerView()
        setupFab()
    }
    
    private fun setupViewModel() {
        viewModel = ViewModelProvider(this).get(TodoViewModel::class.java)
        
        viewModel.allTodos.observe(this, Observer { todos ->
            adapter.setTodos(todos)
        })
    }
    
    private fun setupRecyclerView() {
        adapter = TodoAdapter(
            onItemClicked = { todo ->
                // 更新待办事项
                viewModel.update(todo)
            },
            onItemDeleted = { todo ->
                // 删除待办事项
                viewModel.delete(todo)
            }
        )
        
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = adapter
    }
    
    private fun setupFab() {
        fab.setOnClickListener {
            showAddTodoDialog()
        }
    }
    
    private fun showAddTodoDialog() {
        val dialogView = layoutInflater.inflate(R.layout.dialog_add_todo, null)
        val titleEditText = dialogView.findViewById<EditText>(R.id.editTitle)
        val descEditText = dialogView.findViewById<EditText>(R.id.editDescription)
        
        AlertDialog.Builder(this)
            .setTitle("添加新待办事项")
            .setView(dialogView)
            .setPositiveButton("添加") { _, _ ->
                val title = titleEditText.text.toString()
                val description = descEditText.text.toString()
                
                if (title.isNotEmpty()) {
                    val newTodo = TodoItem(
                        title = title,
                        description = description
                    )
                    viewModel.insert(newTodo)
                }
            }
            .setNegativeButton("取消", null)
            .show()
    }
}

第六部分:性能优化与最佳实践

6.1 内存管理

避免内存泄漏的常见模式:

// 错误示例:静态Context导致内存泄漏
public class MyManager {
    private static Context context; // 静态引用Context
    
    public static void init(Context context) {
        MyManager.context = context; // 危险!
    }
}

// 正确做法:使用ApplicationContext
public class MyManager {
    private static Context context;
    
    public static void init(Context context) {
        MyManager.context = context.getApplicationContext(); // 安全
    }
}

使用WeakReference:

// 使用WeakReference避免内存泄漏
public class MyActivity extends AppCompatActivity {
    
    private WeakReference<Handler> handlerRef;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        Handler handler = new Handler(Looper.getMainLooper());
        handlerRef = new WeakReference<>(handler);
        
        // 使用时检查是否为null
        Handler h = handlerRef.get();
        if (h != null) {
            h.post(() -> {
                // 执行操作
            });
        }
    }
}

6.2 网络请求优化

使用OkHttp的拦截器:

// 添加日志拦截器
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
    .addInterceptor(chain -> {
        Request originalRequest = chain.request();
        
        // 添加公共请求头
        Request newRequest = originalRequest.newBuilder()
            .header("User-Agent", "MyApp/1.0")
            .header("Authorization", "Bearer " + getAuthToken())
            .build();
        
        return chain.proceed(newRequest);
    })
    .build();

使用缓存策略:

// Retrofit缓存配置
Cache cache = new Cache(getCacheDir(), 10 * 1024 * 1024); // 10MB缓存

OkHttpClient client = new OkHttpClient.Builder()
    .cache(cache)
    .addInterceptor(chain -> {
        Request request = chain.request();
        
        // 检查网络状态
        if (!isNetworkAvailable()) {
            request = request.newBuilder()
                .header("Cache-Control", "public, only-if-cached, max-stale=604800")
                .build();
        } else {
            request = request.newBuilder()
                .header("Cache-Control", "public, max-age=60")
                .build();
        }
        
        return chain.proceed(request);
    })
    .build();

6.3 电池优化

使用WorkManager进行后台任务:

// 定义Worker类
public class SyncWorker extends Worker {
    
    public SyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }
    
    @NonNull
    @Override
    public Result doWork() {
        // 执行后台同步任务
        try {
            // 模拟网络请求
            Thread.sleep(2000);
            
            // 保存结果
            SharedPreferencesHelper helper = new SharedPreferencesHelper(getApplicationContext());
            helper.saveLastSyncTime(System.currentTimeMillis());
            
            return Result.success();
        } catch (Exception e) {
            return Result.failure();
        }
    }
}

安排定期同步:

// 安排每日同步
PeriodicWorkRequest syncRequest = new PeriodicWorkRequest.Builder(
    SyncWorker.class,
    1, // 间隔时间
    TimeUnit.DAYS
).setConstraints(new Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED)
    .setRequiresBatteryNotLow(true)
    .build())
.build();

WorkManager.getInstance(this).enqueueUniquePeriodicWork(
    "daily_sync",
    ExistingPeriodicWorkPolicy.KEEP,
    syncRequest
);

第七部分:测试与调试

7.1 单元测试

使用JUnit和Mockito:

// TodoViewModelTest.java
@RunWith(MockitoJUnitRunner.class)
public class TodoViewModelTest {
    
    @Mock
    private TodoRepository repository;
    
    @Mock
    private Application application;
    
    private TodoViewModel viewModel;
    
    @Before
    public void setUp() {
        viewModel = new TodoViewModel(application);
        // 通过反射设置repository
        try {
            Field field = TodoViewModel.class.getDeclaredField("repository");
            field.setAccessible(true);
            field.set(viewModel, repository);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    @Test
    public void testInsertTodo() {
        // 准备数据
        TodoItem todo = new TodoItem(0, "Test", "Description", false);
        
        // 模拟Repository行为
        doAnswer(invocation -> {
            TodoItem inserted = invocation.getArgument(0);
            assertEquals("Test", inserted.title);
            return null;
        }).when(repository).insert(any(TodoItem.class));
        
        // 执行测试
        viewModel.insert(todo);
        
        // 验证
        verify(repository, times(1)).insert(todo);
    }
}

7.2 UI测试

使用Espresso:

// MainActivityTest.java
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
    
    @Rule
    public ActivityScenarioRule<MainActivity> activityRule =
        new ActivityScenarioRule<>(MainActivity.class);
    
    @Test
    public void testAddTodo() {
        // 点击FAB按钮
        onView(withId(R.id.fab)).perform(click());
        
        // 输入标题
        onView(withId(R.id.editTitle)).perform(typeText("测试待办事项"));
        
        // 输入描述
        onView(withId(R.id.editDescription)).perform(typeText("这是一个测试"));
        
        // 点击添加按钮
        onView(withText("添加")).perform(click());
        
        // 验证新项目是否显示
        onView(withText("测试待办事项")).check(matches(isDisplayed()));
    }
    
    @Test
    public void testDeleteTodo() {
        // 先添加一个待办事项
        onView(withId(R.id.fab)).perform(click());
        onView(withId(R.id.editTitle)).perform(typeText("待删除项目"));
        onView(withText("添加")).perform(click());
        
        // 点击删除按钮
        onView(withId(R.id.deleteButton)).perform(click());
        
        // 验证项目是否被删除
        onView(withText("待删除项目")).check(doesNotExist());
    }
}

第八部分:发布与部署

8.1 生成签名APK

配置签名:

  1. 在Android Studio中,选择Build → Generate Signed Bundle/APK
  2. 创建或选择现有的密钥库
  3. 配置构建变体(release)
  4. 生成APK

Gradle配置:

// app/build.gradle
android {
    signingConfigs {
        release {
            storeFile file("my-release-key.jks")
            storePassword "password"
            keyAlias "my-key-alias"
            keyPassword "password"
        }
    }
    
    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

8.2 Google Play发布

发布前检查清单:

  1. 应用图标和截图
  2. 应用描述和功能列表
  3. 隐私政策链接
  4. 内容分级问卷
  5. 目标API级别(至少30)

发布流程:

  1. 登录Google Play Console
  2. 创建新应用
  3. 上传APK/AAB
  4. 填写商店信息
  5. 设置定价和分发
  6. 提交审核

总结

通过本文的完整指南,我们从Android开发的基础知识开始,逐步深入到高级主题和实战项目。关键要点包括:

  1. 基础扎实:理解Activity生命周期、Fragment使用和UI布局
  2. 数据管理:掌握SharedPreferences、Room数据库和网络通信
  3. 现代开发:学习Jetpack Compose和MVVM架构
  4. 性能优化:关注内存管理、电池优化和网络效率
  5. 测试驱动:编写单元测试和UI测试确保代码质量
  6. 发布部署:了解签名和Google Play发布流程

下一步建议:

  • 尝试将待办事项应用扩展为完整项目
  • 学习Kotlin协程进行异步编程
  • 探索更多Jetpack组件(Navigation、Data Binding等)
  • 参与开源项目或创建自己的应用

记住,Android开发是一个持续学习的过程。保持对新技术的关注,不断实践和优化,你将成为一名优秀的Android开发者。