Essential knowledge for Android development: I have a date with Gradle

Essential knowledge for Android development: I have a date with Gradle

0. Tell a story

0.1 Ant, I really thought you were an ant

I really started to get close to programming in 2012. At the end of the year, my boss said that it would take too long to manually build our app when it was released, so we should study the ant script.

At that time, I didn't even know what HashMap was, and I had almost no development experience. A small ant script really made me feel deeply that the world was full of malice. Fortunately, I later figured out what target and other weird things were, otherwise there would be no more.

0.2 Maven, do you really know how to read this word?

Maven /`meivn/

I came into contact with Maven because I read Chen Xionghua's "Spring in Action". His source code was actually built with Maven. As a result, I learned Spring in a mess, but I was able to use Maven smoothly.

Like Ant, Maven can be used to build Java projects. Like Ant, Maven's configuration is described in XML. However, Maven can manage dependencies, which allows you to "get what you want with just one sentence." For example, if I want gson, Maven says yes, just write it down and I'll get it for you when I build it later.

  1. <dependency>
  2. <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId>
  3. <version> 2.4 </version>
  4. </dependency>

You are really the boss. However, Maven is a bit difficult to learn. Many beginners get stuck when setting up the environment. Do you think it is because of the steep learning curve of Maven? Of course not. It is because the central repository of Maven was xed, so you just have to watch the "cannot resolve dependencies" every day.

Later, OSChina got close to Alibaba, and there was maven.oschina.net. When I found a job last year and was thinking about doing something, I found out that maven.oschina.net was probably locked up by Alibaba, and died for a few days, but now it is alive again. So what? Anyway, the incident of the central warehouse being hacked is a thing of the past.

0.3 Gradle, is your father Google? !

In 2013, when I excitedly told the big brother mentioned above that Maven was a good comrade, the big brother said that Google recommended using Gradle. So, I thought, Gradle, is your father Google? Or at least a godfather.

In fact, none of this matters. After all, Gradle is really easy to use. Compared with the XML configuration methods of the previous two, using code directly is definitely more flexible. Not only that, Gradle can actually use Maven warehouse to manage dependencies, just like a simplified version of Maven. If you don’t see the pom file, you would think you are still using Maven (of course, since you are using Maven’s warehouse, you naturally can’t do without Maven). Oh, you are an Ant user, that doesn’t matter, if you don’t believe it, just look:

  1. task helloTAS < < {
  2. ant.echo(message: 'Hello TAS.')
  3. }

1. Build with Gradle

1.1 Project Structure

As shown in the figure, this is an Android gradle project that couldn’t be more ordinary.

The settings.gradle file in the root directory is mainly used to include submodules. For example, if our project has a submodule called app, the content of settings.gradle is as follows:

  1. include ':app'  

·

The build.gradle file in the root directory contains some common configurations that can be used in various submodules.

·

The properties contained in the gradle.properties file will become members of the project's properties. For example, we added the property hello.

·

hello=Hello Tas!

Then we create the task in build.gradle:

  1. task hello < < {
  2. println hello
  3. println project.getProperties().get("hello")
  4. }

The output is the same:

  1. 14:28:11: Executing external task 'hello'...
  2. Configuration on demand is an incubating feature.
  3. :app:hello
  4. Hello Tas!
  5. Hello Tas!
  6.  
  7. BUILD SUCCESSFUL
  8.  
  9. Total time: 0.54 secs
  10. 14:28:12: External task execution finished 'hello'.

· The local.properties file is encountered in Android projects. We usually set the Android SDK and NDK paths in it. Of course, Android Studio will help us set it up. To understand this more clearly, I have extracted part of the source code of the Android Gradle plugin:

SDK.groovy, the following code mainly includes the operation of loading sdk and ndk paths.

  1. private void findLocation() {
  2. if (TEST_SDK_DIR != null) {
  3. androidSdkDir = TEST_SDK_DIR      
  4. return
  5. }
  6.  
  7. def rootDir = project .rootDir
  8. def localProperties = new File(rootDir, FN_LOCAL_PROPERTIES)
  9. if (localProperties.exists()) {
  10. Properties properties = new Properties()
  11. localProperties.withInputStream { instr - >   
  12. properties.load(instr)
  13. }
  14. def sdkDirProp = properties .getProperty('sdk.dir')
  15.  
  16. if (sdkDirProp != null) {
  17. androidSdkDir = new File(sdkDirProp)
  18. } else {
  19. sdkDirProp = properties .getProperty('android.dir')
  20. if (sdkDirProp != null) {
  21. androidSdkDir = new File(rootDir, sdkDirProp)
  22. isPlatformSdk = true  
  23. } else {
  24. throw new RuntimeException(
  25. "No sdk.dir property defined in local.properties file.")
  26. }
  27. }
  28.  
  29. def ndkDirProp = properties .getProperty('ndk.dir')
  30. if (ndkDirProp != null) {
  31. androidNdkDir = new File(ndkDirProp)
  32. }
  33. } else {
  34. String envVar = System .getenv("ANDROID_HOME")
  35. if (envVar != null) {
  36. androidSdkDir = new File(envVar)
  37. } else {
  38. String property = System .getProperty("android.home")
  39. if (property != null) {
  40. androidSdkDir = new File(property)
  41. }
  42. }
  43.  
  44. envVar = System .getenv("ANDROID_NDK_HOME")
  45. if (envVar != null) {
  46. androidNdkDir = new File(envVar)
  47. }
  48. }
  49. }

BasePlugin.groovy, through these two methods, we can get the paths of sdk and ndk in the gradle script

  1. File getSdkDirectory() {
  2. return sdk.sdkDirectory
  3. }
  4. File getNdkDirectory() {
  5. return sdk.ndkDirectory
  6. }

For example:

  1. task hello < < {
  2. println android.getSdkDirectory()
  3. }
  4. 14:37:33: Executing external task 'hello'...
  5. Configuration on demand is an incubating feature
  6. .:app:hello
  7. /Users/benny/Library/Android/sdk
  8.  
  9. BUILD SUCCESSFUL
  10. Total time: 0.782 secs
  11. 14:37:35: External task execution finished 'hello'.

The above is just the most common hierarchy structure. There is also a flat structure. Figure 1 is a flat structure and Figure 2 is a hierarchy structure. If you are interested, you can Google it.

1.2 Several important concepts

The order of appearance in this section is basically the same as that in build.gradle.

1.2.1 Repository and Dependency

If you are just writing Android programs, then the dependency problem may not be so annoying - but if you are writing server-side programs in Java, then it will be a lot of pain and tears.

The emergence of the repository has completely solved this problem. When developing, we only need to know the dependency ID and version. As for where it is stored, I don’t care; what it depends on, the build tool can help us find and fix it in the repository. All this is so natural. How about a latte and let the code build for a while?

It is said that in the history of Java development, many repositories have emerged, but the most popular one is of course Maven. Maven uses groupId and artifactId to lock components, and then configures the version. Then the Maven repository can finally lock a certain version of the component for you to use. For example, in the example at the beginning,

  1. < dependency >  
  2. <groupId> com.google.code.gson </groupId>    <artifactId> gson </artifactId>  
  3. < version > 2.4 </ version >  
  4. </ dependency >  

Maven can help you get gson-2.4.jar with just a few configurations. Not only that, it will also help you get javadoc and source according to your settings. Mom no longer has to worry about me not being able to see the source code of the component.

So where is this magical Maven repository? Maven Central, the central repository, is the originator of the Maven repository. Most other repositories will proxy it and add their own special repositories according to needs. Here are a few concepts:

·

Proxy warehouse: If you want to rent a house, go to Soufang.com. If you want to sign up for a driving school, I am the driving school agent. You find me and I will find the driving school. Here, there is a little difference. Once someone downloads a specific component from the proxy warehouse, the component will be cached by the proxy warehouse, and there is no need to find the proxy warehouse to download it in the future.

·

Private warehouse: Socialism with Chinese characteristics. Go your own way, why do you care about me? There are several hosted warehouses in the company's internal warehouse. These warehouses are unique to our company, and the components in them are also uploaded by our internal colleagues for team development and use.

·

Local warehouse: A hidden treasure hidden in the city. It is very similar to the proxy warehouse, except that this warehouse is stored on your own hard drive.

·

By the way, there is an extra directory under Android SDK, and many dependencies in it are also organized in the form of Maven repositories. But this is a Google feature, and they are so awesome that they don't upload to Maven's central repository. There is really nothing we can do about it.

1.2.2 SourceSets

Source code set, which mainly contains the paths of your various types of code, such as 'src/main/java' and so on.

1.2.3 Properties

As we mentioned earlier, properties are actually properties of gradle. In the gradle source code, we find the Project.java interface and see:

  1. /**
  2. * < p > Determines if this project has the given property. See < a  
  3. href = "#properties" > here </ a > for details of the
  4. * properties which are available for a project. </ p >  
  5. *
  6. * @param propertyName The name of the property to locate.
  7. * @return True if this project has the given property, false otherwise.
  8. */
  9. boolean hasProperty(String propertyName);
  10.  
  11. /**
  12. * < p > Returns the properties of this project. See < a   href = "#properties" > here </ a > for details of the properties which
  13. * are available for a project. </ p >   
  14. *
  15. * @return A map from property name to value.
  16. */
  17. Map < String , ?> getProperties();
  18.  
  19. /**
  20. * < p > Returns the value of the given property. This method locates a property as follows: </ p >   
  21. *
  22. * < ol >   
  23. *
  24. * < li > If this project object has a property with the given name, return the value of the property. </ li >  
  25. *
  26. * < li > If this project has an extension with the given name, return the extension. </ li >  
  27. *
  28. * < li > If this project's convention object has a property with the given name, return the value of the
  29. * property. </ li >   
  30. *
  31. * < li > If this project has an extra property with the given name, return the value of the property. </ li >  
  32. *
  33. * < li > If this project has a task with the given name, return the task. </ li >   
  34. *
  35. * < li > Search up through this project's ancestor projects for a convention property or extra property with the
  36. * given name. </ li >   
  37. *
  38. * < li > If not found, a {@link MissingPropertyException} is thrown. </ li >   
  39. *
  40. * </ ol >   
  41. *
  42. * @param propertyName The name of the property.
  43. * @return The value of the property, possibly null.
  44. * @throws MissingPropertyException When the given property is unknown.
  45. */Object property(String propertyName) throws MissingPropertyException;
  46.  
  47. /**
  48. * < p > Sets a property of this project. This method searches for a property with the given name in the following
  49. * locations, and sets the property on the first location where it finds the property. </ p >   
  50. *
  51. * < ol >   
  52. *
  53. * < li > The project object itself. For example, the < code > rootDir </ code > project property. </ li >   
  54. *
  55. * < li > The project's {@link Convention} object. For example, the < code > srcRootName </ code > java plugin
  56. * property. </ li >   
  57. *
  58. * < li > The project's extra properties. </ li >   
  59. *
  60. * </ ol >   
  61. *
  62. * If the property is not found, a {@link groovy.lang.MissingPropertyException} is thrown.
  63. *
  64. *@param name The name of the property
  65. * @param value The value of the property
  66. */
  67. void setProperty(String name, Object value) throws MissingPropertyException;

It is not difficult to know that properties is actually a map. We can define properties in gradle.properties or through gradle scripts:

  1. setProperty( 'hello' , 'Hello Tas again!' )

We have already mentioned how to use it, so I won’t go into details here.

1.2.4 Project and Task

If you have used ant, then project is basically similar to ant's project tag, and task is similar to ant's target tag.

  1. task hello << {
  2. ......
  3. }

In fact, calling

  1. Task Project.task(String name) throws InvalidUserDataException;

A task is created and the behavior of the task is defined through <<. We can see that the task has the following overloads:

  1. Task task(String name, Closure configureClosure);

So the following definition is also legal:

  1. task('hello2',{
  2. println hello
  3. })

Simply put, project is a logical entity of the entire build project, and task is the specific task point of this project. For more information, please refer to the official website documentation and gradle source code.

2. Release components

To publish components, we still rely on the repository. We still take the Maven repository as an example. Most private repositories use sonatype.

2.1 UI Release

If the administrator has given you this permission, you will see the Upload Artifact tab on the UI. Select the artifact you want to upload, configure the corresponding parameters, and click Upload.

2.2 Using Maven Plugin

This means using Maven's gradle plugin to upload directly during the build process. The built artifacts need to be signed, please download GPG4WIN (windows) or GPGTOOLS (mac) to generate your own key.

Directly on the code:

gradle.properties

  1. sonatypeUsername = your username
  2. sonatypePassword = your password
  3. signing.keyId = your keyid
  4. signing.password = your keypass
  5. #Note that this is usually the path.
  6. # mac/linux
  7. signing.secretKeyRingFile = /Users/yourusername/.gnupg/secring.gpg
  8.  
  9. # Window XP and earlier (XP/2000/NT)
  10. # signing.secretKeyRingFile = C :\\Documents and Settings\\ < username > \\Application Data\\GnuPG\\secring.gpg
  11.  
  12. # Windows Vista and Windows 7
  13. #
  14. signing.secretKeyRingFile = C :\\Users\\ < username > \\AppData\\Roaming\\gnupg\\secring.
  15.  
  16.  
  17. gpgprojectName = your artifact name
  18. group = your component groupid
  19. artifactId = your component artifactid
  20. # Version number, in the form of three digits. If it is an unstable version, be sure to add SNAPSHOT
  21. version = 0.0.1 -SNAPSHOT

build.gradle

  1. apply plugin: 'com.android.library'
  2. apply plugin: 'maven'
  3. apply plugin: 'signing'
  4.  
  5. android {
  6. compileSdkVersion 21
  7. buildToolsVersion "21.1.2"
  8.  
  9. defaultConfig {
  10. minSdkVersion 17
  11. targetSdkVersion 21
  12. versionCode 1
  13. versionName "0.2"
  14. }
  15. buildTypes {
  16. release {
  17. minifyEnabled false
  18. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  19. }
  20. }
  21. }
  22.  
  23. dependencies {
  24. compile fileTree(include: ['*.jar'], dir: 'libs')
  25. ......
  26. }
  27.  
  28. def isSnapshot = version .endsWith('-SNAPSHOT')
  29. def sonatypeRepositoryUrl
  30. if(isSnapshot) {
  31. sonatypeRepositoryUrl =
  32. "http://maven.oa.com/nexus/content/repositories/thirdparty-snapshots/"  
  33. } else {
  34. sonatypeRepositoryUrl = "http://maven.oa.com/nexus/content/repositories/thirdparty/"  
  35. }
  36.  
  37. sourceSets {
  38. main {
  39. java {
  40. srcDir 'src/main/java'
  41. }
  42. }
  43. }
  44. task sourcesJar(type: Jar) {
  45. from sourceSets.main.allSource
  46. classifier = 'sources'  
  47. }
  48.  
  49. artifacts
  50. //archives javadocJar
  51. archives sourcesJar}
  52.  
  53. signing
  54. if(project.hasProperty('signing.keyId') && project.hasProperty('signing.password') &&
  55. project.hasProperty('signing.secretKeyRingFile')) {
  56. sign configurations.archives
  57. } else {
  58. println "Signing information missing/incomplete for ${project.name}"
  59. }
  60. }
  61.  
  62. uploadArchives {
  63. repositories {
  64. mavenDeployer {
  65. if(project.hasProperty('preferedRepo') && project.hasProperty('preferedUsername')
  66. && project.hasProperty('preferredPassword')) {
  67.  
  68. configuration = configurations.archives
  69. repository(url: preferredRepo) {
  70. authentication(userName: preferredUsername, password: preferredPassword)
  71. }
  72. } else if(project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword')) {
  73.  
  74. beforeDeployment { MavenDeployment deployment - > signing.signPom(deployment) }
  75.  
  76. repository(url: sonatypeRepositoryUrl) { authentication(userName: sonatypeUsername, password: sonatypePassword)
  77.  
  78. }
  79. } else {
  80. println "Settings sonatypeUsername/sonatypePassword missing/incomplete for ${project.name}"
  81. }
  82.  
  83. pom.artifactId = artifactId
  84. pom.project {
  85. name projectName
  86. packaging 'aar'
  87.  
  88. developers {
  89. developer {
  90. id 'wecar'
  91. name 'wecar'
  92. }
  93. }
  94. }
  95. }
  96. }
  97. }

Then run gradle uploadArchives to publish the packaged aar to the company's Maven repository. The method of jar package is similar and will not be listed here.

2.3 Using Maven Commands

This can be done by publishing the component directly in cmdline through mvn. Command instructions:

  1. mvn deploy:deploy-file -Durl = file ://C:\m2-repo \ -DrepositoryId = some .id \
  2. -Dfile = your -artifact-1.0.jar \
  3. [ -DpomFile = your -pom.xml] \
  4. [ -DgroupId = org .some.group] \
  5. [ -DartifactId = your -artifact] \
  6. [ -Dversion = 1.0 ] \
  7. [ -Dpackaging = jar ] \
  8. [ -Dclassifier = test ] \
  9. [ -DgeneratePom = true ] \
  10. [ -DgeneratePom.description = "My Project Description" ] \ [ -DrepositoryLayout = legacy ] \
  11. [ -DuniqueVersion = false ]

Of course, there is still an authentication problem here. We need to add it to the Maven settings configuration first:

  1. < servers >  
  2. < server >  
  3. < id > Maven.oa.com </ id >  
  4. <username> rdm </username>  
  5. <password> rdm </password>  
  6. </ server >  
  7. </ servers >  

Then we can upload it using the command:

  1. mvn deploy :deploy-file -DgroupId = com.tencent.test -DartifactId = test   
  2. -Dversion = 1.0.0 -Dpackaging = aar   -Dfile = test.aar
  3. -Durl = http ://maven.oa.com/nexus/content/repositories/thirdparty
  4. -DrepositoryId = Maven.oa.com

3. Plugins

3.1 What is a plugin?

Plugins are actually used to make us lazy. Without plugins, if we want to build a Java project, we have to define sourceSets, classpath, build steps, etc. by ourselves.

Simply put, a plugin is actually a collection of configurations and tasks.

There are three main forms of gradle plugins:

gradle file, you can write a plugin in your build.gradle to import it directly:

  1. apply plugin: GreetingPlugin
  2.  
  3. class GreetingPlugin implements Plugin < Project {
  4. void apply(Project project) {
  5. project.task('hello') < < {
  6. println "Hello from the GreetingPlugin"
  7. }
  8. }
  9. }

·

The buildSrc project is a standard Groovy plug-in project under the root directory of your project. The directory is buildSrc, and you can directly reference the plug-ins written in it.

·

Independent projects are structurally the same as buildSrc projects, except that they need to be referenced by publishing to a repository. Usually, the plugins we come into contact with are in this form.

·

For details, please refer to: Chapter 61. Writing Custom Plugins

3.2 Common plugins

The plug-ins currently available are as follows:

java, build java projects

war, used to publish war packages and build web projects

groovy, build groovy projects

com.android.application, build Android app project

com.android.library, builds Android library, usually outputs aar

sign

Maven, publish to the Maven repository

org.jetbrains.intellij, build IntelliJ plugin project

3.3 Write a plugin yourself

Create a normal groovy project (it doesn't matter if it's a java project), create a src/main/groovy directory, and write the following code:

  1. package com.tencent.wecar.plugin
  2.  
  3. import org.gradle.api.Plugin
  4. import org.gradle.api.internal.project.ProjectInternal
  5.  
  6. class GreetingPlugin implements Plugin < ProjectInternal > {
  7. void apply(ProjectInternal project) {
  8. project.task('hello') < < {
  9. println 'hello'
  10. }
  11. }
  12. }

Create a META-INF/gradle-plugins directory in src/main/resources and create a greetings.properties file:

  1. implementation -class = com.tencent.wecar.plugin.GreetingPlugin

Where greettings is your plugin id.

build.gradle

  1. group 'com.tencent.wecar.plugin'
  2. version '1.1-SNAPSHOT'
  3. buildscript {
  4. repositories {
  5. mavenLocal()
  6. }
  7. }
  8.  
  9. apply plugin: 'groovy'
  10. apply plugin: 'java'
  11.  
  12. repositories {
  13. mavenCentral()
  14. }
  15.  
  16. sourceSets {
  17. main {
  18. groovy {
  19. srcDirs = [
  20. 'src/main/groovy',
  21. 'src/main/java'
  22. ]
  23. } // compile everything in src/ with groovy
  24. java { srcDirs = []}// no source dirs for the java compiler
  25. }
  26. }
  27.  
  28. dependencies {
  29. //tasks.withType(Compile) { options.encoding = "UTF-8" }
  30. compile gradleApi()
  31. }
  32.  
  33. // custom tasks for creating source jars
  34. task sourcesJar(type: Jar, dependsOn:classes) {
  35. classifier = 'sources'  
  36. from sourceSets.main.allSource
  37. }
  38.  
  39. // add source jar tasks as artifacts
  40. artifacts { archives sourcesJar }
  41.  
  42. // upload to local
  43. uploadArchives {
  44. repositories{
  45. mavenLocal()
  46. }
  47. }

Run uploadArchives to publish to the local repository, then you can find your own plug-in. Since no artifactId is specified, the artifactId of our plug-in is the name of our project, such as deployplugin here.

So how do we introduce this plugin?

First, add dependencies to buildScript:

  1. buildscript {
  2. repositories {
  3. mavenLocal()
  4. }
  5. dependencies {
  6. classpath 'com.tencent.wecar.plugin:deployplugin:1.1-SNAPSHOT'
  7. }
  8. }

Then:

  1. apply plugin: 'greetings'

In this way, our task "hello" is introduced.

4. Gradle runs slowly?

Friends who have used Gradle will feel that it is sometimes slow. We can speed up your Gradle through the following three methods.

Don't use central repository. If your repository is configured with mavenCentral, leave it alone. People all over the world are trying to abuse it, so don't get involved. Try jCenter.

· Upgrade the latest Gradle version. Currently the latest version is 2.4. Android Studio uses Gradle 2.4 by default since 1.3.

· Turn on Gradle's electric motor. In gradle.properties (look familiar? Yes, that's it!!)

Add the following configuration inside:

If your task has no timing requirements, then turning on this option can process multiple tasks concurrently and make full use of hardware resources. Well, if you have a single-core CPU. Just ignore what I said. org.gradle.parallel=true This can also be started in the command line as a parameter and is valid for 3 hours. The daemon process can greatly shorten the compilation time org.gradle.daemon=true It depends on the requirements. Gradle runs on the Java virtual machine. This specifies that the heap memory of this virtual machine is initialized to 256M and the maximum is 1G. If you only have 2G of memory, just ignore what I said. org.gradle.jvmargs=-Xms256m -Xmx1024m

Of course, the recommended way is to create a gradle.properties file under .gradle/ in your user directory to avoid pitfalls for your teammates. . .

For more information, please follow our WeChat ID: weixinBugly or search for our WeChat ID: weixinBugly.

Tencent Bugly

Bugly is an external version of Tencent's internal product quality monitoring platform, supporting the two major platforms of iOS and Android. Its main function is to monitor and report crashes and freezes that occur on the user side after the App is released, so that developers can understand the quality of the App in real time and make timely modifications. Currently, all Tencent's internal products are using it to monitor the crash of online products.

The internal team of Tencent spent 4 years to polish it. Currently, all internal products of Tencent are using it, basically covering the mobile devices and network environments in the Chinese market, and the reliability is guaranteed. When using Bugly, you use the same quality assurance method as mobile QQ, QQ Space, and Mobile Manager.

<<:  iOS 9.2 released, 360 found 5 security vulnerabilities and was thanked

>>:  Weekly crooked review: The verification code of your hometown is waving at you. Your willfulness is all because of your aloofness.

Recommend

The auto parts giant sounded the death knell for the engine

The global production capacity of 100 million set...

Baidu information flow account opening, Baidu information flow display

Baidu information flow account opening generally ...

Remember the rhino monster in Journey to the West? The Ice Age really existed!

In the vast river of evolution The rhino family h...

Baidu Marketing 618 Delivery Guide and Plan

618 is coming and the battle is about to begin. H...

Are personal websites not allowed to post advertisements?

Question: My personal website has traffic but is ...

A large collection of promotion channels, see which ones you need!

1 WeChat is a semi-closed circle. “Good wine need...

iPhones shut down due to freezing? Apple responds: It's too cold

This cold wave was so severe that it affected eve...

Ten thousand words interpretation: The top ten brand marketing keywords in 2019!

Looking back at the marketing industry in 2019, I...

Spring marketing, advanced branding strategies!

The annual "Spring Limited" marketing c...

Three popular command line file conversion tools under Linux

【51CTO.com Quick Translation】Recently, a friend n...

2019 Bilibili product operation analysis report!

bilibili, also known as Bilibili or simply B Stat...