引言:为什么选择Android开发?
在当今移动互联网时代,Android作为全球市场占有率最高的移动操作系统,拥有超过30亿活跃设备。对于初学者而言,Android开发不仅提供了广阔的就业机会,更是一个能够快速看到成果、持续成长的领域。本文将通过一系列精心设计的实例,从零基础开始,逐步深入,帮助你掌握Android开发的核心技能与实战技巧。
第一部分:环境搭建与基础认知
1.1 开发环境配置
Android Studio是Google官方推荐的集成开发环境(IDE),它基于IntelliJ IDEA构建,提供了代码编辑、调试、性能分析等全方位工具。
安装步骤:
访问Android开发者官网下载最新版本
运行安装程序,选择标准安装(包含Android SDK、模拟器等)
配置环境变量(可选,但推荐): “`bash
Windows系统
ANDROID_HOME = C:\Users\YourName\AppData\Local\Android\Sdk PATH = %ANDROID_HOME%\platform-tools;%ANDROID_HOME%\tools
# macOS/Linux系统 export ANDROID_HOME=\(HOME/Library/Android/sdk export PATH=\)PATH:\(ANDROID_HOME/platform-tools export PATH=\)PATH:$ANDROID_HOME/tools
**验证安装:**
打开终端/命令提示符,输入:
```bash
adb version
如果显示版本信息,说明安装成功。
1.2 Android项目结构解析
创建一个新项目后,你会看到以下核心目录结构:
app/
├── src/
│ ├── main/
│ │ ├── java/ # Java/Kotlin源代码
│ │ ├── res/ # 资源文件
│ │ │ ├── layout/ # XML布局文件
│ │ │ ├── values/ # 字符串、颜色等
│ │ │ ├── drawable/ # 图片资源
│ │ │ └── ...
│ │ └── AndroidManifest.xml # 应用配置文件
│ └── test/ # 单元测试
├── build.gradle # 模块级构建配置
└── ...
关键文件说明:
AndroidManifest.xml:声明应用组件、权限、版本信息build.gradle:定义依赖库、编译选项MainActivity.java:主活动类,应用入口
第二部分:核心组件与UI开发
2.1 Activity生命周期详解
Activity是Android应用的基本组件,负责管理用户界面。理解其生命周期至关重要。
生命周期方法:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化UI,设置监听器
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()
实战技巧:
- 在
onCreate()中初始化UI和耗时操作 - 在
onResume()中恢复数据、重新注册监听器 - 在
onPause()中保存数据、暂停动画 - 在
onDestroy()中释放资源
2.2 布局系统与UI组件
Android提供多种布局方式,最常用的是ConstraintLayout(约束布局)。
示例:创建一个登录界面
<!-- activity_login.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"
android:padding="24dp">
<EditText
android:id="@+id/etUsername"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="用户名"
android:inputType="textEmailAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/etPassword"
app:layout_constraintVertical_chainStyle="packed"
android:layout_marginBottom="16dp" />
<EditText
android:id="@+id/etPassword"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="密码"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etUsername"
app:layout_constraintBottom_toTopOf="@id/btnLogin"
android:layout_marginBottom="24dp" />
<Button
android:id="@+id/btnLogin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="登录"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etPassword"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
在Activity中使用布局:
public class LoginActivity extends AppCompatActivity {
private EditText etUsername, etPassword;
private Button btnLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// 绑定视图
etUsername = findViewById(R.id.etUsername);
etPassword = findViewById(R.id.etPassword);
btnLogin = findViewById(R.id.btnLogin);
// 设置点击监听
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(LoginActivity.this,
"请输入用户名和密码", Toast.LENGTH_SHORT).show();
return;
}
// 模拟登录逻辑
performLogin(username, password);
}
});
}
private void performLogin(String username, String password) {
// 实际开发中这里会调用网络API
if ("admin".equals(username) && "123456".equals(password)) {
Toast.makeText(this, "登录成功!", Toast.LENGTH_SHORT).show();
// 跳转到主界面
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(this, "用户名或密码错误", Toast.LENGTH_SHORT).show();
}
}
}
2.3 RecyclerView列表展示
RecyclerView是展示列表数据的高效组件,支持动态添加、删除、更新数据。
步骤1:创建列表项布局
<!-- item_user.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="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:id="@+id/ivAvatar"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_user"
android:layout_marginEnd="16dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvEmail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#666666" />
</LinearLayout>
</LinearLayout>
步骤2:创建数据模型
// User.java
public class User {
private String name;
private String email;
private int avatarResId;
public User(String name, String email, int avatarResId) {
this.name = name;
this.email = email;
this.avatarResId = avatarResId;
}
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public int getAvatarResId() { return avatarResId; }
public void setAvatarResId(int avatarResId) { this.avatarResId = avatarResId; }
}
步骤3:创建适配器
// UserAdapter.java
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
private List<User> userList;
private Context context;
public UserAdapter(Context context, List<User> userList) {
this.context = context;
this.userList = userList;
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_user, parent, false);
return new UserViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
User user = userList.get(position);
holder.tvName.setText(user.getName());
holder.tvEmail.setText(user.getEmail());
holder.ivAvatar.setImageResource(user.getAvatarResId());
// 设置点击事件
holder.itemView.setOnClickListener(v -> {
Toast.makeText(context, "点击了: " + user.getName(), Toast.LENGTH_SHORT).show();
});
}
@Override
public int getItemCount() {
return userList.size();
}
// ViewHolder内部类
static class UserViewHolder extends RecyclerView.ViewHolder {
ImageView ivAvatar;
TextView tvName, tvEmail;
public UserViewHolder(@NonNull View itemView) {
super(itemView);
ivAvatar = itemView.findViewById(R.id.ivAvatar);
tvName = itemView.findViewById(R.id.tvName);
tvEmail = itemView.findViewById(R.id.tvEmail);
}
}
// 更新数据的方法
public void updateData(List<User> newUsers) {
userList.clear();
userList.addAll(newUsers);
notifyDataSetChanged();
}
}
步骤4:在Activity中使用
public class UserListActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private UserAdapter adapter;
private List<User> userList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_list);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 初始化数据
userList = new ArrayList<>();
userList.add(new User("张三", "zhangsan@example.com", R.drawable.ic_user));
userList.add(new User("李四", "lisi@example.com", R.drawable.ic_user));
userList.add(new User("王五", "wangwu@example.com", R.drawable.ic_user));
adapter = new UserAdapter(this, userList);
recyclerView.setAdapter(adapter);
// 添加分割线
recyclerView.addItemDecoration(new DividerItemDecoration(this,
DividerItemDecoration.VERTICAL));
}
}
第三部分:数据存储与网络通信
3.1 SharedPreferences轻量级存储
SharedPreferences适合存储简单的键值对数据,如用户设置、登录状态等。
示例:保存用户登录状态
public class SharedPreferencesManager {
private static final String PREF_NAME = "app_prefs";
private static final String KEY_IS_LOGGED_IN = "is_logged_in";
private static final String KEY_USERNAME = "username";
private SharedPreferences sharedPreferences;
public SharedPreferencesManager(Context context) {
sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
}
// 保存登录状态
public void saveLoginState(boolean isLoggedIn, String username) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(KEY_IS_LOGGED_IN, isLoggedIn);
editor.putString(KEY_USERNAME, username);
editor.apply(); // 异步提交
}
// 获取登录状态
public boolean isLoggedIn() {
return sharedPreferences.getBoolean(KEY_IS_LOGGED_IN, false);
}
// 获取用户名
public String getUsername() {
return sharedPreferences.getString(KEY_USERNAME, "");
}
// 清除数据
public void clear() {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.clear();
editor.apply();
}
}
使用示例:
public class MainActivity extends AppCompatActivity {
private SharedPreferencesManager prefsManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
prefsManager = new SharedPreferencesManager(this);
// 检查登录状态
if (!prefsManager.isLoggedIn()) {
// 跳转到登录页
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();
return;
}
// 显示用户名
String username = prefsManager.getUsername();
Toast.makeText(this, "欢迎回来," + username, Toast.LENGTH_SHORT).show();
}
}
3.2 Room数据库操作
Room是Google推荐的SQLite封装库,提供编译时检查,简化数据库操作。
步骤1:添加依赖
// app/build.gradle
dependencies {
def room_version = "2.6.1"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
}
步骤2:定义实体类
// UserEntity.java
@Entity(tableName = "users")
public class UserEntity {
@PrimaryKey(autoGenerate = true)
private int id;
@ColumnInfo(name = "name")
private String name;
@ColumnInfo(name = "email")
private String email;
@ColumnInfo(name = "created_at")
private long createdAt;
// 构造函数
public UserEntity(String name, String email, long createdAt) {
this.name = name;
this.email = email;
this.createdAt = createdAt;
}
// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public long getCreatedAt() { return createdAt; }
public void setCreatedAt(long createdAt) { this.createdAt = createdAt; }
}
步骤3:定义DAO(数据访问对象)
// UserDao.java
@Dao
public interface UserDao {
@Insert
void insert(UserEntity user);
@Insert
void insertAll(List<UserEntity> users);
@Update
void update(UserEntity user);
@Delete
void delete(UserEntity user);
@Query("SELECT * FROM users ORDER BY created_at DESC")
List<UserEntity> getAllUsers();
@Query("SELECT * FROM users WHERE id = :userId")
UserEntity getUserById(int userId);
@Query("DELETE FROM users")
void deleteAll();
}
步骤4:定义数据库
// AppDatabase.java
@Database(entities = {UserEntity.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;
}
}
步骤5:在ViewModel中使用
// UserViewModel.java
public class UserViewModel extends AndroidViewModel {
private final UserDao userDao;
private final LiveData<List<UserEntity>> allUsers;
public UserViewModel(@NonNull Application application) {
super(application);
AppDatabase db = AppDatabase.getDatabase(application);
userDao = db.userDao();
allUsers = userDao.getAllUsersLiveData();
}
// 获取所有用户
public LiveData<List<UserEntity>> getAllUsers() {
return allUsers;
}
// 插入用户
public void insert(UserEntity user) {
AppDatabase.databaseWriteExecutor.execute(() -> {
userDao.insert(user);
});
}
// 删除用户
public void delete(UserEntity user) {
AppDatabase.databaseWriteExecutor.execute(() -> {
userDao.delete(user);
});
}
}
3.3 Retrofit网络请求
Retrofit是目前最流行的HTTP客户端库,基于注解简化网络请求。
步骤1:添加依赖
// app/build.gradle
dependencies {
def retrofit_version = "2.9.0"
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
}
步骤2:定义数据模型
// ApiResponse.java
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
// Getters and Setters
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
}
// User.java
public class User {
private int id;
private String name;
private String email;
private String avatar;
// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getAvatar() { return avatar; }
public void setAvatar(String avatar) { this.avatar = avatar; }
}
步骤3:定义API接口
// ApiService.java
public interface ApiService {
@GET("users")
Call<ApiResponse<List<User>>> getUsers();
@GET("users/{id}")
Call<ApiResponse<User>> getUserById(@Path("id") int id);
@POST("users")
Call<ApiResponse<User>> createUser(@Body User user);
@FormUrlEncoded
@POST("login")
Call<ApiResponse<User>> login(
@Field("username") String username,
@Field("password") String password
);
}
步骤4:创建Retrofit实例
// RetrofitClient.java
public class RetrofitClient {
private static final String BASE_URL = "https://api.example.com/";
private static Retrofit retrofit;
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
// 添加日志拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
public static ApiService getApiService() {
return getRetrofitInstance().create(ApiService.class);
}
}
步骤5:发起网络请求
public class UserRepository {
private ApiService apiService;
public UserRepository() {
apiService = RetrofitClient.getApiService();
}
// 获取用户列表
public void getUsers(Callback<ApiResponse<List<User>>> callback) {
Call<ApiResponse<List<User>>> call = apiService.getUsers();
call.enqueue(callback);
}
// 登录请求
public void login(String username, String password,
Callback<ApiResponse<User>> callback) {
Call<ApiResponse<User>> call = apiService.login(username, password);
call.enqueue(callback);
}
}
步骤6:在Activity中使用
public class NetworkActivity extends AppCompatActivity {
private UserRepository userRepository;
private List<User> userList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_network);
userRepository = new UserRepository();
userList = new ArrayList<>();
// 获取用户列表
userRepository.getUsers(new Callback<ApiResponse<List<User>>>() {
@Override
public void onResponse(Call<ApiResponse<List<User>>> call,
Response<ApiResponse<List<User>>> response) {
if (response.isSuccessful() && response.body() != null) {
ApiResponse<List<User>> apiResponse = response.body();
if (apiResponse.isSuccess()) {
userList = apiResponse.getData();
// 更新UI
updateUI(userList);
} else {
Toast.makeText(NetworkActivity.this,
apiResponse.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onFailure(Call<ApiResponse<List<User>>> call, Throwable t) {
Toast.makeText(NetworkActivity.this,
"网络请求失败: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
private void updateUI(List<User> users) {
// 更新RecyclerView或其他UI组件
// ...
}
}
第四部分:高级主题与实战技巧
4.1 ViewModel与LiveData
ViewModel用于管理UI相关数据,生命周期比Activity更长。LiveData是可观察的数据持有者。
示例:用户列表ViewModel
// UserListViewModel.java
public class UserListViewModel extends ViewModel {
private MutableLiveData<List<User>> userListLiveData;
private UserRepository userRepository;
public UserListViewModel() {
userRepository = new UserRepository();
userListLiveData = new MutableLiveData<>();
}
public LiveData<List<User>> getUserList() {
return userListLiveData;
}
public void loadUsers() {
userRepository.getUsers(new Callback<ApiResponse<List<User>>>() {
@Override
public void onResponse(Call<ApiResponse<List<User>>> call,
Response<ApiResponse<List<User>>> response) {
if (response.isSuccessful() && response.body() != null) {
ApiResponse<List<User>> apiResponse = response.body();
if (apiResponse.isSuccess()) {
userListLiveData.setValue(apiResponse.getData());
}
}
}
@Override
public void onFailure(Call<ApiResponse<List<User>>> call, Throwable t) {
// 处理错误
userListLiveData.setValue(new ArrayList<>());
}
});
}
}
在Activity中使用:
public class UserListActivity extends AppCompatActivity {
private UserListViewModel viewModel;
private RecyclerView recyclerView;
private UserAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_list);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 创建ViewModel
viewModel = new ViewModelProvider(this).get(UserListViewModel.class);
// 观察LiveData
viewModel.getUserList().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(List<User> users) {
if (users != null) {
adapter.updateData(users);
}
}
});
// 加载数据
viewModel.loadUsers();
}
}
4.2 Jetpack Compose基础
Jetpack Compose是Android的现代UI工具包,使用Kotlin声明式编程。
示例:创建一个简单的Compose界面
// MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// 使用Compose主题
MyApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
GreetingScreen()
}
}
}
}
}
@Composable
fun GreetingScreen() {
var text by remember { mutableStateOf("Hello, Android!") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = text,
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
Button(
onClick = { text = "Welcome to Compose!" }
) {
Text("Change Text")
}
}
}
Compose与传统View的互操作:
@Composable
fun ComposeWithView() {
AndroidView(
factory = { context ->
// 创建传统View
TextView(context).apply {
text = "This is a TextView in Compose"
textSize = 16f
setPadding(16, 16, 16, 16)
}
},
modifier = Modifier.fillMaxWidth()
)
}
4.3 性能优化技巧
1. 避免内存泄漏:
// 错误示例:匿名内部类持有Activity引用
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 匿名内部类隐式持有外部类引用
startActivity(new Intent(MainActivity.this, NextActivity.class));
}
});
// 正确示例:使用静态内部类或弱引用
private static class MyClickListener implements View.OnClickListener {
private final WeakReference<MainActivity> activityRef;
public MyClickListener(MainActivity activity) {
activityRef = new WeakReference<>(activity);
}
@Override
public void onClick(View v) {
MainActivity activity = activityRef.get();
if (activity != null) {
activity.doSomething();
}
}
}
2. RecyclerView优化:
// 使用DiffUtil高效更新列表
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
private List<User> userList = new ArrayList<>();
public void updateData(List<User> newUsers) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
new UserDiffCallback(userList, newUsers)
);
userList.clear();
userList.addAll(newUsers);
diffResult.dispatchUpdatesTo(this);
}
// DiffUtil.Callback实现
static class UserDiffCallback extends DiffUtil.Callback {
private final List<User> oldList;
private final 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));
}
}
}
3. 图片加载优化:
// 使用Glide加载图片(需添加依赖)
implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
// 使用示例
Glide.with(context)
.load(imageUrl)
.placeholder(R.drawable.placeholder) // 占位图
.error(R.drawable.error) // 错误图
.diskCacheStrategy(DiskCacheStrategy.ALL) // 缓存策略
.into(imageView);
第五部分:完整项目实战 - 天气预报应用
5.1 项目架构设计
采用MVVM架构模式:
- Model:数据层(Room数据库、Retrofit网络请求)
- View:UI层(Activity/Fragment + XML布局)
- ViewModel:业务逻辑层,连接View和Model
5.2 数据模型设计
// Weather.java
public class Weather {
private String city;
private double temperature;
private String condition;
private int humidity;
private double windSpeed;
private long timestamp;
// 构造函数、Getters、Setters
// ...
}
// WeatherResponse.java
public class WeatherResponse {
private boolean success;
private Weather data;
private String message;
// Getters and Setters
// ...
}
5.3 网络层实现
// WeatherApiService.java
public interface WeatherApiService {
@GET("weather")
Call<WeatherResponse> getWeather(@Query("city") String city);
}
// WeatherRepository.java
public class WeatherRepository {
private WeatherApiService apiService;
private WeatherDao weatherDao;
public WeatherRepository(Context context) {
apiService = RetrofitClient.getApiService();
weatherDao = AppDatabase.getDatabase(context).weatherDao();
}
// 获取天气数据(先查本地,再查网络)
public LiveData<Weather> getWeather(String city) {
MutableLiveData<Weather> result = new MutableLiveData<>();
// 先从数据库查询
new Thread(() -> {
Weather cachedWeather = weatherDao.getWeatherByCity(city);
if (cachedWeather != null) {
result.postValue(cachedWeather);
}
// 再从网络获取
apiService.getWeather(city).enqueue(new Callback<WeatherResponse>() {
@Override
public void onResponse(Call<WeatherResponse> call,
Response<WeatherResponse> response) {
if (response.isSuccessful() && response.body() != null) {
WeatherResponse weatherResponse = response.body();
if (weatherResponse.isSuccess()) {
Weather weather = weatherResponse.getData();
// 保存到数据库
weatherDao.insert(weather);
result.postValue(weather);
}
}
}
@Override
public void onFailure(Call<WeatherResponse> call, Throwable t) {
// 网络失败,使用缓存数据
if (cachedWeather != null) {
result.postValue(cachedWeather);
}
}
});
}).start();
return result;
}
}
5.4 ViewModel实现
// WeatherViewModel.java
public class WeatherViewModel extends AndroidViewModel {
private WeatherRepository repository;
private MutableLiveData<Weather> weatherLiveData;
private MutableLiveData<Boolean> isLoading;
private MutableLiveData<String> errorLiveData;
public WeatherViewModel(@NonNull Application application) {
super(application);
repository = new WeatherRepository(application);
weatherLiveData = new MutableLiveData<>();
isLoading = new MutableLiveData<>(false);
errorLiveData = new MutableLiveData<>();
}
public LiveData<Weather> getWeather() {
return weatherLiveData;
}
public LiveData<Boolean> getIsLoading() {
return isLoading;
}
public LiveData<String> getError() {
return errorLiveData;
}
public void loadWeather(String city) {
isLoading.setValue(true);
repository.getWeather(city).observeForever(new Observer<Weather>() {
@Override
public void onChanged(Weather weather) {
isLoading.setValue(false);
if (weather != null) {
weatherLiveData.setValue(weather);
} else {
errorLiveData.setValue("获取天气数据失败");
}
}
});
}
}
5.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"
android:padding="24dp">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/weatherContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tvCity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textStyle="bold"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/tvTemperature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="48sp"
android:textStyle="bold"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="8dp" />
<TextView
android:id="@+id/tvCondition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/tvDetails"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_gravity="center_horizontal"
android:textColor="#666666" />
</LinearLayout>
<TextView
android:id="@+id/tvError"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="加载失败"
android:textColor="#FF0000"
android:textSize="16sp"
android:visibility="gone"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/etCity"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="输入城市名称"
android:inputType="text"
app:layout_constraintBottom_toTopOf="@id/btnSearch"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="16dp" />
<Button
android:id="@+id/btnSearch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="查询天气"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etCity" />
</androidx.constraintlayout.widget.ConstraintLayout>
// WeatherActivity.java
public class WeatherActivity extends AppCompatActivity {
private WeatherViewModel viewModel;
private EditText etCity;
private Button btnSearch;
private ProgressBar progressBar;
private LinearLayout weatherContainer;
private TextView tvCity, tvTemperature, tvCondition, tvDetails, tvError;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather);
initViews();
setupViewModel();
setupObservers();
setupListeners();
}
private void initViews() {
etCity = findViewById(R.id.etCity);
btnSearch = findViewById(R.id.btnSearch);
progressBar = findViewById(R.id.progressBar);
weatherContainer = findViewById(R.id.weatherContainer);
tvCity = findViewById(R.id.tvCity);
tvTemperature = findViewById(R.id.tvTemperature);
tvCondition = findViewById(R.id.tvCondition);
tvDetails = findViewById(R.id.tvDetails);
tvError = findViewById(R.id.tvError);
}
private void setupViewModel() {
viewModel = new ViewModelProvider(this).get(WeatherViewModel.class);
}
private void setupObservers() {
// 观察天气数据
viewModel.getWeather().observe(this, weather -> {
if (weather != null) {
updateWeatherUI(weather);
}
});
// 观察加载状态
viewModel.getIsLoading().observe(this, isLoading -> {
progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE);
weatherContainer.setVisibility(isLoading ? View.GONE : View.VISIBLE);
tvError.setVisibility(View.GONE);
});
// 观察错误信息
viewModel.getError().observe(this, error -> {
if (error != null && !error.isEmpty()) {
tvError.setText(error);
tvError.setVisibility(View.VISIBLE);
weatherContainer.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
}
});
}
private void setupListeners() {
btnSearch.setOnClickListener(v -> {
String city = etCity.getText().toString().trim();
if (city.isEmpty()) {
Toast.makeText(this, "请输入城市名称", Toast.LENGTH_SHORT).show();
return;
}
viewModel.loadWeather(city);
});
}
private void updateWeatherUI(Weather weather) {
tvCity.setText(weather.getCity());
tvTemperature.setText(String.format("%.1f°C", weather.getTemperature()));
tvCondition.setText(weather.getCondition());
tvDetails.setText(String.format("湿度: %d%% | 风速: %.1f km/h",
weather.getHumidity(), weather.getWindSpeed()));
}
}
第六部分:发布与优化
6.1 应用签名与打包
生成签名密钥:
# 使用keytool生成密钥库
keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias \
-keyalg RSA -keysize 2048 -validity 10000
配置签名:
// app/build.gradle
android {
signingConfigs {
release {
storeFile file("my-release-key.keystore")
storePassword "your_password"
keyAlias "my-key-alias"
keyPassword "your_password"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
6.2 性能监控与调试
使用Android Profiler:
- 在Android Studio中打开Profiler
- 监控CPU、内存、网络使用情况
- 分析性能瓶颈
使用LeakCanary检测内存泄漏:
// app/build.gradle
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
6.3 持续集成与部署
使用GitHub Actions自动化构建:
# .github/workflows/android.yml
name: Android CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew assembleRelease
- name: Upload APK
uses: actions/upload-artifact@v3
with:
name: app-release
path: app/build/outputs/apk/release/app-release.apk
结语:持续学习与成长
Android开发是一个快速发展的领域,新技术不断涌现。建议你:
- 定期阅读官方文档:Android开发者官网
- 参与开源项目:在GitHub上贡献代码
- 关注技术社区:Stack Overflow、Reddit、掘金等
- 构建个人项目:将所学知识应用到实际项目中
- 学习Kotlin:这是Android开发的未来趋势
记住,编程是一门实践的艺术。通过不断编码、调试、优化,你将逐步掌握Android开发的精髓。祝你在Android开发的道路上取得成功!
