Brick
github gitee
introduce
Assist Android developers to build an annotation processing framework based on the MVVM framework built by JetPack components. The Factory class and lazy method of ViewModel are automatically generated by annotations. Support for injecting ROOM’s DAO layer interface anywhere in the project and the API interface in the Retrofit library.
The characteristics of
Android developers can think of Brick as a lightweight injection framework that is very simple to use and works with 4-6 annotations. The brick works primarily at compile time, incurs no additional performance cost when the App is running, and only 1 annotation library is packaged into your Android project, so don’t worry about bulk.
Scope of application
- use
androidx
Rather thansupport
Library. - use
JetPack
theViewModel
Components. - use
Retrofit
As a network request library. - use
ROOM
Database framework. (optional) - The server has multiple ports and multiple IP addresses. (optional)
The introduction of
- Add the following code to the appropriate location in your Build. gradle file at the root of your Android project:
buildscript { ... Ext {brick_version = '0.2.0'} Repositories {... maven { url 'https://jitpack.io' } } dependencies { classpath "com.gitee.numeron.brick:plugin:$brick_version" } } allprojects { repositories { ... maven { url 'https://jitpack.io' } } }Copy the code
- Add the following code to the appropriate location in the build.gradle file of the Android module for which brick is to be enabled in your Android project:
. apply plugin: 'kotlin-kapt' apply plugin: 'brick' ... dependencies { ... implementation "com.gitee.numeron.brick:annotation:$brick_version" kapt "com.gitee.numeron.brick:compiler:$brick_version" }Copy the code
use
How to use @provide annotation
- Add the @provide annotation to your ViewModel subclass
@Provide
class WxAuthorViewModel: ViewModel() {
...
}
Copy the code
- There are three ways to get the brick annotation processor to work:
- Enter on the Terminal
gradlew :[ModuleName]:kaptDebugKotlin
Run the script; - Find them in the Gradle extension bar on the right side of AndroidStudio
[PrjectName] -> [ModuneName] -> Tasks -> other -> kaptDebugKotlin
Double-click to run the script. Ctrl + F9
Compile the entire project.
Run the Brick annotation processor in any of these three ways. 3. After the script is run, two package-level methods are generated:
lazyWxAuthorViewModel()
Extension method, called directly in an Activity or Fragment.get()
Method, which can be used to get an instance of the ViewModel when the lazy method is inconvenient.
Note: The lazyWxAuthorViewModel method is just a wrapper around the get() method. Using the generated method directly, you can create the corresponding ViewModel instance:
private val wxAuthorViewModel by lazyWxAuthorViewModel()
Copy the code
Or after onCreate(), with get:
private lateinit var wxAuthorViewModel: WxAuthorViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
wxAuthorViewModel = get(this)
}
Copy the code
2. Use of @inject annotation
-2. (required) add @retrofitInstance to the method that gets the Retrofit instance.
@RetrofitInstance
val retrofit: Retrofit by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
Retrofit.Builder()
.client(okHttpClient)
.baseUrl(WANDROID_BASE_URL)
.addConverterFactory(MoshiConverterFactory.create())
.build()
}
val okHttpClient: OkHttpClient by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
val logInterceptor = HttpLoggingInterceptor()
logInterceptor.level = HttpLoggingInterceptor.Level.BODY
OkHttpClient.Builder()
.addInterceptor(logInterceptor)
.callTimeout(15, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.connectTimeout(15, TimeUnit.SECONDS)
.build()
}
Copy the code
Note:@RetrofitInstance
Annotations can only be marked inpublic
Modification of theval
Attribute or method,val
Properties or methods can be inThe object singleton
orcompanion object
, or package-level properties/methods.
-1. (Optional) mark @RoomInstance on the properties or methods that get the RoomDatabase instance, such as:
@RoomInstance
val wandroidDatabase: WandroidDatabase by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
Room.databaseBuilder(CONTEXT, WandroidDatabase::class.java, "wandroid.db")
.build()
}
Copy the code
Note:@RoomInstance
Annotations can only be marked inpublic
Modification of theval
Attribute or method,val
Properties or methods can be inThe object singleton
orcompanion object
, or package-level properties/methods.
- Assumptions have been
Retrofit Api
The interface andWxAuthorRepo
class
interface WxAuthorApi {
@GET("wxarticle/chapters/json ")
suspend fun getWxAuthorList(): List<WxAuthor>
}
class WxAuthorRepo {
...
}
Copy the code
- Add in WxAuthorRepo
lateinit var
Modification of theWxAuthorApi
Field, and use@Inject
Tags:
class WxAuthorRepo {
@Inject
lateinit var wxAuthorApi: WxAuthorApi
}
Copy the code
- Create it in the ViewModel
lateinit var
Modification of theWxAuthorRepo
Field, and use@Inject
Tags:
@Provide
class WxAuthorViewModel: ViewModel() {
@Inject
private lateinit var wxAuthorRepo: WxAuthorRepo
}
Copy the code
All fields marked by @Inject will be automatically obtained or created at compile time, without worrying about when they are assigned. Note: Do not attempt to assign values to fields that are modified by Lateinit var. This will lead to a fatal error. Note: The only types @inject can be injected are Retrofit’s API and ROOM’s DAO interface, and classes constructed with or without parameters.
Three, multi-server or multi-port processing method:
Suppose you have another Retrofit API interface with a different access address or Port than the one in baseUrl. In this case, you can add @port and @URL annotations to the Retrofit API interface to set their Url or Port.
@Port
The use of:
@Port(1080)
interface ArticleApi {
@GET("wxarticle/list/{chapterId}/{page}/json")
suspend fun getArticleList(@Path("chapterId") chapterId: Int, @Path("page") page: Int): Paged<Article>
}
Copy the code
After adding this annotation, brick will create a new Retrofit instance at compile time based on the @RetroFitInstance annotation tagged Retrofit instance and @Port Port number, and use the new Retrofit instance to create an instance of The ArticleApi.
@Url
The use of:
@Url("http://www.wanandroid.com:1080/")
interface ArticleApi {
@GET("wxarticle/list/{chapterId}/{page}/json")
suspend fun getArticleList(@Path("chapterId") chapterId: Int, @Path("page") page: Int): Paged<Article>
}
Copy the code
The implementation principle is the same as @port.
Appendix 1
Wxauthorviewmodels.kt file generated:
/ / kotlin extension methods, in the Activity/fragments by invoking fun ViewModelStoreOwner. LazyWxAuthorViewModel () : Lazy < WxAuthorViewModel > = LazyWeChatAuthorViewModel (this) / / package level method, after the Activity/fragments onCreate method call fun get (the owner: ViewModelStoreOwner): WxAuthorViewModel { val factory = WxAuthorViewModelFactory() return ViewModelProvider(owner, factory).get(WxAuthorViewModel::class.java) } private class WxAuthorViewModelFactory : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <VM : ViewModel> create(clazz: Class<VM>): VM = WxAuthorViewModel() as VM } private class LazyWxAuthorViewModel( private val owner: ViewModelStoreOwner ) : Lazy<WxAuthorViewModel> { private var _value: WxAuthorViewModel? = null override val value: WxAuthorViewModel get() { if(_value == null) { _value = get(owner) } return _value!! } override fun isInitialized(): Boolean = _value ! = null }Copy the code
Appendix 2
Decompiled WxAuthorViewModel.class:
class WxAuthorViewModel extends ViewModel {
private final WxAuthorRepo wxAuthorRepo = new WxAuthorRepo();
}
Copy the code
The appendix 3
Decompiled WxAuthorrepo.class:
class WxAuthorRepo { private final WxAuthorApi wxAuthorApi = RuntimeKt.getRetrofit().create(WxAuthorApi.class); public final WxAuthorApi getWxAuthorApi() { ... return wxAuthorApi; }}Copy the code
Appendix 4
Wxauthorrepo.class with @port annotated WxAuthorApi:
class WxAuthorRepo { private final WxAuthorApi wxAuthorApi = newRetrofit(RuntimeKt.getRetrofit(), 1080, null).create(WxAuthorApi.class); public final WxAuthorApi getWxAuthorApi() { ... return wxAuthorApi; } private final Retrofit newRetrofit(Retrofit retrofit, int port, String url) { if (port > 0) { HttpUrl httpUrl = retrofit.baseUrl().newBuilder().port(port).build(); return retrofit.newBuilder().baseUrl(httpUrl).build(); } else if(url ! = null && url.length() ! = 0) { return retrofit.newBuilder().baseUrl(url).build(); } return retrofit; }}Copy the code
conclusion
After decompiling the code of the class and the whole article, we can draw a general conclusion: Brick is an injection framework implemented by modifying the bytecode of the class after Java is compiled into class and before class is compiled into dex. It assigns values to fields marked @Inject. ViewModel injection is initialized manually by calling the generated method, which is marked with red error on AS before compiling the code. The main task is to make @Inject support ViewModel.