5月7日Android Studio学习笔记
一、代码优化与架构设计
(一)代码架构模式探索
MVC、MVP、MVVM 架构模式对比与实践
MVC(Model - View - Controller) :
回顾 MVC 的基本原理,Controller 接收用户输入并更新 Model 和 View,View 负责显示数据,Model 管理数据和业务逻辑。在 Android 中,Activity 或 Fragment 通常充当 Controller 和 View 的角色,数据模型为 Model。然而,MVC 在 Android 中的界限不够清晰,导致 Activity 过于臃肿。
MVP(Model - View - Presenter) :
学习 MVP 的核心思想,将 View 和业务逻辑分离,Presenter 作为中间层处理业务逻辑并与 Model 交互,View 只负责展示数据和接收用户输入。通过示例代码,实践 MVP 模式的简单应用:
定义 View 接口,声明 UI 操作方法,如显示数据、显示加载状态等。
Presenter 实现业务逻辑,包括数据处理、与 Model 的交互,并在获取数据后调用 View 接口的方法更新 UI。
Activity 或 Fragment 实现 View 接口,将用户输入传递给 Presenter,并更新 UI。
例如:
java
// View 接口
public interface LoginView {
void showLoading();
void hideLoading();
void loginSuccess();
void loginFailed(String errorMsg);
String getUserName();
String getPassword();
}
// Presenter
public class LoginPresenter {
private LoginView view;
private LoginModel model;
public LoginPresenter(LoginView view) {
this.view = view;
this.model = new LoginModel();
}
public void login() {
view.showLoading();
String userName = view.getUserName();
String password = view.getPassword();
model.login(userName, password, new LoginCallback() {
@Override
public void onSuccess() {
view.loginSuccess();
view.hideLoading();
}
@Override
public void onFailure(String errorMsg) {
view.loginFailed(errorMsg);
view.hideLoading();
}
});
}
}
// Model
public class LoginModel {
public void login(String userName, String password, LoginCallback callback) {
// 模拟登录请求
if ("user".equals(userName) && "password".equals(password)) {
callback.onSuccess();
} else {
callback.onFailure("用户名或密码错误");
}
}
public interface LoginCallback {
void onSuccess();
void onFailure(String errorMsg);
}
}
// Activity 实现 View 接口
public class LoginActivity extends AppCompatActivity implements LoginView {
private LoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
presenter = new LoginPresenter(this);
}
public void onLoginClick(View view) {
presenter.login();
}
// 实现 LoginView 接口的方法
@Override
public void showLoading() {
// 显示加载提示
}
@Override
public void hideLoading() {
// 隐藏加载提示
}
@Override
public void loginSuccess() {
// 跳转到主页
}
@Override
public void loginFailed(String errorMsg) {
// 显示错误信息
}
@Override
public String getUserName() {
return editTextUserName.getText().toString();
}
@Override
public String getPassword() {
return editTextPassword.getText().toString();
}
}
- MVVM(Model - View - ViewModel) :
-
深入学习 MVVM 的架构思想,通过 LiveData 和 ViewModel 组件实现数据的自动更新和生命周期管理。ViewModel 作为数据和业务逻辑的管理器,与 View 通过观察者模式进行交互,当数据变化时,View 能够自动更新。
-
使用 LiveData 的示例:
- 在 ViewModel 中定义 LiveData 变量,用于存储需要观察的数据。
- 在 View(Activity 或 Fragment)中观察 LiveData 变量,当数据变化时,自动更新 UI。
-
示例代码:
java
-
// ViewModel
public class UserViewModel extends ViewModel {
private LiveData
public LiveData<User> getUser() {
if (currentUser == null) {
currentUser = loadUser();
}
return currentUser;
}
private LiveData<User> loadUser() {
// 返回数据源的 LiveData,如从数据库或网络请求获取的 LiveData
}
}
// Activity 观察 LiveData
public class ProfileActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
UserViewModel viewModel = new ViewModelProvider(this).get(UserViewModel.class);
viewModel.getUser().observe(this, new Observer<User>() {
@Override
public void onChanged(@Nullable User user) {
// 更新 UI
userNameTextView.setText(user.getName());
userEmailTextView.setText(user.getEmail());
}
});
}
}
选择合适的架构模式
根据项目规模、团队技术栈和开发需求,选择合适的架构模式。对于小型项目,MVC 可能满足需求;对于中大型项目,MVP 或 MVVM 更有利于代码的维护和扩展。
(二)代码复用与模块化开发
组件化开发
学习组件化开发的基本概念和优势,将应用划分为多个独立的组件模块,每个模块可以独立开发、测试和部署,提高代码的复用性和可维护性。
实现组件化的基本步骤:
项目结构划分:根据功能或业务划分模块,如用户认证模块、首页模块、个人中心模块等。
模块间通信:通过定义接口和事件总线(如 EventBus)实现模块之间的数据传递和交互。
依赖管理:合理配置模块之间的依赖关系,避免循环依赖。
独立运行与集成:每个模块可以独立运行(通过设置 AndroidManifest.xml 的应用标签等),也可以集成到主项目中统一运行。
代码复用策略
创建通用的工具类、基类和组件,如通用的网络请求工具类、基础 Activity 或 Fragment、可复用的 UI 组件等,减少重复代码的编写。
利用 Android 的资源系统,将通用的字符串、颜色、尺寸等资源抽取到单独的资源文件中,方便在多个模块和项目中共享。
二、插件开发深度实践
(一)插件功能拓展
代码分析与质量检测插件
开发一个插件,用于分析项目的代码质量,检测潜在的代码问题,如未使用的变量、硬编码的字符串、过度复杂的函数等。
使用 Android Studio 的 PSI(Program Structure Interface)API 遍历项目的代码结构,识别代码中的问题,并在编辑器中标记出来,提供快速修复的建议。
例如,检测未使用的变量:
java
public class UnusedVariableDetector extends LocalInspectionTool {
@Override
public ProblemDescriptor[] checkFile(@Nonnull PsiFile file, @Nonnull InspectionManager manager, boolean isOnTheFly) {
List
if (file instanceof PsiJavaFile) {
PsiJavaFile javaFile = (PsiJavaFile) file;
PsiClass[] classes = javaFile.getClasses();
for (PsiClass clazz : classes) {
PsiMethod[] methods = clazz.getMethods();
for (PsiMethod method : methods) {
PsiCodeBlock body = method.getBody();
if (body != null) {
PsiElement[] children = body.getChildren();
for (PsiElement child : children) {
if (child instanceof PsiVariable) {
PsiVariable variable = (PsiVariable) child;
if (!variable.isUsed()) { // 假设有一个 isUsed 方法判断变量是否被使用
ProblemDescriptor problem = manager.createProblemDescriptor(
variable,
"Unused variable",
new LocalQuickFix[] {
new DeleteUnusedVariableFix(variable.getName())
},
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOnTheFly
);
problems.add(problem);
}
}
}
}
}
}
}
return problems.toArray(new ProblemDescriptor[0]);
}
private static class DeleteUnusedVariableFix implements LocalQuickFix {
private final String variableName;
public DeleteUnusedVariableFix(String variableName) {
this.variableName = variableName;
}
@Nonnull
@Override
public String getName() {
return "Delete unused variable '" + variableName + "'";
}
@Nonnull
@Override
public String getFamilyName() {
return "Delete unused variable";
}
@Override
public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getPsiElement();
if (element instanceof PsiVariable) {
element.delete();
}
}
}
}
- 在插件的
plugin.xml文件中注册该检查工具:
xml
public class PluginSettingsConfigurable implements Configurable {
private JPanel settingsPanel;
private JTextField apiKeyField;
private JTextField serverUrlField;
@Nonnull
@Override
public String getId() {
return "com.example.plugin.settings";
}
@Nullable
@Override
public String getDisplayName() {
return "Example Plugin Settings";
}
@Nullable
@Override
public String getHelpTopic() {
return null;
}
@Nullable
public JComponent createComponent() {
settingsPanel = new JPanel(new GridLayout(2, 2));
settingsPanel.add(new JLabel("API Key:"));
apiKeyField = new JTextField();
settingsPanel.add(apiKeyField);
settingsPanel.add(new JLabel("Server URL:"));
serverUrlField = new JTextField();
settingsPanel.add(serverUrlField);
loadSettings();
return settingsPanel;
}
private void loadSettings() {
// 从配置文件或插件存储中加载设置
String apiKey = PluginSettings.getInstance().getApiKey();
String serverUrl = PluginSettings.getInstance().getServerUrl();
apiKeyField.setText(apiKey);
serverUrlField.setText(serverUrl);
}
private void saveSettings() {
String apiKey = apiKeyField.getText();
String serverUrl = serverUrlField.getText();
PluginSettings.getInstance().setApiKey(apiKey);
PluginSettings.getInstance().setServerUrl(serverUrl);
}
@Override
public boolean isModified() {
String currentApiKey = PluginSettings.getInstance().getApiKey();
String currentServerUrl = PluginSettings.getInstance().getServerUrl();
return !currentApiKey.equals(apiKeyField.getText()) || !currentServerUrl.equals(serverUrlField.getText());
}
@Override
public void apply() {
saveSettings();
}
@Override
public void reset() {
loadSettings();
}
@Override
public void disposeUIResources() {
settingsPanel = null;
apiKeyField = null;
serverUrlField = null;
}
}
- 在插件的
plugin.xml文件中注册设置面板:
xml
浙公网安备 33010602011771号