主頁 >  其他 > “終于懂了“系列:Jetpack AAC完整決議(-)Lifecycle 完全掌握!

“終于懂了“系列:Jetpack AAC完整決議(-)Lifecycle 完全掌握!

2020-11-16 02:22:28 其他

一、Android Jetpack 介紹

1.1 Jetpack是啥

官方定義如下:

Jetpack 是一個由多個庫組成的套件,可幫助開發者遵循最佳做法,減少樣板代碼并撰寫可在各種 Android 版本和設備中一致運行的代碼,讓開發者精力集中撰寫重要的代碼,

JetPack更多是一種概念和態度,它是谷歌開發的非Android Framework SDK自帶、但同時是Android開發必備的/推薦的SDK/開發規范合集,相當于Google把自己的Android生態重新整理了一番,確立了Android未來的開發大方向,

使用Jetpack有如下好處:

  • 遵循最佳做法,Android Jetpack 組件采用最新的設計方法構建,具有向后兼容性,可以減少崩潰和記憶體泄露,
  • 消除樣板代碼,Android Jetpack 可以管理各種繁瑣的 Activity(如后臺任務、導航和生命周期管理),以便您可以專注于打造出色的應用,
  • 減少不一致,這些庫可在各種 Android 版本和設備中以一致的方式運作,助您降低復雜性,

Jetpack

Jetpack原意為 噴氣背包,Android背上Jetpack后就直沖云霄,這很形象了~

也就是,Jetpack是幫助開發者高效開發應用的工具集,那么這一工具包含了哪些內容呢?

1.2 Jetpack分類

分類如下圖(現在官網已經找不到這個圖了):

Android Jetpack 組件覆寫以下 4 個方面:架構(Architecture)、基礎(Foundation)、行為(Behavior) 、界面(UI),

真正的精華主要是Architecture,全稱是Android Architecture Component(AAC), 即Android架構組件

其包括比較成功的Lifecycle、LiveData、ViewModel,同時也是我們使用MVVM模式的最好框架工具,可以組合使用,也可以單獨使用,

以上基本都是官網的介紹,我們主要目標就是掌握AAC的組件,深入理解進而運用到MVVM架構中,

如題,我們學習Jetpack的重點就是AAC,這篇就從基礎的Lifecycle講起,

二、Lifecycle

Lifecycle,顧名思義,是用于幫助開發者管理Activity和Fragment 的生命周期,它是LiveData和ViewModel的基礎,下面就先介紹為何及如何使用Lifecycle,

2.1 Lifecycle之前

官方檔案有個例子 來說明使用Lifecycle之前是如何生命周期管理的:

假設我們有一個在螢屏上顯示設備位置的 Activity,常見的實作可能如下所示:

    class MyLocationListener {
        public MyLocationListener(Context context, Callback callback) {
            // ...
        }

        void start() {
            // 連接系統定位服務
        }

        void stop() {
            // 斷開系統定位服務
        }
    }

    class MyActivity extends AppCompatActivity {
        private MyLocationListener myLocationListener;

        @Override
        public void onCreate(...) {
            myLocationListener = new MyLocationListener(this, (location) -> {
                // 更新 UI
            });
        }

        @Override
        public void onStart() {
            super.onStart();
            myLocationListener.start();
            // 管理其他需要回應activity生命周期的組件
        }

        @Override
        public void onStop() {
            super.onStop();
            myLocationListener.stop();
            // 管理其他需要回應activity生命周期的組件
        }
    }
    

雖然此示例看起來沒問題,但在真實的應用中,最侄訓有太多管理界面和其他組件的呼叫,以回應生命周期的當前狀態,管理多個組件會在生命周期方法(如 onStart() 和 onStop())中放置大量的代碼,這使得它們難以維護,

此外,無法保證組件會在 Activity 或 Fragment 停止之前啟動myLocationListener,在我們需要執行長時間運行的操作(如 onStart() 中的某種配置檢查)時尤其如此,在這種情況下,myLocationListener的onStop() 方法會在 onStart() 之前呼叫,這使得組件留存的時間比所需的時間要長,從而導致內次泄漏,如下:

    class MyActivity extends AppCompatActivity {
        private MyLocationListener myLocationListener;

        public void onCreate(...) {
            myLocationListener = new MyLocationListener(this, location -> {
                // 更新 UI
            });
        }

        @Override
        public void onStart() {
            super.onStart();
            Util.checkUserStatus(result -> {
                //如果checkUserStatus耗時較長,在activity停止后才回呼,那么myLocationListener啟動后就沒辦法走stop()方法了,
                //又因為myLocationListener持有activity,所以會造成記憶體泄漏,
                if (result) {
                    myLocationListener.start();
                }
            });
        }

        @Override
        public void onStop() {
            super.onStop();
            myLocationListener.stop();
        }
    }
    

即2個問題點:

  • activity的生命周期內有大量管理組件的代碼,難以維護,
  • 無法保證組件會在 Activity/Fragment停止后不執行啟動

Lifecycle庫 則可以 以彈性和隔離的方式解決這些問題,

