【安卓筆記】lifecycle與viewModel

0. 環境:

電腦:Windows10

Android Studio: 2024.3.2

編程語言: Java

Gradle version:8.11.1

Compile Sdk Version:35

Java 版本:Java11

1. 本篇文章涉及到的內容

lifecycle

livedata

databinding

viewModel

2. lifecycle簡單使用

2.1注解的方法已過時

以MVP架構為例:

我們在basePresenter中實現lifecycleObserver的監聽即可:

package com.liosen.androidnote.presenter;import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;public class BasePresenter implements LifecycleObserver {@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)void onCreateX(LifecycleOwner owner) {}@OnLifecycleEvent(Lifecycle.Event.ON_START)void onStartX(LifecycleOwner owner) {}@OnLifecycleEvent(Lifecycle.Event.ON_STOP)void onStopX(LifecycleOwner owner) {}@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)void onResumeX(LifecycleOwner owner) {}@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)void onPauseX(LifecycleOwner owner) {}@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)void onDestroyX(LifecycleOwner owner) {}@OnLifecycleEvent(Lifecycle.Event.ON_ANY)void onAny(LifecycleOwner owner) {}
}

此時,我們要在實際presenter中監聽生命周期,就可以繼承基類來實現

package com.liosen.androidnote.presenter;import androidx.lifecycle.LifecycleOwner;public class FruitPresent extends BasePresenter{//省略業務代碼···// ------------------------------- 此時,可以監聽activity的生命周期 -------------------------------@Overridevoid onCreateX(LifecycleOwner owner) {super.onCreateX(owner);}@Overridevoid onStartX(LifecycleOwner owner) {super.onStartX(owner);}@Overridevoid onStopX(LifecycleOwner owner) {super.onStopX(owner);}@Overridevoid onResumeX(LifecycleOwner owner) {super.onResumeX(owner);}@Overridevoid onPauseX(LifecycleOwner owner) {super.onPauseX(owner);}@Overridevoid onDestroyX(LifecycleOwner owner) {super.onDestroyX(owner);}@Overridevoid onAny(LifecycleOwner owner) {super.onAny(owner);}
}

示例,mainActivity:

public class MainActivity extends BaseActivity<FruitPresent> {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);}@Overrideprotected FruitPresent createPresenter() {return new FruitPresent();}}

baseActivity的代碼如下:

public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity {protected T presenter;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);presenter = createPresenter();init();}protected abstract T createPresenter();protected void init() {getLifecycle().addObserver(presenter);}}

細心的網友已經看出來了,目前版本@OnLifecycleEvent的注解已經過時了

我們通過源碼可以看到最新的方法:

/*** Annotation that can be used to mark methods on {@link LifecycleObserver} implementations that* should be invoked to handle lifecycle events.** @deprecated This annotation required the usage of code generation or reflection, which should* be avoided. Use {@link DefaultLifecycleObserver} or* {@link LifecycleEventObserver} instead.*/
@SuppressWarnings("unused")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Deprecated
public @interface OnLifecycleEvent {Lifecycle.Event value();
}

2.2 使用DefaultLifecycleObserver

直接實現DefaultLifecycleObserver,就可以重寫這些方法了

3.?livedata的使用

3.1 livedata的簡單使用

先寫一個最簡單的viewModel:

public class TextViewModel extends ViewModel {private MutableLiveData<String> text;   // 用于記錄文字public int count;   // 用于記錄次數public MutableLiveData<String> getText() {if (text == null) {text = new MutableLiveData<>();}return text;}
}

?接著看示例代碼:

public class MainActivity extends AppCompatActivity implements DefaultLifecycleObserver {private TextView textView;private Button button;private TextViewModel model;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);textView = findViewById(R.id.textView);button = findViewById(R.id.button);model = new ViewModelProvider(this).get(TextViewModel.class);observerChange();// 按鈕點擊一次,計數增加一次button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {test();}});}/*** 訂閱 viewModel中,text的變化*/private void observerChange() {Observer<String> observer = new Observer<String>() {@Overridepublic void onChanged(String string) {// 收到數據變化時,通過setText 更新UItextView.setText(string);}};model.getText().observe(this, observer);}/*** 測試文字變化的代碼*/private void test() {String text = "liosen" + model.count++;model.getText().setValue(text);}}

