1.为何要引入Lifecycle?

  我首先来举个大家都比较常见的例子:我们在android开发的时候,经常需要在页面的onCreate()方法中对组件进行初始化,在onPause()方法中停止组件,而在页面的onDestroy()方法中对组件进行资源回收工作。这样的工作非常繁琐,会让页面和组件间的耦合度变高。但这些工作又不得不做,因为可能会引起内存泄漏。

  这里我先给出普通组件和系统组件这两个名词的概念,系统组件是指Activity/Fragment,Service和Application;普通组件是指我们将代码按照功能或作用进行封装的组件。我们希望对自定义组件/普通组件的管理不依赖于页面生命周期的回调方法,同时又在页面生命周期发生变化的时候及时收到通知。这在组件化和架构设计中显得尤为重要。为此,Google引入了Lifecycle作为解决方案,Lifecycle可以帮助开发者创建可感知生命周期的组件。这样,组件就可以在其内部管理自己的生命周期,降低了和页面的耦合度。

  接下来,从三个方面来讲解一下Lifecycle的用法。

2.使用Lifecycle解耦Activity和组件

  案例分析:假设有这样一个需求,在用户打开某个页面的时候,获取用户当前的地理位置,离开这个页面的时候,停止获取。面对这个需求,我们可以这样写代码:

public class MainActivity extends AppCompatActivity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化位置管理器
initLocationManager();
} @Override
protected void onResume() {
super.onResume();
//开始获取用户的地理位置
startGetLocation();
} @Override
protected void onPause() {
super.onPause();
//停止获取用户的地理位置
stopGetLocation();
}
}

  从以上的代码可以看出,获取地理位置这个需求的实现和页面的生命周期息息相关。如果我们想要把这个功能独立成一个组件,那么必须得感知页面的生命周期变化。Lifecycle是如何解决这个问题的呢?Jetpack为我们提供了两个类LifecycleOwner(被观察者)和LifecycleObserver(观察者),即通过观察者模式实现对页面生命周期的监听。

  通过查看ComponentActivity源码,可以得知该类实现了LifecycleOwner接口,并重写了getLifecycle()方法。如下图所示:

  LifecycleOwner接口只有getLifecycle()这一个方法,LifecycleOwner正是通过该方法实现观察者模式的。所以,从以上源码可以得知,ComponentActivity已经帮我们实现了被观察者应该实现的那一部分代码,我们只需要实现观察者应该实现的那一部分代码即可。实现的方式也很简单,就是让自定义组件实现DefaultLifecycleObserver接口,然后将其添加为观察者就行了。

public class MyLocationListener implements DefaultLifecycleObserver {
private LocationManager manager;
private Context context;
private OnLocationChangedListener onLocationChangedListener;
public MyLocationListener(Activity context, OnLocationChangedListener onLocationChangedListener) {
manager = (LocationManager) context.getSystemService(LOCATION_SERVICE);
this.context=context;
this.onLocationChangedListener=onLocationChangedListener;
} @Override
public void onPause(@NonNull LifecycleOwner owner) {
DefaultLifecycleObserver.super.onPause(owner);
Log.i("MyLocationListener","stopGetLocation");
} public interface OnLocationChangedListener{
void onChanged(double latitude,double longitude);
} @Override
public void onResume(@NonNull LifecycleOwner owner) {
DefaultLifecycleObserver.super.onResume(owner);
Log.i("MyLocationListener","startGetLocation");
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
manager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 1, new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
onLocationChangedListener.onChanged(location.getLatitude(),location.getLongitude());
}
});
}
}
public class MainActivity extends AppCompatActivity {
private TextView tv_display;
private MyLocationListener myLocationListener;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_display = findViewById(R.id.tv_display);
myLocationListener=new MyLocationListener(this, new MyLocationListener.OnLocationChangedListener() {
@Override
public void onChanged(double latitude, double longitude) {
tv_display.setText(""+latitude+","+longitude);
}
});
getLifecycle().addObserver(myLocationListener);
}
}

  就这么简单!Lifecycle完美解决了组件对页面生命周期的依赖问题,使得组件可以自行管理生命周期。当将自定义组件作为页面生命周期的观察者时,页面处于哪个生命周期,就会回调DefaultLifecycleObserver接口中的同名方法。是的,就是同名方法!

public interface DefaultLifecycleObserver extends FullLifecycleObserver {