2.2 Lifecycle的使用

Lifecycle是一個庫,也包含Lifecycle這樣一個類,Lifecycle類 用于存盤有關組件(如 Activity 或 Fragment)的生命周期狀態的資訊,并允許其他物件觀察此狀態,

2.2.1 引入依賴

1、非androidX專案 引入:

implementation "android.arch.lifecycle:extensions:1.1.1"

添加這一句代碼就依賴了如下的庫:

2、androidX專案 引入:

如果專案已經依賴了AndroidX:

implementation 'androidx.appcompat:appcompat:1.2.0'

那么我們就可以使用Lifecycle庫了,因為appcompat依賴了androidx.fragment,而androidx.fragment下依賴了ViewModel和 LiveData,LiveData內部又依賴了Lifecycle,

如果想要單獨引入依賴,則如下:

在專案根目錄的build.gradle添加 google() 代碼庫,然后app的build.gradle引入依賴,官方給出的依賴如下:

//根目錄的 build.gradle
    repositories {
        google()
        ...
    }

//app的build.gradle
    dependencies {
        def lifecycle_version = "2.2.0"
        def arch_version = "2.1.0"

        // ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
        // LiveData
        implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
        // 只有Lifecycles (不帶 ViewModel or LiveData)
        implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
    
        // Saved state module for ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

        // lifecycle注解處理器
        annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
        // 替換 - 如果使用Java8,就用這個替換上面的lifecycle-compiler
        implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

	//以下按需引入
        // 可選 - 幫助實作Service的LifecycleOwner
        implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"
        // 可選 - ProcessLifecycleOwner給整個 app行程 提供一個lifecycle
        implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
        // 可選 - ReactiveStreams support for LiveData
        implementation "androidx.lifecycle:lifecycle-reactivestreams:$lifecycle_version"
        // 可選 - Test helpers for LiveData
        testImplementation "androidx.arch.core:core-testing:$arch_version"
    }
    

看著有很多,實際上如果只使用Lifecycle,只需要引入lifecycle-runtime即可,但通常都是和 ViewModel、 LiveData 配套使用的,所以lifecycle-viewmodel、lifecycle-livedata 一般也會引入,

另外,lifecycle-process是給整個app行程提供一個lifecycle,會面也會提到,

2.2.2 使用方法

Lifecycle的使用很簡單:

  • 1、生命周期擁有者 使用getLifecycle()獲取Lifecycle實體,然后代用addObserve()添加觀察者;
  • 2、觀察者實作LifecycleObserver,方法上使用OnLifecycleEvent注解關注對應生命周期,生命周期觸發時就會執行對應方法;

2.2.2.1 基本使用

在Activity(或Fragment)中 一般用法如下:

public class LifecycleTestActivity extends AppCompatActivity {

    private String TAG = "Lifecycle_Test";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lifecycle_test);
        //Lifecycle 生命周期
        getLifecycle().addObserver(new MyObserver());
        Log.i(TAG, "onCreate: ");
    }
    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume: ");
    }
    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause: ");
    }
}

Activity(或Fragment)是生命周期的擁有者,通過getLifecycle()方法獲取到生命周期Lifecycle物件,Lifecycle物件使用addObserver方法 給自己添加觀察者,即MyObserver物件,當Lifecycle的生命周期發生變化時,MyObserver就可以感知到,

MyObserver是如何使用生命周期的呢?看下MyObserver的實作:

public class MyObserver implements LifecycleObserver {

    private String TAG = "Lifecycle_Test";
    
    @OnLifecycleEvent(value = Lifecycle.Event.ON_RESUME)
    public void connect(){
        Log.i(TAG, "connect: ");
    }

    @OnLifecycleEvent(value = Lifecycle.Event.ON_PAUSE)
    public void disConnect(){
        Log.i(TAG, "disConnect: ");
    }
}

首先MyObserver實作了介面LifecycleObserver,LifecycleObserver用于標記一個類是生命周期觀察者,
然后在connectListener()、disconnectListener()上 分別都加了@OnLifecycleEvent注解,且value分別是Lifecycle.Event.ON_RESUME、Lifecycle.Event.ON_PAUSE,這個效果就是:connectListener()會在ON_RESUME時執行,disconnectListener()會在ON_PAUSE時執行,

我們打開LifecycleTestActivity 然后退出,日志列印如下:

2020-11-09 17:25:40.601 4822-4822/com.hfy.androidlearning I/Lifecycle_Test: onCreate: 

2020-11-09 17:25:40.605 4822-4822/com.hfy.androidlearning I/Lifecycle_Test: onResume: 
2020-11-09 17:25:40.605 4822-4822/com.hfy.androidlearning I/Lifecycle_Test: connect: 

2020-11-09 17:25:51.841 4822-4822/com.hfy.androidlearning I/Lifecycle_Test: disConnect: 
2020-11-09 17:25:51.841 4822-4822/com.hfy.androidlearning I/Lifecycle_Test: onPause: 

可見MyObserver的方法 確實是在對應關注的生命周期觸發時呼叫, 當然注解中的value你也寫成其它 你關注的任何一個生命周期,例如Lifecycle.Event.ON_DESTROY,

