引言
Android作为全球最流行的移动操作系统,拥有超过25亿活跃设备。对于开发者而言,掌握Android编程不仅意味着能够开发应用,更是进入移动开发领域的关键技能。本文将通过一系列实例,从基础概念到高级实战,全面解析Android开发的完整流程。
第一部分:Android开发基础
1.1 Android开发环境搭建
在开始编程之前,我们需要搭建完整的开发环境。Android Studio是Google官方推荐的集成开发环境(IDE)。
安装步骤:
- 下载Android Studio(最新版本为2023.1.1)
- 安装JDK(Java Development Kit)11或更高版本
- 配置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
配置签名:
- 在Android Studio中,选择Build → Generate Signed Bundle/APK
- 创建或选择现有的密钥库
- 配置构建变体(release)
- 生成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发布
发布前检查清单:
- 应用图标和截图
- 应用描述和功能列表
- 隐私政策链接
- 内容分级问卷
- 目标API级别(至少30)
发布流程:
- 登录Google Play Console
- 创建新应用
- 上传APK/AAB
- 填写商店信息
- 设置定价和分发
- 提交审核
总结
通过本文的完整指南,我们从Android开发的基础知识开始,逐步深入到高级主题和实战项目。关键要点包括:
- 基础扎实:理解Activity生命周期、Fragment使用和UI布局
- 数据管理:掌握SharedPreferences、Room数据库和网络通信
- 现代开发:学习Jetpack Compose和MVVM架构
- 性能优化:关注内存管理、电池优化和网络效率
- 测试驱动:编写单元测试和UI测试确保代码质量
- 发布部署:了解签名和Google Play发布流程
下一步建议:
- 尝试将待办事项应用扩展为完整项目
- 学习Kotlin协程进行异步编程
- 探索更多Jetpack组件(Navigation、Data Binding等)
- 参与开源项目或创建自己的应用
记住,Android开发是一个持续学习的过程。保持对新技术的关注,不断实践和优化,你将成为一名优秀的Android开发者。
