当前位置:   article > 正文

Jetpack常用库的简单使用(一)_jetpack的使用

jetpack的使用

写在前面

我们经常被面试官问到,你的项目用的什么架构模式呀,MVC、MVP、MVVM ? 其实这些都是我们开发者自己设计的架构模式,非谷歌官方解决方案,我们有时候也很难把控最佳架构模式。 出于这个原因,Google官方给我们提供了Jetpack。

初识Jetpack

Jetpack是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码,并编写可在各种 Android 版本和设备中一致运行的代码,让开发者集中精力编写重要的业务代码。说白了就是谷歌官方给我们开发者提供了一套解决方案,让我们的代码性能更高更稳定,开发效率更快。

为何使用Jetpack

  • 遵循最佳做法:Android Jetpack 组件采用最新的设计方法构建,具有向后兼容性,可以减少崩溃和内存泄漏。
  • 消除样板代码:Android Jetpack 可以管理各种繁琐的Activity(如后台任务、导航和生命周期管理),以便我们更专注业务逻辑,打造出色的应用。
  • 减少不一致:这些库可在各种 Android 版本和设备中以一致的方式运作,降低适配带来的开发难度。

Jetpack 与 AndroidX

  • AndroidX命名空间中包含Android Jetpack 库
  • AndroidX 代替Android Support Library
  • AAC(Android Architecture Component)中的组件纳入AndroidX中
  • 谷歌把其他一些需要频繁更新迭代的特性也都并入了AndroidX中,而不是放在SDK中,这样便于快速更新迭代(船大好掉头)

Lifecycle的诞生

Lifecycle是为了解耦系统组件(比如Activity)和普通组件(比如TextView)而诞生的。它包含两个角色,分别是 LifecycleOwner 和 LifecycleObserver,即生命周期拥有者和生命周期观察者。可以看到这么多系统类都实现了LifecycleOwner:

Lifecycle 解耦页面(Activity)与普通组件

下面我们通过一个计时器的案例比较传统方式和使用 Lifecycle的区别在哪里。先看看传统的实现方式:

  1. import androidx.appcompat.app.AppCompatActivity;
  2. import android.os.Bundle;
  3. import android.os.SystemClock;
  4. import android.widget.Chronometer;
  5. /**
  6. * @author Luffy
  7. * @Description 传统方式实现计时器
  8. * @Date 2022/3/8 23:28
  9. */
  10. public class FirstStepActivity extends AppCompatActivity {
  11. private static final String TAG = "MainActivity";
  12. private Chronometer chronometer;
  13. /**
  14. * 从上一次进入页面到退出页面经历的时间
  15. */
  16. private long elapsedTime;
  17. @Override
  18. protected void onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.activity_first);
  21. chronometer = findViewById(R.id.chronometer);
  22. }
  23. @Override
  24. protected void onResume() {
  25. super.onResume();
  26. // 设置基准时间: SystemClock.elapsedRealtime() --- 返回自启动以来的毫秒数,包括睡眠时间。
  27. chronometer.setBase(SystemClock.elapsedRealtime() - elapsedTime);
  28. chronometer.start();
  29. }
  30. @Override
  31. protected void onPause() {
  32. super.onPause();
  33. elapsedTime = SystemClock.elapsedRealtime() - chronometer.getBase();
  34. chronometer.stop();
  35. }
  36. }

再来看看使用 Lifecycle的实现方式 :

  1. import android.os.Bundle;
  2. import androidx.appcompat.app.AppCompatActivity;
  3. /**
  4. * @author Luffy
  5. * @Description 使用 Lifecycle
  6. * @Date 2022/3/8 23:28
  7. */
  8. public class SecondStepActivity extends AppCompatActivity {
  9. private static final String TAG = "MainActivity";
  10. private MyChronometer mChronometer;
  11. @Override
  12. protected void onCreate(Bundle savedInstanceState) {
  13. super.onCreate(savedInstanceState);
  14. setContentView(R.layout.activity_second_step);
  15. mChronometer = findViewById(R.id.chronometer);
  16. // 给当前 Activity添加观察者
  17. getLifecycle().addObserver(mChronometer);
  18. }
  19. }
  1. import android.content.Context;
  2. import android.os.SystemClock;
  3. import android.util.AttributeSet;
  4. import android.widget.Chronometer;
  5. import androidx.lifecycle.Lifecycle;
  6. import androidx.lifecycle.LifecycleObserver;
  7. import androidx.lifecycle.OnLifecycleEvent;
  8. /**
  9. * @author Luffy
  10. * @Description 使用 Lifecycle 解耦界面和组件,通过 @OnLifecycleEvent注解可以让 MyChronometer感知与其绑定的系统组件生命周期
  11. * 的变化,也就是说在 LifeCycleOwner的生命周期产生变化的时候会调用 LifeCycleObserver中注解修饰的方法。
  12. * @Date 2022/3/8 23:32
  13. */
  14. public class MyChronometer extends Chronometer implements LifecycleObserver {
  15. /**
  16. * 从上一次进入页面到退出页面经历的时间
  17. */
  18. private long elapsedTime;
  19. public MyChronometer(Context context, AttributeSet attrs) {
  20. super(context, attrs);
  21. }
  22. @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
  23. private void startMeter() {
  24. setBase(SystemClock.elapsedRealtime() - elapsedTime);
  25. start();
  26. }
  27. @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
  28. private void stopMeter() {
  29. elapsedTime = SystemClock.elapsedRealtime() - getBase();
  30. stop();
  31. }
  32. }

