Android 架構演進:從 MVC 到 MVVM 的設計之道

在 Android 開發初期,很多開發者會把所有邏輯塞進 Activity—— 網絡請求、數據處理、UI 更新全堆在一起,導致代碼超過數千行,改一個按鈕點擊都要翻半天。這種 “面條式代碼” 的根源是缺乏架構設計。隨著應用復雜度提升,MVC、MVP、MVVM 三種架構逐漸成為主流,它們通過 “分層設計” 解決代碼耦合問題。本文將從核心思想、代碼實現到適用場景,全面解析這三種架構的設計邏輯,幫你找到適合項目的架構方案。

一、架構設計的核心目標

無論哪種架構,最終目的都是解決三個核心問題:

  • 解耦:分離 UI、業務邏輯、數據處理,避免 “改一處動全身”;
  • 可測試:業務邏輯可獨立于 UI 測試(如無需啟動 Activity 就能測試登錄邏輯);
  • 可維護:分層清晰,新人能快速定位代碼位置(如 “UI 相關找 View 層,網絡請求找 Model 層”)。

形象比喻:架構就像 “餐廳分工”—— 廚師(Model)負責做菜(數據處理),服務員(Presenter/ViewModel)負責傳遞需求(業務邏輯),顧客(View)只負責點餐(UI 交互),各司其職才高效。

二、MVC 架構:最基礎的分層思想

MVC(Model-View-Controller)是最早普及的分層架構,核心是 “將數據、UI、邏輯分離”。在 Android 中,MVC 的實現有其特殊性 —— 因 Activity 同時承擔部分 View 和 Controller 職責,與傳統 MVC 略有差異。

2.1 MVC 核心結構與職責

層級

核心職責

Android 中的載體

示例操作

Model

數據管理(網絡請求、數據庫、實體)

JavaBean、Repository、Dao

調用登錄接口、從數據庫查用戶信息

View

展示 UI、接收用戶輸入

XML 布局、Activity(部分)、View

顯示登錄按鈕、輸入用戶名密碼

Controller

處理業務邏輯、協調 Model 和 View

Activity(主要)、Fragment

點擊登錄后調用 Model 校驗,通知 View 顯示結果

2.2 Android MVC 的實現(登錄案例)

以 “登錄功能” 為例,MVC 的代碼結構如下:

(1)Model 層:數據與數據處理
// 1. 數據實體(User.java)
public class User {private String username;private String password;// 構造方法、getter、setter
}// 2. 數據處理(登錄接口調用,LoginModel.java)
public class LoginModel {// 模擬網絡請求public void login(User user, LoginCallback callback) {new Thread(() -> {try {// 模擬網絡延遲Thread.sleep(1000);// 簡單校驗邏輯if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) {callback.onSuccess("登錄成功");} else {callback.onFail("用戶名或密碼錯誤");}} catch (InterruptedException e) {callback.onFail("網絡異常");}}).start();}// 回調接口(Model通知Controller結果)public interface LoginCallback {void onSuccess(String msg);void onFail(String msg);}
}
(2)View 層:UI 展示(XML 布局)
<!-- activity_login.xml -->
<LinearLayout><EditTextandroid:id="@+id/et_username"hint="用戶名"/><EditTextandroid:id="@+id/et_password"hint="密碼"inputType="textPassword"/><Buttonandroid:id="@+id/btn_login"text="登錄"/><TextViewandroid:id="@+id/tv_result"/>
</LinearLayout>
(3)Controller 層:邏輯協調(Activity)
public class LoginActivity extends AppCompatActivity implements LoginModel.LoginCallback {private EditText etUsername;private EditText etPassword;private TextView tvResult;private LoginModel loginModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);// 初始化ViewetUsername = findViewById(R.id.et_username);etPassword = findViewById(R.id.et_password);tvResult = findViewById(R.id.tv_result);loginModel = new LoginModel();// 登錄按鈕點擊(用戶輸入觸發Controller)findViewById(R.id.btn_login).setOnClickListener(v -> {String username = etUsername.getText().toString();String password = etPassword.getText().toString();// 調用Model處理數據loginModel.login(new User(username, password), this);});}// Model回調:更新UI(Controller通知View)@Overridepublic void onSuccess(String msg) {runOnUiThread(() -> tvResult.setText(msg));}@Overridepublic void onFail(String msg) {runOnUiThread(() -> tvResult.setText(msg));}
}

