Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法、减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者可将精力集中于真正重要的编码工作。
JetPack的优点:
遵循最佳做法:
AndroidJetpack组件采用最新的设计方法构建,具有向后兼容性,可以减少崩溃和内存泄露。
消除样板代码:
AndroidJetpack可以管理各种繁琐的Activity(如后台任务、导航和生命周期管理),以便您可以专注于打造出色的应用。
减少不一致:
这些库可在各种Android版本和设备中以一致的方式运作,助您降低复杂性。
LifeCycle
LifeCycle会通知组件的生命周期变化并进行解耦,省略了onCreate()、onDestroy()等方法中的操作。
1.监听activity的生命周期
下面我们先来一个用LifeCycle的定时器案例
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Lifecycle.MainActivity">
<com.example.jetpacktest.Lifecycle.MyChronometer
android:id="@+id/chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
这里引用的是我们自定义的一个定时器类(activity显示时计时,退出后暂停计时)。
public class MyChronometer extends Chronometer implements LifecycleObserver {
private long mElapsedTime;
public MyChronometer(Context context, AttributeSet attrs) {
super(context, attrs);
}
//配置onResume的监听
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private void startMeter(){
//恢复计时并保证是接着退出前的时间计时
setBase(SystemClock.elapsedRealtime()-mElapsedTime);
start();
}
//配置onPause的监听
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
private void stopMeter(){
//记录计时的时间
mElapsedTime = SystemClock.elapsedRealtime() - getBase();
stop();
}
}
Lifecycle的使用方式(如上):
1.实现LifecycleObserver接口.
2.配置注解并指定生命周期
public class MainActivity extends AppCompatActivity {
private MyChronometer mViewById;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewById = findViewById(R.id.chronometer);
//绑定activity的生命周期监听
getLifecycle().addObserver(mViewById);
}
}
效果图:
2.监听service的生命周期
先来添加一下这个依赖:
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
在AndroidManifest.xml文件里添加定位所需权限:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
这次我们通过一个在Service中打印坐标的案例来说明使用方法。
我们先来一个布局xml:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Lifecycle.MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#03A9F4"
android:text="start"
android:onClick="startGPS"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chronometer"
app:layout_constraintVertical_bias="0.346" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00BCD4"
android:text="stop"
android:onClick="stopGPS"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/chronometer"
app:layout_constraintVertical_bias="0.613" />
</androidx.constraintlayout.widget.ConstraintLayout>
在Xml里有两个按钮分别,启动和停止Service。
public class MyLocationService extends LifecycleService {
public MyLocationService() {
Log.i("ning","MyLocationService");
MyLocationObserver myLocationObserver = new MyLocationObserver(this);
//绑定生命周期
getLifecycle().addObserver(myLocationObserver);
}
}
在Service中我们首先继承LifecycleService,接着初始化定位监听的类,并绑定自己生命周期。
public class MyLocationObserver implements LifecycleObserver {
private Context mContext;
private LocationManager mSystemService;
private MyLocationListener mMyLocationListener;
public MyLocationObserver(Context pContext) {
mContext = pContext;
}
//监听Service执行onCreate()时执行
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
private void startGetLocation() {
Log.i("ning","startGetLocation");
mSystemService = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
mMyLocationListener = new MyLocationListener();
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, 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;
}
//开启Gps
mSystemService.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 1, mMyLocationListener);
}
//监听Service执行onDestroy()时执行
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
private void stopGetLocation(){
Log.i("ning","stopGetLocation");
//关闭Gps
mSystemService.removeUpdates(mMyLocationListener);
}
static class MyLocationListener implements LocationListener {
@Override
public void onLocationChanged(@NonNull Location location) {
Log.d("ning","onLocationChanged"+location.getLongitude()+","+location.getLatitude());
}
}
}
和在activity中的写法一样,继承LifecycleObserver接口并添加注解。
public class MainActivity extends AppCompatActivity {
private MyChronometer mViewById;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
}
}
public void startGPS(View view) {
startService(new Intent(this,MyLocationService.class));
}
public void stopGPS(View view) {
stopService(new Intent(this,MyLocationService.class));
}
}
最后我通过adb命令来修改gps的位置
adb -s emulator-5554 emu geo fix 121.4961236714487 31.24010934431376
adb -s emulator-5554 emu geo fix 122.4961236714487 31.24010934431376
打印结果:
2021-12-20 15:32:15.166 18692-18692/com.example.jetpacktest D/ning: onLocationChanged121.49612333333333,31.240108333333332
3.监听App的生命周期
我们定以一个App生命周期的监听类。
public class ApplicationObserver implements LifecycleObserver {
private static String TAG="TAG";
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
private void onCreate(){
Log.i(TAG,"Lifecycle.Event.ON_CREATE");
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private void onStop() {
Log.i(TAG,"Lifecycle.Event.ON_STOP");
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onStart() {
Log.i(TAG,"Lifecycle.Event.ON_START");
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private void onResume() {
Log.i(TAG,"Lifecycle.Event.ON_RESUME");
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
private void onPause() {
Log.i(TAG,"Lifecycle.Event.ON_PAUSE");
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
private void onDestroy() {
Log.i(TAG,"Lifecycle.Event.ON_DESTROY");
}
}
方法还是和之前一样。
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//绑定生命周期的监听类
ProcessLifecycleOwner.get().getLifecycle().addObserver(new ApplicationObserver());
}
}
接着在Application绑定一下就成了!
4.Lifecycle的优点
1.帮助开发者建立可感知生命周期的组件
2.组件在其内部管理自己的生命周期,从而降低模块耦合度
3.降低内存泄漏发生的可能性
4.Activity、Fragment、Application均有LifeCycle支持
二.ViewModel
1.ViewModel的作用
1.它是介于View(视图)和Model(数据模型)之间的桥梁。
2.是视图和数据能够分离,也能保持通信。
2.ViewModel的应用
我们通过一个案例来演示ViewModel的用法。
首先引入这个依赖
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ViewModel.ViewModelActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.23" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="ViewModelClick"
android:text="点击加1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.532"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.45" />
</androidx.constraintlayout.widget.ConstraintLayout>
public class ViewModelActivity extends AppCompatActivity {
private ApplicationViewModel mApplicationViewModel;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_model);
mTextView = findViewById(R.id.textView);
//初始化viewModel对象
mApplicationViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(ApplicationViewModel.class);
//赋值
mTextView.setText(String.valueOf(mApplicationViewModel.num));
}
public void ViewModelClick(View view) {
//每点击一次加一
mTextView.setText(String.valueOf(mApplicationViewModel.num++));
}
}
ViewModel是独立于activity的生命周期之外的,我们初始化了ViewModel,并直接修改ViewModel中的值,这样activity就算销毁了,也不会影响ViewModel的值。
//要使用Content时推荐用AndroidViewModel,不用上下文时可以用ViewModel
public class ApplicationViewModel extends AndroidViewModel {
public int num;
public ApplicationViewModel(@NonNull Application application) {
super(application);
}
}
结果演示:
3.AndroidViewModel
不要向ViewModel中传入Context,会导致内存泄露
如果要使用Context,请使用AndroidViewModel中的Application
三.LiveData
1.LiveData和ViewModel的关系
ViewModel中的数据发生变化是通知页面
2.LiveData应用
我们通过简单案例来介绍LiveData加ViewModel的用法
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LiveData.LiveDataActivity">
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="RequestLiveData"
android:text="请求"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.492"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.237" />
</androidx.constraintlayout.widget.ConstraintLayout>
这是案例的Xml文件。
public class LiveDataActivity extends AppCompatActivity {
private TextView mTextView2;
private MyViewMode mMyViewMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data);
mTextView2 = findViewById(R.id.textView2);
mMyViewMode = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewMode.class);
mTextView2.setText(String.valueOf(mMyViewMode.getCurrentSecond().getValue()));
//LiveData监听器 每当监听的值发生变化,都会触发回调
mMyViewMode.getCurrentSecond().observe(this, pInteger -> mTextView2.setText(String.valueOf(pInteger)));
}
//定义定时器
public void RequestLiveData(final View view) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
//非UI线程 用 Post
//UI线程 用set 每一秒加一
mMyViewMode.getCurrentSecond().postValue(mMyViewMode.getCurrentSecond().getValue()+1);
}
},1000,1000);
}
}
在activity中初始化ViewModel,定义定时器自动增加1,而通过LiveData的监听器每次ViewModel中的只改变都会出发回调,以更新UI。
public class MyViewMode extends ViewModel {
private MutableLiveData<Integer> currentSecond;
public MutableLiveData<Integer> getCurrentSecond(){
if (currentSecond==null){
currentSecond= new MutableLiveData<>();
//赋值
currentSecond.setValue(0);
}
return currentSecond;
}
}
在 ViewModel里初始化了LiveData。
结果演示:
第二个案例:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".liveDataFragment.LiveDataPagerActivity">
<fragment
android:id="@+id/fragment2"
android:name="com.example.jetpacktest.liveDataFragment.SecondFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<fragment
android:id="@+id/fragment"
android:name="com.example.jetpacktest.liveDataFragment.LiveDataFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fragment2" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
Xml文件上下两个fragment,中间一条间隔线。
public class LiveDataPagerActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data_pager);
}
}
activity什么都不做。
public class LiveDataViewModel extends ViewModel {
// TODO: Implement the ViewModel
private MutableLiveData<Integer> progress;
public MutableLiveData<Integer> getProgress(){
if (progress==null){
progress=new MutableLiveData<>();
progress.setValue(0);
}
return progress;
}
}
和之前一样
public class LiveDataFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View inflate = inflater.inflate(R.layout.live_data_fragment, container, false);
final SeekBar seekBar = inflate.findViewById(R.id.seekBar);
//初始化ViewModel
final LiveDataViewModel liveDataViewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(LiveDataViewModel.class);
//liveData的监听
liveDataViewModel.getProgress().observe(getActivity(), new Observer<Integer>() {
@Override
public void onChanged(Integer pInteger) {
//配置进度条的进度
seekBar.setProgress(pInteger);
}
});
//进度条监听
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//配置滑到的进度
liveDataViewModel.getProgress().setValue(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return inflate;
}
}
public class SecondFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View inflate = inflater.inflate(R.layout.fragment_second, container, false);
final SeekBar seekBar = inflate.findViewById(R.id.seekBar);
//初始化ViewModel
final LiveDataViewModel liveDataViewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(LiveDataViewModel.class);
//liveData的监听
liveDataViewModel.getProgress().observe(getActivity(), new Observer<Integer>() {
@Override
public void onChanged(Integer pInteger) {
//配置进度条的进度
seekBar.setProgress(pInteger);
}
});
//进度条监听
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//配置滑到的进度
liveDataViewModel.getProgress().setValue(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return inflate;
}
}
以上两个fragment所干的是都一样,进度条把进度赋值给LiveData,又通过liveData的监听,值改变时就会触发回调,达到了两个fragment进度条同步的效果
效果演示:
3.LiveData的优势
1.确保界面符合数据状态
2.不会发生内存泄漏
3.不会因Activity停止而导致崩溃
4.不再需要手动处理生命周期
5.数据始终保持最新状态
6.适当的配置更改
7.共享资源