    @Override
default void onCreate(@NonNull LifecycleOwner owner) {
} @Override
default void onStart(@NonNull LifecycleOwner owner) {
} @Override
default void onResume(@NonNull LifecycleOwner owner) {
} @Override
default void onPause(@NonNull LifecycleOwner owner) {
} @Override
default void onStop(@NonNull LifecycleOwner owner) {
} @Override
default void onDestroy(@NonNull LifecycleOwner owner) {
}
}

  可以看到这些方法名前面加了一个default关键词,就是你可以有选择性的重写所需要的方法,而不需要全部重写。是不是很简单?

3.使用LifecycleService解耦Service与组件

  这个类继承自Service并且实现了LifecycleOwner接口, 它也提供了getLifecycle()方法供我们使用。我们只需要继承这个类即可。下面给出代码:

public class MyService extends LifecycleService {
private MyServiceObserver myServiceObserver;
public MyService(){
myServiceObserver=new MyServiceObserver();
getLifecycle().addObserver(myServiceObserver);
}
}

  MyServiceObserver同样要实现DefaultLifecycleObserver接口,和前面的一模一样,这里不再赘述。

4.使用ProcessLifecycleOwner监听应用程序的生命周期

  具有生命周期的系统组件除了Activity,Fragment,Service外,还有Application。很多时候,我们会遇到这样的需求:我们想知道应用程序当前处在前台还是后台,以便作出不同的处理。这个功能用ProcessLifecycleOwner实现起来十分简单,和前面非常的相似。不过这次我们想要观察的是整个应用程序,因此需要在Application中进行相关代码的编写。

public class AppApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppApplicationObserver());
}
}

  AppApplicationObserver也是要实现同样的接口:DefaultLifecycleObserver。

public class AppApplicationObserver implements DefaultLifecycleObserver {
private String TAG=getClass().getName(); @Override
public void onCreate(@NonNull LifecycleOwner owner) {
Log.i(TAG,"onCreate");
DefaultLifecycleObserver.super.onCreate(owner);
} @Override
public void onStart(@NonNull LifecycleOwner owner) {
Log.i(TAG,"onStart");
DefaultLifecycleObserver.super.onStart(owner);
} @Override
public void onResume(@NonNull LifecycleOwner owner) {
Log.i(TAG,"onResume");
DefaultLifecycleObserver.super.onResume(owner);
} @Override
public void onPause(@NonNull LifecycleOwner owner) {
Log.i(TAG,"onPause");
DefaultLifecycleObserver.super.onPause(owner);
} @Override
public void onStop(@NonNull LifecycleOwner owner) {
Log.i(TAG,"onStop");
DefaultLifecycleObserver.super.onStop(owner);
} @Override
public void onDestroy(@NonNull LifecycleOwner owner) {
Log.i(TAG,"onDestroy");
DefaultLifecycleObserver.super.onDestroy(owner);
}
}

  应用程序创建的时候调用onCreate()方法并只调用一次,在前台的时候分别调用onStart()和onResume(),应用程序在后台的时候调用onPause()和onStop()。不过需要注意的是,onDestroy()永远不会调用,系统不会分发调用这个事件。

  Lifecycle的用法到这里就基本讲解完毕了,怎么样,感觉如何?