此時,我們每次點擊按鈕,count都會增加1次。然后通過viewModel儲存數據,觸發了數據變化。監聽者監聽到數據變化后,調用了onChanged,實現了UI更新。

好處是,即使發生了重新繪制(例如屏幕旋轉)也可以保證數據不會被回收。重新加載頁面時,也能顯示livedata中緩存的數據。

3.2 livedata的消息通知

3.1中,我們學會了一個對象的數據存儲。但是一個項目中,需要存儲的對象肯定不止一個。如果每個對象都建一個viewModel,肯定會讓項目非常不優雅。所以需要批量管理。于是,我們可以創建一個LiveDataBus

public class LiveDataBus {private Map<String, MutableLiveData<Object>> bus;   // hashMap用于存放訂閱者private static LiveDataBus liveDataBus = new LiveDataBus();public LiveDataBus() {bus = new HashMap<>();}public static LiveDataBus getInstance() {return liveDataBus;}/*** 注冊訂閱者*/public synchronized <T> MutableLiveData<T> with(String key, Class<T> type) {if (!bus.containsKey(key)) {bus.put(key, new MutableLiveData<Object>());}return (MutableLiveData<T>) bus.get(key);}
}

此時,activity修改為:

public class MainActivity extends AppCompatActivity implements DefaultLifecycleObserver {private TextView textView;private Button button;private TextViewModel model;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);textView = findViewById(R.id.textView);button = findViewById(R.id.button);model = new ViewModelProvider(this).get(TextViewModel.class);observerChange();// 按鈕點擊一次,計數增加一次button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {test();}});}private void observerChange() {LiveDataBus.getInstance().with("text", String.class).observe(this, new Observer<String>() {@Overridepublic void onChanged(String string) {// 收到數據變化時,通過setText 更新UItextView.setText(string);}});}/*** 測試文字變化的代碼*/private void test() {new Thread() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {// livedata 發消息通知觀察者LiveDataBus.getInstance().with("text", // 這個key,為自定義。最終會儲存在LiveDataBus中hashMap中String.class).postValue("livedata: liosen" + count++    // 這個是具體變化的內容);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}.start();}}

這樣,我們就把每個viewModel都抽離出來,通過LiveDataBus中的map給保存好。

只需要每次通過

LiveDataBus.getInstance().with(key, String.class)

既可以postValue(),也可以observe();

這樣就解決了項目中viewModel過多的問題

4.?databinding和viewModel的使用

4.1 databinding的簡單使用

可以參考我這篇文章:【安卓筆記】用MVC、MVP、MVVM來實現井字棋案例-CSDN博客。

我貼出文章中的部分內容:

······

4. MVVM實現井字棋功能
文件結構如下:(忽略mvc文件夾和mvp文件夾)

4.1 第一步增加dataBinding
在app級別(如果有使用其他module,那該module也需要增加)的build.gradle中,android下增加,如下所示:

android {···dataBinding {enable true}
}


?這里我插一嘴:dataBinding和viewBinding的區別:

viewBinding:省略findViewById 的功能dataBinding:除了viewBinding的功能,還能綁定data部分

