Basic introduction to the project

  • 1. The whole framework is mainly used for MVVM framework, after writing interface interface, through custom annotations can automatically generate interface methods
  • 2. Use Kotlin Flow instead of Rxjava, because I find Rxjava is very powerful, but most people only use it at Http level. Since Kotlin already has Flow in it, I might as well add one less library
  • 3. The storage of network requests is realized through Jetpack’s Room database, and the caching strategy is also completed with annotations.
  • 4. Network requests initiated are tied to the host life cycle and will be interrupted if the host is destroyed before the network request comes back

Making portal

This is a lot of stuff, because this annotation was written directly in my own demo, but it feels cumbersome to pull it out

Making portal

Basic usage

1. Define the interface class

As with Retrofit, you need to define an interface class

Among them@AutoApi.@AutoFlowApi.@NetStrategyAre custom annotations, which will be described later.

2. To compile, an xxxRepository.class will be generated in your interface class folder

This is a file automatically generated by annotation, using KotlinPoet and here apiService is the interface proxy obtained through Retrofit

3. Get the Repository method in viewModel

4. Call from viewModel in the corresponding place

Call the interface, passing in the corresponding argument

Observe in the right place

Retrofit of encapsulation

The apiService in the Repository class is the interface proxy class obtained through Retrofit. So go ahead and check out apiService

You can see that the apiService is a variable of BaseRepository and that the Repository we generate inherits from BaseRepository

When we call a method in the ConfigRepository class, we pass ConfigRepository to findNeedType

whilefindNeedTypeThe method is going to takeConfigRepositoryThe correspondingConfigServiceGet and go back out

So apiService is kind of like that, kind of like Retrofit’s create method

var apiService: T = HttpProvider.defaultCreate(ConfigService) as Class<out T>)
Copy the code

We continue to enter HttpProvider defaultCreate

You can see newRetrofit(), and you pass in an HttpConfig, which is the Http configuration from the name and then newCreate(), which receives our interface service class

First look at the newRetrofit method

These lines of code create a Retrofit object, save it, and return it. But how does it relate to HttpConfig?

We can see that the generated Retrofit.Builder() is passed hereHttpConfig method buildLet’s go in and have a look

You can see that this is the configuration part of Retrofit that we’re all too familiar withSo with the newRetrofit method, we configure the Retrofit object and take the Retrofit object and save it for reuse

Take a look at the newCreate() method

This is an extension function, an extension function of Retrofit that generates a proxy class from the ConfigService passed in through Retrofit.create() and saves it for reuse

Introduction of annotations

1.AutoApi

Let’s start with the simplest AutoApi and introduce the entire annotation framework

As soon as you use this annotation in your interface class method, it generates the suspend method, which is pretty simple and then let’s see how it does that, okay

See how this annotation is defined

Annotations are supported with default values, because Kotlin’s methods can assign initial values directly to variables, so that the call does not need to pass values, so there is also a support to make the call more concise

Generated process

Code analysis

Again, use ConfigService for analysis

  • 1. First flowchart, we will walk through the classes that use this annotation. At this point, we have all the information for the ConfigService element.

  • 2. Next we’ll put the ConfigService package to the RepositoryClass.

RepositoryClass holds the Class name, package name, type, and all methods of ConfigService

  • 3. Package the methods in ConfigService as AutoMethod(different annotations have different types) to the Method variable RepositoryClass

Once repositoryMap is repositoryMap to all the classes that used the AutoAPi annotations, it’s iterated through and passed to the Repository generator RepositoryClassBuilder

This code will only be generated if the startFuncBuild method is not added

open class ConfigRepository : BaseRepository<ConfigService>() {
      
}
Copy the code

Take a look atstartFunBuildSelect the corresponding method handler based on the annotations used by the methods in your current class

All method handlers are inheritanceAbsFuncBuilderThe subclass needs to output the specific contents of the method, and can also add parameters to the method

The AbsFuncBuilder class simply generates the following code, which will output the information collected by the RepositoryMethod earlier. But it is up to the subclasses to output the content, because each annotation corresponds to the output method body is different

Suspend Fun config2(Page: String = "GS"): List<String> {suspend fun config2(page: String = "GS"): List<String>Copy the code

2. net Strategy annotation

This annotation can be passed with four parameters strategy indicates the cache policy, effectiveTime indicates the cache time, and timeUnit indicates the timeUnit.

Caching policies are added to methods by default, and sometimes the same interface may use different caching policies for different scenarios.

  • For example, when you first enter the home page, use the page to initialize CacheFirst
  • After the page is initialized, the data is pulled down again, using NetCache
  • Pull load on the current home page, using NetOnly

In this case, an interface uses three different cache policies

So useisNeedAddParameterTo determine whether you need to add the cache policy parameter to the method parameter

Code analysis

The NetStrategy collection must be placed at the back of the annotation processor, because I can’t think of a good way to know which method annotation NetStrategy is bundled with.

After the annotations are repositoryMap, I will get the method name and compare it with the method name of the class repositoryMap. If it is the same, the method uses NetStrategy annotations and needs to be cached

3. Introduction to AutoFlowApi annotations

Code generated using annotations

Let’s examine the various methods that generate methods

viewModelScopeCoroutine

A coroutine that is bound to the ViewModel lifecycle and runs on the main thread by default

It’s hard to explain here, but I’ll just draw it

CoroutineDataFetcher { apiService.getData() }.startFetchData()

Apiservice.getdata () is to initiate a network request, see CoroutineDataFetcher

StartFetchData (), which is a method for making Http requests based on the cache policy that was passed in

Code analysis

From the above analysis, we can see that when you write a new annotation, you only need to write two classes

  • An inheritanceRepositoryMethodParameter collector of
  • An inheritanceAbsFuncBuilderMethod specific content output

So we look directly at these two classes of the AutoFlowApi

AutoFlowMethod

It is the same as AtoMethod, collecting default parameters. The difference is the following configuration

Modify the method’s configuration information by overwriting the exposed configuration method from the AbsFuncBuilder, as shown in the figure

  • isNullableCan the method return a value of null
  • isNeedSuspend, whether the method requires the suspend keyword
  • isNeedReturnType, whether the method needs to return a value

AutoFlowApiFuncBuilder

This is the most critical method of AutoFlowApi annotation, there is a lot of code, but there is nothing to explain, is the use of kotlinpoet, tedious and boring.

Write out the statement you want to generate and replace the variables with the specified characters

The next step is to create a sentence, and replace the specified characters in the statement with your variables

At the end

In fact, the framework was written without considering its comprehensiveness and compatibility, so I decided to try it out first. There are a lot of things that can be modified and extended.