2.2.2.2 MVP架構中的使用

如果是 在MVP架構中,那么就可以把presenter作為觀察者:

public class LifecycleTestActivity extends AppCompatActivity implements IView {
    private String TAG = "Lifecycle_Test";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lifecycle_test);
        //Lifecycle 生命周期
//        getLifecycle().addObserver(new MyObserver());

        //MVP中使用Lifecycle
        getLifecycle().addObserver(new MyPresenter(this));
        Log.i(TAG, "onCreate: ");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume: ");
    }
    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause: ");
    }

    @Override
    public void showView() {}
    @Override
    public void hideView() {}
}

//Presenter
class MyPresenter implements LifecycleObserver {
    private static final String TAG = "Lifecycle_Test";
    private final IView mView;

    public MyPresenter(IView view) {mView = view;}

    @OnLifecycleEvent(value = Lifecycle.Event.ON_START)
    private void getDataOnStart(LifecycleOwner owner){
        Log.i(TAG, "getDataOnStart: ");
        
        Util.checkUserStatus(result -> {
                //checkUserStatus是耗時操作,回呼后檢查當前生命周期狀態
                if (owner.getLifecycle().getCurrentState().isAtLeast(STARTED)) {
                	start();
                    mView.showView();
                }
            });        
    }
    @OnLifecycleEvent(value = Lifecycle.Event.ON_STOP)
    private void hideDataOnStop(){
        Log.i(TAG, "hideDataOnStop: ");
        stop();
        mView.hideView();
    }
}

//IView
interface IView {
    void showView();
    void hideView();
}

這里是讓Presenter實作LifecycleObserver介面,同樣在方法上注解要觸發的生命周期,最后在Activity中作為觀察者添加到Lifecycle中,

這樣做好處是啥呢? 當Activity生命周期發生變化時,MyPresenter就可以感知并執行方法,不需要在MainActivity的多個生命周期方法中呼叫MyPresenter的方法了,

  • 所有方法呼叫操作都由組件本身管理:Presenter類自動感知生命周期,如果需要在其他的Activity/Fragment也使用這個Presenter,只需添加其為觀察者即可,
  • 讓各個組件存盤自己的邏輯,減輕Activity/Fragment中代碼,更易于管理

—— 上面提到的第一個問題點就解決了,

另外,注意到 getDataOnStart()中耗時校驗回呼后,對當前生命周期狀態進行了檢查:至少處于STARTED狀態才會繼續執行start()方法,也就是保證了Activity停止后不會走start()方法;

—— 上面提到的第二個問題點也解決了,

2.2.3 自定義LifecycleOwner

在Activity中呼叫getLifecycle()能獲取到Lifecycle實體,那getLifecycle()是哪里定義的方法呢
?是介面LifecycleOwner,顧明來思義,生命周期擁有者:

/**
 * 生命周期擁有者
 * 生命周期事件可被 自定義的組件 用來 處理生命周期事件的變化,同時不會在Activity/Fragmen中寫任何代碼
 */
public interface LifecycleOwner {
    @NonNull
    Lifecycle getLifecycle();
}

Support Library 26.1.0及以上、AndroidX的 Fragment 和 Activity 已實作 LifecycleOwner 介面,所以我們在Activity中可以直接使用getLifecycle(),

如果有一個自定義類并希望使其成為LifecycleOwner,可以使用LifecycleRegistry類,它是Lifecycle的實作類,但需要將事件轉發到該類:

    public class MyActivity extends Activity implements LifecycleOwner {
        private LifecycleRegistry lifecycleRegistry;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            lifecycleRegistry = new LifecycleRegistry(this);
            lifecycleRegistry.markState(Lifecycle.State.CREATED);
        }
        @Override
        public void onStart() {
            super.onStart();
            lifecycleRegistry.markState(Lifecycle.State.STARTED);
        }
        @NonNull
        @Override
        public Lifecycle getLifecycle() {
            return lifecycleRegistry;
        }
    }

MyActivity實作LifecycleOwner,getLifecycle()回傳lifecycleRegistry實體,lifecycleRegistry實體則是在onCreate創建,并且在各個生命周期內呼叫markState()方法完成生命周期事件的傳遞,這就完成了LifecycleOwner的自定義,也即MyActivity變成了LifecycleOwner,然后就可以和 實作了LifecycleObserver的組件配合使用了,

補充一點,觀察者的方法可以接受一個引數LifecycleOwner,就可以用來獲取當前狀態、或者繼續添加觀察者, 若注解的是ON_ANY還可以接收Event,用于區分是哪個事件,如下:

    class TestObserver implements LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        void onCreated(LifecycleOwner owner) {
//            owner.getLifecycle().addObserver(anotherObserver);
//            owner.getLifecycle().getCurrentState();
        }
        @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
        void onAny(LifecycleOwner owner, Lifecycle.Event event) {
//            event.name()
        }
    }

2.3 Application生命周期 ProcessLifecycleOwner