我们发现用传统的实现方式,会导致组件与 Activity耦合度特别高,依赖于 Activity生命周期的 onResume 和 onPause方法,而使用 Lifecycle 则不需要重写 Activity的 onResume 和 onPause等生命周期相关的方法,而是自定义组件在其内部管理自己的生命周期,自定义组件于上述案例中的 Activity而言,就是观察者。

LifecycleService 解耦Service 与普通组件

首先我们创建一个 Activity,布局两个按钮分别用来启动和停止服务:

  1. import android.app.Activity;
  2. import android.content.Intent;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.View;
  6. import androidx.annotation.Nullable;
  7. /**
  8. * @author Luffy
  9. * @Description Lifecycle 解耦 service与组件案例
  10. * @Date 2022/3/9 0:12
  11. */
  12. public class ThirdStepActivity extends Activity {
  13. private static final String TAG = "ThirdStepActivity";
  14. @Override
  15. protected void onCreate(@Nullable Bundle savedInstanceState) {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.activity_third_step);
  18. }
  19. public void startService(View view) {
  20. Log.d(TAG, "startService: ");
  21. startService(new Intent(ThirdStepActivity.this, MyLocationService.class));
  22. }
  23. public void stopService(View view) {
  24. Log.d(TAG, "stopService: ");
  25. stopService(new Intent(ThirdStepActivity.this, MyLocationService.class));
  26. }
  27. }

先把观察者创建出来:

  1. import android.Manifest;
  2. import android.content.Context;
  3. import android.content.pm.PackageManager;
  4. import android.location.Location;
  5. import android.location.LocationListener;
  6. import android.location.LocationManager;
  7. import android.util.Log;
  8. import androidx.annotation.NonNull;
  9. import androidx.core.app.ActivityCompat;
  10. import androidx.lifecycle.Lifecycle;
  11. import androidx.lifecycle.LifecycleObserver;
  12. import androidx.lifecycle.OnLifecycleEvent;
  13. /**
  14. * @author Luffy
  15. * @Description 位置信息更新的观察者
  16. * @Date 2022/3/9 0:14
  17. */
  18. public class MyLocationObserver implements LifecycleObserver {
  19. private static final String TAG = "MyLocationObserver";
  20. private Context mContext;
  21. private LocationManager mLocationManager;
  22. private MyLocationListener mMyLocationListener;
  23. public MyLocationObserver(Context context) {
  24. mContext = context;
  25. mMyLocationListener = new MyLocationListener();
  26. }
  27. @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
  28. private void startGetLocation() {
  29. Log.d(TAG, "startGetLocation: ");
  30. mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
  31. if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) !=
  32. PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext,
  33. Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
  34. return;
  35. }
  36. /*
  37. * 方法说明:
  38. * requestLocationUpdates:使用给定参数注册来自给定提供者的位置更新。
  39. *
  40. * 参数说明:
  41. * String provider:通过什么方式获取GPS信息
  42. * long minTimeMs:位置更新之间的最小时间间隔(以毫秒为单位)
  43. * float minDistanceM:位置更新之间的最小距离(以米为单位)
  44. * LocationListener listener:位置更新的监听
  45. */
  46. mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 1,
  47. mMyLocationListener);
  48. }
  49. @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
  50. private void stopGetLocation() {
  51. Log.d(TAG, "stopGetLocation: ");
  52. mLocationManager.removeUpdates(mMyLocationListener);
  53. }
  54. static class MyLocationListener implements LocationListener {
  55. @Override
  56. public void onLocationChanged(@NonNull Location location) {
  57. Log.d(TAG, "onLocationChanged: location :" + location.toString());
  58. }
  59. }
  60. }

