引言
Android开发是一个充满活力的领域,从智能手机到智能手表、电视和汽车,Android系统无处不在。对于初学者来说,Android开发可能看起来很复杂,但通过系统的学习和实践,任何人都可以掌握它。本指南将从零基础开始,通过详细的实例分析,逐步引导你成为一名合格的Android开发者,并涵盖在实战开发中常见的问题及其解决方案。
第一部分:Android开发基础
1.1 Android开发环境搭建
在开始编写代码之前,我们需要搭建一个完整的开发环境。Android Studio是Google官方推荐的集成开发环境(IDE),它提供了代码编辑、调试、性能分析和模拟器管理等所有必要的工具。
安装步骤:
- 访问Android Studio官网(https://developer.android.com/studio)下载最新版本。
- 运行安装程序,按照向导完成安装。
- 首次启动时,Android Studio会引导你安装Android SDK和其他必要组件。
- 配置模拟器或连接真实设备进行测试。
验证安装: 创建一个新项目,选择”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应用由四个核心组件构成:
- Activity:用户交互界面,一个Activity通常对应一个屏幕
- Service:后台服务,执行长时间运行的操作
- BroadcastReceiver:接收和响应系统广播
- 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元素:
- LinearLayout:线性布局,元素按水平或垂直方向排列
- RelativeLayout:相对布局,元素相对于其他元素或父容器定位
- ConstraintLayout:约束布局,最灵活的布局方式,支持扁平化视图层次
- 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进行网络请求
- 添加依赖
在
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'
}
- 定义数据模型
// 用户数据模型
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省略
}
- 定义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();
}
- 创建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;
}
}
- 在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开发中最常见的问题之一,会导致应用占用内存不断增加,最终导致应用崩溃。
常见原因:
- 非静态内部类持有外部类引用
- 未取消的监听器或回调
- 静态Context引用
- 未关闭的资源(如数据库连接、文件流)
解决方案:
// 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. 将耗时操作移到后台线程
// 错误示例:
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 屏幕适配问题
问题: 不同设备的屏幕尺寸、密度和方向各不相同,如何确保应用在所有设备上都能正常显示。
解决方案:
- 使用ConstraintLayout:自动适应不同屏幕尺寸
- 使用dp和sp单位:dp用于布局,sp用于字体大小
- 创建多个布局文件:为不同屏幕尺寸创建不同的布局
- 使用百分比布局:使用百分比控制元素大小
示例:创建多个布局文件
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:天气预报应用
功能需求:
- 显示当前天气信息
- 搜索城市天气
- 保存历史搜索记录
- 显示未来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)
功能需求:
- 添加、编辑、删除待办事项
- 标记事项完成状态
- 按优先级排序
- 数据持久化存储
- 搜索和过滤功能
技术栈:
- 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-2个月)
- Java/Kotlin语言基础
- Android基础组件(Activity、Fragment、Service)
- UI开发(布局、常用控件)
- 数据存储(SharedPreferences、SQLite)
进阶阶段(2-3个月)
- 网络通信(Retrofit、OkHttp)
- 架构组件(ViewModel、LiveData、Room)
- Jetpack Compose(现代UI开发)
- 多线程与异步处理
高级阶段(3-6个月)
- 性能优化
- 安全与加密
- 测试与调试
- CI/CD集成
7.2 推荐的学习资源
官方资源:
- Android开发者官网:https://developer.android.com/
- Android开发者博客:https://android-developers.googleblog.com/
- Android官方示例:https://github.com/android
在线课程:
- 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开发的道路上取得成功!