之前對App進入前后臺的判斷是通過registerActivityLifecycleCallbacks(callback)方法,然后在callback中利用一個全域變數做計數,在onActivityStarted()中計數加1,在onActivityStopped方法中計數減1,從而判斷前后臺切換,

而使用ProcessLifecycleOwner可以直接獲取應用前后臺切換狀態,(記得先引入lifecycle-process依賴)

使用方式和Activity中類似,只不過要使用ProcessLifecycleOwner.get()獲取ProcessLifecycleOwner,代碼如下:

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

	//注冊App生命周期觀察者
        ProcessLifecycleOwner.get().getLifecycle().addObserver(new ApplicationLifecycleObserver());
    }
    
    /**
     * Application生命周期觀察,提供整個應用行程的生命周期
     *
     * Lifecycle.Event.ON_CREATE只會分發一次,Lifecycle.Event.ON_DESTROY不會被分發,
     *
     * 第一個Activity進入時,ProcessLifecycleOwner將分派Lifecycle.Event.ON_START, Lifecycle.Event.ON_RESUME,
     * 而Lifecycle.Event.ON_PAUSE, Lifecycle.Event.ON_STOP,將在最后一個Activit退出后后延遲分發,如果由于配置更改而銷毀并重新創建活動,則此延遲足以保證ProcessLifecycleOwner不會發送任何事件,
     *
     * 作用:監聽應用程式進入前臺或后臺
     */
    private static class ApplicationLifecycleObserver implements LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        private void onAppForeground() {
            Log.w(TAG, "ApplicationObserver: app moved to foreground");
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        private void onAppBackground() {
            Log.w(TAG, "ApplicationObserver: app moved to background");
        }
    }
}

看到確實很簡單,和前面Activity的Lifecycle用法幾乎一樣,而我們使用ProcessLifecycleOwner就顯得很優雅了, 生命周期分發邏輯已在注釋里說明,

三、 原始碼分析

Lifecycle的使用很簡單,接下來就是對Lifecycle原理和原始碼的決議了,

我們可以先猜下原理:LifecycleOwner(如Activity)在生命周期狀態改變時(也就是生命周期方法執行時),遍歷觀察者,獲取每個觀察者的方法上的注解,如果注解是@OnLifecycleEvent且value是和生命周期狀態一致,那么就執行這個方法, 這個猜測合理吧?下面你來看看,

3.1 Lifecycle類

先來瞅瞅Lifecycle:

public abstract class Lifecycle {
    //添加觀察者
    @MainThread
    public abstract void addObserver(@NonNull LifecycleObserver observer);
    //移除觀察者
    @MainThread
    public abstract void removeObserver(@NonNull LifecycleObserver observer);
    //獲取當前狀態
    public abstract State getCurrentState();

//生命周期事件,對應Activity生命周期方法
    public enum Event {
        ON_CREATE,
        ON_START,
        ON_RESUME,
        ON_PAUSE,
        ON_STOP,
        ON_DESTROY,
        ON_ANY  //可以回應任意一個事件
    }
    
    //生命周期狀態. (Event是進入這種狀態的事件)
    public enum State {
        DESTROYED,
        INITIALIZED,
        CREATED,
        STARTED,
        RESUMED;

        //判斷至少是某一狀態
        public boolean isAtLeast(@NonNull State state) {
            return compareTo(state) >= 0;
        }
    }

Lifecycle 使用兩種主要列舉跟蹤其關聯組件的生命周期狀態:

  1. Event,生命周期事件,這些事件對應Activity/Fragment生命周期方法,
  2. State,生命周期狀態,而Event是指進入一種狀態的事件,
    Event觸發的時機:
  • ON_CREATE、ON_START、ON_RESUME事件,是在LifecycleOwner對應的方法執行 之后 分發,
  • ON_PAUSE、ON_STOP、ON_DESTROY事件,是在LifecycleOwner對應的方法呼叫 之前 分發,
    這保證了LifecycleOwner是在這個狀態內,

官網有個圖很清晰:

構成 Android Activity 生命周期的狀態和事件

3.2 Activity對LifecycleOwner的實作

前面提到Activity實作了LifecycleOwner,所以才能直接使用getLifecycle(),具體是在androidx.activity.ComponentActivity中:

//androidx.activity.ComponentActivity,這里忽略了一些其他代碼,我們只看Lifecycle相關
public class ComponentActivity extends androidx.core.app.ComponentActivity implements LifecycleOwner{
    ...
   
    private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
    ...
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSavedStateRegistryController.performRestore(savedInstanceState);
        ReportFragment.injectIfNeededIn(this); //使用ReportFragment分發生命周期事件
        if (mContentLayoutId != 0) {
            setContentView(mContentLayoutId);
        }
    }
    @CallSuper
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        Lifecycle lifecycle = getLifecycle();
        if (lifecycle instanceof LifecycleRegistry) {
            ((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);
        }
        super.onSaveInstanceState(outState);
        mSavedStateRegistryController.performSave(outState);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
}

這里忽略了一些其他代碼,我們只看Lifecycle相關,

看到ComponentActivity實作了介面LifecycleOwner,并在getLifecycle()回傳了LifecycleRegistry實體,前面提到LifecycleRegistry是Lifecycle具體實作,

然后在onSaveInstanceState()中設定mLifecycleRegistry的狀態為State.CREATED,然后怎么沒有了?其他生命周期方法內咋沒處理?what?和猜測的不一樣啊, 別急,在onCreate()中有這么一行:ReportFragment.injectIfNeededIn(this);,這個就是關鍵所在,

3.3 生命周期事件分發——ReportFragment

//專門用于分發生命周期事件的Fragment
public class ReportFragment extends Fragment {
    
