AACHulk


AACHulk is based on Google’s ViewModel+DataBinding+LiveData+Lifecycles framework, Combining Okhttp + Retrofit + BaseRecyclerViewAdapterHelper + SmartRefreshLayout + ARouter to build a fast development framework, development language is Kotlin, Combined with AACHulkTemplate template development for development, to avoid some tedious operations, to provide development efficiency

Function is introduced

1. Supports the configuration of multiple server addresses, multiple success codes, various timeout periods, various interceptors, Arouter, etc

2. Support to customize various abnormal state View replacement

3. Retry if the interface invocation fails

4. Supports the display of various activities and fragments to meet business requirements

5. Support multiple layout adapters

6. Support general-purpose code generation AACHulkTemplate template

Third-party libraries

  1. OkhttpAn HTTP client for Android, Kotlin, and Java
  2. RetrofitSecure HTTP client for Android and Java
  3. BaseRecyclerViewAdapterHelperPowerful and flexible universal adapter
  4. SmartRefreshLayoutAndroid Smart pull-down refresh framework
  5. ARouterA routing framework to help Android apps perform componentized transformations

Basis function

1. DataBinding is enabled for the master project

    dataBinding {
        enabled true
    }
Copy the code

2. Add a dependency

Add build. Grade in Project

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
        google()
        jcenter()
    }
}
Copy the code

Add to build.grade in the main project app

api 'com. Madreain: libhulk: 1.0.4'
Copy the code

3. Inherit HulkApplication and configure related configuration items

//.setretSuccess (buildConfig.code_Success) .setretSuccessList (buildConfig.codelist_Success) // Set multiple BaseurL retcode.addretSuccess (hulkkey.wanandroid_domain_name, BuildConfig.WANANDROID_CODELIST_SUCCESS) .addRetSuccess(HulkKey.GANK_DOMAIN_NAME, Buildconfig.gank_codelist_success).setBaseurL (buildConfig.base_URL) // Set multiple BaseurLs .addDomain(HulkKey.WANANDROID_DOMAIN_NAME, HulkKey.WANANDROID_API) .addDomain(HulkKey.GANK_DOMAIN_NAME, HulkKey.GANK_API) .setLogOpen(BuildConfig.OPEN_LOG) .setArouterOpen(BuildConfig.OPEN_AROUTER) .addokhttpinterceptor (RequestHeaderInterceptor()) // RequestHeaderInterceptor.addokhttpinterceptor (buildconfig.open_log, HttpLoggingInterceptor (). SetLevel (HttpLoggingInterceptor. Level. BODY)) / / okhttp request log switch + message interceptor. The md .addRetCodeInterceptors(SessionInterceptor()) //returnSetRetrofit (apiclient.getInstance ().geTretroFit (apiclient.getInstance ().getokHttpClient ().setretroFit ().getretroFit ().getokHttpClient () HulkConfig.getOkHttpInterceptors() ) ) ) .build()Copy the code

You can refer to Demo to configure the preceding configuration items

Here you can also configure a unified style according to SmartRefreshLayout related documents, or set it separately, or customize it according to your own project

4. Inherit IRes, and accept the unified data encapsulated by its own project

5. Write ApiService, put the interface

6. Write a common Toolbar(optional) because the Kotlin-Android-Extensions plugin may only use the resource file of its own module, so it is not possible to write the common Toolbar. XML in libhulk for your app. Therefore, you can only write the generic Toolbar.xml in your app project

⚠️ if the big guys have a good way to achieve welcome advice

️🔥️🔥️🔥 AACHulkTemplate template. This template must be used to ensure that the ApiService and tooltool. XML are created, and users can modify the template according to their own projects

Rapid development of

The AACHulkTemplate template is pretty tasty to use. Let’s take a look at your own manual steps, using SingleActivity as an example

1. New SingleActivity inherits 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() {}}Copy the code

ViewDataBinding will replace the BaseViewModel with an Activity_single.xml binding and will replace the BaseViewModel with the new SingleViewModel inheriting the BaseViewModel

2. Create the corresponding object

@Keep
class SingleData {
    var code: String? = null
    var name: String? = null
}
Copy the code

3. The associated ViewDataBing

Attach an ActivitySingleBinding to activity_single.xml

<? 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= 'before interface call'}"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:text="Result of interface call" />

        </androidx.constraintlayout.widget.ConstraintLayout>


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Copy the code

