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. day84:luffy:优惠活动策略&用户认证&购物车商品的勾选/结算

    目录 1.课程列表页活动和真实价格计算 1.优惠活动策略的model表结构 2.课程列表页显示优惠类型名称 3.课程列表页显示真实价格 4.将优惠类型名称和真实价格显示到前端页面上 5.课程列表页显示 ...

  2. spring事务里面开启线程插入,报错了是否会回滚?

    1.前言 一道非常有意思的面试题目.大概是这样子的,如果在一个事务中,开启线程进行插入更新等操作,如果报错了,事务是否会进行回滚 2.代码 示例1 @RequestMapping("/tes ...

  3. SSL CA 证书生成shell

    gencert ssl证书生成 要保证Web浏览器到服务器的安全连接,HTTPS几乎是唯一选择.HTTPS其实就是HTTP over SSL,也就是让HTTP连接建立在SSL安全连接之上. SSL使用 ...

  4. 10分钟带你徒手做个Java线程池

    摘要:花10分钟开发一个极简版的Java线程池,让小伙伴们更好的理解线程池的核心原理. 本文分享自华为云社区<放大招了,冰河带你10分钟手撸Java线程池,yyds,赶快收藏吧>,作者:冰 ...

  5. DRF的限流组件(源码分析)

    DRF限流组件(源码分析) 限流,限制用户访问频率,例如:用户1分钟最多访问100次 或者 短信验证码一天每天可以发送50次, 防止盗刷. 对于匿名用户,使用用户IP作为唯一标识. 对于登录用户,使用 ...

  6. GraalVM(云原生时代的Java)和IoT在边缘侧落地与实践

    环顾四周,皆是对手! 云时代的掉队者,由于Java启动的高延时.对资源的高占用.导致在Serverless及FaaS架构下力不从心,在越来越流行的边缘计算.IoT方向上也是难觅踪影; Java语言在业 ...

  7. UniApp小程序开发项目创建与运行

    1.准备工作:HbuiderX  +  微信开发者工具下载安装+小程序账号申请开通(这里就不例举了,可以看同账号uniapp小程序开发准备) 2.创建项目 新版本的HbuilderX点击新建项目--选 ...

  8. Centos7.x 更换Jenkins构建目录

    原由:最近因为原来的Jenkins构建目录,已经要满了,想着更换下构建目录,此篇文件简单介绍下更换过程. 注:此文章可能仅适用于我个人,仅供参考.如有其他办法,欢迎评论指教. 查了几种方法,最终选为使 ...

  9. Python工具箱系列(三十)

    PostgreSQL MySQL的口号是"世界上最流行的开源关系型数据库",而PostgreSQL的Slogan则是"世界上最先进的开源关系型数据库(PostgreSQL ...

  10. MySQL(Zip版)安装配置

    MySQL官网下载地址:MySQL 将压缩包内文件夹解压至任意目录,以mysql-5.7.40版本为例 第一步 添加环境变量 将mysql-5.7.40-winx64文件夹内的bin文件夹添加到环境变 ...