然后我们发现在Service类的构造方法中只要写一行关键代码就OK了:

  1. import android.util.Log;
  2. import androidx.lifecycle.LifecycleService;
  3. /**
  4. * @author Luffy
  5. * @Description 位置服务
  6. * @Date 2022/3/9 0:07
  7. */
  8. public class MyLocationService extends LifecycleService {
  9. private static final String TAG = "MyLocationService";
  10. public MyLocationService() {
  11. Log.d(TAG, "MyLocationService: init.");
  12. // 给Service类添加观察者
  13. getLifecycle().addObserver(new MyLocationObserver(this));
  14. }
  15. }

adb geo 改变虚拟机GPS信息:
进入studioSDK\platform-tools 这个目录下,然后cmd打开命令行窗口,输入:adb -s emulator-5554 emu geo fix 121.4961236714487 31.24010934431376 

ProcessLifecycleOwner 监听应用程序生命周期

  1. import android.app.Application;
  2. import androidx.lifecycle.ProcessLifecycleOwner;
  3. /**
  4. * @author Luffy
  5. * @Description 给 ProcessLifecycleOwner(该类为整个应用程序进程提供生命周期) 添加观察者实现监听应用程序生命周期
  6. * @Date 2022/3/9 8:10
  7. */
  8. public class MyApplication extends Application {
  9. @Override
  10. public void onCreate() {
  11. super.onCreate();
  12. ProcessLifecycleOwner.get().getLifecycle().addObserver(new MyApplicationObserver());
  13. }
  14. }

监听应用程序的每一个生命周期: 

  1. import android.util.Log;
  2. import androidx.lifecycle.Lifecycle;
  3. import androidx.lifecycle.LifecycleObserver;
  4. import androidx.lifecycle.OnLifecycleEvent;
  5. /**
  6. * @author Luffy
  7. * @Description
  8. * @Date 2022/3/9 8:12
  9. */
  10. public class MyApplicationObserver implements LifecycleObserver {
  11. private static final String TAG = "MyApplicationObserver";
  12. @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
  13. private void onCreate() {
  14. Log.d(TAG, "Lifecycle.Event.ON_CREATE");
  15. }
  16. @OnLifecycleEvent(Lifecycle.Event.ON_START)
  17. public void onStart() {
  18. Log.d(TAG, "Lifecycle.Event.ON_START");
  19. }
  20. @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
  21. public void onResume() {
  22. Log.d(TAG, "Lifecycle.Event.ON_RESUME");
  23. }
  24. @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
  25. public void onPause() {
  26. Log.d(TAG, "Lifecycle.Event.ON_PAUSE");
  27. }
  28. @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
  29. public void onStop() {
  30. Log.d(TAG, "Lifecycle.Event.ON_STOP");
  31. }
  32. @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
  33. public void onDestroy() {
  34. Log.d(TAG, "Lifecycle.Event.ON_DESTROY");
  35. }
  36. }

ProcessLifecycleOwner 是针对整个应用程序的监听,与 Activity 数量无关。

Lifecycle.Event.ON_CREATE只会在应用启动时调用一次,Lifecycle.Event.ON_DESTROY
永远不会被调用。 

LifeCycle的好处

  •  帮助开发者建立可感知生命周期的组件
  • 组件在其内部管理自己的生命周期,从而降低代码耦合度
  • 降低内存泄漏发生的可能性(目前还没有体现出来)
  • Activity、Fragment、Service、Application均有 LifeCycle支持

Lifecycle面试题

Lifecycle用来监听生命周期的变化处理相应的操作,那组件的onStart、onStop、onPause不也可以处理相应的操作吗?他们具体差别在哪呢?

确实是可以的。区别在于使用的方便程度和代码的简洁度。比如一个自定义控件,需要 onStart,onStop,onPause中处理一些逻辑。不使用 LifeCycle则需要在每个使用它的页面中的 onStart,onStop,onPause中调用这个自定义控件的方法。如果使用LifeCycle,则只需要把自定义控件作为观察者与Activity绑定即可,自定义控件你内部会管理自己的生命周期。

ViewModel的诞生