2.3 MVC 的優缺點與適用場景

優勢:
  • 簡單直觀:無需額外類和接口,新手易上手;
  • 開發快速:適合小型項目(如工具類 APP),無需復雜設計。
缺陷:
  • Activity 職責過重:既做 Controller(邏輯)又做 View(UI),代碼易膨脹(上千行很常見);
  • 耦合度較高:View 和 Controller 通過 Activity 強耦合,難以單獨測試(測登錄邏輯需啟動 Activity);
  • 復用性差:邏輯與 Activity 綁定,換個界面(如從 Activity 換成 Dialog)需重寫邏輯。
適用場景:
  • 小型項目(如單個 Activity 的工具 APP);
  • 快速原型開發(需快速驗證功能,不考慮長期維護)。

三、MVP 架構:解耦 View 與邏輯的中間層

MVP(Model-View-Presenter)是為解決 MVC 中 “Activity 職責過重” 而誕生的架構。其核心是引入Presenter 作為中間層,徹底分離 View(UI)和業務邏輯,讓 Activity 只專注于 UI 展示。

3.1 MVP 核心結構與職責

MVP 在 MVC 基礎上拆分出 Presenter,各層職責更清晰:

層級

核心職責

Android 中的載體

核心交互

Model

數據管理(與 MVC 一致)

JavaBean、Repository

登錄接口調用、數據校驗

View

純 UI 層(展示、用戶輸入)

Activity、Fragment、XML 布局

顯示加載框、暴露更新 UI 的方法

Presenter

業務邏輯核心、協調 Model 和 View

Presenter 類(獨立于 Android 框架)

接收 View 的登錄請求→調用 Model→通知 View 更新

核心改進

  • View 與 Presenter 通過接口交互(View 只暴露 UI 方法,不包含邏輯);
  • Presenter 完全獨立于 Android 框架(不持有 Activity 上下文),可單獨測試。

3.2 Android MVP 的實現(登錄案例)

同樣以登錄功能為例,MVP 通過 “接口定義交互” 實現解耦:

(1)Model 層:與 MVC 一致(復用 LoginModel)
// 復用MVC中的LoginModel和User,無需修改
public class LoginModel {public void login(User user, LoginCallback callback) { ... }// 回調接口public interface LoginCallback { ... }
}
(2)View 層:定義 UI 接口 + 實現
// 1. View接口(定義UI操作,與Presenter交互)
public interface LoginView {// 顯示加載狀態void showLoading();// 隱藏加載狀態void hideLoading();// 更新登錄結果void showResult(String msg);// 獲取用戶輸入String getUsername();String getPassword();
}// 2. View實現(Activity只做UI,不處理邏輯)
public class LoginActivity extends AppCompatActivity implements LoginView {private EditText etUsername;private EditText etPassword;private TextView tvResult;private ProgressDialog loadingDialog;private LoginPresenter presenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);// 初始化UIetUsername = findViewById(R.id.et_username);etPassword = findViewById(R.id.et_password);tvResult = findViewById(R.id.tv_result);loadingDialog = new ProgressDialog(this);loadingDialog.setMessage("登錄中...");// 創建Presenter,傳入View接口presenter = new LoginPresenter(this);// 登錄按鈕點擊(View只通知Presenter,不處理邏輯)findViewById(R.id.btn_login).setOnClickListener(v -> presenter.login());}// 實現LoginView接口的UI方法@Overridepublic void showLoading() {loadingDialog.show();}@Overridepublic void hideLoading() {loadingDialog.dismiss();}@Overridepublic void showResult(String msg) {tvResult.setText(msg);}@Overridepublic String getUsername() {return etUsername.getText().toString();}@Overridepublic String getPassword() {return etPassword.getText().toString();}// 生命周期管理:避免內存泄漏@Overrideprotected void onDestroy() {super.onDestroy();presenter.detachView(); // 斷開Presenter與View的引用}
}
(3)Presenter 層:邏輯核心
public class LoginPresenter {// 持有View接口(而非具體Activity)和Modelprivate LoginView loginView;private LoginModel loginModel;// 弱引用(避免Presenter持有Activity導致內存泄漏)private WeakReference<LoginView> viewRef;// 構造方法:關聯View和Modelpublic LoginPresenter(LoginView view) {this.viewRef = new WeakReference<>(view);this.loginModel = new LoginModel();}// 登錄邏輯(核心)public void login() {LoginView view = viewRef.get();if (view == null) return;// 1. 通知View顯示加載view.showLoading();// 2. 獲取View的輸入數據String username = view.getUsername();String password = view.getPassword();// 3. 調用Model處理登錄loginModel.login(new User(username, password), new LoginModel.LoginCallback() {@Overridepublic void onSuccess(String msg) {// 4. 通知View更新結果if (viewRef.get() != null) {viewRef.get().hideLoading();viewRef.get().showResult(msg);}}@Overridepublic void onFail(String msg) {if (viewRef.get() != null) {viewRef.get().hideLoading();viewRef.get().showResult(msg);}}});}// 斷開View引用(避免內存泄漏)public void detachView() {if (viewRef != null) {viewRef.clear();viewRef = null;}}
}

