赞
踩
写在前面
我们经常被面试官问到,你的项目用的什么架构模式呀,MVC、MVP、MVVM ? 其实这些都是我们开发者自己设计的架构模式,非谷歌官方解决方案,我们有时候也很难把控最佳架构模式。 出于这个原因,Google官方给我们提供了Jetpack。
Jetpack是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码,并编写可在各种 Android 版本和设备中一致运行的代码,让开发者集中精力编写重要的业务代码。说白了就是谷歌官方给我们开发者提供了一套解决方案,让我们的代码性能更高更稳定,开发效率更快。
Lifecycle是为了解耦系统组件(比如Activity)和普通组件(比如TextView)而诞生的。它包含两个角色,分别是 LifecycleOwner 和 LifecycleObserver,即生命周期拥有者和生命周期观察者。可以看到这么多系统类都实现了LifecycleOwner:
下面我们通过一个计时器的案例比较传统方式和使用 Lifecycle的区别在哪里。先看看传统的实现方式:
- import androidx.appcompat.app.AppCompatActivity;
-
- import android.os.Bundle;
-
- import android.os.SystemClock;
- import android.widget.Chronometer;
-
- /**
- * @author Luffy
- * @Description 传统方式实现计时器
- * @Date 2022/3/8 23:28
- */
- public class FirstStepActivity extends AppCompatActivity {
- private static final String TAG = "MainActivity";
-
- private Chronometer chronometer;
- /**
- * 从上一次进入页面到退出页面经历的时间
- */
- private long elapsedTime;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_first);
- chronometer = findViewById(R.id.chronometer);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- // 设置基准时间: SystemClock.elapsedRealtime() --- 返回自启动以来的毫秒数,包括睡眠时间。
- chronometer.setBase(SystemClock.elapsedRealtime() - elapsedTime);
- chronometer.start();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- elapsedTime = SystemClock.elapsedRealtime() - chronometer.getBase();
- chronometer.stop();
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
再来看看使用 Lifecycle的实现方式 :
- import android.os.Bundle;
-
- import androidx.appcompat.app.AppCompatActivity;
-
- /**
- * @author Luffy
- * @Description 使用 Lifecycle
- * @Date 2022/3/8 23:28
- */
- public class SecondStepActivity extends AppCompatActivity {
- private static final String TAG = "MainActivity";
-
- private MyChronometer mChronometer;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_second_step);
- mChronometer = findViewById(R.id.chronometer);
- // 给当前 Activity添加观察者
- getLifecycle().addObserver(mChronometer);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- import android.content.Context;
- import android.os.SystemClock;
- import android.util.AttributeSet;
- import android.widget.Chronometer;
-
- import androidx.lifecycle.Lifecycle;
- import androidx.lifecycle.LifecycleObserver;
- import androidx.lifecycle.OnLifecycleEvent;
-
- /**
- * @author Luffy
- * @Description 使用 Lifecycle 解耦界面和组件,通过 @OnLifecycleEvent注解可以让 MyChronometer感知与其绑定的系统组件生命周期
- * 的变化,也就是说在 LifeCycleOwner的生命周期产生变化的时候会调用 LifeCycleObserver中注解修饰的方法。
- * @Date 2022/3/8 23:32
- */
- public class MyChronometer extends Chronometer implements LifecycleObserver {
- /**
- * 从上一次进入页面到退出页面经历的时间
- */
- private long elapsedTime;
-
- public MyChronometer(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
- private void startMeter() {
- setBase(SystemClock.elapsedRealtime() - elapsedTime);
- start();
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
- private void stopMeter() {
- elapsedTime = SystemClock.elapsedRealtime() - getBase();
- stop();
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
我们发现用传统的实现方式,会导致组件与 Activity耦合度特别高,依赖于 Activity生命周期的 onResume 和 onPause方法,而使用 Lifecycle 则不需要重写 Activity的 onResume 和 onPause等生命周期相关的方法,而是自定义组件在其内部管理自己的生命周期,自定义组件于上述案例中的 Activity而言,就是观察者。
首先我们创建一个 Activity,布局两个按钮分别用来启动和停止服务:
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
-
- import androidx.annotation.Nullable;
-
- /**
- * @author Luffy
- * @Description Lifecycle 解耦 service与组件案例
- * @Date 2022/3/9 0:12
- */
- public class ThirdStepActivity extends Activity {
- private static final String TAG = "ThirdStepActivity";
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_third_step);
- }
-
- public void startService(View view) {
- Log.d(TAG, "startService: ");
- startService(new Intent(ThirdStepActivity.this, MyLocationService.class));
- }
-
- public void stopService(View view) {
- Log.d(TAG, "stopService: ");
- stopService(new Intent(ThirdStepActivity.this, MyLocationService.class));
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
先把观察者创建出来:
- import android.Manifest;
- import android.content.Context;
- import android.content.pm.PackageManager;
- import android.location.Location;
- import android.location.LocationListener;
- import android.location.LocationManager;
- import android.util.Log;
-
- import androidx.annotation.NonNull;
- import androidx.core.app.ActivityCompat;
- import androidx.lifecycle.Lifecycle;
- import androidx.lifecycle.LifecycleObserver;
- import androidx.lifecycle.OnLifecycleEvent;
-
- /**
- * @author Luffy
- * @Description 位置信息更新的观察者
- * @Date 2022/3/9 0:14
- */
- public class MyLocationObserver implements LifecycleObserver {
- private static final String TAG = "MyLocationObserver";
- private Context mContext;
- private LocationManager mLocationManager;
- private MyLocationListener mMyLocationListener;
-
- public MyLocationObserver(Context context) {
- mContext = context;
- mMyLocationListener = new MyLocationListener();
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
- private void startGetLocation() {
- Log.d(TAG, "startGetLocation: ");
- mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
- if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) !=
- PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext,
- Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
- return;
- }
- /*
- * 方法说明:
- * requestLocationUpdates:使用给定参数注册来自给定提供者的位置更新。
- *
- * 参数说明:
- * String provider:通过什么方式获取GPS信息
- * long minTimeMs:位置更新之间的最小时间间隔(以毫秒为单位)
- * float minDistanceM:位置更新之间的最小距离(以米为单位)
- * LocationListener listener:位置更新的监听
- */
- mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 1,
- mMyLocationListener);
-
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- private void stopGetLocation() {
- Log.d(TAG, "stopGetLocation: ");
- mLocationManager.removeUpdates(mMyLocationListener);
- }
-
- static class MyLocationListener implements LocationListener {
-
- @Override
- public void onLocationChanged(@NonNull Location location) {
- Log.d(TAG, "onLocationChanged: location :" + location.toString());
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
然后我们发现在Service类的构造方法中只要写一行关键代码就OK了:
- import android.util.Log;
-
- import androidx.lifecycle.LifecycleService;
-
- /**
- * @author Luffy
- * @Description 位置服务
- * @Date 2022/3/9 0:07
- */
- public class MyLocationService extends LifecycleService {
- private static final String TAG = "MyLocationService";
-
- public MyLocationService() {
- Log.d(TAG, "MyLocationService: init.");
- // 给Service类添加观察者
- getLifecycle().addObserver(new MyLocationObserver(this));
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
adb geo 改变虚拟机GPS信息:
进入studioSDK\platform-tools 这个目录下,然后cmd打开命令行窗口,输入:adb -s emulator-5554 emu geo fix 121.4961236714487 31.24010934431376
- import android.app.Application;
-
- import androidx.lifecycle.ProcessLifecycleOwner;
-
- /**
- * @author Luffy
- * @Description 给 ProcessLifecycleOwner(该类为整个应用程序进程提供生命周期) 添加观察者实现监听应用程序生命周期
- * @Date 2022/3/9 8:10
- */
- public class MyApplication extends Application {
- @Override
- public void onCreate() {
- super.onCreate();
- ProcessLifecycleOwner.get().getLifecycle().addObserver(new MyApplicationObserver());
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
监听应用程序的每一个生命周期:
- import android.util.Log;
-
- import androidx.lifecycle.Lifecycle;
- import androidx.lifecycle.LifecycleObserver;
- import androidx.lifecycle.OnLifecycleEvent;
-
- /**
- * @author Luffy
- * @Description
- * @Date 2022/3/9 8:12
- */
- public class MyApplicationObserver implements LifecycleObserver {
- private static final String TAG = "MyApplicationObserver";
-
- @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
- private void onCreate() {
- Log.d(TAG, "Lifecycle.Event.ON_CREATE");
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_START)
- public void onStart() {
- Log.d(TAG, "Lifecycle.Event.ON_START");
- }
-
-
- @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
- public void onResume() {
- Log.d(TAG, "Lifecycle.Event.ON_RESUME");
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
- public void onPause() {
- Log.d(TAG, "Lifecycle.Event.ON_PAUSE");
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
- public void onStop() {
- Log.d(TAG, "Lifecycle.Event.ON_STOP");
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- public void onDestroy() {
- Log.d(TAG, "Lifecycle.Event.ON_DESTROY");
- }
-
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
ProcessLifecycleOwner 是针对整个应用程序的监听,与 Activity 数量无关。
Lifecycle.Event.ON_CREATE只会在应用启动时调用一次,Lifecycle.Event.ON_DESTROY
永远不会被调用。
Lifecycle用来监听生命周期的变化处理相应的操作,那组件的onStart、onStop、onPause不也可以处理相应的操作吗?他们具体差别在哪呢?
确实是可以的。区别在于使用的方便程度和代码的简洁度。比如一个自定义控件,需要 onStart,onStop,onPause中处理一些逻辑。不使用 LifeCycle则需要在每个使用它的页面中的 onStart,onStop,onPause中调用这个自定义控件的方法。如果使用LifeCycle,则只需要把自定义控件作为观察者与Activity绑定即可,自定义控件你内部会管理自己的生命周期。
ViewModel是介于View(视图)和Model(数据模型)之间的桥梁,使视图和数据能够分离,也能保持通信。
经典案例:解决屏幕旋转之后用户操作数据丢失问题
- import android.app.Application;
- import android.content.Context;
-
- import androidx.annotation.NonNull;
- import androidx.lifecycle.AndroidViewModel;
-
- /**
- * @author Luffy
- * @Description ViewModel 案例:不要向 ViewModel中传入Context,会导致内存泄漏。如果非要使用Context,需使用 AndroidViewModel
- * 中的Application
- * @Date 2022/3/9 8:44
- */
- public class MyViewModel extends /*ViewModel*/ AndroidViewModel {
- /**
- * 在 ViewModel 中声明成员,即把数据与 ViewModel 关联起来了
- */
- int number;
-
- public MyViewModel(@NonNull Application application) {
- super(application);
- Context applicationContext = application.getApplicationContext();
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
在Activity中,操作的数据其实都是从 ViewModel中获取的:
- import androidx.appcompat.app.AppCompatActivity;
- import androidx.lifecycle.ViewModelProvider;
-
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.widget.TextView;
-
- public class MainActivity extends AppCompatActivity {
- private static final String TAG = "MainActivity";
- private TextView mTvNum;
- private MyViewModel mMyViewModel;
- private int mNumber;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Log.d(TAG, "onCreate: ");
- mTvNum = findViewById(R.id.tv_num);
- /* 通过 ViewModelProvider.get 获取 ViewModel 对象:
- * 第一个参数:ViewModelStoreOwner --- 表示谁拥有这个 ViewModel 对象,其实最终是通过 ViewModelStore 保留 ViewModel
- * 第二个参数:Factory --- 用于实例化 ViewModel 的工厂
- */
- mMyViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication()))
- .get(MyViewModel.class);
- mTvNum.setText(String.valueOf(mMyViewModel.number));
- }
-
- public void autoIncrement(View view) {
- mTvNum.setText(String.valueOf(++mMyViewModel.number));
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
注意事项:
不要向ViewModel中传入Context,会导致内存泄漏。如果非要使用Context,请使用 AndroidViewModel中的Application
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态()的应用组件观察者。官方详细介绍:LiveData 概览
一般情况下,我们会把 LiveData放在 ViewModel中,以便在 ViewModel中的数据发生变化时通知页面更新UI。
案例一:秒表
- import androidx.lifecycle.MutableLiveData;
- import androidx.lifecycle.ViewModel;
-
- /**
- * @author Luffy
- * @Description ViewModel 与 LiveData 关联
- * @Date 2022/3/9 21:54
- */
- public class MyViewModel extends ViewModel {
- // 记录当前已经历的秒数
- private MutableLiveData<Integer> currentSecond;
-
- public MutableLiveData<Integer> getCurrentLiveData() {
- if (currentSecond == null) {
- currentSecond = new MutableLiveData<>();
- // 设置初始值
- currentSecond.setValue(0);
- }
- return currentSecond;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- import androidx.appcompat.app.AppCompatActivity;
- import androidx.lifecycle.ViewModelProvider;
-
- import android.os.Bundle;
- import android.util.Log;
- import android.widget.TextView;
-
- import java.util.Timer;
- import java.util.TimerTask;
-
- /**
- * @author Luffy
- * @Description ViewModel + LiveData 数据实时更新
- * @Date 2022/3/9 21:50
- */
- public class MainActivity extends AppCompatActivity {
- private static final String TAG = "MainActivity";
-
- private TextView mTvCurrentSecond;
- private MyViewModel mMyViewModel;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Log.d(TAG, "onCreate: ");
- mTvCurrentSecond = findViewById(R.id.tv_current_second);
- // 1.实例化 ViewModel
- mMyViewModel = new ViewModelProvider(this,
- new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
- // 2.从 ViewModel 实例中获取数据,并设置到文本上
- mTvCurrentSecond.setText(String.valueOf(mMyViewModel.getCurrentLiveData()));
-
- // 2.给 MyViewModel 设置观察者,当 MyViewModel 数据发生变化时,观察者可以察觉到
- mMyViewModel.getCurrentLiveData().observe(this, integer ->
- mTvCurrentSecond.setText(String.valueOf(integer.intValue())));
- // 开启计时器
- startTimer();
- }
-
- private void startTimer() {
- // 三个参数含义分别是:时间任务、延迟时间、间隔时间
- new Timer().schedule(new TimerTask() {
- @Override
- public void run() {
- // 非 UI 线程 postValue;UI 线程 setValue
- mMyViewModel.getCurrentLiveData().postValue(mMyViewModel.getCurrentLiveData().getValue() + 1);
- }
- }, 1000, 1000);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
案例二:Fragment通信,实现两个Fragment进度条同步更新的效果
- import androidx.lifecycle.MutableLiveData;
- import androidx.lifecycle.ViewModel;
-
- /**
- * @author Luffy
- * @Description 将 SeekBar 的进度与 ViewModel 绑定
- * @Date 2022/3/9 22:12
- */
- public class MyViewModel extends ViewModel {
- public MutableLiveData<Integer> progress;
-
- public MutableLiveData<Integer> getProgress() {
- if (progress == null) {
- progress = new MutableLiveData<>();
- progress.setValue(0);
- }
- return progress;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- import android.os.Bundle;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.SeekBar;
-
- import androidx.fragment.app.Fragment;
- import androidx.lifecycle.ViewModelProvider;
-
- /**
- * @author Luffy
- * @Description ViewModel + LiveData 实现 Fragment 间通信
- * @Date 2022/3/9 22:06
- */
- public class FirstFragment extends Fragment {
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.fragment_first, container, false);
- SeekBar seekBar = rootView.findViewById(R.id.seekBar);
-
- // 1.实例化 MyViewModel
- MyViewModel mMyViewModel = new ViewModelProvider(getActivity(),
- new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MyViewModel.class);
- // 2.给 MyViewModel 设置观察者,当 MyViewModel 数据发生变化时,观察者可以察觉到,此处 seekBar是观察者
- mMyViewModel.getProgress().observe(getActivity(), seekBar::setProgress);
- // 3.设置 SeekBar 进度更新的监听
- seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- // 3.1 当 SeekBar 进度更新时,同步数据到对应的 ViewModel 上
- mMyViewModel.progress.setValue(seekBar.getProgress());
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
-
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
-
- }
- });
-
- return rootView;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
- import android.os.Bundle;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.SeekBar;
-
- import androidx.fragment.app.Fragment;
- import androidx.lifecycle.ViewModelProvider;
-
- /**
- * @author Luffy
- * @Description 将 SeekBar 的进度与 ViewModel 绑定
- * @Date 2022/3/9 22:13
- */
- public class SecondFragment extends Fragment {
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.fragment_second, container, false);
- SeekBar seekBar = rootView.findViewById(R.id.seekBar);
-
- MyViewModel mMyViewModel = new ViewModelProvider(getActivity(),
- new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MyViewModel.class);
- mMyViewModel.getProgress().observe(getActivity(), seekBar::setProgress);
- seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- mMyViewModel.progress.setValue(seekBar.getProgress());
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
-
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
-
- }
- });
-
- return rootView;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
确保界面符合数据状态
不会发生内存泄漏
不会因Activity停止而导致崩溃
不再需要手动处理生命周期
数据始终保持最新状态
适当的配置更改
共享资源
本篇博文先讲这么多,不当之处,还望指正,欢迎评论区留言交流~
涉及的代码已上传至码云:https://gitee.com/zhangningke/jetpack-study
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。