引言:为什么选择Android开发?

在当今移动互联网时代,Android作为全球市场占有率最高的移动操作系统,拥有超过30亿活跃设备。对于初学者而言,Android开发不仅提供了广阔的就业机会,更是一个能够快速看到成果、持续成长的领域。本文将通过一系列精心设计的实例,从零基础开始,逐步深入,帮助你掌握Android开发的核心技能与实战技巧。

第一部分:环境搭建与基础认知

1.1 开发环境配置

Android Studio是Google官方推荐的集成开发环境(IDE),它基于IntelliJ IDEA构建,提供了代码编辑、调试、性能分析等全方位工具。

安装步骤:

  1. 访问Android开发者官网下载最新版本

  2. 运行安装程序,选择标准安装(包含Android SDK、模拟器等)

  3. 配置环境变量(可选,但推荐): “`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:

  1. 在Android Studio中打开Profiler
  2. 监控CPU、内存、网络使用情况
  3. 分析性能瓶颈

使用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开发是一个快速发展的领域,新技术不断涌现。建议你:

  1. 定期阅读官方文档Android开发者官网
  2. 参与开源项目:在GitHub上贡献代码
  3. 关注技术社区:Stack Overflow、Reddit、掘金等
  4. 构建个人项目:将所学知识应用到实际项目中
  5. 学习Kotlin:这是Android开发的未来趋势

记住,编程是一门实践的艺术。通过不断编码、调试、优化,你将逐步掌握Android开发的精髓。祝你在Android开发的道路上取得成功!