    public static void injectIfNeededIn(Activity activity) {
        if (Build.VERSION.SDK_INT >= 29) {
            //在API 29及以上,可以直接注冊回呼 獲取生命周期
            activity.registerActivityLifecycleCallbacks(
                    new LifecycleCallbacks());
        }
        //API29以前,使用fragment 獲取生命周期
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            manager.executePendingTransactions();
        }
    }

    @SuppressWarnings("deprecation")
    static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
        if (activity instanceof LifecycleRegistryOwner) {//這里廢棄了,不用看
            ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
            return;
        }

        if (activity instanceof LifecycleOwner) {
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if (lifecycle instanceof LifecycleRegistry) {
                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);//使用LifecycleRegistry的handleLifecycleEvent方法處理事件
            }
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        dispatch(Lifecycle.Event.ON_CREATE);
    }
    @Override
    public void onStart() {
        super.onStart();
        dispatch(Lifecycle.Event.ON_START);
    }
    @Override
    public void onResume() {
        super.onResume();
        dispatch(Lifecycle.Event.ON_RESUME);
    }
    @Override
    public void onPause() {
        super.onPause();
        dispatch(Lifecycle.Event.ON_PAUSE);
    }
    ...省略onStop、onDestroy
    
    private void dispatch(@NonNull Lifecycle.Event event) {
        if (Build.VERSION.SDK_INT < 29) {
            dispatch(getActivity(), event);
        }
    }
    
    //在API 29及以上,使用的生命周期回呼
    static class LifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
        ...
        @Override
        public void onActivityPostCreated(@NonNull Activity activity,@Nullable Bundle savedInstanceState) {
            dispatch(activity, Lifecycle.Event.ON_CREATE);
        }
        @Override
        public void onActivityPostStarted(@NonNull Activity activity) {
            dispatch(activity, Lifecycle.Event.ON_START);
        }
        @Override
        public void onActivityPostResumed(@NonNull Activity activity) {
            dispatch(activity, Lifecycle.Event.ON_RESUME);
        }
        @Override
        public void onActivityPrePaused(@NonNull Activity activity) {
            dispatch(activity, Lifecycle.Event.ON_PAUSE);
        }
        ...省略onStop、onDestroy
    }
}

首先injectIfNeededIn()內進行了版本區分:在API 29及以上 直接使用activity的registerActivityLifecycleCallbacks 直接注冊了生命周期回呼,然后給當前activity添加了ReportFragment,注意這個fragment是沒有布局的,

然后, 無論LifecycleCallbacks、還是fragment的生命周期方法 最后都走到了 dispatch(Activity activity, Lifecycle.Event event)方法,其內部使用LifecycleRegistry的handleLifecycleEvent方法處理事件,

而ReportFragment的作用就是獲取生命周期而已,因為fragment生命周期是依附Activity的,好處就是把這部分邏輯抽離出來,實作activity的無侵入,如果你對圖片加載庫Glide比較熟,就會知道它也是使用透明Fragment獲取生命周期的,

3.4 生命周期事件處理——LifecycleRegistry

到這里,生命中周期事件的處理有轉移到了 LifecycleRegistry 中:

//LifecycleRegistry.java
   //系統自定義的保存Observer的map,可在遍歷中增刪
    private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap = new FastSafeIterableMap<>();
            
    public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
        State next = getStateAfter(event);//獲取event發生之后的將要處于的狀態
        moveToState(next);//移動到這個狀態
    }

    private void moveToState(State next) {
        if (mState == next) {
            return;//如果和當前狀態一致,不處理
        }
        mState = next; //賦值新狀態
        if (mHandlingEvent || mAddingObserverCounter != 0) {
            mNewEventOccurred = true;
            return;
        }
        mHandlingEvent = true;
        sync(); //把生命周期狀態同步給所有觀察者
        mHandlingEvent = false;
    }
    
        private void sync() {
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
                    + "garbage collected. It is too late to change lifecycle state.");
        }
        while (!isSynced()) {  //isSynced()意思是 所有觀察者都同步完了
            mNewEventOccurred = false;
            //mObserverMap就是 在activity中添加observer后 用于存放observer的map
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                backwardPass(lifecycleOwner);
            }
            Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
            if (!mNewEventOccurred && newest != null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                forwardPass(lifecycleOwner);
            }
        }
        mNewEventOccurred = false;
    }
    ...
    
     static State getStateAfter(Event event) {
        switch (event) {
            case ON_CREATE:
            case ON_STOP:
                return CREATED;
            case ON_START:
            case ON_PAUSE:
                return STARTED;
            case ON_RESUME:
                return RESUMED;
            case ON_DESTROY:
                return DESTROYED;
            case ON_ANY:
                break;
        }
        throw new IllegalArgumentException("Unexpected event value " + event);
    }

