The article contains 2,689 words and will take about 11 minutes to read
A framework that introduces quality open source libraries
Retrofit2 Network request library
Glide picture loading library
BaseRecyclerViewAdapterHelper adapter libraries
Permissionsdispatcher – KTX Permission request library used with Kotlin
MMKV uses Tencent MMKV to replace SharePreference’s high-performance key-value component
SmartRefresh pull-up load pull-down refresh library
Shimmerlayout is a high degree of freedom and easy to use for bone loading
LiveEvent message bus, based on LiveData, has lifecycle awareness
Unpeek-livedata Resolves the LiveData flooding problem
ARouter routing
Disklrucache Hard drive cache
QMUI module is used to introduce QMUI. Since QMUI has many functions, some frequently used functions are extracted separately
You can do this easily
Project structure at a glance
Easy to write, easy to maintain
Class rarely
To write an interface you need to create three files: XML layout, Activity /fragment, ViewModel, and nothing else.
LiveEventBus
Message bus, compared to evnetBus and RxBus, is lifecycle aware and less dependent because it is dependent on Google while RxBus is dependent on RxJava
Liveeventbus.get (constant.del_address_event).post("") liveEventbus.get (constant.event).observe(this, Observer { })Copy the code
Network request code minimalism
Viewmodel Two lines of code do the basic network call and pass the data back to the interface via liveData. The demo illustrates three request scenarios: single entity, receiving NULL, and paging. The structure of paging requests is similar to non-paging, and the code doesn’t need to change much.
A single entity
val singEntityLiveData = StateLiveData<User>()
fun singEntity() = launchUI(
response = { api.singEntity().data!! },
liveData = singEntityLiveData
)
Copy the code
Receive a null
When receiving NULL, StateLiveData writes to Any? ,? Kotlin allows null. Set allowNull to true, otherwise LiveData will not be sent back
val mayNullLiveData = StateLiveData<Any? >().allowNull(true) fun mayNull() = launchUI( response = { api.mayNull().data }, liveData = mayNullLiveData )Copy the code
paging
val pageEntityLiveData = StateLiveData<Page<PageUser>>()
fun pageUser(pageNum: Int) = launchUIPage(
pageNum,
response = { api.pageEntity(pageNum).data!! },
liveData = pageEntityLiveData
)
Copy the code
Page cache (offline cache)
Requirements:
-
The network request and the read cache must be executed at the same time, not simultaneously.
-
If the network request is faster than the read cache, the cached data cannot overwrite the network request.
-
If there is no network, read the cache, the interface message network error.
-
Each new data overwrites the previously cached data.
fun cacheData() = launchUIPageCache(
cache = {
CacheManager.getInstance().getCacheList(
Constant.CACHE,
CacheEntity::class.java
)
},
response = { api.cacheEntity().data!! },
liveData = cacheEntityLiveData
)
Copy the code
Easy to monitor interface status
Retry retry (the maximum number of retry times can be set individually or globally and conditional or unconditional)
fun singEntity() { launchUI( response = { api.singEntity().data!! }, liveData = singEntityLiveData, error = {e, code -> // Error logic}, retry = {e, RetryUtil(3). RetryToException (e is SocketTimeoutException) {singEntity()}}, Complete = {// interface completes})}Copy the code
Processing interface data
The activity listens for data requested by the interface and processes it
Viewmodel. run {/** ** pageEntityLiveData */ observe(pageEntityLiveData, ::setData).toState(::pageEntityState) /** * Single entity callback */ observe(singEntityLiveData) {}. ToState (::dialogState) /** * */ observe(mayNullLiveData) {}. ToState (::dialogState) /** * Cache callback */ observe(singEntityLiveData) {}.toState(::dialogState) }Copy the code
/** * private fun pageEntityResult(data: Page<PageUser>) {adapter.setpagedata (Page, data.list) page++} / private fun pageEntityState(state: State) { apiState(state, complete = { refresh.complete() }, stateLayout = { adapter.setEmptyView(it) }, hasMore = { refresh.finishMore(it) } ) }Copy the code
Use StateLiveData to listen for status. Complete (),finishMore() and setPageData() are all KT extension methods
/** * @param state status class * @param before interface request * @param complete interface request after (success/failure) * @param hasMore whether there is a next page, @param stateLayout */ fun apiState(state: state, before: () -> Unit)? = null, complete: (() -> Unit)? = null, error: ((code: Int) -> Unit)? = null, hasMore: ((page: Page<*>) -> Unit)? = null, stateLayout: ((layout: Int) -> Unit)? = null ) { when (state.stateCode) { Constant.BEFORE -> before? .invoke() Constant.COMPLETE -> complete? .invoke() Constant.ERROR -> error? .invoke(state.errorCode) Constant.HAS_MORE -> hasMore? .invoke(state.page) Constant.STATE_LAYOUT -> stateLayout? .invoke(state.stateLayout) } }Copy the code
Componentization is supported
Why componentization
When a small function is done very big, for better maintenance and management, separate out to make a component, convenient reuse, separate compilation.
Component technology solutions
Use ARouter+LiveEventBus to realize page jump + component communication
Use viewBinding instead of Kotlin synthetic
Use the activity
private val viewBind by viewBinding(ActivityMainBinding::inflate)
Copy the code
Fragment use (automatically empty when destroying)
private val viewBind by viewBinding(FragmentTestBinding::bind)
Copy the code
Customize many kotlin extension methods (see demo for more methods)
Let’s say you want to load an image
imageView.loadImage("url")
Copy the code
The countdown
Call the lifecycleCountdown method from the Activity/Fragment interface. The value can be determined by 5 parameters: time, interval, start monitoring, progress monitoring and end monitoring.
lifecycleCountdown(10, 1,
start = {},
schedule = {},
completion = {})
Copy the code
Overriding methods
Rewrite an interface and find that you need to rewrite all of them but only one or two?
viewpager2.setOnPageChangeListener {
}
editText.setOnTextChanged { s, start, before, count ->
}
Copy the code
Click on the event
view.onClick{}
Copy the code
Sets the strikeout line for text
textView.centerLine()
Copy the code
Easy skeletal loading
When using skeleton loading with smartRefresh, you may encounter problems with pull-up loading and need to adjust the timing of skeleton loading hide
recyclerView.initSkeletonRecyclerView(adapter)
Copy the code
File download function
fileDownloader(context, downloadUrl,
completed = {},
pending = {},
progress = { soFarBytes, totalBytes ->
})
Copy the code
User Agreement and Privacy Policy show and click hard?
textView.protocol("... User Agreement and Privacy Policy..." ) { when(it){ 0 -> WebViewActivity.start(context,Constant.USER_PROTOCOL) 1 -> WebViewActivity.start(context,Constant.PRIVACY_PROTOCOL) } }Copy the code
Why write your own framework
newbie
I was just starting out, so I don’t know if you have the same Android.
I had no concept of frameworks at the time, and usually projects were directly imported libraries. So all the code is in the Activity, and it’s not well encapsulated. One problem with that is that simple functions require a lot of repetitive code.
As a beginner, many problems need to be solved even if you know the problems.
Skilled workers
After almost a year of this, I started trying to emulate MVC, MVP, MVVM patterns. But I always feel that what I write is not what I want (excessive pursuit of framework standards, design patterns, ideas).
I also often browse the tech post, Github, and find some excellent frameworks like MVPArms, Jetpack-MvVm-best-practice… In this way, I began to understand their framework, logic and ideas little by little and carried out practical projects, such as MVPArms, which I used for two years.
The advanced
As I grew older and more experienced, I began to develop my own code style and methodology.
MVPArms, for example, is a great framework, but it was always written by someone else, so it can’t be 100% true to itself. The framework used Dagger2 and RxJava, both of which were too big, and I only used a few operators for RxJava.
In fact, I think single-function apps are the trend of the future, although there are some super apps (feature aggregation) in China.
Lightweight apps are also more in line with my personal habits, and I usually download the Lite version of the App, regardless of whether it is something or not. Although App functions are single and simple. Aesthetically, simple doesn’t mean mean simple, and every lightweight application deserves consideration.
After thinking about this, I realized that WHAT I needed was a lightweight framework, and since the projects I developed over the years were small and medium-sized, lightweight was probably more suitable for me.
In the future
When I tried to write the process of all kinds of framework, also met a lot of problems, as stated above, at first I standard of excessive pursuit of framework and design patterns, ideas, lead to write framework isn’t have a problem, is very strange, of course, these problems are attributed to my own technical problems, no real understanding framework, didn’t understand the underlying principle.
Later, I reflected on myself, always thinking that these are not the way, can not write framework for the framework, so or a little bit, from simple to simple. In the future, I will continue to learn to conform to Google’s framework and try to write a framework suitable for larger project development. The current plan is that jetpack Compose will be updated into my framework as soon as the stable version is released. If the company is willing, it would also like to practice the project.
I always think that Android should have its own style, rather than follow the style of iOS, or both ends look the same. Before 5.0, Android was really ugly, but after 5.0, when Material Design was added, it felt very different. At that time, I thought that the mainstream software would follow the MD style, but now so many years have passed. Mainstream software still rarely sees MD style. , of course, not to say that the android must conform to standards or MD, it’s just I personally prefer, I think twitter, cables, some of Google’s APP, the experience is very good, in other software may get stuck on my phone, but they don’t card, and the interaction is great, I have joined some MD interface, the framework of the future will add more.
plan
- 1. Constantly improve the framework to minimize unexpected errors.
- 2. Try adding new technologies to make the framework more robust without losing weight and simplicity.
- 3. Add more Material Design styles and interactions (in progress)
- 4. Try using Jetpack Compose to refactor your layout
- 5. More need to optimize code
- 6…
conclusion
I am a person with open source spirit, but I am not strong in technology, so I feel embarrassed to share what I have learned. At the beginning of this line, I knew nothing at that time, so I wrote a blog and a demo when I learned something. I helped some people at that time, but the more experience I had, the more AFRAID I was to write a blog.
Github address: JetPackMVVMLight