AACHulk
---
> AACHulk是以Google的ViewModel+DataBinding+LiveData+Lifecycles框架爲基礎,
結合Okhttp+Retrofit+BaseRecyclerViewAdapterHelper+SmartRefreshLayout+ARouter打造的一款快速開發框架,
開發語言是Kotlin,再結合[AACHulkTemplate模版開發](https://github.com/madreain/AACHulkTemplate)進行開發,
避免一些繁瑣的操作,提供開發效率
## 功能介紹
1.支持服務器地址、成功碼、各種超時時間、各種攔截器、Arouter、EventBus等的配置
2.支持自定義各種非正常態View替換
3.支持接口調用出錯時重試
4.支持多種Activity、Fragment展示,滿足業務需求
5.支持多佈局適配器
6.支持通用代碼生成[AACHulkTemplate模版](https://github.com/madreain/AACHulkTemplate)
## 第三方庫
1. [`Okhttp` 一個用於Android、Kotlin和Java的HTTP客戶端](https://github.com/square/okhttp)
2. [`Retrofit` 爲Android和Java提供安全的HTTP客戶端](https://github.com/square/retrofit)
3. [`BaseRecyclerViewAdapterHelper` 功能強大、靈活的萬能適配器](https://github.com/CymChad/BaseRecyclerViewAdapterHelper)
4. [`SmartRefreshLayout` Android智能下拉刷新框架](https://github.com/scwang90/SmartRefreshLayout)
5. [`EventBus` Android和Java的事件總線,簡化了活動、片段、線程、服務等之間的通信。代碼越少,質量越好](https://github.com/greenrobot/EventBus)
6. [`ARouter` 幫助 Android App 進行組件化改造的路由框架](https://github.com/alibaba/ARouter)
## 基礎功能
1.主項目啓用dataBinding
```
dataBinding {
enabled true
}
```
2.添加依賴
在project的build.grade加入
```
allprojects {
repositories {
maven { url 'https://jitpack.io' }
google()
jcenter()
}
}
```
在主項目app的build.grade加入
```
api 'com.madreain:libhulk:1.0.0'
```
3.繼承HulkApplication,配置相關配置項
```
HulkConfig.builder() //這裏只需要選擇設置一個
.setRetSuccess(BuildConfig.CODE_SUCCESS)//單一的成功響應碼
.setRetSuccessList(BuildConfig.CODELIST_SUCCESS)////多種請求對應不同成功響應碼
.setBaseUrl(BuildConfig.BASE_URL)//服務器地址
.setLogOpen(BuildConfig.OPEN_LOG)//網絡日誌開關
.setArouterOpen(BuildConfig.OPEN_AROUTER)//Arouter的開關
.setEventBusOpen(BuildConfig.OPEN_EVENTBUS)//EventBus的開關
.addOkHttpInterceptor(RequestHeaderInterceptor()) //請求頭攔截器
.addOkHttpInterceptor(
BuildConfig.OPEN_LOG,
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
) //okhttp請求日誌開關+消息攔截器
.addRetCodeInterceptors(SessionInterceptor()) // returnCode非正常態攔截器
.setRetrofit(
ApiClient.instance!!.getRetrofit(
ApiClient.instance!!.getOkHttpClient(
HulkConfig.getOkHttpInterceptors()
)
)
)//
.build()
```
上面這些配置項的配置可參考demo進行自身項目的配置
這裏還可根據[SmartRefreshLayout相關文檔](https://github.com/scwang90/SmartRefreshLayout)配置統一樣式,也可單獨設置,也可自定義,根據自身項目選擇
4.繼承IRes,根據自身項目封裝統一的數據接受
5.編寫ApiService,放接口
6.編寫通用的Toolbar(自行選擇)
因受kotlin-android-extensions這個插件可能只管自己module的資源文件的影響,沒法將通用的toolbar.xml寫在libhulk中供app使用,因此只能在app項目中寫通用的toolbar.xml
⚠️ 如果大佬們有好的實現方法歡迎指教
️🔥️🔥️🔥 [AACHulkTemplate模版](https://github.com/madreain/AACHulkTemplate),此模版使用得保證ApiService、toolbar.xml已創建,使用者也可根據自身項目進行修改
## 快速開發
AACHulkTemplate模版用起來是相當香的,接下來講一下自已手動的步驟,以SingleActivity舉例
1.新建SingleActivity繼承BaseActivity
```
class SingleActivity : BaseActivity<BaseViewModel, ViewDataBinding>() {
override fun getLayoutId(): Int {
return R.layout.activity_single
}
override fun getReplaceView(): View {
return layout
}
override fun init(savedInstanceState: Bundle?) {
}
/**
* 設置SmartRefreshLayout
*/
override fun getSmartRefreshLayout(): SmartRefreshLayout? {
return null
}
override fun refreshData() {
}
}
```
ViewDataBinding將會用在activity_single.xml中關聯ActivitySingleBinding替換掉
BaseViewModel將會用新建的SingleViewModel繼承BaseViewModel替換掉
2.創建對應的對象
```
@Keep
class SingleData {
var code: String? = null
var name: String? = null
}
```
3.關聯ViewDataBing
在activity_single.xml中關聯ActivitySingleBinding
```
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<import type="java.util.List" />
<import type="com.madreain.aachulk.module.single.SingleData" />
<variable
name="singleDataS"
type="List<SingleData>" />
<variable
name="singleData"
type="SingleData" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/single_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.madreain.aachulk.module.main.MainActivity">
<include
android:id="@+id/tbar"
layout="@layout/toolbar" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv"
android:layout_width="@dimen/dp60"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:text="@{singleData.code,default=`接口調用之前`}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="接口調用結果" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
```
4.新建SingleViewModel繼承BaseViewModel
```
class SingleViewModel : BaseViewModel<ApiService>() {
public override fun onStart() {
cityList()
}
//這裏舉例的是相關接口的調用,具體可參考demo
var result = MutableLiveData<List<SingleData>>()
private fun cityList() {
launchOnlyresult(
//調用接口方法
block = {
getApiService().getCityList()
},
//重試
reTry = {
//調用重試的方法
cityList()
},
//成功
success = {
//成功回調
result.value = it
}, type = RequestDisplay.REPLACE
)
}
}
```
5.替換ViewDataBinding、BaseViewModel
ActivitySingleBinding替換掉ViewDataBinding
SingleViewModel替換掉BaseViewModel
6.調用接口
```
//請求接口
mViewModel.onStart()
//接口請求的數據變化
mViewModel.result.observe(this, Observer {
mBinding!!.singleDataS = it
mBinding!!.singleData = it[0]
})
```
7.ARoute的配置
根據自身項目需求來決定是否配置ARoute來進行路由控制
```
@Route(path = "/aachulk/ui/SingleActivity")
```
到此爲止,簡單的一個接口調用到數據展示就完成了
⚠️⚠️⚠️ 帶適配器的demo參考[ListActivity](https://github.com/madreain/AACHulk/tree/master/app/src/main/java/com/madreain/aachulk/module/list)
## 用法進階
1.自定義各種非正常態View替換
以demo中的MyVaryViewHelperController舉例,只是修改了showLoading,其他的都可根據自身項目需求進行修改
```
class MyVaryViewHelperController private constructor(private val helper: VaryViewHelper) :
IVaryViewHelperController {
//是否已經調用過restore方法
private var hasRestore: Boolean = false
constructor(replaceView: View) : this(VaryViewHelper(replaceView)) {}
override fun showNetworkError(onClickListener: View.OnClickListener?) {
showNetworkError("網絡狀態異常,請刷新重試", onClickListener)
}
override fun showNetworkError(
msg: String?,
onClickListener: View.OnClickListener?
) {
hasRestore = false
val layout = helper.inflate(R.layout.hulk_page_error)
val againBtn =
layout.findViewById<Button>(R.id.pager_error_loadingAgain)
val tv_title = layout.findViewById<TextView>(R.id.tv_title)
tv_title.visibility = View.GONE
val tv_msg = layout.findViewById<TextView>(R.id.tv_msg)
tv_msg.text = msg
if (null != onClickListener) {
againBtn.setOnClickListener(onClickListener)
}
helper.showView(layout)
}
override fun showCustomView(
drawableInt: Int,
title: String?,
msg: String?,
btnText: String?,
listener: View.OnClickListener?
) {
hasRestore = false
val layout = helper.inflate(R.layout.hulk_page_error)
val iv_flag =
layout.findViewById<ImageView>(R.id.iv_flag)
val tv_title = layout.findViewById<TextView>(R.id.tv_title)
val tv_msg = layout.findViewById<TextView>(R.id.tv_msg)
val againBtn =
layout.findViewById<Button>(R.id.pager_error_loadingAgain)
iv_flag.setImageResource(drawableInt)
if (TextUtils.isEmpty(title)) {
tv_title.visibility = View.GONE
} else {
tv_title.visibility = View.VISIBLE
tv_title.text = title
}
if (TextUtils.isEmpty(msg)) {
tv_msg.visibility = View.GONE
} else {
tv_msg.visibility = View.VISIBLE
tv_msg.text = msg
}
if (TextUtils.isEmpty(btnText)) {
againBtn.visibility = View.GONE
} else {
againBtn.text = btnText
if (null != listener) {
againBtn.setOnClickListener(listener)
}
}
helper.showView(layout)
}
override fun showEmpty(emptyMsg: String?) {
hasRestore = false
val layout = helper.inflate(R.layout.hulk_page_no_data)
val textView = layout.findViewById<TextView>(R.id.tv_no_data)
if (!TextUtils.isEmpty(emptyMsg)) {
textView.text = emptyMsg
}
helper.showView(layout)
}
override fun showEmpty(
emptyMsg: String?,
onClickListener: View.OnClickListener?
) {
hasRestore = false
val layout = helper.inflate(R.layout.hulk_page_no_data_click)
val againBtn =
layout.findViewById<Button>(R.id.pager_error_loadingAgain)
val textView = layout.findViewById<TextView>(R.id.tv_no_data)
if (!TextUtils.isEmpty(emptyMsg)) {
textView.text = emptyMsg
}
if (null != onClickListener) {
againBtn.setOnClickListener(onClickListener)
// againBtn.setVisibility(View.VISIBLE);
againBtn.visibility = View.GONE //按鈕都隱藏,空頁面沒有刷新 2018.9.5
} else {
againBtn.visibility = View.GONE
}
helper.showView(layout)
}
override fun showLoading() {
hasRestore = false
val layout = helper.inflate(R.layout.view_page_loading)
helper.showView(layout)
}
override fun showLoading(msg: String?) {
hasRestore = false
val layout = helper.inflate(R.layout.view_page_loading)
val tv_msg = layout.findViewById<TextView>(R.id.tv_msg)
tv_msg.text = msg
helper.showView(layout)
}
override fun restore() {
hasRestore = true
helper.restoreView()
}
override val isHasRestore: Boolean
get() = hasRestore
}
```
2.攔截器
2.1 請求頭攔截器
```
class RequestHeaderInterceptor : Interceptor {
//統一請求頭的封裝根據自身項目添加
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val authorised: Request
val headers = Headers.Builder()
.add("app_id", "wpkxpsejggapivjf")
.add("app_secret", "R3FzaHhSSXh4L2tqRzcxWFBmKzBvZz09")
.build()
authorised = request.newBuilder().headers(headers).build()
return chain.proceed(authorised)
}
}
```
2.2 非正常態響應碼攔截器
實際應用:可應用於App中用戶的互踢
```
class SessionInterceptor : IReturnCodeErrorInterceptor {
//和接口定義互踢的相關參數返回,然後在doWork方法進行跳轉
override fun intercept(returnCode: String?): Boolean {
return "-100" == returnCode
}
override fun doWork(returnCode: String?, msg: String?) {
}
}
```
🌟🌟🌟
推薦Carson_Ho大佬的[Kotlin:這是一份全面 & 詳細的 類使用 的語法學習指南](https://blog.csdn.net/carson_ho/article/details/105356518)
[項目地址,如果覺得不錯歡迎star,歡迎指教,不斷學習,不斷進步,你的支持是我前進的動力](https://github.com/madreain/AACHulk)