邏輯很清晰:使用getStateAfter()獲取event發生之后的將要處于的狀態(看前面那張圖很好理解),moveToState()是移動到新狀態,最后使用sync()把生命周期狀態同步給所有觀察者,

注意到sync()中有個while回圈,很顯然是在遍歷觀察者,并且很顯然觀察者是存放在mObserverMap中的,而mObserverMap對觀察者的添加 很顯然 就是 Activity中使用getLifecycle().addObserver()這里:

//LifecycleRegistry.java
    @Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        //帶狀態的觀察者,這個狀態的作用:新的事件觸發后 遍歷通知所有觀察者時,判斷是否已經通知這個觀察者了
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
        //observer作為key,ObserverWithState作為value,存到mObserverMap

        if (previous != null) {
            return;//已經添加過,不處理
        }
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            return;//lifecycleOwner退出了,不處理
        }
	//下面代碼的邏輯:通過while回圈,把新的觀察者的狀態 連續地 同步到最新狀態mState,
    //意思就是:雖然可能添加的晚,但把之前的事件一個個分發給你(upEvent方法),即粘性
        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        State targetState = calculateTargetState(observer);//計算目標狀態
        mAddingObserverCounter++;
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
            pushParentState(statefulObserver.mState);
            statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
            popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }

        if (!isReentrance) {
            sync();
        }
        mAddingObserverCounter--;
    }

用observer創建帶狀態的觀察者ObserverWithState,observer作為key、ObserverWithState作為value,存到mObserverMap, 接著做了安全判斷,最后把新的觀察者的狀態 連續地 同步到最新狀態mState,意思就是:雖然可能添加的晚,但會把之前的事件一個個分發給你,即粘性,

回到剛剛sync()的while回圈,看看如何處理分發事件:

    private void sync() {
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            Log.w(LOG_TAG, "LifecycleOwner is garbage collected, you shouldn't try dispatch "
                    + "new events from it.");
            return;
        }
        while (!isSynced()) {
            mNewEventOccurred = false;
            // no need to check eldest for nullability, because isSynced does it for us.
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                backwardPass(lifecycleOwner);
            }
            Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
            if (!mNewEventOccurred && newest != null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                forwardPass(lifecycleOwner);
            }
        }
        mNewEventOccurred = false;
    }
    
    private boolean isSynced() {
        if (mObserverMap.size() == 0) {
            return true; 
        }//最老的和最新的觀察者的狀態一致,都是ower的當前狀態,說明已經同步完了
        State eldestObserverState = mObserverMap.eldest().getValue().mState;
        State newestObserverState = mObserverMap.newest().getValue().mState;
        return eldestObserverState == newestObserverState && mState == newestObserverState;
    }
    
    private void forwardPass(LifecycleOwner lifecycleOwner) {
        Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator = mObserverMap.iteratorWithAdditions();
        while (ascendingIterator.hasNext() && !mNewEventOccurred) {//正向遍歷,從老到新
            Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();
            ObserverWithState observer = entry.getValue();
            while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred && mObserverMap.contains(entry.getKey()))) {
                pushParentState(observer.mState);
                observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));//observer獲取事件
                popParentState();
            }
        }
    }

    private void backwardPass(LifecycleOwner lifecycleOwner) {
        Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator = mObserverMap.descendingIterator();
        while (descendingIterator.hasNext() && !mNewEventOccurred) {//反向遍歷,從新到老
            Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next();
            ObserverWithState observer = entry.getValue();
            while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred && mObserverMap.contains(entry.getKey()))) {
                Event event = downEvent(observer.mState);
                pushParentState(getStateAfter(event));
                observer.dispatchEvent(lifecycleOwner, event);//observer獲取事件
                popParentState();
            }
        }
    }

回圈條件是!isSynced(),若最老的和最新的觀察者的狀態一致,且都是ower的當前狀態,說明已經同步完了,

沒有同步完就進入回圈體:

  • mState比最老觀察者狀態小,走backwardPass(lifecycleOwner):從新到老分發,回圈使用downEvent()和observer.dispatchEvent(),連續分發事件;
  • mState比最新觀察者狀態大,走forwardPass(lifecycleOwner):從老到新分發,回圈使用upEvent()和observer.dispatchEvent(),連續分發事件,

接著ObserverWithState型別的observer就獲取到了事件,即observer.dispatchEvent(lifecycleOwner, event),下面來看看它是如何讓加了對應注解的方法執行的,

3.5 事件回呼后 方法執行

我們繼續看下 ObserverWithState:

    static class ObserverWithState {
        State mState;
        GenericLifecycleObserver mLifecycleObserver;

        ObserverWithState(LifecycleObserver observer, State initialState) {
            mLifecycleObserver = Lifecycling.getCallback(observer);
            mState = initialState;
        }

        void dispatchEvent(LifecycleOwner owner, Event event) {
            State newState = getStateAfter(event);
            mState = min(mState, newState);
            mLifecycleObserver.onStateChanged(owner, event);
            mState = newState;
        }
    }

