0
点赞
收藏
分享

微信扫一扫

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

进击的包籽 2022-03-12 阅读 53
android

写在前面

我们经常被面试官问到,你的项目用的什么架构模式呀,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 

举报

相关推荐

0 条评论