引言
Android作为全球最流行的移动操作系统,拥有庞大的开发者社区和丰富的应用场景。从简单的工具应用到复杂的游戏和企业级应用,Android开发涵盖了广泛的技术领域。本文将通过一系列实例分析,从基础入门到高级技巧,帮助读者系统掌握Android编程的核心技术,并通过实战案例加深理解。
第一部分:Android开发基础
1.1 Android开发环境搭建
在开始Android开发之前,需要搭建完整的开发环境。主要工具包括:
- Android Studio:官方集成开发环境(IDE),基于IntelliJ IDEA
- Android SDK:软件开发工具包,包含API库和开发工具
- 模拟器或真机:用于测试应用
安装步骤:
- 下载并安装Android Studio(官网:developer.android.com/studio)
- 配置SDK路径和模拟器
- 创建第一个项目(Hello World)
示例代码:创建一个简单的Activity显示”Hello World”
// MainActivity.java
package com.example.helloworld;
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);
// 通过ID获取TextView
TextView textView = findViewById(R.id.textView);
textView.setText("Hello, Android!");
}
}
<!-- activity_main.xml -->
<?xml version="1.1" 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>
1.2 Android应用基本结构
Android应用由四个核心组件构成:
- Activity:用户界面的单个屏幕
- Service:后台运行的服务
- Broadcast Receiver:响应系统广播事件
- Content Provider:管理共享数据
示例:创建一个简单的Service
// MyService.java
package com.example.myservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
public IBinder onBind(Intent intent) {
return null; // 不绑定时返回null
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service started");
// 执行后台任务
performBackgroundTask();
return START_STICKY; // 如果被杀死,尝试重启
}
private void performBackgroundTask() {
// 模拟后台任务
new Thread(() -> {
for (int i = 0; i < 5; i++) {
Log.d(TAG, "Task progress: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service destroyed");
}
}
第二部分:UI设计与交互
2.1 布局系统详解
Android提供多种布局方式,包括LinearLayout、RelativeLayout、ConstraintLayout等。
ConstraintLayout示例:创建一个响应式布局
<!-- activity_constraint.xml -->
<?xml version="1.1" 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="0dp"
android:layout_height="wrap_content"
android:text="欢迎使用"
android:textSize="24sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="32dp" />
<Button
android:id="@+id/btn_login"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="登录"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintWidth_percent="0.6" />
<Button
android:id="@+id/btn_register"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="注册"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@id/btn_login"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintWidth_percent="0.6" />
</androidx.constraintlayout.widget.ConstraintLayout>
2.2 RecyclerView高级用法
RecyclerView是现代Android开发中最重要的列表组件,支持高效的数据展示和复杂交互。
示例:创建一个带点击事件的RecyclerView
// 1. 创建数据模型
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...
}
// 2. 创建Adapter
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
private List<User> userList;
private OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(User user);
}
public UserAdapter(List<User> userList, OnItemClickListener listener) {
this.userList = userList;
this.listener = listener;
}
@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.tvName.setText(user.getName());
holder.tvEmail.setText(user.getEmail());
holder.itemView.setOnClickListener(v -> {
if (listener != null) {
listener.onItemClick(user);
}
});
}
@Override
public int getItemCount() {
return userList.size();
}
static class UserViewHolder extends RecyclerView.ViewHolder {
TextView tvName, tvEmail;
public UserViewHolder(@NonNull View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tv_name);
tvEmail = itemView.findViewById(R.id.tv_email);
}
}
}
// 3. 在Activity中使用
public class UserListActivity extends AppCompatActivity {
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));
// 模拟数据
List<User> users = new ArrayList<>();
users.add(new User("张三", "zhangsan@example.com", R.drawable.avatar1));
users.add(new User("李四", "lisi@example.com", R.drawable.avatar2));
users.add(new User("王五", "wangwu@example.com", R.drawable.avatar3));
adapter = new UserAdapter(users, user -> {
Toast.makeText(this, "点击了: " + user.getName(), Toast.LENGTH_SHORT).show();
});
recyclerView.setAdapter(adapter);
}
}
2.3 Material Design组件
Material Design是Google推出的设计语言,提供了丰富的UI组件。
示例:使用Material Components
<!-- activity_material.xml -->
<?xml version="1.1" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
android:padding="16dp">
<!-- Material Button -->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_primary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="主要按钮"
app:icon="@drawable/ic_add"
app:iconGravity="textStart"
app:cornerRadius="8dp" />
<!-- Material CardView -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="卡片标题"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="这是卡片的内容描述,可以包含多行文本。"
android:layout_marginTop="8dp" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Material Text Field -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:hintEnabled="true"
app:hintAnimationEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="邮箱地址"
android:inputType="textEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
<!-- Material Floating Action Button -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/ic_add"
app:backgroundTint="@color/colorPrimary" />
</LinearLayout>
第三部分:数据存储与网络通信
3.1 SharedPreferences存储
SharedPreferences是Android中轻量级的键值对存储方式,适合存储少量数据。
示例:使用SharedPreferences保存用户设置
// SharedPreferencesHelper.java
public class SharedPreferencesHelper {
private static final String PREF_NAME = "app_settings";
private static final String KEY_USER_NAME = "user_name";
private static final String KEY_IS_LOGGED_IN = "is_logged_in";
private static final String KEY_THEME = "theme";
private SharedPreferences sharedPreferences;
public SharedPreferencesHelper(Context context) {
sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
}
// 保存用户名
public void saveUserName(String userName) {
sharedPreferences.edit().putString(KEY_USER_NAME, userName).apply();
}
// 获取用户名
public String getUserName() {
return sharedPreferences.getString(KEY_USER_NAME, "Guest");
}
// 保存登录状态
public void saveLoginStatus(boolean isLoggedIn) {
sharedPreferences.edit().putBoolean(KEY_IS_LOGGED_IN, isLoggedIn).apply();
}
// 获取登录状态
public boolean isLoggedIn() {
return sharedPreferences.getBoolean(KEY_IS_LOGGED_IN, false);
}
// 保存主题设置
public void saveTheme(String theme) {
sharedPreferences.edit().putString(KEY_THEME, theme).apply();
}
// 获取主题设置
public String getTheme() {
return sharedPreferences.getString(KEY_THEME, "light");
}
// 清除所有数据
public void clearAll() {
sharedPreferences.edit().clear().apply();
}
}
// 在Activity中使用
public class SettingsActivity extends AppCompatActivity {
private SharedPreferencesHelper helper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
helper = new SharedPreferencesHelper(this);
// 保存数据
helper.saveUserName("张三");
helper.saveLoginStatus(true);
helper.saveTheme("dark");
// 读取数据
String userName = helper.getUserName();
boolean isLoggedIn = helper.isLoggedIn();
String theme = helper.getTheme();
Log.d("Settings", "User: " + userName + ", Logged in: " + isLoggedIn + ", Theme: " + theme);
}
}
3.2 Room数据库操作
Room是Google推荐的SQLite数据库框架,提供编译时SQL验证和简化数据库操作。
示例:使用Room实现简单的待办事项应用
// 1. 定义实体类
@Entity(tableName = "todo_items")
public class TodoItem {
@PrimaryKey(autoGenerate = true)
private int id;
private String title;
private String description;
private boolean completed;
private long createdAt;
// 构造函数、getters和setters...
public TodoItem(String title, String description, boolean completed, long createdAt) {
this.title = title;
this.description = description;
this.completed = completed;
this.createdAt = createdAt;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public boolean isCompleted() { return completed; }
public void setCompleted(boolean completed) { this.completed = completed; }
public long getCreatedAt() { return createdAt; }
public void setCreatedAt(long createdAt) { this.createdAt = createdAt; }
}
// 2. 定义DAO(数据访问对象)
@Dao
public interface TodoDao {
@Insert
void insert(TodoItem item);
@Update
void update(TodoItem item);
@Delete
void delete(TodoItem item);
@Query("SELECT * FROM todo_items ORDER BY createdAt DESC")
LiveData<List<TodoItem>> getAllItems();
@Query("SELECT * FROM todo_items WHERE completed = 0 ORDER BY createdAt DESC")
LiveData<List<TodoItem>> getActiveItems();
@Query("SELECT * FROM todo_items WHERE completed = 1 ORDER BY createdAt DESC")
LiveData<List<TodoItem>> getCompletedItems();
}
// 3. 定义数据库
@Database(entities = {TodoItem.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract TodoDao todoDao();
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, "todo_database")
.build();
}
}
}
return INSTANCE;
}
}
// 4. 在ViewModel中使用
public class TodoViewModel extends AndroidViewModel {
private TodoDao todoDao;
private LiveData<List<TodoItem>> allItems;
public TodoViewModel(@NonNull Application application) {
super(application);
AppDatabase db = AppDatabase.getDatabase(application);
todoDao = db.todoDao();
allItems = todoDao.getAllItems();
}
public LiveData<List<TodoItem>> getAllItems() {
return allItems;
}
public void insert(TodoItem item) {
AppDatabase.databaseWriteExecutor.execute(() -> {
todoDao.insert(item);
});
}
public void update(TodoItem item) {
AppDatabase.databaseWriteExecutor.execute(() -> {
todoDao.update(item);
});
}
public void delete(TodoItem item) {
AppDatabase.databaseWriteExecutor.execute(() -> {
todoDao.delete(item);
});
}
}
3.3 Retrofit网络请求
Retrofit是Square公司开发的HTTP客户端,简化了REST API的调用。
示例:使用Retrofit获取GitHub用户信息
// 1. 定义API接口
public interface GitHubApiService {
@GET("users/{username}")
Call<GithubUser> getUser(@Path("username") String username);
@GET("users/{username}/repos")
Call<List<Repository>> getRepositories(@Path("username") String username);
}
// 2. 定义数据模型
public class GithubUser {
private String login;
private String name;
private String avatar_url;
private String bio;
private int public_repos;
private int followers;
private int following;
// Getters and setters...
}
public class Repository {
private String name;
private String description;
private String language;
private int stargazers_count;
// Getters and setters...
}
// 3. 创建Retrofit实例
public class RetrofitClient {
private static final String BASE_URL = "https://api.github.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;
}
}
// 4. 在Activity中使用
public class GitHubUserActivity extends AppCompatActivity {
private TextView tvResult;
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_github_user);
tvResult = findViewById(R.id.tv_result);
progressBar = findViewById(R.id.progressBar);
fetchGitHubUser("google");
}
private void fetchGitHubUser(String username) {
progressBar.setVisibility(View.VISIBLE);
GitHubApiService service = RetrofitClient.getClient().create(GitHubApiService.class);
Call<GithubUser> call = service.getUser(username);
call.enqueue(new Callback<GithubUser>() {
@Override
public void onResponse(Call<GithubUser> call, Response<GithubUser> response) {
progressBar.setVisibility(View.GONE);
if (response.isSuccessful() && response.body() != null) {
GithubUser user = response.body();
String result = String.format(
"用户名: %s\n姓名: %s\nBio: %s\n仓库数: %d\n粉丝数: %d\n关注数: %d",
user.getLogin(),
user.getName(),
user.getBio(),
user.getPublic_repos(),
user.getFollowers(),
user.getFollowing()
);
tvResult.setText(result);
} else {
tvResult.setText("请求失败: " + response.code());
}
}
@Override
public void onFailure(Call<GithubUser> call, Throwable t) {
progressBar.setVisibility(View.GONE);
tvResult.setText("网络错误: " + t.getMessage());
}
});
}
}
第四部分:高级技巧与性能优化
4.1 多线程与异步处理
Android中处理耗时操作需要使用异步机制,避免阻塞主线程。
示例:使用Kotlin协程处理异步任务
// 1. 添加依赖(build.gradle)
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
}
// 2. 在ViewModel中使用协程
class UserViewModel : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> get() = _users
private val _isLoading = MutableLiveData<Boolean>()
val isLoading: LiveData<Boolean> get() = _isLoading
fun loadUsers() {
viewModelScope.launch {
_isLoading.value = true
try {
// 模拟网络请求
val result = withContext(Dispatchers.IO) {
delay(2000) // 模拟耗时操作
listOf(
User("张三", "zhangsan@example.com"),
User("李四", "lisi@example.com"),
User("王五", "wangwu@example.com")
)
}
_users.value = result
} catch (e: Exception) {
// 处理错误
Log.e("UserViewModel", "Error loading users", e)
} finally {
_isLoading.value = false
}
}
}
}
// 3. 在Activity中观察数据
class UserListActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_list)
viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
// 观察数据变化
viewModel.users.observe(this) { users ->
// 更新UI
updateUI(users)
}
viewModel.isLoading.observe(this) { isLoading ->
progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
}
// 触发数据加载
viewModel.loadUsers()
}
private fun updateUI(users: List<User>) {
// 更新RecyclerView或其他UI组件
}
}
4.2 内存优化技巧
Android应用内存管理至关重要,以下是一些优化技巧:
示例:避免内存泄漏
// 1. 避免静态Context引用
public class Utils {
private static Context context; // 错误做法
public static void init(Context context) {
Utils.context = context; // 可能导致内存泄漏
}
// 正确做法:使用Application Context
public static void init(Context context) {
Utils.context = context.getApplicationContext();
}
}
// 2. 避免非静态内部类持有外部类引用
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 这个内部类会持有MainActivity的引用
// 如果Handler有延迟消息,可能导致内存泄漏
}
};
// 正确做法:使用静态内部类
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> activityRef;
public MyHandler(MainActivity activity) {
activityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityRef.get();
if (activity != null) {
// 处理消息
}
}
}
}
// 3. 使用LeakCanary检测内存泄漏
// 在build.gradle中添加依赖
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
}
4.3 性能优化工具
Android Studio提供了多种性能分析工具:
示例:使用Profiler分析应用性能
- CPU Profiler:分析方法执行时间和调用栈
- Memory Profiler:监控内存使用情况
- Network Profiler:分析网络请求
- Energy Profiler:监控电池消耗
使用步骤:
- 连接设备或启动模拟器
- 在Android Studio中点击”Profiler”标签
- 选择要分析的进程
- 运行应用并观察性能数据
第五部分:实战案例分析
5.1 案例一:天气预报应用
项目结构:
WeatherApp/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/example/weatherapp/
│ │ │ │ ├── data/
│ │ │ │ │ ├── model/
│ │ │ │ │ │ ├── WeatherData.java
│ │ │ │ │ │ └── Forecast.java
│ │ │ │ │ ├── repository/
│ │ │ │ │ │ └── WeatherRepository.java
│ │ │ │ │ └── api/
│ │ │ │ │ └── WeatherApiService.java
│ │ │ │ ├── ui/
│ │ │ │ │ ├── viewmodel/
│ │ │ │ │ │ └── WeatherViewModel.java
│ │ │ │ │ └── activity/
│ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ └── DetailActivity.java
│ │ │ │ └── utils/
│ │ │ │ └── NetworkUtils.java
│ │ │ └── res/
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── activity_detail.xml
│ │ │ └── values/
│ │ │ └── strings.xml
核心代码实现:
// 1. 数据模型
public class WeatherData {
private String city;
private double temperature;
private String condition;
private double humidity;
private double windSpeed;
private List<Forecast> forecasts;
// Getters and setters...
}
// 2. API接口
public interface WeatherApiService {
@GET("weather")
Call<WeatherData> 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(WeatherApiService apiService, WeatherDao weatherDao) {
this.apiService = apiService;
this.weatherDao = weatherDao;
}
public LiveData<WeatherData> getWeather(String city) {
MutableLiveData<WeatherData> result = new MutableLiveData<>();
apiService.getCurrentWeather(city, BuildConfig.API_KEY)
.enqueue(new Callback<WeatherData>() {
@Override
public void onResponse(Call<WeatherData> call, Response<WeatherData> response) {
if (response.isSuccessful() && response.body() != null) {
WeatherData data = response.body();
result.setValue(data);
// 保存到本地数据库
new Thread(() -> {
weatherDao.insertWeather(data);
}).start();
}
}
@Override
public void onFailure(Call<WeatherData> call, Throwable t) {
// 从本地数据库加载
new Thread(() -> {
WeatherData localData = weatherDao.getWeather(city);
result.postValue(localData);
}).start();
}
});
return result;
}
}
// 4. ViewModel
public class WeatherViewModel extends ViewModel {
private WeatherRepository repository;
private MutableLiveData<WeatherData> currentWeather = new MutableLiveData<>();
private MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
public WeatherViewModel(WeatherRepository repository) {
this.repository = repository;
}
public void loadWeather(String city) {
isLoading.setValue(true);
repository.getWeather(city).observeForever(new Observer<WeatherData>() {
@Override
public void onChanged(WeatherData data) {
currentWeather.setValue(data);
isLoading.setValue(false);
repository.getWeather(city).removeObserver(this);
}
});
}
public LiveData<WeatherData> getCurrentWeather() {
return currentWeather;
}
public LiveData<Boolean> getIsLoading() {
return isLoading;
}
}
// 5. MainActivity
public class MainActivity extends AppCompatActivity {
private WeatherViewModel viewModel;
private TextView tvCity, tvTemperature, tvCondition;
private EditText etCity;
private Button btnSearch;
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
setupViewModel();
setupListeners();
}
private void initViews() {
tvCity = findViewById(R.id.tv_city);
tvTemperature = findViewById(R.id.tv_temperature);
tvCondition = findViewById(R.id.tv_condition);
etCity = findViewById(R.id.et_city);
btnSearch = findViewById(R.id.btn_search);
progressBar = findViewById(R.id.progressBar);
}
private void setupViewModel() {
WeatherApiService apiService = RetrofitClient.getClient().create(WeatherApiService.class);
WeatherDao weatherDao = AppDatabase.getDatabase(this).weatherDao();
WeatherRepository repository = new WeatherRepository(apiService, weatherDao);
viewModel = new ViewModelProvider(this, new ViewModelProvider.Factory() {
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new WeatherViewModel(repository);
}
}).get(WeatherViewModel.class);
// 观察数据
viewModel.getCurrentWeather().observe(this, weatherData -> {
if (weatherData != null) {
updateUI(weatherData);
}
});
viewModel.getIsLoading().observe(this, isLoading -> {
progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE);
});
}
private void setupListeners() {
btnSearch.setOnClickListener(v -> {
String city = etCity.getText().toString().trim();
if (!city.isEmpty()) {
viewModel.loadWeather(city);
} else {
Toast.makeText(this, "请输入城市名称", Toast.LENGTH_SHORT).show();
}
});
}
private void updateUI(WeatherData data) {
tvCity.setText(data.getCity());
tvTemperature.setText(String.format("%.1f°C", data.getTemperature()));
tvCondition.setText(data.getCondition());
}
}
5.2 案例二:新闻阅读器应用
技术栈:
- MVVM架构
- Room数据库(离线缓存)
- Retrofit + Gson(网络请求)
- RecyclerView + DiffUtil(高效列表更新)
- WorkManager(定时更新)
关键实现:
// 1. 使用DiffUtil优化RecyclerView更新
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.NewsViewHolder> {
private List<NewsItem> newsList = new ArrayList<>();
public void setNewsList(List<NewsItem> newList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
new NewsDiffCallback(newsList, newList)
);
newsList = new ArrayList<>(newList);
diffResult.dispatchUpdatesTo(this);
}
// DiffUtil.Callback实现
static class NewsDiffCallback extends DiffUtil.Callback {
private final List<NewsItem> oldList;
private final List<NewsItem> newList;
public NewsDiffCallback(List<NewsItem> oldList, List<NewsItem> 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. WorkManager定时更新
public class NewsUpdateWorker extends Worker {
public NewsUpdateWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
// 执行后台更新任务
try {
NewsRepository repository = new NewsRepository(getApplicationContext());
repository.fetchLatestNews();
return Result.success();
} catch (Exception e) {
return Result.failure();
}
}
}
// 3. 在Application中调度WorkManager
public class NewsApp extends Application {
@Override
public void onCreate() {
super.onCreate();
// 每6小时更新一次新闻
PeriodicWorkRequest newsUpdateWork = new PeriodicWorkRequest.Builder(
NewsUpdateWorker.class, 6, TimeUnit.HOURS)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build())
.build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"news_update",
ExistingPeriodicWorkPolicy.KEEP,
newsUpdateWork
);
}
}
第六部分:发布与测试
6.1 应用测试
单元测试示例:
// 1. 添加测试依赖
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:4.8.0'
testImplementation 'androidx.arch.core:core-testing:2.1.0'
}
// 2. 编写单元测试
@RunWith(MockitoJUnitRunner.class)
public class WeatherViewModelTest {
@Mock
private WeatherRepository repository;
@Mock
private Observer<WeatherData> observer;
@Mock
private Observer<Boolean> loadingObserver;
private WeatherViewModel viewModel;
@Before
public void setUp() {
viewModel = new WeatherViewModel(repository);
}
@Test
public void testLoadWeather_Success() {
// 准备测试数据
WeatherData testData = new WeatherData();
testData.setCity("Beijing");
testData.setTemperature(25.0);
MutableLiveData<WeatherData> liveData = new MutableLiveData<>();
liveData.setValue(testData);
when(repository.getWeather("Beijing")).thenReturn(liveData);
// 观察数据
viewModel.getCurrentWeather().observeForever(observer);
viewModel.getIsLoading().observeForever(loadingObserver);
// 执行测试
viewModel.loadWeather("Beijing");
// 验证结果
verify(observer).onChanged(testData);
verify(loadingObserver).onChanged(true);
verify(loadingObserver).onChanged(false);
}
@Test
public void testLoadWeather_Error() {
MutableLiveData<WeatherData> liveData = new MutableLiveData<>();
liveData.setValue(null);
when(repository.getWeather("Beijing")).thenReturn(liveData);
viewModel.getCurrentWeather().observeForever(observer);
viewModel.loadWeather("Beijing");
verify(observer).onChanged(null);
}
}
6.2 应用发布
发布前检查清单:
- 性能优化:确保应用无内存泄漏,响应时间<200ms
- 安全检查:移除调试代码,保护API密钥
- 兼容性测试:在不同Android版本和设备上测试
- 权限管理:只请求必要的权限
- 隐私政策:如果收集用户数据,需要提供隐私政策
生成签名APK:
- 在Android Studio中选择”Build” → “Generate Signed Bundle/APK”
- 创建或选择签名密钥
- 选择发布版本(Release)
- 生成APK文件
第七部分:进阶主题
7.1 Jetpack Compose
Jetpack Compose是Android的现代UI工具包,使用Kotlin声明式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 WeatherScreen(viewModel: WeatherViewModel = viewModel()) {
val weatherData by viewModel.currentWeather.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
if (isLoading) {
CircularProgressIndicator()
} else if (weatherData != null) {
WeatherContent(weatherData!!)
} else {
Text("请输入城市名称")
}
}
}
@Composable
fun WeatherContent(data: WeatherData) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = data.city,
style = MaterialTheme.typography.h4,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "${data.temperature}°C",
style = MaterialTheme.typography.h3,
color = MaterialTheme.colors.primary
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = data.condition,
style = MaterialTheme.typography.body1
)
}
}
}
// 3. 在Activity中使用
class ComposeActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WeatherAppTheme {
WeatherScreen()
}
}
}
}
7.2 Kotlin多平台开发
Kotlin Multiplatform允许共享业务逻辑代码。
示例:共享数据模型和网络逻辑
// 1. 创建共享模块(shared/src/commonMain/kotlin)
expect class Platform() {
val platformName: String
}
class SharedData {
fun getPlatformInfo(): String {
return "Running on ${Platform().platformName}"
}
}
// 2. Android平台实现(shared/src/androidMain/kotlin)
actual class Platform {
actual val platformName: String = "Android"
}
// 3. iOS平台实现(shared/src/iosMain/kotlin)
actual class Platform {
actual val platformName: String = "iOS"
}
// 4. 在Android项目中使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val sharedData = SharedData()
val info = sharedData.getPlatformInfo()
Toast.makeText(this, info, Toast.LENGTH_SHORT).show()
}
}
总结
通过本文的系统学习,读者应该能够:
- 掌握基础:理解Android应用的基本结构和开发环境
- 精通UI:熟练使用各种布局和Material Design组件
- 数据处理:掌握SharedPreferences、Room和Retrofit的使用
- 性能优化:了解多线程、内存管理和性能分析工具
- 实战能力:通过案例分析获得实际项目开发经验
- 进阶技能:了解Jetpack Compose和Kotlin多平台开发
Android开发是一个持续学习的过程,建议读者:
- 定期阅读官方文档(developer.android.com)
- 参与开源项目
- 关注Android开发社区(如Stack Overflow、Reddit)
- 实践更多项目,积累经验
记住,优秀的Android开发者不仅需要掌握技术,还需要关注用户体验、性能优化和代码质量。通过不断实践和学习,你一定能成为Android开发领域的专家。
