The title
The things I have done recently are quite trivial, and it is difficult to organize them into a fixed system to share with everyone. Therefore, today’s article comes into being.
By the way, I am happy that the project of AndroidAutoTrack has finally gained 420 stars after three years of efforts. I will be the licking dog of all the leaders in the future. Thank you for your support and LOVE you.
Today’s article will start with four themes of sand sculpture.
kotlin dsl
Simple learning of- AGP upgraded 4.1.1 craters
- Yaml is sweet
The body of the
Simple learning of kotlin DSL
Domain specific Language (DSL) : A computer language dedicated to solving a specific problem, such as SQL and regular expressions.
When you’re writing Kotlin, I’ve always been fascinated by the block functions that big guys play. Before because oneself write not to come, also coquettishly not rise, can only shout 666 to big men.
This is a bit of a play for myself. Let’s start with route constructors.
val request = KRequest("https://www.baidu.com/test", onSuccess = {
Log.i("KRequest"."onSuccess")
}, onFail = {
Log.i("KRequest"."onFail")
}).apply {
activityResultCode = 12345
}.start(this)
Copy the code
I used to define a route to redirect the Request, you can see that this part of the construction parameters are actually quite disgusting, although I have added the default value, but the first sense is that this person is not coquetty. This part, if adjusted with the constructor mode, is relatively more interesting.
@RouterDsl
class KRequest(
val url: String,
val bundle: Bundle? = null.val onSuccess: () -> Unit = {},
val onFail: (exception: Exception) -> Unit = {
throw it
}
) {
var activityResultCode: Int = 0
constructor(builder: Builder) : this(
builder.url,
builder.bundle,
builder.onSuccess,
builder.onFail
) {
this.activityResultCode = builder.activityResultCode
}
fun start(context: Context) {
Router.sharedRouter().open(this, context)
}
fun newBuilder(a): Builder {
val newBuilder = Builder(url)
newBuilder.onSuccess = onSuccess
newBuilder.onFail = onFail
newBuilder.bundle = bundle
newBuilder.activityResultCode = activityResultCode
return newBuilder
}
@RouterDsl
class Builder(val url: String) {
var bundle: Bundle? = null
var onSuccess: () -> Unit = {}
var onFail: (exception: Exception) -> Unit = {
throw it
}
var activityResultCode: Int = 0
fun putBundle(bundle: Bundle) {
this.bundle = bundle
}
fun build(a): KRequest {
return KRequest(this)}}}Copy the code
This is the first version that was refactored. This allowed us to create the Request using the constructor’s pattern, but I still found it slightly unsexy. Now let’s look at the DSL that we finally present to you.
request("https://www.baidu.com/test") {
activityResultCode = 12345
success {
}
fail {
}
bundle {
putString("1234"."1234")
}
}.start(this)
Copy the code
This version does seem to be very coquettish, first request (” https://www.baidu.com/test “) {} this closure, on behalf of the constructed a request, then we can within the closure, definition to add bundle and jump time parameters, We can define a success or failure callback function and also define whether or not a request returns a result. The route jump function is then called at the end of the closure. It’s natural, it’s coquettish.
What is the advantage of this definition over the previous constructor pattern? In short, we define functions and closures more clearly, each field is responsible for its own functions, and without functions, we can better understand what the current field is, although this depends on your actual naming conventions, but it will improve the readability of your code. And the last thing is you can be a douche, you know, a douche, like Yasso.
How do you do that?
fun request(url: String, build: KRequest.Builder. () - >Unit): KRequest {
val requestBuilder = KRequest.Builder(url)
build.invoke(requestBuilder)
return requestBuilder.build()
}
fun KRequest.newRequest(build: KRequest.Builder. () - >Unit): KRequest {
val builder = newBuilder()
build.invoke(builder)
return builder.build()
}
fun KRequest.Builder.bundle(invoke: Bundle. () - >Unit) {
if (bundle == null) { bundle = Bundle() } bundle? .apply(invoke) }@RouterDsl
fun KRequest.Builder.success(invoke: () -> Unit) {
onSuccess = invoke
}
@RouterDsl
fun KRequest.Builder.fail(invoke: (exception: Exception) - >Unit) {
onFail = invoke
}
Copy the code
So this is just a little bit of code, by defining blocks and abstracting out the Dsl.
The @dslmaker attribute is also used here, which is a reasonable way to disallow nesting dolls in closures
Project learning address router-Android
AGP upgraded 4.1.1 craters
Recently, the project was secretly doing some upgrade operations about AGP (Android Gradle Plugin), and inevitably encountered some strange problems.
The first Manifest PleaceHolder cannot be inserted in Varint.
The second resValue also failed to insert.
In fact, these two problems are caused by one reason, and the second one is more difficult to troubleshoot. Normally the Extension we define in the plug-in will be evaluated after the afterEvaluate function in Procect is executed. Occasionally, I will get the expanded content of Android when this function is executed. Based on the Variant, I will perform some operations of dynamic generation or dependent insertion or force.
However, Variant has some changes in AGP4.1.0 version. After afterEvaluate is called, some attributes inserted by the method cannot take effect. The specific reason is that although you called the method, the later code was called due to the late timing. You don’t actually add what you need to the real block. Then during file generation or pleaceholder generation, you won’t be able to insert the code you need.
val androidPreLoad = project.extensions.getByName("android") as BaseAppModuleExtension
androidPreLoad.onVariantProperties {
addAPGClassFile(this."key"."value")}@Incubating
private fun addAPGClassFile(@Incubating config: VariantProperties, key: String, value: String) {
config.addResValue(key, "string", value, "")}Copy the code
The solution is also relatively simple, Android DSL provides another closure, this closure is executed when our extended field has already got the value, we just need to do the code insertion operation within this time, can achieve the compatibility of AGP plug-in. This section simply replaces afterEvaluate with this closure.
When upgrading big AGP versions, we must be cautious and try to estimate the time as much as possible. Unexpected situations often happen. We failed to receive the push after the test feedback, and then we started to reverse the whole thing. And that’s not even counting the time it took to solve the compilation problem.
Yaml is sweet
YAML is a recursive abbreviation of “YAML Ain’t a Markup Language” (YAML is not a Markup Language). At the time the Language was developed, YAML actually meant “Yet Another Markup Language” (still a Markup Language), but was renamed with a reverse acronym to emphasize the Language’s data-centric, rather than Markup language-centric focus.
When you see XML recently, you hate it, it’s not simple, and it’s not readable. Json is probably better, but it’s still gross if you don’t have format.
So I tried yamL, the file format used in the compilation phase, which is used on Gitlab CI. Gradle Task is a multi-repository plugin.
<repo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3school.com.cn"
xsi:schemaLocation="http://www.w3school.com.cn repo.xsd">
<default srcBuild="false" />
<module name="QrCodeLibrary" branch="master" origin="https://github.com/Leifzhang/QRScaner.git"
srcBuild="false" substitute="com.github.leifzhang:QrCodeLibrary" />
</repo>
Copy the code
src: false
modules:
- branch: master
origin: https://github.com/Leifzhang/QRScaner.git
srcBuild: true
name: QRScaner
- branch: master
origin: https://github.com/Leifzhang/QRScaner.git
srcBuild: false
name: Router
Copy the code
Above is the original XML format, and below is the new format I defined with YAML. If you are the user, which format you choose is up to you.
Yaml still smells good from a readability and convenience standpoint. But it’s a little bit harder to parse.
Project learning address GradleTask
conclusion
One o ‘clock, go to bed first. I’ll revise it tomorrow.
Wake up, refill some nonsense, in addition to want to fill the cup and there is no special want to say.