學(xué)習(xí)記錄(2) - LifeCycle

前言

學(xué)習(xí)記錄系列是通過閱讀學(xué)習(xí)《Android Jetpack應(yīng)用指南》對書中內(nèi)容學(xué)習(xí)記錄的Blog,《Android Jetpack應(yīng)用指南》京東天貓有售氢哮,本文是學(xué)習(xí)記錄的第二篇。


誕生

在 Android 應(yīng)用程序開發(fā)中羽资,解耦很大程序上表現(xiàn)為系統(tǒng)組件的生命周期與普通組件之間的解耦绵疲。普通組件在使用過程中通常需要依賴于系統(tǒng)組件的生命周期。有時候褥民,我們不得不在系統(tǒng)組件的生命周期回調(diào)方法中,主動對普通組件進行調(diào)用或控制洗搂。因為普通組件無法主動獲知系統(tǒng)組件的生命周期事件消返。

系統(tǒng)組件:指的是 Activity/Fragment、Service 和 Application
普通組件:為了讓代碼更容易管理和維護耘拇,通常會將代碼按照功能或作用封裝成組件

舉例:我們經(jīng)常需要在頁面的 onCreate()方法中對組件初始化撵颊,在 onPause()方法中停止組件,而在頁面的 onDestroy()方法中對組件進行資源回收工作惫叛。這樣的工作非常煩瑣倡勇,會讓頁面與組件之間的耦合度變高。但這些工作又不得不做嘉涌,因為這可能會引發(fā)內(nèi)存泄漏妻熊。
我們希望對自定義組件的管理,不依賴于頁面生命周期的回調(diào)方法洛心。同時固耘,在頁面周期發(fā)生變化時题篷,也能及時收到通知词身。

簡介

LifeCycle 可以幫助開發(fā)者創(chuàng)建可以感知生命周期的組件蹂楣。這樣虽风,組件便能夠在其內(nèi)部管理自己的生命周期审轮,從而降低模塊間的耦合度烂斋,并降低內(nèi)存泄漏發(fā)生的可能性。LifeCycle不只對 Activity/Fragment 有用深啤,在 Service 和 Application 中也能大顯身手拗馒。

使用LifeCycle解耦頁面與組件

1.案例分析

需求:在用戶打開某個頁面時,獲取用戶當(dāng)前的地理位置溯街。
代碼實現(xiàn):

public class LifeCycleActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_life_cycle);

        // 初始化位置管理器
        initLocationManager();
    }

    @Override
    protected void onResume() {
        super.onResume();
        // 開始獲取用戶的地理位置
        startGetLocation();
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 停止獲取用戶的地理位置
        stopGetLocation();
    }
}

從以上代碼可以看出诱桂,獲取地理位置這個需求的實現(xiàn),與頁面的生命周期息息相關(guān)呈昔。如果要將獲取地理位置這一功能獨立成一個組件挥等,那么生命周期是必須考慮在內(nèi)的。我們不得不在頁面生命周期的各個回調(diào)方法中堤尾,對組件進行通知肝劲,因為組件不能主動感知生命周期的變化。

2.LifeCycle的原理

Jetpack 提供了兩個類:LifecycleOwner(被觀察者)和 LifecycleObserver(觀察者)郭宝。即通過觀察者模式辞槐,實現(xiàn)對頁面生命周期的監(jiān)聽。

在新版本的SDK包中粘室,Activity已經(jīng)默認(rèn)實現(xiàn)了 LifecycleOwner 接口榄檬。LifecycleOwner 接口中只有一個getLifecycle(LifecycleObserver observer)方法,LifecycleOwner 正是通過該方法實現(xiàn)觀察者模式的育特,

3.解決方案

利用LifecyCycle改寫需求丙号,將該功能從Activity中獨立出去,在減少耦合度的同時缰冤,又不影響對生命周期的監(jiān)聽犬缨。