mState的作用是:新的事件觸發后 遍歷通知所有觀察者時,判斷是否已經通知這個觀察者了,即防止重復通知,

mLifecycleObserver是使用Lifecycling.getCallback(observer)獲取的GenericLifecycleObserver實體,GenericLifecycleObserver是介面,繼承自LifecycleObserver:

//接受生命周期改變并分發給真正的觀察者
public interface LifecycleEventObserver extends LifecycleObserver {
    //生命周期狀態變化
    void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}

也就說,LifecycleEventObserver 給 LifecycleObserver 增加了感知生命周期狀態變化的能力,

看看Lifecycling.getCallback(observer):

    @NonNull
    static LifecycleEventObserver lifecycleEventObserver(Object object) {
        ...省略很多型別判斷的代碼
        return new ReflectiveGenericLifecycleObserver(object);
    }

方法內有很多對observer進行型別判斷的代碼,我們這里關注的是ComponentActivity,所以LifecycleEventObserver的實作類就是ReflectiveGenericLifecycleObserver了:

class ReflectiveGenericLifecycleObserver implements LifecycleEventObserver {
    private final Object mWrapped;
    private final CallbackInfo mInfo;

    ReflectiveGenericLifecycleObserver(Object wrapped) {
        mWrapped = wrapped;
        mInfo = ClassesInfoCache.sInstance.getInfo(mWrapped.getClass());//存放了event與加了注解方法的資訊
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Event event) {
        mInfo.invokeCallbacks(source, event, mWrapped);//執行對應event的觀察者的方法
    }
}

它的onStateChanged()方法內部使用CallbackInfo的invokeCallbacks方法,這里應該就是執行觀察者的方法了,

ClassesInfoCache內部用Map存了 所有觀察者的回呼資訊,CallbackInfo是當前觀察者的回呼資訊,

先看下CallbackInfo實體的創建,ClassesInfoCache.sInstance.getInfo(mWrapped.getClass()):

//ClassesInfoCache.java
    private final Map<Class, CallbackInfo> mCallbackMap = new HashMap<>();//所有觀察者的回呼資訊
    private final Map<Class, Boolean> mHasLifecycleMethods = new HashMap<>();//觀察者是否有注解了生命周期的方法
    
    CallbackInfo getInfo(Class<?> klass) {
        CallbackInfo existing = mCallbackMap.get(klass);//如果已經存在當前觀察者回呼資訊 直接取
        if (existing != null) {
            return existing;
        }
        existing = createInfo(klass, null);//沒有就去收集資訊并創建
        return existing;
    }
    
    private CallbackInfo createInfo(Class<?> klass, @Nullable Method[] declaredMethods) {
        Class<?> superclass = klass.getSuperclass();
        Map<MethodReference, Lifecycle.Event> handlerToEvent = new HashMap<>();//生命周期事件到來 對應的方法
        ...
        Method[] methods = declaredMethods != null ? declaredMethods : getDeclaredMethods(klass);//反射獲取觀察者的方法
        boolean hasLifecycleMethods = false;
        for (Method method : methods) {//遍歷方法 找到注解OnLifecycleEvent
            OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
            if (annotation == null) {
                continue; //沒有注解OnLifecycleEvent 就return
            }
            hasLifecycleMethods = true;//有注解OnLifecycleEvent
            Class<?>[] params = method.getParameterTypes(); //獲取方法引數
            int callType = CALL_TYPE_NO_ARG;
            if (params.length > 0) { //有引數
                callType = CALL_TYPE_PROVIDER;
                if (!params[0].isAssignableFrom(LifecycleOwner.class)) {
                    throw new IllegalArgumentException(//第一個引數必須是LifecycleOwner
                            "invalid parameter type. Must be one and instanceof LifecycleOwner");
                }
            }
            Lifecycle.Event event = annotation.value();

            if (params.length > 1) {
                callType = CALL_TYPE_PROVIDER_WITH_EVENT;
                if (!params[1].isAssignableFrom(Lifecycle.Event.class)) {
                    throw new IllegalArgumentException(//第二個引數必須是Event
                            "invalid parameter type. second arg must be an event");
                }
                if (event != Lifecycle.Event.ON_ANY) {
                    throw new IllegalArgumentException(//有兩個引數 注解值只能是ON_ANY
                            "Second arg is supported only for ON_ANY value");
                }
            }
            if (params.length > 2) { //引數不能超過兩個
                throw new IllegalArgumentException("cannot have more than 2 params");
            }
            MethodReference methodReference = new MethodReference(callType, method);
            verifyAndPutHandler(handlerToEvent, methodReference, event, klass);//校驗方法并加入到map handlerToEvent 中
        }
        CallbackInfo info = new CallbackInfo(handlerToEvent);//獲取的 所有注解生命周期的方法handlerToEvent,構造回呼資訊實體
        mCallbackMap.put(klass, info);//把當前觀察者的回呼資訊存到ClassesInfoCache中
        mHasLifecycleMethods.put(klass, hasLifecycleMethods);//記錄 觀察者是否有注解了生命周期的方法
        return info;
    }
  • 如果不存在當前觀察者回呼資訊,就使用createInfo()方法收集創建
  • 先反射獲取觀察者的方法,遍歷方法 找到注解了OnLifecycleEvent的方法,先對方法的引數進行了校驗,
  • 第一個引數必須是LifecycleOwner;第二個引數必須是Event;有兩個引數 注解值只能是ON_ANY;引數不能超過兩個
  • 校驗方法并加入到map,key是方法,value是Event,map handlerToEvent是所有的注解了生命周期的方法,
  • 遍歷完,然后用 handlerToEvent來構造 當前觀察者回呼資訊CallbackInfo,存到ClassesInfoCache的mCallbackMap中,并記錄 觀察者是否有注解了生命周期的方法,