3.3 MVP 的核心改進與優缺點

核心改進:
  • 完全解耦:View 只做 UI,Presenter 只做邏輯,修改 UI 不影響邏輯;
  • 可測試性:Presenter 不依賴 Android 框架,可通過 JUnit 直接測試(無需啟動 APP);
    // 測試Presenter(純Java測試,不依賴Android)
    public class LoginPresenterTest {@Testpublic void testLoginSuccess() {// 模擬ViewLoginView mockView = Mockito.mock(LoginView.class);// 模擬輸入Mockito.when(mockView.getUsername()).thenReturn("admin");Mockito.when(mockView.getPassword()).thenReturn("123456");LoginPresenter presenter = new LoginPresenter(mockView);presenter.login();// 驗證邏輯:是否調用了顯示加載和隱藏加載Mockito.verify(mockView).showLoading();Mockito.verify(mockView).hideLoading();Mockito.verify(mockView).showResult("登錄成功");}
    }

  • 復用性提升:Presenter 可搭配不同 View(如用同一 LoginPresenter 支持 Activity 和 Fragment)。
缺陷:
  • 代碼量增加:需定義大量接口(View 接口、回調),簡單功能也需多個類;
  • 生命周期管理復雜:Presenter 需手動處理 View 的生命周期(如detachView),否則易內存泄漏;
  • 接口冗余:View 接口可能定義大量方法(如 10 個 UI 更新方法),維護成本高。
適用場景:
  • 中型項目(如多模塊應用,需團隊協作);
  • 需頻繁迭代的項目(邏輯與 UI 分離,便于維護);
  • 對測試有要求的項目(需單元測試覆蓋核心邏輯)。

四、MVVM 架構:數據驅動 UI 的響應式設計

MVVM(Model-View-ViewModel)是當前 Android 主流架構,借助 “數據綁定(DataBinding)” 和 “響應式數據(如 LiveData)” 實現 “數據驅動 UI”——UI 自動響應數據變化,無需手動調用更新方法。

4.1 MVVM 核心結構與職責

MVVM 的核心是ViewModel 與 View 的數據綁定,各層職責如下:

層級

核心職責

Android 中的載體

核心交互

Model

數據管理(與前兩種架構一致)

JavaBean、Repository、Room

登錄接口、數據庫操作

View

UI 層(自動響應數據變化)

Activity、Fragment、XML+DataBinding

聲明式綁定數據,無需手動更新

ViewModel

持有可觀察數據、處理業務邏輯

ViewModel(Jetpack 組件)

調用 Model 獲取數據→更新 LiveData→View 自動刷新

核心優勢

  • 數據與 UI 通過 DataBinding 綁定,省去大量setText等更新代碼;
  • ViewModel 生命周期獨立于 Activity(屏幕旋轉時不重建),數據自動保留;
  • 響應式編程(LiveData 自動通知數據變化),邏輯更清晰。

4.2 Android MVVM 的實現(登錄案例)

結合 Jetpack 組件(ViewModel、LiveData、DataBinding)實現登錄功能:

(1)Model 層:數據與倉庫(引入 Repository 模式)
// 1. 數據實體(User.java)
public class User { ... }// 2. 數據源(登錄接口,LoginDataSource.java)
public class LoginDataSource {public void login(User user, LoginCallback callback) {// 模擬網絡請求(與MVP的Model一致)new Thread(() -> {try {Thread.sleep(1000);if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) {callback.onSuccess("登錄成功");} else {callback.onFail("用戶名或密碼錯誤");}} catch (InterruptedException e) {callback.onFail("網絡異常");}}).start();}public interface LoginCallback { ... }
}// 3. 倉庫(統一管理數據源,LoginRepository.java)
public class LoginRepository {private static LoginRepository instance;private LoginDataSource dataSource;// 單例倉庫(可同時管理網絡和本地數據源)public static LoginRepository getInstance() {if (instance == null) {instance = new LoginRepository(new LoginDataSource());}return instance;}private LoginRepository(LoginDataSource dataSource) {this.dataSource = dataSource;}// 暴露登錄接口給ViewModelpublic void login(User user, LoginDataSource.LoginCallback callback) {dataSource.login(user, callback);}
}
(2)ViewModel 層:持有 LiveData 與邏輯
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;public class LoginViewModel extends ViewModel {// 可觀察數據(登錄結果,View會自動監聽)private MutableLiveData<String> loginResult = new MutableLiveData<>();// 加載狀態private MutableLiveData<Boolean> isLoading = new MutableLiveData<>();// 倉庫引用private LoginRepository repository;public LoginViewModel() {repository = LoginRepository.getInstance();}// 暴露給View的只讀LiveData(防止View直接修改)public LiveData<String> getLoginResult() {return loginResult;}public LiveData<Boolean> getIsLoading() {return isLoading;}// 登錄邏輯public void login(String username, String password) {isLoading.setValue(true); // 通知加載開始User user = new User(username, password);repository.login(user, new LoginDataSource.LoginCallback() {@Overridepublic void onSuccess(String msg) {isLoading.postValue(false); // 子線程用postValueloginResult.postValue(msg);}@Overridepublic void onFail(String msg) {isLoading.postValue(false);loginResult.postValue(msg);}});}
}
(3)View 層:DataBinding 綁定數據
<!-- activity_login.xml(啟用DataBinding) -->
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><!-- 數據變量定義 --><data><variablename="viewModel"type="com.example.mvvm.LoginViewModel" /><variablename="activity"type="com.example.mvvm.LoginActivity" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditTextandroid:id="@+id/et_username"android:hint="用戶名"/><EditTextandroid:id="@+id/et_password"android:hint="密碼"android:inputType="textPassword"/><Buttonandroid:text="登錄"android:onClick="@{() -> activity.login()}"/><TextViewandroid:text="@{viewModel.loginResult}" /> <!-- 自動綁定結果 --><ProgressBarandroid:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}" /> <!-- 自動綁定加載狀態 --></LinearLayout>
</layout>
(4)Activity:關聯 ViewModel 與 DataBinding
public class LoginActivity extends AppCompatActivity {private ActivityLoginBinding binding; // DataBinding自動生成的類private LoginViewModel loginViewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 1. 初始化DataBindingbinding = DataBindingUtil.setContentView(this, R.layout.activity_login);// 2. 獲取ViewModel(通過ViewModelProvider,確保生命周期正確)loginViewModel = new ViewModelProvider(this).get(LoginViewModel.class);// 3. 綁定ViewModel到布局binding.setViewModel(loginViewModel);binding.setActivity(this);// 綁定生命周期所有者(讓LiveData感知Activity生命周期)binding.setLifecycleOwner(this);}// 登錄按鈕點擊(調用ViewModel的登錄方法)public void login() {String username = binding.etUsername.getText().toString();String password = binding.etPassword.getText().toString();loginViewModel.login(username, password);}
}

4.3 MVVM 的核心優勢與優缺點

核心優勢:
  • 數據驅動 UI:通過 LiveData+DataBinding,數據變化自動更新 UI,省去runOnUiThread和setText;
  • 生命周期安全:ViewModel 在屏幕旋轉時不重建(數據保留),避免重復請求網絡;
  • 低耦合:View 只綁定數據,ViewModel 只處理邏輯,Model 只管數據,修改 UI 不影響邏輯;
  • 可測試性:ViewModel 獨立于 Android 框架,可直接測試(如驗證登錄邏輯是否正確更新 LiveData)。
缺陷:
  • 學習成本高:需掌握 DataBinding、LiveData、ViewModel 等 Jetpack 組件;
  • 調試難度增加:數據綁定是黑盒操作,UI 異常時需排查綁定關系;
  • 簡單功能冗余:小功能(如單個按鈕)用 MVVM 顯得繁瑣。
適用場景:
  • 大型項目(如電商 APP、社交 APP,需長期維護);
  • 頻繁更新 UI 的場景(如列表刷新、實時數據展示);
  • 團隊協作項目(架構規范統一,新人易接手)。

五、三種架構對比與選擇指南

維度

MVC

MVP

MVVM

核心思想

分層但 View 與 Controller 耦合

Presenter 中間層解耦

數據綁定 + 響應式數據驅動

代碼量

少(無額外接口)

中(需定義 View 接口)

多(需 ViewModel 和綁定)

耦合度

高(Activity 承擔多重職責)

中(接口交互,需手動管理)

低(數據綁定,自動響應)

可測試性

低(需依賴 Activity)

高(Presenter 可獨立測試)

高(ViewModel 獨立測試)

維護成本

高(后期改不動)

中(接口清晰但需維護)

低(分層明確,數據驅動)

Android 適配

原生支持(簡單但粗糙)

需手動實現接口和生命周期管理

依賴 Jetpack(官方推薦)

5.1 架構選擇建議

  1. 按項目規模選擇
  • 小型項目(<5 個 Activity)→ MVC(快速開發);
  • 中型項目(5-20 個頁面)→ MVP(平衡開發效率和維護性);
  • 大型項目(>20 個頁面)→ MVVM(長期維護,團隊協作)。
  1. 按團隊情況選擇
  • 新手團隊→ MVC(降低學習成本);
  • 有經驗團隊→ MVVM(利用 Jetpack 提升效率)。
  1. 按功能復雜度選擇
  • 簡單功能(如設置頁面)→ MVC 或 MVP;
  • 復雜功能(如首頁列表 + 購物車 + 實時消息)→ MVVM。

六、架構設計的本質:靈活應變

無論 MVC、MVP 還是 MVVM,都不是 “銀彈”。實際開發中不必嚴格遵守某一種架構,可根據需求混合使用:

  • 小型項目用 MVC,但抽取工具類減少 Activity 代碼;
  • MVP 中引入 DataBinding 簡化 View 更新;
  • MVVM 中保留 Presenter 的部分邏輯(如復雜表單校驗)。

架構的本質是 “解決當前問題”—— 能讓團隊高效開發、代碼易于維護的就是好架構。隨著項目演進,架構也可逐步升級(如從 MVC 重構為 MVVM),關鍵是保持 “分層清晰、職責單一” 的核心原則。

掌握這三種架構后,你會發現:優秀的 Android 代碼不是 “堆功能”,而是通過合理設計讓每一行代碼都有明確的位置和職責 —— 這也是從 “會寫代碼” 到 “能設計系統” 的關鍵一步。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93147.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93147.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93147.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用 gh-pages 將 next.js15 靜態項目部署到 github pages

以下我使用 next.js15 寫的 Todo List 為例,假設我們本地已經存在一個 next.js15 的 Todo List 項目。 說明:解決了項目部署到 github pages 后訪問不到 css、js、字體以及訪問不到 public 目錄下的圖片問題。 第一步 安裝 gh-pages: npm i gh-pages第二步 在 public 目…

rename系統調用及示例

21. rename - 重命名文件或目錄 函數介紹 rename系統調用用于重命名文件或目錄&#xff0c;也可以將文件或目錄移動到另一個位置。如果目標文件已存在&#xff0c;則會被替換。 函數原型 #include <stdio.h>int rename(const char *oldpath, const char *newpath);功能 將…

PHP框架之Laravel框架教程:3. 數據庫操作(簡要)

3. 數據庫操作&#xff08;簡要&#xff09; 配置 數據庫的配置文件在 config/database.php 文件中&#xff0c;你可以在這個文件中定義所有的數據庫連接配置&#xff0c;并指定默認的數據庫連接。這個文件中提供了大部分 Laravel 能夠支持的數據庫配置示例。 mysql > [driv…

項目七.AI大模型部署

環境準備此處使用的是rock linux8.9操作系統k8s集群三個設備&#xff0c;使用centos7.9操作系統設備配置##上傳ollama工具的壓縮包 [rootproject ~]# ll total 1497732 -rw-r--r-- 1 root root 1533674176 Jul 21 11:27 ollama-linux-amd64.tgz [rootproject ~]# tar -C /usr -…

Oracle 19C RU 19.28 升級和安裝

背景介紹 概述 本次升級包括安全漏掃中所有19c數據庫,漏掃預警版本19.3到19.27各個版本,數據庫需要升級至19.28版本滿足安全要求。 原端19C 升級目標端19.28 db_name racdb racdb ORACLE_SID racdb1/2 racdb1/2 ORACLE_HOME GI:/oracle/asm DB:/oracle/db GI:/orac…

嵌入式學習日志————對射式紅外傳感器計次

前言這是第二次學習這部分內容了&#xff0c;第一次是大一上學期&#xff0c;因為大二下忙著其他事一直沒來得及吧STM32學完&#xff0c;所以假期從頭開始再學&#xff0c;比第一次也有了更深的理解&#xff0c;以下內容均是看【STM32入門教程-2023版 細致講解 中文字幕】https…

ONLYOFFICE深度解鎖系列.13-如何復制、重新排序 PDF 頁面:onlyoffice 9.0.3 新功能

在處理合同、講義、研究資料或掃描文檔時&#xff0c;保持頁面順序井然尤為重要。有時文件頁數繁多、排序混亂或缺少邏輯&#xff0c;這不僅影響閱讀體驗&#xff0c;也不利于后續使用或分享。好消息是&#xff0c;借助 ONLYOFFICE PDF 編輯器&#xff0c;您可以輕松拖拽頁面&a…

vue遞歸樹形結構刪除不符合數據 生成一個新數組

首先看一下數據結構&#xff08;我的是路由菜單&#xff09;{"code": 200,"message": "請求成功!","success": true,"data": [{"startDate": null,"endDate": null,"createTime": "2023…

【機器學習之推薦算法】基于K最近鄰的協同過濾推薦與基于回歸模型的協同過濾推薦

基于K最近鄰的協同過濾推薦 基于K最近鄰的協同過濾推薦其實本質上就是MemoryBased CF&#xff0c;只不過在選取近鄰的時候&#xff0c;加上K最近鄰的限制。 這里我們直接根據MemoryBased CF的代碼實現 修改以下地方 class CollaborativeFiltering(object):based Nonedef __ini…

望言OCR視頻字幕提取2025終極評測:免費版VS專業版提全方位對比(含免費下載)

大家好&#xff0c;歡迎來到程序視點&#xff01;我是你們的老朋友.小二&#xff01;一、產品定位&#xff1a;AI時代的視頻字幕處理專家望言OCR作為專業的視頻硬字幕提取工具&#xff0c;在AI視頻處理領域占據重要地位。最新評測顯示&#xff0c;其免費版本依然保持著驚人的97…

Matplotlib(二)- Matplotlib簡單繪圖

文章目錄一、pyplot模塊介紹二、Matplotlib簡單繪圖1. 繪制折線圖1.1 折線圖介紹1.2 plt.plot()函數介紹1.3 繪制簡單折線圖1.3.1 繪制單條折線圖1.3.2 繪制多條折線圖1.4 示例&#xff1a;繪制天氣氣溫折線圖2. 繪制柱形圖2.1 柱形圖介紹2.2 plt.bar()函數介紹2.3 繪制柱形圖2…

【世紀龍科技】數字化技術解鎖新能源汽車電驅動總成裝調與檢修

隨著新能源汽車產業加速升級&#xff0c;電驅動總成裝調與檢修技術已成為職業院校汽車專業教學的核心挑戰。傳統實訓模式面臨設備投入高、更新周期長、高壓操作安全隱患多、教學與產業需求脫節等現實問題&#xff0c;導致學生實踐能力培養滯后于行業發展。如何通過數字化手段突…

springboot基于Java與MySQL庫的健身俱樂部管理系統設計與實現

用戶&#xff1a;注冊&#xff0c;登錄&#xff0c;健身教練&#xff0c;健身課程&#xff0c;健身器材&#xff0c;健身資訊&#xff0c;課程報名管理&#xff0c;教練預約管理&#xff0c;會員充值管理&#xff0c;個人中心管理員&#xff1a;登錄&#xff0c;個人中心&#…

如何修改debian的ip地址

編輯配置文件&#xff1a; sudo nano /etc/network/interfaces修改內容&#xff08;示例將 eth0 設為靜態IP&#xff09;&#xff1a; auto eth0 iface eth0 inet static address 192.168.1.100 netmask 255.255.255.0 gateway 192.168.1.1 dns-nameservers 8.8.8.8 8.8.4.4 #…

haproxy七層代理(知識點+相關實驗部署)

目錄 1.負載均衡介紹 1.1 什么是負載均衡 1.2 為什么用負載均衡 1.3 負載均衡類型 1.3.1 四層負載均衡 1.3.2 七層負載均衡 1.3.3 四層和七層的區別 2.haproxy簡介 2.1 haproxy主要特性 2.2 haproxy的優點與缺點 3.haproxy的安裝和服務信息 3.1 實驗環境 3.1.1 hap…

【集合】JDK1.8 HashMap 底層數據結構深度解析

一、核心數據結構&#xff1a;為什么是 "數組 鏈表 紅黑樹"&#xff1f;?HashMap 的底層設計本質是用空間換時間&#xff0c;通過哈希表的快速定位特性&#xff0c;結合鏈表和紅黑樹處理沖突&#xff0c;平衡查詢與插入效率。?1.1 基礎容器&#xff1a;哈希桶數組…

【element-ui】HTML引入本地文件出現font找不到/fonts/element-icons.woff

文章目錄目錄結構問題復現解決辦法目錄結構 |-web|- public|- lib|- ...|- index.htmlindex.html <!DOCTYPE html> <html> <head><meta charset"UTF-8"><!-- import CSS --><link rel"stylesheet" href"./public/…

Windows|CUDA和cuDNN下載和安裝,默認安裝在C盤和不安裝在C盤的兩種方法

本篇文章將詳細介紹在Windows操作系統中配置CUDA和cuDNN的步驟。通過本教程&#xff0c;您將能夠輕松完成CUDA和cuDNN的安裝、環境變量配置以及與深度學習框架&#xff08;如TensorFlow和PyTorch&#xff09;兼容性測試&#xff0c;從而為您的深度學習項目提供強大的硬件支持。…

Vue 項目動態接口獲取翻譯數據實現方案(前端處理語言翻譯 vue-i18n)

在大型多語言項目中&#xff0c;將翻譯數據硬編碼在項目中往往不夠靈活。通過接口動態獲取翻譯數據&#xff0c;并結合本地緩存提升性能&#xff0c;是更優的國際化實現方式。本文將詳細介紹如何在 Vue 項目中實現這一方案。 方案優勢 靈活性高&#xff1a;翻譯內容更新無需修改…

Mybatis-plus多數據源

適用于多種場景&#xff1a;純粹多庫、 讀寫分離、 一主多從、 混合模式等目前我們就來模擬一個純粹多庫的一個場景&#xff0c;其他場景類似場景說明&#xff1a;我們創建兩個庫&#xff0c;分別為&#xff1a; mybatis_plus&#xff08;以前的庫不動&#xff09;與my…