Ktea是kotlin開發的Android庫, 它可讓Android開發更簡單更快速更容易維護. 它很容易入門和使用, 方便構建高質量的應用, 減小崩潰和內存泄露.git
dependencies {
//... 其餘庫的引用
implementation 'com.mengwei:ktea:1.1.1'
}
複製代碼
class MyApp : Application() {
private val activitys: LinkedList<Activity> = LinkedList()
override fun onCreate() {
super.onCreate()
// 初始化KTea的主要配置參數
Settings.init {
appCtx = this@MyApp //設置應用Module的ApplicationContext
baseUrl = "http://..." //網絡請求時的baseUrl
activityStack = activitys //傳入一個activity任務棧
isDEBUG = BuildConfig.DEBUG //當是debug模式時,會打印日誌;release時是不打印的
jsonObjectStyle = ... //這行根據需求定義, 可不寫, 具體使用方法看下面的教程
}
}
}
複製代碼
KTea的首要目標是提供一種更簡潔更高效更安全的Android開發方式, 讓咱們從一個小例子開始, 來看看KTea的使用:github
http {
url = "getRequest/demo"
onSuccess = {
//這裏是請求成功請求到的數據
logger(it)
}
onError = {
//這裏是請求失敗返回的錯誤信息
logger(it)
}
}
複製代碼
網絡請求是Android開發中常見的任務, 在Ktea中能夠經過http函數來完成這個任務, 上面的代碼就是一個網絡請求的例子.json
你喜歡這樣的代碼麼? Ktea處理網絡請求這樣的常見任務不但能夠節省大量的樣板代碼, 並且已經作好了線程的切換, 你不須要再去關心.安全
若是返回的是json格式的數據, 你可使用AndroidStudio的GsonFormat插件先自動生成一個Entity類, 而後在onSuccess中把String數據直接轉換成對應的Entity對象:bash
// 解析形如這樣的jsonobject格式: {key1:value1, key2:value2, ...}
// 對應json的entity
class xxxEntity {
private String key1;
private String key2;
...get/set 方法
}
onSuccess = {
val entity = it.toEntity<xxEntity>()
}
複製代碼
一樣, 若是是JsonArray的數據, 你能夠直接轉換成Entity的List服務器
// 解析形如這樣的jsonarray數據 : [{...},{...},...]
onSuccess = {
val entityList = it.toEntityList<xxEntity>()
}
複製代碼
toEntity和toEntityList是ktea的內置函數, 能夠對String對象直接使用, 不過必須保證該String對象必須是xxEntity對應jsonobject或者jsonarray格式.網絡
下面讓咱們來看一個開發中更常見的例子, 來展現Ktea中網絡請求組件的使用:架構
想讓數據接收到的直接是entity對象, 咱們可使用另一個函數httpEntityapp
fun login(mobile: String, password: String) {
// 網絡請求代碼
httpEntity<LoginEntity> {
url = "user/login"
method = Method.POST
params = mutableMapOf("mobile" to mobile, "password" to password.MD5())
onSuccess = {
it //這裏的it就是LoginEntity對象
}
onError = { logger(it) }
}
}
複製代碼
咱們看到這裏有兩個新的參數名稱: method 和 params ,分別表明請求的方式和請求參數.ide
httpEntity函數和http函數使用時的區別是須要一個泛型類型, 用於解析json數據對應的Entity類型, 這樣在onSuccess下就能夠直接接收到entity類型的對象.
string.MD5是ktea的內置函數, 能夠把string類型進行MD5加密, 返回加密後的字符串, 類似的內置函數還有string.AESEncrypt, string.Base64等可使用.
在開發中, 你能夠靈活運用ktea中的多個網絡請求函數來知足須要, 除了上面介紹的http和httpEntity, 還有和httpEntity相似的httpEntityList ,它能夠把JsonArray字符串數據直接解析成Entity的List, 使用方法和httpEntity同樣.
httpEntityList<xxEntity> {
...
onSuccess = {
it //這裏的it就是一個xxEntity的List集合
}
}
複製代碼
爲了知足網絡請求中更復雜的任務, 下面來介紹另一個函數httpBase, 它用來把嵌套Json數據解析成Entity實體類.
{
"status": 1,
"msg": "success",
"datas": {...}
}
複製代碼
形如上面的json格式, 也是開發中常見的服務器返回json串, 它的外層是狀態碼和提示信息, 實際須要的數據在"datas"的內層, 下面咱們來解決這個問題.
Settings.init {
...
jsonObjectStyle= JsonStyle().apply {
statusName = "status"
dataName = "datas"
messageName = "msg"
successStatusValue = "1"
}
}
複製代碼
根據實際服務器請求數據的格式, 把外層的json鍵名稱寫上以後, 就可使用httpBase函數了.
httpBase<xxxEntity> {
...
onSuccess = {
it // 這裏就是json內層的datas數據解析出來entity對象
}
}
複製代碼
httpBase函數和httpEntity函數使用上沒有區別, 只不過須要設置一個JsonStyle對象來指定外層的鍵名, 這樣就能夠把內層的json數據而不是整個json數據解析成entity對象了.
jsonObjectStyle只須要在ktea初始化時設置一次便可, 以後使用httpBase函數時都不須要設置.
一樣, 和httpBase函數對應的還有一個httpBaseList函數, 用來把內層的jsonarray直接解析成entity的List.
httpBaseList<Entity> { ... }
複製代碼
移動端網絡請求的複雜性在於服務器端請求約定沒有固定的標準, 咱們無法控制. 每一個服務器端的代碼都是不一樣的開發人員編寫, 因此每一個公司幾乎都不相同. 在ktea中還涵蓋了一些其餘常見問題的解決方法.
HttpHead.params["ADVICEID"] = "..."
HttpHead.params["TIMESTAMPS"] = "..."
複製代碼
// 在這裏給token賦值
Token.token= "TOKEN" to "..."
複製代碼
上面的代碼會在token賦值後把token保存在本地, 當app關閉再打開時一樣不會丟失, 可是重啓app後須要判斷一下token是否存在, 每次重啓只須要一次判斷便可, 不用每次請求時都調用這個方法. 因此最佳的作法是在啓動頁或者我的中心頁面(具體看需求)調用一次:
class SplashActivity : BaseActivity() {
...
if (Token.token == null) {
gotoLoginActivity()
} else {
gotoMainActivity()
}
}
複製代碼
修改請求頭參數的HttpHead.params和Token.token都是單例的全局變量, 只須要賦值一次便可, 你能夠根據需求在合適的時機進行賦值和修改, 而不用每次網絡請求都進行賦值. 可是當這些數據變化時, 好比時間戳, 這樣就須要在使用網絡請求函數前進行賦值.
還有更多的網絡請求功能並不能一一講解,須要在使用中去慢慢發現和體會. 下面咱們來介紹一些更重要的內容.
在實際開發中, 若是把網絡請求的代碼直接放在activity中不但違背了類的單一性原則而讓維護代碼很是困難, 更重要的是還會產生一個很嚴重的問題: 內存泄露. 爲了不內存泄露, 就須要額外的代碼來處理, 並且由於不少人對內存泄漏的不瞭解, 當應用表現偶爾崩潰時每每沒法定位緣由.
構建易維護應用的關鍵在於減小代碼之間的耦合, 遵照類的職能單一性原則. 爲此, 移動端的架構設計引入了流行的MVP和MVVM的模式.
MVP的缺點很明顯, 它須要額外建立契約接口類和其餘分層的類並聲明功能重複的方法在各層來回調用, 當頁面原本只須要添加一個很小的功能時卻要同時修改不少的類, 須要大量的煩人的代碼, 大大加劇開發負擔.
我不喜歡MVVM模式的緣由在於databinding須要在xml文件中寫功能代碼, 我仍是但願xml能維持單一的佈局功能, 這樣出現問題時就能夠在儘可能小的範圍進行排查.
下面咱們來看如何使用它們:
class NewsListModel:BaseViewModel() {
val errorLiveData by lazy { MutableLiveData<String>() }
val newsLiveData by lazy { MutableLiveData<List<NewsEntity>>() }
fun getNews(pageNum: Int) {
jobs + httpBaseList<NewsEntity> {
url = "article/listNews"
params = mutableMapOf("pageNum" to pageNum, "pageSize" to 20)
onSuccess = { newsLiveData.value = it }
onError = { errorLiveData.value = it }
}
}
}
複製代碼
ViewModel層老是繼承BaseViewModel並由一系列的請求數據的函數和LiveData組成. 經常使用的LiveData是MutableLiveData, 泛型指定數據源對象的類型, 經過給LiveData.value賦值來通知數據的變化. 在ViewModel中咱們只須要獲取數據並通知給LiveData便可, 不須要和任何的類和模塊交互.
咱們在ViewModel中並無持有activity的引用, 那麼咱們如何去更新UI呢? 因此咱們必須在activity訂閱LiveData, 這樣當數據變化時, 咱們就會獲得通知.
class NewsListActivity : BaseActivity() {
private val model by lazy { getViewModel<NewsListModel>() }
private val adapter by lazy { NewsAdapter() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_news_list)
}
override fun initData() {
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
// 訂閱livedate的數據變化通知
model.errorLiveData.observe(this) {
dismissLoading()
errorToast(it)
}
model.newsLiveData.observe(this) {
adapter.update(it)
dismissLoading()
}
//獲取數據
model.getNews(index)
showLoading()
}
}
複製代碼
Ktea中定義了BaseViewModel和BaseActivity類, 它們加入了不少簡化開發和提升性能的代碼, 在開發中繼承它們會幫助你省了不少麻煩.
要在activity中獲得viewmodel對象必須經過函數getViewModel獲取而不能直接建立, 由於viewmodel須要獲取當前activity的生命週期對象.
上面的代碼還演示了經過livedata的observe方法訂閱livedata的數據變化來更新UI的方法.
上面只是介紹了KTea庫裏不多的一部分功能, 之後會陸續提供更多的Ktea教程來針對性的深刻介紹每一個模塊的使用技巧.
Ktea庫是我開源的第一個庫, 開源以前已經在實際項目中使用了一段時間, 提升開發效率方面在團隊中也獲得了證實.