ViewModel是介于View(视图)和Model(数据模型)之间的桥梁,使视图和数据能够分离,也能保持通信。

 经典案例:解决屏幕旋转之后用户操作数据丢失问题

  1. import android.app.Application;
  2. import android.content.Context;
  3. import androidx.annotation.NonNull;
  4. import androidx.lifecycle.AndroidViewModel;
  5. /**
  6. * @author Luffy
  7. * @Description ViewModel 案例:不要向 ViewModel中传入Context,会导致内存泄漏。如果非要使用Context,需使用 AndroidViewModel
  8. * 中的Application
  9. * @Date 2022/3/9 8:44
  10. */
  11. public class MyViewModel extends /*ViewModel*/ AndroidViewModel {
  12. /**
  13. * 在 ViewModel 中声明成员,即把数据与 ViewModel 关联起来了
  14. */
  15. int number;
  16. public MyViewModel(@NonNull Application application) {
  17. super(application);
  18. Context applicationContext = application.getApplicationContext();
  19. }
  20. }

 在Activity中,操作的数据其实都是从 ViewModel中获取的:

  1. import androidx.appcompat.app.AppCompatActivity;
  2. import androidx.lifecycle.ViewModelProvider;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.View;
  6. import android.widget.TextView;
  7. public class MainActivity extends AppCompatActivity {
  8. private static final String TAG = "MainActivity";
  9. private TextView mTvNum;
  10. private MyViewModel mMyViewModel;
  11. private int mNumber;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.activity_main);
  16. Log.d(TAG, "onCreate: ");
  17. mTvNum = findViewById(R.id.tv_num);
  18. /* 通过 ViewModelProvider.get 获取 ViewModel 对象:
  19. * 第一个参数:ViewModelStoreOwner --- 表示谁拥有这个 ViewModel 对象,其实最终是通过 ViewModelStore 保留 ViewModel
  20. * 第二个参数:Factory --- 用于实例化 ViewModel 的工厂
  21. */
  22. mMyViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication()))
  23. .get(MyViewModel.class);
  24. mTvNum.setText(String.valueOf(mMyViewModel.number));
  25. }
  26. public void autoIncrement(View view) {
  27. mTvNum.setText(String.valueOf(++mMyViewModel.number));
  28. }
  29. }

 ViewModel的作用

  • 解决瞬时数据丢失
  • 解决异步调用的内存泄露
  • 解决类膨胀带来的维护难度和测试难度

注意事项:
不要向ViewModel中传入Context,会导致内存泄漏。如果非要使用Context,请使用 AndroidViewModel中的Application 

LiveData的诞生

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态()的应用组件观察者。官方详细介绍:LiveData 概览

LiveData和ViewModel关系

一般情况下,我们会把 LiveData放在 ViewModel中,以便在 ViewModel中的数据发生变化时通知页面更新UI。

 案例一:秒表

  1. import androidx.lifecycle.MutableLiveData;
  2. import androidx.lifecycle.ViewModel;
  3. /**
  4. * @author Luffy
  5. * @Description ViewModel 与 LiveData 关联
  6. * @Date 2022/3/9 21:54
  7. */
  8. public class MyViewModel extends ViewModel {
  9. // 记录当前已经历的秒数
  10. private MutableLiveData<Integer> currentSecond;
  11. public MutableLiveData<Integer> getCurrentLiveData() {
  12. if (currentSecond == null) {
  13. currentSecond = new MutableLiveData<>();
  14. // 设置初始值
  15. currentSecond.setValue(0);
  16. }
  17. return currentSecond;
  18. }
  19. }
  1. import androidx.appcompat.app.AppCompatActivity;
  2. import androidx.lifecycle.ViewModelProvider;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.widget.TextView;
  6. import java.util.Timer;
  7. import java.util.TimerTask;
  8. /**
  9. * @author Luffy
  10. * @Description ViewModel + LiveData 数据实时更新
  11. * @Date 2022/3/9 21:50
  12. */
  13. public class MainActivity extends AppCompatActivity {
  14. private static final String TAG = "MainActivity";
  15. private TextView mTvCurrentSecond;
  16. private MyViewModel mMyViewModel;
  17. @Override
  18. protected void onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.activity_main);
  21. Log.d(TAG, "onCreate: ");
  22. mTvCurrentSecond = findViewById(R.id.tv_current_second);
  23. // 1.实例化 ViewModel
  24. mMyViewModel = new ViewModelProvider(this,
  25. new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
  26. // 2.从 ViewModel 实例中获取数据,并设置到文本上
  27. mTvCurrentSecond.setText(String.valueOf(mMyViewModel.getCurrentLiveData()));
  28. // 2.给 MyViewModel 设置观察者,当 MyViewModel 数据发生变化时,观察者可以察觉到
  29. mMyViewModel.getCurrentLiveData().observe(this, integer ->
  30. mTvCurrentSecond.setText(String.valueOf(integer.intValue())));
  31. // 开启计时器
  32. startTimer();
  33. }
  34. private void startTimer() {
  35. // 三个参数含义分别是:时间任务、延迟时间、间隔时间
  36. new Timer().schedule(new TimerTask() {
  37. @Override
  38. public void run() {
  39. // 非 UI 线程 postValue;UI 线程 setValue
  40. mMyViewModel.getCurrentLiveData().postValue(mMyViewModel.getCurrentLiveData().getValue() + 1);
  41. }
  42. }, 1000, 1000);
  43. }
  44. }