4. Create SingleViewModel to inherit BaseViewModel

class SingleViewModel : BaseViewModel<ApiService>() {

    public override fun onStart() {cityList()} demo var result = MutableLiveData<List<SingleData>>() private funcityList() {launchOnlyresult(block = {getApiService().getCityList()}, // Launch the launchOnlyresult(block = {getApiService().getCityList()}, // launch the launchOnlyresult(block = {getApiService().getCityList()}, // launch the launchOnlyresult(block = {getApiService().getCityList()}, // Success = {// success callback result.value = it},type = RequestDisplay.REPLACE
        )
    }
}

Copy the code

5. Replace ViewDataBinding, BaseViewModel ActivitySingleBinding replace ViewDataBinding SingleViewModel replace BaseViewModel

6. Invoke the interface

/ / request interface mViewModel. OnStart () / / interface request data mViewModel change result. Observe (this, the Observer {mBinding!!!!! .singleDataS = it mBinding!! .singleData = it[0] })Copy the code

7. ARoute configuration

Determine whether to configure ARoute for routing control based on your project requirements

@Route(path = "/aachulk/ui/SingleActivity")
Copy the code

At this point, a simple interface call to the data presentation is complete

⚠️⚠️⚠️ Demo with adapter refer to ListActivity

Use the advanced

1. Customize various abnormal state View replacements

In the demo MyVaryViewHelperController, for example, just modify showLoading, other can be modified according to the requirements of their project

class MyVaryViewHelperController private constructor(private val helper: VaryViewHelper) : IVaryViewHelperController {/ / whether the method has been called restore private var hasRestore: Boolean =false

    constructor(replaceView: View) : this(VaryViewHelper(replaceView)) {}

    override fun showNetworkError(onClickListener: View.OnClickListener?) {
        showNetworkError("Network status is abnormal, please refresh and try again", 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 againbtn. visibility = view. GONE againBtn.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

}
Copy the code

2. The interceptor

2.1 Request header interceptor

class RequestHeaderInterceptor : @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)
    }
}
Copy the code

2.2 Abnormal state response code interceptor

Practical application: It can be applied to users’ mutual kicking in the App

The class SessionInterceptor: IReturnCodeErrorInterceptor {/ / and the related parameters in the interface definition and kick back, and then indoOverride Fun Intercept (returnCode: String?) : Boolean {return "100"= =returnCode
    }

    override fun doWork(returnCode: String? , msg: String?) {}}Copy the code

3. Multiple BaseUrl and multiple status codes

3.1 Setting multiple BaseurLs

.addDomain(HulkKey.WANANDROID_DOMAIN_NAME, HulkKey.WANANDROID_API)
Copy the code

If more than one BaseUrl is set, the corresponding status code must be set. Otherwise, an exception is reported that the status code is not set

3.2 Setting the status code

.addRetSuccess(HulkKey.WANANDROID_DOMAIN_NAME, BuildConfig.WANANDROID_CODELIST_SUCCESS)

Copy the code

3.3 Setting currentDomainName of the Invoked interface Method

 fun getWxArticle() {launchOnlyresult(// call interface method block = {getApiService().getwxArticle ()}, // reTry = {// call reTry method getWxArticle()}, }, currentDomainName = hulkkey.wanandroid_domain_name,type = RequestDisplay.REPLACE
        )
    }
Copy the code

You can refer to Demo to configure the preceding configuration items

Multiple BaseUrl design ideas refer to the implementation of RetrofitUrlManager

4. Message bus

LiveEventBus(disadvantages: it does not support thread distribution) is used to replace the previous EventBus. The switch setting of setEventBusOpen in HulkConfig is eliminated. You can choose your own message bus according to your project

LiveEventBusMessage bus, based on LiveData, with life cycle awareness, support Sticky, support AndroidX, support cross process, support cross APP

For details, refer to the official documents

The relevant data

🌟🌟🌟 recommended by Carson_Ho’s Kotlin: This is a comprehensive & detailed grammar guide for class use

Thank you

I would like to thank all the authors of the tripartite libraries used in this framework, as well as all the developers and organizations who make selfless contributions to open source, so that we can work and study better. I will also give back my spare time to the open source community

About me

License

   Copyright [2020] [madreain]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except inThe compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed toin writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
Copy the code