整體思路還是很清晰的,繼續看CallbackInfo的invokeCallbacks方法:

    static class CallbackInfo {
        final Map<Lifecycle.Event, List<MethodReference>> mEventToHandlers;//Event對應的多個方法
        final Map<MethodReference, Lifecycle.Event> mHandlerToEvent;//要回呼的方法

        CallbackInfo(Map<MethodReference, Lifecycle.Event> handlerToEvent) {
            mHandlerToEvent = handlerToEvent;
            mEventToHandlers = new HashMap<>();
            //這里遍歷mHandlerToEvent來獲取mEventToHandlers
            for (Map.Entry<MethodReference, Lifecycle.Event> entry : handlerToEvent.entrySet()) {
                Lifecycle.Event event = entry.getValue();
                List<MethodReference> methodReferences = mEventToHandlers.get(event);
                if (methodReferences == null) {
                    methodReferences = new ArrayList<>();
                    mEventToHandlers.put(event, methodReferences);
                }
                methodReferences.add(entry.getKey());
            }
        }

        @SuppressWarnings("ConstantConditions")
        void invokeCallbacks(LifecycleOwner source, Lifecycle.Event event, Object target) {
            invokeMethodsForEvent(mEventToHandlers.get(event), source, event, target);//執行對應event的方法
            invokeMethodsForEvent(mEventToHandlers.get(Lifecycle.Event.ON_ANY), source, event,target);//執行注解了ON_ANY的方法
        }

        private static void invokeMethodsForEvent(List<MethodReference> handlers,
                LifecycleOwner source, Lifecycle.Event event, Object mWrapped) {
            if (handlers != null) {
                for (int i = handlers.size() - 1; i >= 0; i--) {//執行Event對應的多個方法
                    handlers.get(i).invokeCallback(source, event, mWrapped);
                }
            }
        }
    }

很好理解,執行對應event的方法、執行注解了ON_ANY的方法,其中mEventToHandlers是在創建CallbackInfo時由遍歷mHandlerToEvent來獲取,存放了每個Event對應的多個方法,

最后看看handlers.get(i).invokeCallback,即MethodReference中:

    static class MethodReference {
        ...

        void invokeCallback(LifecycleOwner source, Lifecycle.Event event, Object target) {
            try {
                switch (mCallType) {
                    case CALL_TYPE_NO_ARG:
                        mMethod.invoke(target);//沒有引數的
                        break;
                    case CALL_TYPE_PROVIDER:
                        mMethod.invoke(target, source);//一個引數的:LifecycleOwner
                        break;
                    case CALL_TYPE_PROVIDER_WITH_EVENT:
                        mMethod.invoke(target, source, event);//兩個引數的:LifecycleOwner,Event
                        break;
                }
            } 
           ...
        }
...
    }

根據不同引數型別,執行對應方法,

到這里,整個流程就完整了,實際看了這么一大圈,基本思路和我們的猜想是一致的,

這里借Android Jetpack架構組件(三)一文帶你了解Lifecycle(原理篇)的圖總結下:

總結

四、總結

本文先介紹了Jetpack和AAC的概念,這是Android官方推薦的通用開發工具集,其中AAC是架構組件,是本系列文章的介紹內容,接著介紹了AAC的基礎組件Lifecycle,它能讓開發者更好的管理Activity/Fragment生命周期,最后詳細分析了Lifecycle原始碼及原理,

Jetpack的AAC是我們后續開發Android必備知識,也是完成MVVM架構的基礎,Lifecycle更是AAC中的基礎,所以完整掌握本篇內容十分必要,

.

感謝與參考:

Lifecycle官方檔案

Android Jetpack架構組件(三)一文帶你了解Lifecycle(原理篇)

Android架構組件(2)LifecycleRegistry 原始碼分析

.

你的 點贊、評論,是對我的巨大鼓勵!

歡迎關注我的 公 眾 號:胡飛洋

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/218831.html

標籤:AI

上一篇:Android 救援模式(Rescue Mode)原理剖析

下一篇:懸賞:請教LFXP2-8E 6MNC芯片的程式放在什么地方?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more