4.2 修改activity.xml
?

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><data><import type="android.view.View" /><variablename="viewModel"type="com.liosen.androidnote.mvvm.viewmodel.TicTacToeViewModel" /></data><LinearLayoutandroid:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="60dp"android:fitsSystemWindows="true"android:gravity="center_horizontal"android:orientation="vertical"tools:context=".TicTacToeActivity"><GridLayoutandroid:id="@+id/gl_chessboard"android:layout_width="wrap_content"android:layout_height="wrap_content"android:columnCount="3"android:rowCount="3"><Buttonstyle="@style/chessboard_btn"android:onClick="@{()->viewModel.onClickedChessboard(0,0)}"android:text='@{viewModel.board["00"]}' /><Buttonstyle="@style/chessboard_btn"android:onClick="@{()->viewModel.onClickedChessboard(0,1)}"android:text='@{viewModel.board["01"]}' /><Buttonstyle="@style/chessboard_btn"android:onClick="@{()->viewModel.onClickedChessboard(0,2)}"android:text='@{viewModel.board["02"]}' /><Buttonstyle="@style/chessboard_btn"android:onClick="@{()->viewModel.onClickedChessboard(1,0)}"android:text='@{viewModel.board["10"]}' /><Buttonstyle="@style/chessboard_btn"android:onClick="@{()->viewModel.onClickedChessboard(1,1)}"android:text='@{viewModel.board["11"]}' /><Buttonstyle="@style/chessboard_btn"android:onClick="@{()->viewModel.onClickedChessboard(1,2)}"android:text='@{viewModel.board["12"]}' /><Buttonstyle="@style/chessboard_btn"android:onClick="@{()->viewModel.onClickedChessboard(2,0)}"android:text='@{viewModel.board["20"]}' /><Buttonstyle="@style/chessboard_btn"android:onClick="@{()->viewModel.onClickedChessboard(2,1)}"android:text='@{viewModel.board["21"]}' /><Buttonstyle="@style/chessboard_btn"android:onClick="@{()->viewModel.onClickedChessboard(2,2)}"android:text='@{viewModel.board["22"]}' /></GridLayout><LinearLayoutandroid:id="@+id/ll_winner"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"android:visibility="@{viewModel.winner == null ? View.GONE : View.VISIBLE}"><TextViewandroid:id="@+id/tv_winner"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="20dp"android:textSize="40sp"tools:text="X"android:text="@{viewModel.winner}"/><TextViewandroid:id="@+id/tv_tips"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{viewModel.result}"android:textSize="30sp" /></LinearLayout></LinearLayout></layout>

可以看到有一些新內容:

首先,必須要用<layout></layout>包裹原有的xml

然后,<variable>標簽需要至少引入viewModel,其中name為自定義的名稱,type為綁定的ViewModel。此處,我需要將該xml和TicTacToeViewModel.java 進行綁定。

最后,看到<Button>標簽中onClick方法變成了

android:onClick="@{()->viewModel.onClickedChessboard(0,0)}"

格式為 "@{}" ,此處中間為lamda,viewModel中的onClickedChessboard方法,在后面的viewModel文件中會有。

text文字變成了

android:text='@{viewModel.board["00"]}'

數據來源為viewModel中的board,同樣的 在viewModel文件中會有。

4.3 增加viewModel文件?
?

package com.liosen.androidnote.mvvm.viewmodel;import androidx.databinding.ObservableArrayMap;
import androidx.databinding.ObservableField;import com.liosen.androidnote.mvvm.model.GameState;
import com.liosen.androidnote.mvvm.model.Board;
import com.liosen.androidnote.mvvm.model.Player;public class TicTacToeViewModel {public Board model;public final ObservableArrayMap<String, String> board = new ObservableArrayMap<>(); // 此處為被觀察者,被觀察的數據為map,用于存 <棋盤格子, 棋手>public final ObservableField<String> winner = new ObservableField<>(); ?// 此處也為被觀察者,被觀察的數據為String類型的對象public final ObservableField<String> result = new ObservableField<>();public TicTacToeViewModel() {model = new Board();}public void reset() {model.restartGame();winner.set(null);board.clear();}public void onClickedChessboard(int row, int col) {Player player = model.mark(row, col);if (player != null) {// 棋盤格子 顯示玩家X或者Oboard.put("" + row + col, player == null ? null : player.toString());if (model.getWinner() != null) { ? // 如果勝利的棋手 不為空,則顯示勝利的信息winner.set(model.getWinner() == null ? null : model.getWinner().toString());result.set("你贏了");} else if (model.getState() == GameState.FINISHED) {winner.set("");result.set("本局平局");}}}
}