案例二:Fragment通信,实现两个Fragment进度条同步更新的效果

  1. import androidx.lifecycle.MutableLiveData;
  2. import androidx.lifecycle.ViewModel;
  3. /**
  4. * @author Luffy
  5. * @Description 将 SeekBar 的进度与 ViewModel 绑定
  6. * @Date 2022/3/9 22:12
  7. */
  8. public class MyViewModel extends ViewModel {
  9. public MutableLiveData<Integer> progress;
  10. public MutableLiveData<Integer> getProgress() {
  11. if (progress == null) {
  12. progress = new MutableLiveData<>();
  13. progress.setValue(0);
  14. }
  15. return progress;
  16. }
  17. }
  1. import android.os.Bundle;
  2. import android.view.LayoutInflater;
  3. import android.view.View;
  4. import android.view.ViewGroup;
  5. import android.widget.SeekBar;
  6. import androidx.fragment.app.Fragment;
  7. import androidx.lifecycle.ViewModelProvider;
  8. /**
  9. * @author Luffy
  10. * @Description ViewModel + LiveData 实现 Fragment 间通信
  11. * @Date 2022/3/9 22:06
  12. */
  13. public class FirstFragment extends Fragment {
  14. @Override
  15. public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  16. View rootView = inflater.inflate(R.layout.fragment_first, container, false);
  17. SeekBar seekBar = rootView.findViewById(R.id.seekBar);
  18. // 1.实例化 MyViewModel
  19. MyViewModel mMyViewModel = new ViewModelProvider(getActivity(),
  20. new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MyViewModel.class);
  21. // 2.给 MyViewModel 设置观察者,当 MyViewModel 数据发生变化时,观察者可以察觉到,此处 seekBar是观察者
  22. mMyViewModel.getProgress().observe(getActivity(), seekBar::setProgress);
  23. // 3.设置 SeekBar 进度更新的监听
  24. seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
  25. @Override
  26. public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
  27. // 3.1 当 SeekBar 进度更新时,同步数据到对应的 ViewModel 上
  28. mMyViewModel.progress.setValue(seekBar.getProgress());
  29. }
  30. @Override
  31. public void onStartTrackingTouch(SeekBar seekBar) {
  32. }
  33. @Override
  34. public void onStopTrackingTouch(SeekBar seekBar) {
  35. }
  36. });
  37. return rootView;
  38. }
  39. }
  1. import android.os.Bundle;
  2. import android.view.LayoutInflater;
  3. import android.view.View;
  4. import android.view.ViewGroup;
  5. import android.widget.SeekBar;
  6. import androidx.fragment.app.Fragment;
  7. import androidx.lifecycle.ViewModelProvider;
  8. /**
  9. * @author Luffy
  10. * @Description 将 SeekBar 的进度与 ViewModel 绑定
  11. * @Date 2022/3/9 22:13
  12. */
  13. public class SecondFragment extends Fragment {
  14. @Override
  15. public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  16. View rootView = inflater.inflate(R.layout.fragment_second, container, false);
  17. SeekBar seekBar = rootView.findViewById(R.id.seekBar);
  18. MyViewModel mMyViewModel = new ViewModelProvider(getActivity(),
  19. new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MyViewModel.class);
  20. mMyViewModel.getProgress().observe(getActivity(), seekBar::setProgress);
  21. seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
  22. @Override
  23. public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
  24. mMyViewModel.progress.setValue(seekBar.getProgress());
  25. }
  26. @Override
  27. public void onStartTrackingTouch(SeekBar seekBar) {
  28. }
  29. @Override
  30. public void onStopTrackingTouch(SeekBar seekBar) {
  31. }
  32. });
  33. return rootView;
  34. }
  35. }

LiveData的优势(先有个印象)

  • 确保界面符合数据状态

  • 不会发生内存泄漏

  • 不会因Activity停止而导致崩溃

  • 不再需要手动处理生命周期

  • 数据始终保持最新状态

  • 适当的配置更改

  • 共享资源

本篇博文先讲这么多,不当之处,还望指正,欢迎评论区留言交流~

涉及的代码已上传至码云:https://gitee.com/zhangningke/jetpack-study 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/253999
推荐阅读
相关标签
  

闽ICP备14008679号