目錄
-
一、什麼是斷言,什麼情況下應該使用androidAssert?
-
二、在release版本中移除斷言代碼,只在debug中保留
-
三、集成AndroidAssert庫
-
總結
一、什麼是斷言,什麼情況下應該使用androidAssert?
通常斷言(assert)是在單元測試時,用來校驗函數返回的結果。在自動化測試用來校驗程序運行結果。
但是我們接下來要討論的並不是單元測試中使用斷言,而是在項目業務代碼中使用斷言。
我們一起來看幾個,大家非常熟悉的例子。這些情況下使用斷言會讓代碼更加優雅,更加健壯。
例子1,writeFile
/**
* 我們希望只在子線程中調用writeFile(),主線程中調用可能會導致ui卡頓或者anr。
*
* 如果在主線程調用writeFile(),我們打印日誌警告開發者
*/
public void writeFile() {
//主線程,打印日誌警告開發者
if(!ThreadTypeUtil.isSubThread()){
Log.e(TG, "you should call writeFile at sub thread");
}
// write file ...
}
當出現有開發人員,嘗試在主線程中調用writeFile()方法時,我們通過 日誌警告 ,開發者。但是,日誌提示很弱,往往會被開發者忽略掉。
於是我們改良了下代碼:
public void writeFile() {
//debug版本,主線程中調用 writeFile ,直接拋出異常中斷程序運行
if(!ThreadTypeUtil.isSubThread()){
if(BuildConfig.DEBUG) {
throw new RuntimeException("you should call writeFile at sub thread");
}
}
// write file ...
}
當程序員企圖在主線程中調用writeFile(),在debug模式下,我們直接拋出異常,讓程序崩潰。以中斷他的開發。強制他優化代碼。
我們引入斷言庫,繼續改造代碼,讓代碼更簡潔漂亮:
/**
* 在debug模式,下將直接拋出異常 AssertionFailedError。讓開發者
* 在release模式,不會拋出異常,會正常執行writeFile()函數。
*/
public void writeFile() {
AndroidAssert.assertSubThread();
// write file ...
Log.i(TG, "writeFile...");
}
AndroidAssert.assertSubThread()斷言爲子線程的意思是,斷定當前線程一定是子線程,如果不是,那麼拋出異常 AssertionFailedError。
AndroidAssert是一個開源庫,使用前需要引入這個開源庫,後面會講。
繼續優化
只有在debug版本,AndroidAssert 類,纔有用;
在release版本的apk上,能否把 AndroidAssert 相關調用的代碼刪除?
我們先挖個坑,把例子講完,再將release刪除代碼的方法。大家先忍一忍。
例子2,updateUI
/**
* 在release版本,如果在子線程中調用updateUI,我們直接return,不做ui更新操作。
* 但是在debug版本,如果在子線程中調用updateUI,直接出異常,讓開發者發現異常調用並解決。
*/
public void updateUI() {
boolean isMainThread = ThreadTypeUtil.isMainThread();
if (!isMainThread) {
AndroidAssert.fail("updateUI must be called at main thread");
return;
}
//update ui ....
Log.i(TG, "updateUI...");
}
例子3,startMainActivity
/**
* 斷言context爲非空,如果爲null,debug模式下拋出異常 AssertionFailedError
*/
public void startMainActivity(Context context) {
AndroidAssert.assertNotNull("context must not null", context);
if (context == null) {
return;
}
//startMainActivity...
Log.i(TG, "startMainActivity...");
}
二、我們繼續改良,例子1,在release版本中移除斷言代碼,只在debug中保留
只有在debug版本,AndroidAssert 類,纔有用;
在release版本的apk上,能否把 AndroidAssert 相關調用的代碼刪除?
或者說打包的時候,把 AndroidAssert 相關的調用的代碼 和 AndroidAssert類的代碼 全部刪除,再打包。
於是我想到了proguard。
在proguard中添加如下配置即可:
# -dontoptimize ## 注意注意注意,proguard中配置dontoptimize;將會導致proguard不做代碼優化,不會刪除AndroidAssert類
-assumenosideeffects class com.it.uncle.lib.util.AndroidAssert{
public *;
}
注意,注意,千萬注意:不能開-dontoptimize,開了assumenosideeffects將失效
對用法有疑惑的可以,看下這篇blog:https://blog.csdn.net/jiese1990/article/details/21752159
以及官方wiki:https://www.guardsquare.com/en/products/proguard/manual/usage#assumenosideeffects
校驗assumenosideeffects是否生效
- 反編譯debug和release包對比。
比如,我們demo裏的TestActivity
public class TestActivity extends MainActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
AndroidAssert.assertNotNull(getIntent());
}
}
我們分別反編譯debug和release包,找到TestActivity類的代碼對比:
- 配置成功後,打包在mapping中搜索:com.it.uncle.lib.util.AndroidAssert
proguard未配置 -assumenosideeffects 的mapping.txt文件
proguard配置了 -assumenosideeffects 的mapping.txt文件:
三、集成AndroidAssert庫
-
gradle
implementation 'com.ituncle:android-assert:0.0.2'
-
初始化sdk,儘早調用,建議在Application#onCreate的時候調用。
//初始化----斷言失敗時,是否拋出異常
AndroidAssert.enableThrowError(BuildConfig.DEBUG);//我們設置爲debug模式下,斷言失敗才拋出異常
-
添加proguard,在開啓混淆的版本中,移除AndroidAssert的代碼
# -dontoptimize ## 注意注意注意,proguard中配置dontoptimize;將會導致proguard不做代碼優化,不會刪除AndroidAssert類 -assumenosideeffects class com.it.uncle.lib.util.AndroidAssert{ public *; }
總結
android-assert是一個非常簡單輕量的android斷言庫。類似於junit的Assert類。
android-assert不是用來寫測試用例的,可以直接在項目代碼中使用他。
在debug模式下,斷言失敗將會拋出斷言異常 AssertionFailedError,在release模式下,將不會拋出異常。
更多關於android-assert,看github文檔:https://github.com/AITUncle/AndroidAssert