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. PopupWindow点击空白区域消失

    下面三个条件必须要有,要在popupWindow显示之前调用popupWindow.setOutsideTouchable(true);popupWindow.setFocusable(true);p ...

  2. 创建SVN和设置密码以及SVN自动更新

    重新创建版本库:    svnadmin create /usr/local/svn/month_exam //创建一个svn版本仓库month_exam(month_exam可以随便起名字) cd ...

  3. 安装Zookeeper和Kafka集群

    安装Zookeeper和Kafka集群 本文介绍如何安装Zookeeper和Kafka集群.为了方便,介绍的是在一台服务器上的安装,实际应该安装在多台服务器上,但步骤是一样的. 安装Zookeeper ...

  4. [操作系统] - 进程切换&进程控制

    2.1.6 进程切换 名称解析 进程的上下文(Context) 当一个进程在执行时,CPU的所有寄存器的值.进程的状态以及堆栈中的内容被称为进程的上下文Context 进程的切换(switch) 当内 ...

  5. SQLLabs靶场 less11-20

    SQLLabs靶场 less11-20 Less-11-16 请求方式 注入类型 拼接方式 POST 联合.报错.布尔盲注.延时盲注 username='x'11 请求方式 注入类型 拼接方式 POS ...

  6. 2021-07-28:最短的桥。在给定的二维二进制数组 A 中,存在两座岛。(岛是由四面相连的 1 形成的一个最大组。)现在,我们可以将 0 变为 1,以使两座岛连接起来,变成一座岛。返回必须翻转的

    2021-07-28:最短的桥.在给定的二维二进制数组 A 中,存在两座岛.(岛是由四面相连的 1 形成的一个最大组.)现在,我们可以将 0 变为 1,以使两座岛连接起来,变成一座岛.返回必须翻转的 ...

  7. 2021-10-30:有效的字母异位词。给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位

    2021-10-30:有效的字母异位词.给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词.注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位 ...

  8. 2021-10-27:课程表。你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisi

    2021-10-27:课程表.你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 .在选修某些课程之前需要一些先修课程. 先修课程按数组 prerequisi ...

  9. uni-app Flex布局

    Flexbox #Flex 容器 Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性. nvue布局模型基于 CSS Flexbox, ...

  10. Solon v2.2.17 发布,Java 新的生态型应用开发框架

    相对于 Spring Boot 和 Spring Cloud 的项目: 启动快 5 - 10 倍. (更快) qps 高 2- 3 倍. (更高) 运行时内存节省 1/3 ~ 1/2. (更少) 打包 ...