寫在前面
我們經常被面試官問到,你的項目用的什麼架構模式呀,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的差別在哪裡。先看看傳統的實作方式:
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));
}
}
adb geo 改變虛拟機GPS資訊:
進入studioSDK\platform-tools 這個目錄下,然後cmd打開指令行視窗,輸入:adb -s emulator-5554 emu geo fix 121.4961236714487 31.24010934431376
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");
}
}
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(資料模型)之間的橋梁,使視圖和資料能夠分離,也能保持通信。
經典案例:解決螢幕旋轉之後使用者操作資料丢失問題
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的作用
- 解決瞬時資料丢失
- 解決異步調用的記憶體洩露
- 解決類膨脹帶來的維護難度和測試難度
注意事項:
不要向ViewModel中傳入Context,會導緻記憶體洩漏。如果非要使用Context,請使用 AndroidViewModel中的Application
LiveData的誕生
LiveData 是一種可觀察的資料存儲器類。與正常的可觀察類不同,LiveData 具有生命周期感覺能力,意指它遵循其他應用元件(如 Activity、Fragment 或 Service)的生命周期。這種感覺能力可確定 LiveData 僅更新處于活躍生命周期狀态()的應用元件觀察者。官方詳細介紹: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停止而導緻崩潰
- 不再需要手動處理生命周期
- 資料始終保持最新狀态
- 适當的配置更改
- 共享資源