写在前面
我们经常被面试官问到,你的项目用的什么架构模式呀,MVC、MVP、MVVM ? 其实这些都是我们开发者自己设计的架构模式,非谷歌官方解决方案,我们有时候也很难把控最佳架构模式。 出于这个原因,Google官方给我们提供了Jetpack。
初识Jetpack
为何使用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的区别在哪里。先看看传统的实现方式:
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();
}
}
再来看看使用 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);
}
}
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();
}
}
我们发现用传统的实现方式,会导致组件与 Activity耦合度特别高,依赖于 Activity生命周期的 onResume 和 onPause方法,而使用 Lifecycle 则不需要重写 Activity的 onResume 和 onPause等生命周期相关的方法,而是自定义组件在其内部管理自己的生命周期,自定义组件于上述案例中的 Activity而言,就是观察者。
LifecycleService 解耦Service 与普通组件
首先我们创建一个 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));
}
}
先把观察者创建出来:
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());
}
}
}
然后我们发现在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));
}
}
ProcessLifecycleOwner 监听应用程序生命周期
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());
}
}
监听应用程序的每一个生命周期:
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");
}
}
LifeCycle的好处
- 帮助开发者建立可感知生命周期的组件
- 组件在其内部管理自己的生命周期,从而降低代码耦合度
- 降低内存泄漏发生的可能性(目前还没有体现出来)
- Activity、Fragment、Service、Application均有 LifeCycle支持
Lifecycle面试题
Lifecycle用来监听生命周期的变化处理相应的操作,那组件的onStart、onStop、onPause不也可以处理相应的操作吗?他们具体差别在哪呢?
确实是可以的。区别在于使用的方便程度和代码的简洁度。比如一个自定义控件,需要 onStart,onStop,onPause中处理一些逻辑。不使用 LifeCycle则需要在每个使用它的页面中的 onStart,onStop,onPause中调用这个自定义控件的方法。如果使用LifeCycle,则只需要把自定义控件作为观察者与Activity绑定即可,自定义控件你内部会管理自己的生命周期。
ViewModel的诞生
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();
}
}
在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));
}
}
ViewModel的作用
- 解决瞬时数据丢失
- 解决异步调用的内存泄露
- 解决类膨胀带来的维护难度和测试难度
LiveData的诞生
LiveData和ViewModel关系
一般情况下,我们会把 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;
}
}
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);
}
}
案例二: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;
}
}
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;
}
}
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;
}
}
LiveData的优势(先有个印象)
-
确保界面符合数据状态
-
不会发生内存泄漏
-
不会因Activity停止而导致崩溃
-
不再需要手动处理生命周期
-
数据始终保持最新状态
-
适当的配置更改
-
共享资源
本篇博文先讲这么多,不当之处,还望指正,欢迎评论区留言交流~
涉及的代码已上传至码云:https://gitee.com/zhangningke/jetpack-study