/**
 * 自定義組件,讓組件實現(xiàn)LifecycleObserver接口棉浸。
 * 與獲取地理位置相關(guān)的代碼在該類中完成
 * 對于組件中那些需要在頁面周期發(fā)生變化時得到通知的方法怀薛,我們需要在這些方法上使用@OnLifecycleEvent(Lifecycle.Event.On_XXX)標(biāo)簽進行標(biāo)識。
 * 這樣迷郑,當(dāng)頁面生命周期發(fā)生變化時枝恋,這些被標(biāo)識過的方法便會被自動調(diào)用
 *
 * @author JinXin 2020/7/26
 */
public class MyLocationListener implements LifecycleObserver {

    private static final String TAG = "MyLocationListener";

    private OnLocationChangedListener onLocationChangedListener;

    public MyLocationListener(Context context, OnLocationChangedListener onLocationChangedListener) {
        this.onLocationChangedListener = onLocationChangedListener;

        // 初始化操作
        initLocationManager();
    }

    private void initLocationManager() {

    }

    /**
     * 當(dāng)Activity執(zhí)行onResume()方法時,該方法會被自動調(diào)用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private void startGetLocation() {
        Log.d(TAG, "startGetLocation: ");
        if (onLocationChangedListener != null) {
            onLocationChangedListener.onChanged(0,0);
        }
    }

    /**
     * 當(dāng)Activity執(zhí)行onPause()方法時嗡害,該方法會被自動調(diào)用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void stopGetLocation() {
        Log.d(TAG, "stopGetLocation: ");
        if (onLocationChangedListener != null) {
            onLocationChangedListener.onChanged(0,0);
        }
    }

    /**
     * 當(dāng)?shù)乩砦恢冒l(fā)生變化時焚碌,通過該接口通知調(diào)用者
     */
    public interface OnLocationChangedListener {

        /**
         * 地理位置發(fā)生變化
         * @param latitude 緯度
         * @param longitude 經(jīng)度
         */
        void onChanged(double latitude, double longitude);

    }
}

在LifeCycleActivity中,只需要引用 MyLocationListener 即可霸妹,不再關(guān)心 Activity 生命周期變化對組件所帶來的影響十电。生命周期的管理完全交給 MyLocationListener 內(nèi)部自行處理。在 Activity 中要做的只是通過 getLifecycle().addObserver()方法,將觀察者和被觀察者綁定起來

public class LifeCycleActivity extends AppCompatActivity {

    private static final String TAG = "LifeCycleActivity";

    private MyLocationListener myLocationListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_life_cycle);
        // 使用Lifecycle感知生命周期
        // 自定義組件MyLocationListener鹃骂,該組件實現(xiàn)LifeCycleObserver接口台盯。
        // 獲取地理位置相關(guān)的代碼在該組件中完成
        myLocationListener = new MyLocationListener(this, (latitude, longitude) -> {

            // 展示收到的位置信息
            Log.d(TAG, "onChanged: " + latitude + " " + longitude);
        });

        // 將觀察者與被觀察者綁定
        getLifecycle().addObserver(myLocationListener);
    }
}


使用LifecycleService 解耦Service組件

1.LifecycleService 基本介紹

為了便于對 Service 生命周期的監(jiān)聽,達(dá)到解耦 Service 與組件的目的畏线, Android 提供了一個名為 LifecycleService 的類静盅。該類繼承自 Service,并實現(xiàn)了 LifecycleOwner 接口寝殴。

2.LifecycleService 的具體使用方法

1.首先蒿叠,需要在 app 的 build.gradle 文件中添加相關(guān)依賴

dependencies {
    // 添加lifecycle相關(guān)依賴
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
}

2.創(chuàng)建 MyService 類并繼承自 LifecycleService ,LifecycleService 是 Service 的直接子類蚣常,使用和普通 Service 沒有差別

public class MyService extends LifecycleService {

    private MyServiceObserver myServiceObserver;

    public MyService() {

        myServiceObserver = new MyServiceObserver();
        // 將觀察者和被觀察者綁定
        getLifecycle().addObserver(myServiceObserver);
    }
}

Service 記得要在 AndroidManifest.xml 中注冊<service android:name=".service.MyService" />

3.創(chuàng)建 MyServiceObserver 類栈虚,該類需要實現(xiàn) LifecycleObserver 接口