4.4 最后activity文件
?

package com.liosen.androidnote.mvvm.view;import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.databinding.DataBindingUtil;import com.liosen.androidnote.R;
import com.liosen.androidnote.databinding.ActivityMainMvvmBinding;
import com.liosen.androidnote.mvvm.viewmodel.TicTacToeViewModel;public class TicTacToeMVVMActivity extends AppCompatActivity {// --------------------- View ---------------------TicTacToeViewModel viewModel = new TicTacToeViewModel(); ? ?//@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);
// ? ? ? ?setContentView(R.layout.activity_main); ? // 此時已經不需要該行代碼了,通過下面兩行實現xml和activity的綁定ActivityMainMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main_mvvm); //?binding.setViewModel(viewModel); ? ?//ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});// 重置游戲viewModel.reset();}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.menu_tictactoe, menu);return super.onCreateOptionsMenu(menu);}@Overridepublic boolean onOptionsItemSelected(@NonNull MenuItem item) {if (item.getItemId() == R.id.reset) {viewModel.reset();return true;} else {return super.onOptionsItemSelected(item);}}
}

activity中綁定viewModel即可。

甚至連findViewById也省了。如果需要在activity中操作UI,可以直接通過binding獲取,例如:

binding.tvWinner

至于tvWinner是哪里來的:是由于在xml文件中,設置的id。這樣生成binding文件(編譯時自動生成)時,就會自動生成該field,可以通過binding獲取到。?

······

?其中4.?MVVM實現井字棋功能,就可以學會簡單的使用。

4.2 結合lifecycle的使用

layout文件:

<?xml version="1.0" encoding="utf-8"?>
<layout><data><import type="android.view.View" /><variablename="viewModel"type="com.liosen.androidnote.viewmodel.TextViewModel" /></data><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{viewModel.text}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="@{viewModel::test}"android:text="count++"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/textView" /></androidx.constraintlayout.widget.ConstraintLayout>
</layout>

修改TextViewModel文件:

public class TextViewModel extends ViewModel {private MutableLiveData<String> text;   // 用于記錄文字public int count;   // 用于記錄次數public MutableLiveData<String> getText() {if (text == null) {text = new MutableLiveData<>();text.setValue("Hello World!");  // 初始化文字}return text;}/*** 測試文字變化的代碼*/public void test(View view) {String text = "binding: liosen" + count++;getText().setValue(text);}
}

activity文件:

public class MainActivity extends AppCompatActivity {ActivityMainBinding binding;private TextViewModel model;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);model = new ViewModelProvider(this).get(TextViewModel.class);binding = DataBindingUtil.setContentView(this, R.layout.activity_main);binding.setViewModel(model);observerChange();}/*** 訂閱 viewModel中,text的變化*/private void observerChange() {Observer<String> observer = new Observer<String>() {@Overridepublic void onChanged(String string) {// 收到數據變化時,通過setText 更新UIbinding.textView.setText(string);}};model.getText().observe(this, observer);}
}

可以看到,每次按鈕的點擊事件,實際上更新的是livedata中的text。由于我們在mainActivity中觀察者觀察了text的值,每當text的值發生變化時,就可以更新到UI當中。

4.3 viewModel

實際上,我們在3.1中已經實現了viewModel。3.2中也實現了viewModel。

3.1中的監聽者在于自身viewModel

3.2中的監聽者在activity中實現綁定了

兩種都可以。

5. 寫在最后

至此,我們就學會最基本的lifecycle、livedata、viewModel的基本使用了

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

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

相關文章

84、逆向工程開發方法

逆向工程開發方法是一種通過分析現有產品、系統或代碼來理解其設計原理、功能實現及潛在缺陷&#xff0c;并在此基礎上進行改進、復制或創新的技術過程。它廣泛應用于軟件、硬件、機械、電子等多個領域&#xff0c;尤其在缺乏原始設計文檔或需要快速掌握復雜系統時具有顯著優勢…

ospf單區域實驗

拓撲圖&#xff1a;AR1&#xff1a;[Huawei]ospf 1 router-id 1.1.1.1 [Huawei-ospf-1]area 0[Huawei-ospf-1-area-0.0.0.0]network 192.168.1.0 0.0.0.255&#xff08;1.當前網段會被ospf的進程1學習到然后通告出去&#xff1b;2.如果接口的IP地址處于這個網段中&#xff0c…

Linux命令基礎完結篇

用戶權限修改 chmod修改文件權限 文字設定法 u&#xff1a;所有者g&#xff1a;所屬組o&#xff1a;其他人a&#xff1a;所有&#xff1a;添加權限-&#xff1a;刪除權限&#xff1a;賦予權限數字設定法 r&#xff1a;4w&#xff1a;2x&#xff1a;1每一組權限&#xff1a;0~7舉…

高效互聯,ModbusTCP轉EtherCAT網關賦能新能源電纜智能制造

在新能源汽車快速發展的背景下&#xff0c;新能源電纜作為關鍵組件&#xff0c;需滿足耐高低溫、阻燃、耐老化等嚴苛要求&#xff0c;這對生產線的工藝與設備提出了更高標準。為提升制造效率&#xff0c;某領先設備制造商創新采用**ModbusTCP轉EtherCAT網關**技術&#xff0c;實…

Java_多線程_生產者消費者模型_互斥鎖,阻塞隊列

生產者消費者模型(Producer-Consumer Model)是計算機科學中一個經典的并發編程模型&#xff0c;用于解決多線程/多進程環境下的協作問題。 基本概念 生產者&#xff1a;負責生成數據或任務的實體 消費者&#xff1a;負責處理數據或執行任務的實體 緩沖區&#xff1a;生產者與消…

Vue3實現視頻播放彈窗組件,支持全屏播放,音量控制,進度條自定義樣式,適配瀏覽器小窗播放,視頻大小自適配,緩沖loading,代碼復制即用

效果圖組件所需VUE3代碼<template><div class"video-dialog" :class"fullScreen && video-dialog-full-screen"><el-dialogv-model"props.visible"draggable:show-close"false"title""centeralign-c…

LLM層歸一化:γβ與均值方差的協同奧秘

LLM層歸一化參數均值和方差;縮放和平移參數是什么 層歸一化(Layer Normalization,LN)是深度學習中用于穩定神經網絡訓練的一種歸一化技術 均值和方差參數用于對輸入數據進行標準化處理,即將輸入數據轉換為均值為0、方差為1的標準正態分布 縮放因子γ\gammaγ:標準化后…

智慧場景:定制開發開源AI智能名片S2B2C商城小程序賦能零售新體驗

摘要&#xff1a;智慧場景作為零售行業創新發展的關鍵載體&#xff0c;正深刻改變著消費者的生活方式。本文聚焦智慧零售模式下智慧場景的構建&#xff0c;以定制開發開源AI智能名片S2B2C商城小程序為切入點&#xff0c;深入探討其在零售企業選址布局、商業模式創新、經營理念轉…

QML WorkerScript

WorkerScript是QML中實現多線程編程的關鍵組件&#xff0c;它允許開發者將耗時操作移至后臺線程執行&#xff0c;避免阻塞主UI線程&#xff0c;從而提升應用響應速度和用戶體驗。本文將全面介紹WorkerScript的核心機制、使用方法和最佳實踐。WorkerScript核心機制WorkerScript通…

銳浪報表 Grid++Report 表頭表尾的隱藏

設計銳浪表格的模板時&#xff0c;可以通過設計多個表頭、表尾&#xff0c;表頭、表尾中放入打印控件&#xff0c;可以打印相關的數據。在真實打印時&#xff0c;可以通過打印時讓表頭、表尾隱藏或顯示&#xff0c;實現用戶的表格樣式。一、表頭的指定1、 表頭可以多個&#xf…

低速信號設計之 QSPI 篇

一、引言? 在服務器技術不斷演進的當下,對高效、穩定的數據存儲和傳輸需求日益增長。QSPI(Quad Serial Peripheral Interface)總線作為一種高速、串行的外圍設備接口,在服務器領域中發揮著關鍵作用。它為服務器中的各類存儲設備及部分外圍芯片與主處理器之間提供了快速可…

別只知道暴力循環!我從用戶名校驗功能中領悟到的高效字符集判斷法(1684. 統計一致字符串的數目)

別只知道暴力循環&#xff01;我從用戶名校驗功能中領悟到的高效字符集判斷法 &#x1f60e; 大家好&#xff0c;日常開發中&#xff0c;我們經常會遇到一些看似不起眼&#xff0c;卻能成為性能瓶頸的小模塊。今天&#xff0c;我想和大家分享一個我親身經歷的故事&#xff0c;…

力扣面試150題--在排序數組中查找元素的第一個和最后一個位置

Day 85 題目描述思路 當 nums[mid] < target 時&#xff0c;說明目標值在右側&#xff0c;移動左指針 left mid 1 當 nums[mid] > target 時&#xff0c;說明目標值可能在當前位置或左側&#xff0c;移動右指針 right mid - 1 循環結束后&#xff0c;left 指針會指向第…

C++實戰:人臉識別7大核心實例

計算機視覺實例應用 基于C++的人臉識別實例 以下是一些基于C++的人臉識別實例的示例和實現方法,涵蓋了多種技術和庫的應用。這些例子可以幫助開發者快速上手并實現人臉識別功能。 OpenCV 基礎人臉檢測 使用OpenCV的預訓練模型進行人臉檢測是入門級示例。OpenCV自帶Haar級聯…

Uniapp中使用vue3語法

在setup語法糖中調用uniapp的頁面生命周期 <script setup>import { onShow } from "dcloudio/uni-app"onShow(() > {//hanlder...}) </script>vue2混入在vue3中建議使用組合式API 新建baseHook.js import { ref } from "vue"; export fu…

C++vector(2)

2.vector深度剖析及模擬實現 2.1std::vector的核心框架接口的模擬實現bit::vector vector的模擬實現 2.2 使用memcpy拷貝問題 假設模擬實現的vector中的reserve接口中&#xff0c;使用memcpy進行的拷貝&#xff0c;以下代碼會發生什么問題&#xff1f; int main() {gxl::ve…

IPSec VPN -- 野蠻模式

一、野蠻模式簡介野蠻模式VPN是指IPsec VPN中IKE協商采用野蠻模式&#xff08;Aggressive Mode&#xff09;的虛擬專用網絡。它是IKE第一階段協商的一種方式&#xff0c;與主模式相對&#xff0c;具有協商速度快但安全性稍低的特點。以下是具體介紹&#xff1a;1、工作原理&…

rk3588開發板使用硬件編碼處理視頻

開發板默認下載的ffmpeg是通用版&#xff0c;無法調用rk3588的硬件編碼器&#xff0c;視頻編碼效率低。 nyanmisaka開發了用于jellyfin的ffmpeg&#xff0c;支持rk3588硬件編碼器&#xff0c;編譯方法&#xff1a; https://github.com/nyanmisaka/ffmpeg-rockchip/wiki/Compil…

`neutron router-gateway-set` 操作失敗的可能原因及解決方案

根據提供的錯誤信息和搜索結果&#xff0c;neutron router-gateway-set 操作失敗的可能原因及解決方案如下&#xff1a;一、常見錯誤原因數據庫字符集配置問題&#xff08;中文名支持&#xff09; 表現&#xff1a;若路由器名稱包含中文字符&#xff0c;可能因數據庫字符集非UT…

(一)ZooKeeper 發展歷史

?博客主頁&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客內容》&#xff1a;.NET、Java.測試開發、Python、Android、Go、Node、Android前端小程序等相關領域知識 &#x1f4e2;博客專欄&#xff1a; https://blog.csdn.net/m0_63815035/cat…