Lifecycle解决了什么问题,以及它的基本用法的更多相关文章

  1. 解决全局变量共享---C语言的extern关键字用法

    在调试程序时,有一个参数需要在多个函数之间传递,因为是作为调试参数,不想将参数引入到函数中. 很自然的想到使用全局变量来表示这个公共参数,工程代码的结构如下: main.c test.c test.h ...

  2. Android VideoView未解决,动态读取权限、BottomNavigationView的用法

    昨天想写的,但是因为Video的毛病,是真找不出为啥了.百度也没用,学长也不清楚. 百度了那么久,大概得出结论,电脑的视频是不能用它来播放的... ..经过两天的奋斗,我居然搞定了,我的视频终于出来了 ...

  3. window.addEventListener来解决让一个js事件执行多个函数

    可能你也碰到过这种情况,就是在js的代码中用了window.onload后,可能会影响到body中的onload事件.这时就要用window.attachEvent和window.addEventLi ...

  4. 006-线程同步解决【ReentrantLock】

    一.解决方案 004-线程同步问题引出.同步问题解决.死锁.生产者与消费者 通过以上文章可知,通过原子性AtomicLong .以及内部锁(synchronized)机制可以解决线程安全问题.以下是一 ...

  5. 痛苦的版本对齐(3) cygwin下的路径引用(sed解决篇)

    上次问题(见http://www.cnblogs.com/yvivid/p/3546649.html),.depend信息路径错误的问题. 主要尝试了,在(虚拟机下构建)linux下编译,确实没有问题 ...

  6. 程序异常崩溃后用windbg辅助调试解决的经验 以及 堆栈问题调试经验

    1,程序异常崩溃后用windbg辅助调试解决的经验  状况:我的程序调用别人的库做 文件写入工作. 在这一过程中出现异常,程序崩溃. 经反复检查,认为自己的程序没有错,但无法判断在别人库里哪里有错. ...

  7. 【VS开发】MFC学习之 解决StretchBlt()图片缩放绘图失真

    vc中位图伸缩函数StretchBlt在对图片进行缩放时会造成严重的图片失真.在了解解决方法前先巩固下StretchBlt的用法: StretchBlt 函数功能:函数从源矩形中复制一个位图到目标矩形 ...

  8. Java中常用的解决乱码的几种方法

    乱码有时候是一个非常让人头疼的问题,这里就总结一下常用的解决乱码的方法. 只知道的用法,却不明白为什么这么用…… 一. 在Java代码中: request.setCharacterEncoding(& ...

  9. JavaScript状态模式及状态机模型

    这是一篇,我自己都看不完的文章... 文章大体就两部分: 状态模式的介绍 状态机模型的函数库javascript-state-machine的用法和源码解析 场景及问题背景: 我们平时开发时本质上就是 ...

  10. 还在一个模块打天下嘛?你知道引入Jetpack架构后,你的App会发生哪些奇妙的变化吗?

    前言 上篇文章我给大家分享了我对Android架构的理解,从思想层面去讲述架构的演进过程.很多小伙伴读完后拍手叫好,表示还想听我讲一下对Jetpack 架构的看法,本着帮人帮到底的精神,今天我将再次动 ...

随机推荐

  1. LeeCode 318周赛复盘

    T1: 对数组执行操作 思路:模拟 public int[] applyOperations(int[] nums) { int n = nums.length; for (int i = 0; i ...

  2. CLion在工程中添加目录&新文件

    1.将新建文件夹添加到cmake.txt文件里 include_directories(Core/新建文件夹 Core/UserInc Drivers/STM32L4xx_HAL_Driver/Inc ...

  3. CI框架内置分页代码

    Controller 控制器代码 <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Welc ...

  4. java 实现逻辑分页

    //逻辑分页PageModel model = new PageModel();long total = list.size();model.setTotal(total);model.setPage ...

  5. Sql Server 数据库事务与锁,同一事务更新又查询锁?期望大家来解惑

    我有一个People表,有三行数据: 如果我们没详细了解数据库事务执行加锁的过程中,会不会有这样一个疑问:如下的这段 SQL 开启了事务,并且在事务中进行了更新和查询操作. BEGIN TRAN up ...

  6. [C++项目] 通讯录管理系统

    文章目录 通讯录管理系统 1.系统需求 2.创建项目 2.1 创建项目 2.2添加文件 3.菜单功能 4.退出功能 5.添加联系人 5.1 设计联系人结构体 5.2 设计通讯录结构体 5.3 main ...

  7. 如何实现一个sync.Once

    sync.Once 是 golang里用来实现单例的同步原语.Once 常常用来初始化单例资源, 或者并发访问只需初始化一次的共享资源,或者在测试的时候初始化一次测试资源. 单例,就是某个资源或者对象 ...

  8. 2023-05-07:给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格 0 变成 1 。 返回执行此操作后,grid 中最大的岛屿面积是多少? 岛屿 由一组上、下、左、右四个方向相

    2023-05-07:给你一个大小为 n x n 二进制矩阵 grid .最多 只能将一格 0 变成 1 . 返回执行此操作后,grid 中最大的岛屿面积是多少? 岛屿 由一组上.下.左.右四个方向相 ...

  9. 2022-03-15:给定一棵树的头节点head,原本是一棵正常的树, 现在,在树上多加了一条冗余的边, 请找到这条冗余的边并返回。

    2022-03-15:给定一棵树的头节点head,原本是一棵正常的树, 现在,在树上多加了一条冗余的边, 请找到这条冗余的边并返回. 答案2022-03-15: 1.指向头,入度没有0的.入度没有2的 ...

  10. Jupyter Notebook (Anaconda3)更改保存文件的默认路径

    打开jupyter 查找路径 1 import os 2 a=os.path.abspath('.') 3 print(a) 创建个人文件夹 E:\pyAPP\JupyterWork 查找修改配置文件 ...