public class MyServiceObserver implements LifecycleObserver {

    private static final String TAG = "MyServiceObserver";

    /**
     * 當(dāng)Service的onCreate()方法被調(diào)用時,該方法會被調(diào)用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    private void startGetLocation() {
        Log.d(TAG, "startGetLocation: 開始獲取地理位置");
    }

    /**
     *  當(dāng)Service的onDestroy()方法被調(diào)用時史隆,該方法會被調(diào)用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    private void stopGetLocation() {
        Log.d(TAG, "stopGetLocation: 停止獲取地理位置");
    }
}

4.在 Activity 中控制 Service 的啟動和停止

public class LifecycleServiceActivity extends AppCompatActivity {

    private static final String TAG = "LifecycleServiceActivit";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lifecycle_service);

        findViewById(R.id.btn_start_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 啟動服務(wù)
                Intent intent = new Intent(LifecycleServiceActivity.this, MyService.class);
                startService(intent);
            }
        });

        findViewById(R.id.btn_stop_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 停止服務(wù)
                Intent intent = new Intent(LifecycleServiceActivity.this, MyService.class);
                stopService(intent);
            }
        });
    }
}

5.通過 LogCat 中的日志可以看出魂务,隨著 Service 生命周期的變化,MyServiceObserver 中帶有 @onLifecycleEvent 標(biāo)簽的方法被自動調(diào)用了泌射。這樣便實現(xiàn)了組件對 Service 生命周期的監(jiān)聽粘姜。

2020-10-21 22:52:25.471 20670-20670/com.jinxin.jetpacktest D/MyServiceObserver: startGetLocation: 開始獲取地理位置
2020-10-21 22:52:26.925 20670-20670/com.jinxin.jetpacktest D/MyServiceObserver: stopGetLocation: 停止獲取地理位置

6.總結(jié)

當(dāng) Service 的生命周期發(fā)生變化時,不再需要主動對組件對組件進行通知熔酷,組件能夠在其內(nèi)部自動管理好生命周期所帶來的變化孤紧。LifecycleService 很好地實現(xiàn)了組件與 Service 之間的解耦。


使用 ProcessLifecycleOwner 監(jiān)聽?wèi)?yīng)用程序的生命周期

1.ProcessLifecycleOwner 存在的意義

監(jiān)聽整個應(yīng)用程序的生命周期情況

2.ProcessLifecycleOwner 的具體使用方法

1.首先拒秘,需要在 app 的 build.gradle 文件中添加相關(guān)依賴

dependencies {
    // 添加lifecycle相關(guān)依賴
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
}

2.ProcessLifecycleOwner 的本質(zhì)也是觀察者模式号显。由于觀察的是整個應(yīng)用程序,因此躺酒,需要在 Application 中進行相關(guān)代碼的編寫押蚤。

public class MyApplication extends Application{

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

        // ProcessLifecycleOwner是針對整個應(yīng)用程序的監(jiān)聽,與Activity數(shù)量無關(guān)
        ProcessLifecycleOwner.get().getLifecycle().addObserver(new ApplicationObserver());
    }
}

3.定義一個名為 ApplicationObserver 的類羹应,讓該類實現(xiàn) LifecycleObserver 接口揽碘,已負(fù)責(zé)對應(yīng)用程序生命周期的監(jiān)聽

/**
 * 負(fù)責(zé)對應(yīng)用程序生命周期的監(jiān)聽
 *
 * Lifecycle.Event.ON_CREATE 只會被調(diào)用一次,而Lifecycle.Event.ON_DESTROY永遠(yuǎn)不會被調(diào)用
 * 當(dāng)應(yīng)用程序從后臺回到前臺园匹,或者應(yīng)用程序被首次打開時雳刺,會依次調(diào)用Lifecycle.Event.ON_START和Lifecycle.Event.ON_RESUME
 * 當(dāng)應(yīng)用程序從前臺回到后臺(用戶按下Home鍵或任務(wù)菜單鍵),會依次調(diào)用Lifecycle.Event.ON_PAUSE和Lifecycle.Event.ON_STOP
 * 
 * 需要注意的是裸违,Lifecycle.Event.ON_START 和 Lifecycle.Event.ON_RESUME掖桦,這兩個方法的調(diào)用會有一定的延后。
 * 這是因為系統(tǒng)需要為 “屏幕旋轉(zhuǎn)供汛,由于配置發(fā)生變化而導(dǎo)致 Activity 重新創(chuàng)建” 的情況預(yù)留一些時間枪汪。也就是說凛俱,系統(tǒng)需要保證當(dāng)設(shè)備出現(xiàn)這種情況時,
 * 這兩個事件不會被調(diào)用料饥,因為當(dāng)旋轉(zhuǎn)屏幕時,你的應(yīng)用程序并沒有推到后臺朱监,它只是進入了橫/豎屏模式而已
 * @author JinXin 2020/7/27
 */
class ApplicationObserver implements LifecycleObserver {

    private static final String TAG = "ApplicationObserver";

    /**
     * 在應(yīng)用程序的整個生命周期中只會被調(diào)用一次
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    private void onCreate() {
        Log.d(TAG, "onCreate: ");
    }

    /**
     * 當(dāng)應(yīng)用程序在前臺出現(xiàn)時被調(diào)用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onStart() {
        Log.d(TAG, "onStart: ");
    }

    /**
     * 當(dāng)應(yīng)用程序在前臺出現(xiàn)時被調(diào)用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private void onResume() {
        Log.d(TAG, "onResume: ");
    }

    /**
     * 當(dāng)應(yīng)用程序退出到后臺時被調(diào)用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    private void onPause() {
        Log.d(TAG, "onPause: ");
    }

    /**
     * 當(dāng)應(yīng)用程序退出到后臺時被調(diào)用
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onStop() {
        Log.d(TAG, "onStop: ");
    }

    /**
     * 永遠(yuǎn)不會被調(diào)用岸啡,系統(tǒng)不會分發(fā)調(diào)用ON_DESTROY
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    private void onDestroy() {
        Log.d(TAG, "onDestroy: ");
    }
}


總結(jié)

Lifecycle 組件存在的主要意義是幫助我們解耦,讓自定義組件也能感受到生命周期的變化赫编。有了 Lifecycle 之后巡蘸,在自定義組件內(nèi)部便可以管理好其生命周期,不再需要擔(dān)心組件的內(nèi)存泄漏等問題了擂送,組件使用起來也更加方便和安全

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悦荒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嘹吨,更是在濱河造成了極大的恐慌搬味,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟀拷,死亡現(xiàn)場離奇詭異碰纬,居然都是意外死亡,警方通過查閱死者的電腦和手機问芬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門悦析,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人此衅,你說我怎么就攤上這事强戴。” “怎么了挡鞍?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵骑歹,是天一觀的道長。 經(jīng)常有香客問我墨微,道長陵刹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任欢嘿,我火速辦了婚禮衰琐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘炼蹦。我一直安慰自己羡宙,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布掐隐。 她就那樣靜靜地躺著狗热,像睡著了一般钞馁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匿刮,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天僧凰,我揣著相機與錄音,去河邊找鬼熟丸。 笑死训措,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的光羞。 我是一名探鬼主播绩鸣,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纱兑!你這毒婦竟也來了呀闻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤潜慎,失蹤者是張志新(化名)和其女友劉穎捡多,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铐炫,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡局服,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了驳遵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淫奔。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖堤结,靈堂內(nèi)的尸體忽然破棺而出唆迁,到底是詐尸還是另有隱情,我是刑警寧澤竞穷,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布唐责,位于F島的核電站,受9級特大地震影響瘾带,放射性物質(zhì)發(fā)生泄漏鼠哥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一看政、第九天 我趴在偏房一處隱蔽的房頂上張望朴恳。 院中可真熱鬧,春花似錦允蚣、人聲如沸于颖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽森渐。三九已至做入,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間同衣,已是汗流浹背竟块。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耐齐,地道東北人浪秘。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像蚪缀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子恕出,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354