{"id":73701,"date":"2023-12-13T15:29:58","date_gmt":"2023-12-13T07:29:58","guid":{"rendered":"https:\/\/version-2.com\/?p=73701"},"modified":"2023-11-27T15:34:44","modified_gmt":"2023-11-27T07:34:44","slug":"improving-nordvpns-android-performance-using-the-compose-compiler-plugin","status":"publish","type":"post","link":"https:\/\/version-2.com\/en\/2023\/12\/improving-nordvpns-android-performance-using-the-compose-compiler-plugin\/","title":{"rendered":"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin"},"content":{"rendered":"<div data-elementor-type=\"wp-post\" data-elementor-id=\"73701\" class=\"elementor elementor-73701\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-35fe5dd post-content elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"35fe5dd\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[{&quot;jet_parallax_layout_image&quot;:{&quot;url&quot;:&quot;&quot;,&quot;id&quot;:&quot;&quot;,&quot;size&quot;:&quot;&quot;},&quot;_id&quot;:&quot;cef08c3&quot;,&quot;jet_parallax_layout_image_tablet&quot;:{&quot;url&quot;:&quot;&quot;,&quot;id&quot;:&quot;&quot;,&quot;size&quot;:&quot;&quot;},&quot;jet_parallax_layout_image_mobile&quot;:{&quot;url&quot;:&quot;&quot;,&quot;id&quot;:&quot;&quot;,&quot;size&quot;:&quot;&quot;},&quot;jet_parallax_layout_speed&quot;:{&quot;unit&quot;:&quot;%&quot;,&quot;size&quot;:50,&quot;sizes&quot;:[]},&quot;jet_parallax_layout_type&quot;:&quot;scroll&quot;,&quot;jet_parallax_layout_direction&quot;:&quot;1&quot;,&quot;jet_parallax_layout_fx_direction&quot;:null,&quot;jet_parallax_layout_z_index&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_x&quot;:50,&quot;jet_parallax_layout_bg_x_tablet&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_x_mobile&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_y&quot;:50,&quot;jet_parallax_layout_bg_y_tablet&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_y_mobile&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_size&quot;:&quot;auto&quot;,&quot;jet_parallax_layout_bg_size_tablet&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_size_mobile&quot;:&quot;&quot;,&quot;jet_parallax_layout_animation_prop&quot;:&quot;transform&quot;,&quot;jet_parallax_layout_on&quot;:[&quot;desktop&quot;,&quot;tablet&quot;]}]}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-409a2e9a\" data-id=\"409a2e9a\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-5a8be8f elementor-widget elementor-widget-text-editor\" data-id=\"5a8be8f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<div class=\"max-w-post-content\"><div class=\"my-32\">At NordVPN, we&#8217;ve harnessed the power of Jetpack Compose and its feature-rich capabilities to craft the user interface of our Android app. While Compose has accelerated our feature development workflow, our commitment to product quality drives us above all. We\u2019re continuously exploring strategies for maintaining optimal performance.<\/div><div><img fetchpriority=\"high\" decoding=\"async\" class=\"alignnone size-full\" src=\"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/featured-image.png\" width=\"1200\" height=\"675\" \/><\/div><div class=\"my-64 text-center\"><p><span style=\"box-sizing: border-box; display: inline-block; overflow: hidden; width: initial; height: initial; background: none; opacity: 1; border: 0px; margin: 0px; padding: 0px; position: relative; max-width: 100%;\"><span style=\"box-sizing: border-box; display: block; width: initial; height: initial; background: none; opacity: 1; border: 0px; margin: 0px; padding: 0px; max-width: 100%;\"><img style=\"display: block; max-width: 100%; width: initial; height: initial; background: none; opacity: 1; border: 0px; margin: 0px; padding: 0px;\" alt=\"\" aria-hidden=\"true\" \/><\/span><span style=\"background-color: initial;\">The new Compose integration has sparked a lot of internal discussion regarding the app\u2019s performance. One of the things we at NordVPN do is to integrate the <\/span><a class=\"cursor-pointer outline-none transition-colors duration-250 ease-out text-blue-500 hover:text-blue-600 focus:text-blue-400\" style=\"background-color: initial;\" href=\"https:\/\/github.com\/androidx\/androidx\/blob\/androidx-main\/compose\/compiler\/design\/compiler-metrics.md\" target=\"_blank\" rel=\"noopener noreferrer\">Compose Compiler plugin<\/a><span style=\"background-color: initial;\"> in our day-to-day development process, which helps us improve our code. In this blog post, we explain what the Compose Compiler plugin is, how it works, and how we use it at <\/span><a class=\"cursor-pointer outline-none transition-colors duration-250 ease-out text-blue-500 hover:text-blue-600 focus:text-blue-400\" style=\"background-color: initial;\" href=\"https:\/\/nordvpn.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">NordVPN<\/a><span style=\"background-color: initial;\">.<\/span><\/span><\/p><\/div><div class=\"style_postContent__uJLGT text-black break-words\" data-content-type-blog=\"true\"><div class=\"Article_wrapper__YyQ2_\"><h2 id=\"What problems did we have?\" class=\"text-h2 leading-none text-left\"><b>What problems did we have?<\/b><\/h2><p class=\"text-base leading-normal\">When working with Jetpack Compose, we encountered performance issues related to the stability of composable functions. Our primary goal in working with composable functions was to make them as stable as possible, or &#8220;skippable&#8221; in Compose terminology. In this context, &#8220;skippable&#8221; means that if a composable function is recomposed and its parameters haven&#8217;t changed since the previous recomposition, Compose will skip the function and reuse the previous values.<\/p><p class=\"text-base leading-normal\">With Compose, we can see significant performance improvements because even minor improvements can reduce recomposition count levels throughout the app. To assess the stability of composable functions, we can leverage the Compose Compiler plugin.<\/p><h2 id=\"The Compose Compiler plugin\" class=\"text-h2 leading-none text-left\"><b>The Compose Compiler plugin<\/b><\/h2><p class=\"text-base leading-normal\">Compose Compiler is a plugin that can generate reports and metrics for components or code written in Compose. These reports provide detailed insights into the behavior of our Compose code. The plugin was added in version 1.2 of the Compose library.<\/p><p class=\"text-base leading-normal\">With this detailed insight, we can begin making improvements to our code.<\/p><h2 id=\"How does it work?\" class=\"text-h2 leading-none text-left\"><b>How does it work?<\/b><\/h2><p class=\"text-base leading-normal\">The Compose Compiler plugin is a Gradle task that generates reports for composable code within a module. It assesses the stability of the code, offering the flexibility to run it locally or in a CI pipeline when necessary.<\/p><blockquote class=\"pl-32 border-gray-300 border-l text-gray-600 my-64\"><p class=\"text-base leading-normal\">It is recommended to generate the report in Release builds.<\/p><\/blockquote><p class=\"text-base leading-normal\">To ensure that the plugin works perfectly, we first need to configure it in the project&#8217;s Gradle file.<\/p><div class=\"border-gray-300 border mb-32\"><div class=\"bg-gray-200 p-24 border-b border-gray-300\"><div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">1<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\">tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">2<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> compilerOptions {<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">3<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> if (project.findProperty(&#8220;nordvpn-app.enableComposeCompilerReports&#8221;) == &#8220;true&#8221;) {<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">4<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> freeCompilerArgs.addAll([<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">5<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> &#8220;-P&#8221;,<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">6<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> &#8220;plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=&#8221; +<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">7<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> project.buildDir.absolutePath + &#8220;\/compose_metrics&#8221;<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">8<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> ])<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">9<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> freeCompilerArgs.addAll([<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">10<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> &#8220;-P&#8221;,<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">11<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> &#8220;plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=&#8221; +<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">12<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> project.buildDir.absolutePath + &#8220;\/compose_metrics&#8221;<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">13<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> ])<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">14<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> }<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">15<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> }<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">16<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> }<\/span><\/div><\/div><pre class=\"text-small overflow-auto\">\u00a0<\/pre><\/div><\/div><p><button class=\"m-24 focus:outline-none focus-visible:underline\" type=\"button\"><span class=\"text-micro leading-normal text-left flex flex-nowrap items-center font-bold duration-250 text-blue-500 hover:text-blue-600 cursor-pointer\"><span class=\"mr-8\">Copy snippet<\/span><\/span><\/button><\/p><\/div><p class=\"text-base leading-normal\">The first part of the code generates <b>reports<\/b>, while the second one generates <b>metrics<\/b> for those reports.<\/p><p class=\"text-base leading-normal\">To run this code, we use the Gradle command as follows:<\/p><div class=\"border-gray-300 border mb-32\"><div class=\"bg-gray-200 p-24 border-b border-gray-300\"><div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">1<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\">.\/gradlew assembleRelease -Pnordvpn-app.enableComposeCompilerReports=true<\/span><\/div><\/div><pre class=\"text-small overflow-auto\">\u00a0<\/pre><\/div><\/div><p><button class=\"m-24 focus:outline-none focus-visible:underline\" type=\"button\"><span class=\"text-micro leading-normal text-left flex flex-nowrap items-center font-bold duration-250 text-blue-500 hover:text-blue-600 cursor-pointer\"><span class=\"mr-8\">Copy snippet<\/span><\/span><\/button><\/p><\/div><blockquote class=\"pl-32 border-gray-300 border-l text-gray-600 my-64\"><p class=\"text-base leading-normal\">Generating a report on release builds is recommended.<\/p><\/blockquote><p class=\"text-base leading-normal\">When the execution is completed, it generates a file in the <b>build<\/b> folder like the one below.<\/p><p><img decoding=\"async\" class=\"alignnone size-full\" src=\"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/Inblog_2.png\" width=\"1200\" height=\"421\" \/><\/p><div class=\"my-64 text-center\"><p><span style=\"box-sizing: border-box; display: inline-block; overflow: hidden; width: initial; height: initial; background: none; opacity: 1; border: 0px; margin: 0px; padding: 0px; position: relative; max-width: 100%;\"><span style=\"box-sizing: border-box; display: block; width: initial; height: initial; background: none; opacity: 1; border: 0px; margin: 0px; padding: 0px; max-width: 100%;\"><img style=\"display: block; max-width: 100%; width: initial; height: initial; background: none; opacity: 1; border: 0px; margin: 0px; padding: 0px;\" alt=\"\" aria-hidden=\"true\" \/><\/span><\/span>compose_generated_report folder<\/p><\/div><p class=\"text-base leading-normal\">Where,<\/p><p class=\"text-base leading-normal\">*-classes.txt: contains information about classes referenced from a composable function.<\/p><p class=\"text-base leading-normal\">*-composables.csv: CSV version of the TXT file<\/p><p class=\"text-base leading-normal\">*-composables.txt: contains a detailed output of each Composable.<\/p><p class=\"text-base leading-normal\">*-module.json: provides detailed statistics as a comprehensive view.<\/p><p class=\"text-base leading-normal\">In our case, we\u2019re primarily focused on the *-composables.txt files and will be working with those.<\/p><p class=\"text-base leading-normal\">The image above displays the generated value for only one module. However, for NordVPN, we have multiple UI modules, and each module generates its own compose_metrics folder (that has its Compose code) with all relevant reports included.<\/p><h2 id=\"Refining the generated report\" class=\"text-h2 leading-none text-left\"><b>Refining the generated report<\/b><\/h2><p class=\"text-base leading-normal\">With all of our modules generating reports, here\u2019s an example of how an individual -composables.txt file can contain multiple blocks of code like:<\/p><div class=\"border-gray-300 border mb-32\"><div class=\"bg-gray-200 p-24 border-b border-gray-300\"><div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">1<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\">restartable scheme(&#8220;[androidx.compose.ui.UiComposable]&#8221;) fun ScreenContent(<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">2<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> stable onBack: Function0&lt;Unit&gt;<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">3<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> stable onSettingToggled: Function0&lt;Unit&gt;<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">4<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> unstable state: State?<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">5<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\"> stable modifier: Modifier? = @static Companion<\/span><\/div><\/div><div class=\"table-row text-black\"><p><span class=\"pr-32 text-gray-600 table-cell text-right select-none\">6<\/span><\/p><div class=\"table-cell\"><span class=\"text-black\">}<\/span><\/div><\/div><pre class=\"text-small overflow-auto\">\u00a0<\/pre><\/div><\/div><p><button class=\"m-24 focus:outline-none focus-visible:underline\" type=\"button\"><span class=\"text-micro leading-normal text-left flex flex-nowrap items-center font-bold duration-250 text-blue-500 hover:text-blue-600 cursor-pointer\"><span class=\"mr-8\">Copy snippet<\/span><\/span><\/button><\/p><\/div><p class=\"text-base leading-normal\">Each of these files contains numerous functions that exhibit a Kotlin-style code structure. Additionally, each module with Compose code has a dedicated text file. Before delving into the details, let&#8217;s take a closer look at the significance of this code:<\/p><p class=\"text-base leading-normal\">Restartable: When Compose detects changes in the function inputs, it restarts the function, invoking it again with the updated inputs.<\/p><p class=\"text-base leading-normal\">Stable: This parameter in the provided function is stable; if they have not changed, Compose will skip it.<\/p><p class=\"text-base leading-normal\">Unstable: This parameter in the provided function is unstable and Compose always recomposes it when the parent is recomposed.<\/p><p class=\"text-base leading-normal\">We then merge all the *-composables.txt files into a single text file within our project using a script we&#8217;ve created for this purpose. This combined file plays a crucial role in our development process. Let\u2019s see how we utilize it.<\/p><h2 id=\"How do we use it in our day-to-day development?\" class=\"text-h2 leading-none text-left\"><b>How do we use it in our day-to-day development?<\/b><\/h2><p class=\"text-base leading-normal\">At NordVPN, we&#8217;ve seamlessly integrated this workflow into our CI pipeline for every pull request we create, ensuring that we merge only stable Compose code (whenever possible) into our main branches.<\/p><p class=\"text-base leading-normal\">However, before implementing this process, we conduct a thorough review to ensure that all of our Composable code contains no unnecessary unstable parameters. This proactive step guarantees that when we introduce this to our pull request flow, we initiate with a clean slate.<\/p><h2 id=\"Let's take a closer look at the steps in our pull request workflow:\" class=\"text-h2 leading-none text-left\"><b>Let&#8217;s take a closer look at the steps in our pull request workflow:<\/b><\/h2><p class=\"text-base leading-normal\">1. <b>Create pull request<\/b>: The process begins with the creation of a pull request (PR).<br \/>2. <b>CI job<\/b>: A Continuous integration (CI) job is triggered for the current PR if there\u2019s a change in any of the UI modules. The CI job performs several tasks:<br \/>a. <b>Generate report<\/b>: We generate a report on the release branch, which results in the creation of multiple text files in each module containing Compose code.<br \/>b.<b> Merge the text files<\/b>: At this stage, we execute a script that combines these text files, retaining only the functions containing unstable parameters.<br \/>c. <b>Create a markdown table<\/b>: Next, we create a markdown table that lists the function names along with their associated unstable parameters.<\/p><p><img decoding=\"async\" class=\"alignnone size-full\" src=\"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/Inblog_3.png\" width=\"1200\" height=\"421\" \/><\/p><div class=\"my-64 text-center\"><p class=\"text-nano leading-normal text-left text-center text-gray-600 my-8\">Output Markdown table<\/p><\/div><p class=\"text-base leading-normal\">d. <b>Post comment<\/b>: We post this markdown table as a comment within the PR. This informs developers about any potential instability introduced in the PR.<br \/>e. <b>Fix<\/b>: If instability issues are identified, we proceed to fix the affected functions and commit the changes.<\/p><p class=\"text-base leading-normal\">The entire process is then rerun, and if the unstable parameter issues have been addressed, no further comment will be posted. Any previous comments on the matter can be resolved.<\/p><p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full\" src=\"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/Inblog_1.png\" width=\"1200\" height=\"352\" \/><\/p><div class=\"my-64 text-center\"><p class=\"text-nano leading-normal text-left text-center text-gray-600 my-8\">Execution of the CI pipeline<\/p><\/div><p class=\"text-base leading-normal\">This practice aids us in utilizing the Compose Compiler plugin as a lint check for our Composable code, which maintains coding standards and contributes to improved performance for the <a class=\"cursor-pointer outline-none transition-colors duration-250 ease-out text-blue-500 hover:text-blue-600 focus:text-blue-400\" href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.nordvpn.android&amp;hl=\" target=\"_blank\" rel=\"noopener noreferrer\">NordVPN Android app<\/a>.<\/p><\/div><\/div><\/div>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-cf03edf elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"cf03edf\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-b576c6d\" data-id=\"b576c6d\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-bfd91ca elementor-widget elementor-widget-shortcode\" data-id=\"bfd91ca\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">\t\t<div data-elementor-type=\"page\" data-elementor-id=\"18103\" class=\"elementor elementor-18103\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-748947f elementor-section-full_width elementor-section-height-default elementor-section-height-default\" data-id=\"748947f\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[{&quot;jet_parallax_layout_image&quot;:{&quot;url&quot;:&quot;&quot;,&quot;id&quot;:&quot;&quot;,&quot;size&quot;:&quot;&quot;},&quot;_id&quot;:&quot;c4f773e&quot;,&quot;jet_parallax_layout_image_tablet&quot;:{&quot;url&quot;:&quot;&quot;,&quot;id&quot;:&quot;&quot;,&quot;size&quot;:&quot;&quot;},&quot;jet_parallax_layout_image_mobile&quot;:{&quot;url&quot;:&quot;&quot;,&quot;id&quot;:&quot;&quot;,&quot;size&quot;:&quot;&quot;},&quot;jet_parallax_layout_speed&quot;:{&quot;unit&quot;:&quot;%&quot;,&quot;size&quot;:50,&quot;sizes&quot;:[]},&quot;jet_parallax_layout_type&quot;:&quot;scroll&quot;,&quot;jet_parallax_layout_direction&quot;:&quot;1&quot;,&quot;jet_parallax_layout_fx_direction&quot;:null,&quot;jet_parallax_layout_z_index&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_x&quot;:50,&quot;jet_parallax_layout_bg_x_tablet&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_x_mobile&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_y&quot;:50,&quot;jet_parallax_layout_bg_y_tablet&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_y_mobile&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_size&quot;:&quot;auto&quot;,&quot;jet_parallax_layout_bg_size_tablet&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_size_mobile&quot;:&quot;&quot;,&quot;jet_parallax_layout_animation_prop&quot;:&quot;transform&quot;,&quot;jet_parallax_layout_on&quot;:[&quot;desktop&quot;,&quot;tablet&quot;]}]}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-7995c19\" data-id=\"7995c19\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-a437045 elementor-widget elementor-widget-image-box\" data-id=\"a437045\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image-box.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"elementor-image-box-wrapper\"><div class=\"elementor-image-box-content\"><h3 class=\"elementor-image-box-title\">About Version 2 Digital<\/h3><p class=\"elementor-image-box-description\">Version 2 Digital is one of the most dynamic IT companies in Asia. The company distributes a wide range of IT products across various areas including cyber security, cloud, data protection, end points, infrastructures, system monitoring, storage, networking, business productivity and communication products.\n<br><br>\nThrough an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, different vertical industries, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.<\/p><\/div><\/div>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t\n\t\t<div data-elementor-type=\"page\" data-elementor-id=\"63561\" class=\"elementor elementor-63561\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-1b6aa2c4 elementor-section-full_width elementor-section-height-default elementor-section-height-default\" data-id=\"1b6aa2c4\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[{&quot;_id&quot;:&quot;c4f773e&quot;,&quot;jet_parallax_layout_image&quot;:{&quot;url&quot;:&quot;&quot;,&quot;id&quot;:&quot;&quot;,&quot;size&quot;:&quot;&quot;},&quot;jet_parallax_layout_image_tablet&quot;:{&quot;url&quot;:&quot;&quot;,&quot;id&quot;:&quot;&quot;,&quot;size&quot;:&quot;&quot;},&quot;jet_parallax_layout_image_mobile&quot;:{&quot;url&quot;:&quot;&quot;,&quot;id&quot;:&quot;&quot;,&quot;size&quot;:&quot;&quot;},&quot;jet_parallax_layout_speed&quot;:{&quot;unit&quot;:&quot;%&quot;,&quot;size&quot;:50,&quot;sizes&quot;:[]},&quot;jet_parallax_layout_type&quot;:&quot;scroll&quot;,&quot;jet_parallax_layout_direction&quot;:&quot;1&quot;,&quot;jet_parallax_layout_fx_direction&quot;:null,&quot;jet_parallax_layout_z_index&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_x&quot;:50,&quot;jet_parallax_layout_bg_x_tablet&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_x_mobile&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_y&quot;:50,&quot;jet_parallax_layout_bg_y_tablet&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_y_mobile&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_size&quot;:&quot;auto&quot;,&quot;jet_parallax_layout_bg_size_tablet&quot;:&quot;&quot;,&quot;jet_parallax_layout_bg_size_mobile&quot;:&quot;&quot;,&quot;jet_parallax_layout_animation_prop&quot;:&quot;transform&quot;,&quot;jet_parallax_layout_on&quot;:[&quot;desktop&quot;,&quot;tablet&quot;]}]}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-1b283ee5\" data-id=\"1b283ee5\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-4e466f1a elementor-widget elementor-widget-text-editor\" data-id=\"4e466f1a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p><strong>About NordLayer<br \/><\/strong>NordLayer is an adaptive network access security solution for modern businesses \u2013 from the world\u2019s most trusted cybersecurity brand, Nord Security.<\/p><p>The web has become a chaotic space where safety and trust have been compromised by cybercrime and data protection issues. Therefore, our team has a global mission to shape a more trusted and peaceful online future for people everywhere.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>At NordVPN, we&#8217;ve harnessed the power of Jetpack  [&hellip;]<\/p>","protected":false},"author":149011790,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":true},"categories":[973,1075,1130,61],"tags":[974,1076,1132],"class_list":["post-73701","post","type-post","status-publish","format-standard","hentry","category-nord-security","category-year2023","category-nordlayer","category-press-release","tag-nord-security","tag-1076","tag-nordlayer"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Improving NordVPN\u2019s Android performance using the Compose Compiler plugin - Version 2<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin - Version 2\" \/>\n<meta property=\"og:description\" content=\"At NordVPN, we&#8217;ve harnessed the power of Jetpack [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler\" \/>\n<meta property=\"og:site_name\" content=\"Version 2\" \/>\n<meta property=\"article:published_time\" content=\"2023-12-13T07:29:58+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/featured-image.png\" \/>\n<meta name=\"author\" content=\"tracylamv2\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"tracylamv2\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/nordsecurity.com\\\/blog\\\/improving-nordvpn-android-compose-compiler#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/version-2.com\\\/2023\\\/12\\\/improving-nordvpns-android-performance-using-the-compose-compiler-plugin\\\/\"},\"author\":{\"name\":\"tracylamv2\",\"@id\":\"https:\\\/\\\/version-2.com\\\/zh\\\/#\\\/schema\\\/person\\\/011bc7c3731c930bcfeecd52fefb6365\"},\"headline\":\"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin\",\"datePublished\":\"2023-12-13T07:29:58+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/version-2.com\\\/2023\\\/12\\\/improving-nordvpns-android-performance-using-the-compose-compiler-plugin\\\/\"},\"wordCount\":1095,\"publisher\":{\"@id\":\"https:\\\/\\\/version-2.com\\\/zh\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/nordsecurity.com\\\/blog\\\/improving-nordvpn-android-compose-compiler#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/res.cloudinary.com\\\/nordsec\\\/f_auto,c_limit,w_1200,q_auto\\\/v1\\\/nord-security-web\\\/blog\\\/categories\\\/engineering\\\/2023\\\/11\\\/featured-image.png\",\"keywords\":[\"Nord Security\",\"2023\",\"NordLayer\"],\"articleSection\":[\"Nord Security\",\"2023\",\"NordLayer\",\"Press Release\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/version-2.com\\\/2023\\\/12\\\/improving-nordvpns-android-performance-using-the-compose-compiler-plugin\\\/\",\"url\":\"https:\\\/\\\/nordsecurity.com\\\/blog\\\/improving-nordvpn-android-compose-compiler\",\"name\":\"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin - Version 2\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/version-2.com\\\/zh\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/nordsecurity.com\\\/blog\\\/improving-nordvpn-android-compose-compiler#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/nordsecurity.com\\\/blog\\\/improving-nordvpn-android-compose-compiler#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/res.cloudinary.com\\\/nordsec\\\/f_auto,c_limit,w_1200,q_auto\\\/v1\\\/nord-security-web\\\/blog\\\/categories\\\/engineering\\\/2023\\\/11\\\/featured-image.png\",\"datePublished\":\"2023-12-13T07:29:58+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/nordsecurity.com\\\/blog\\\/improving-nordvpn-android-compose-compiler#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/nordsecurity.com\\\/blog\\\/improving-nordvpn-android-compose-compiler\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/nordsecurity.com\\\/blog\\\/improving-nordvpn-android-compose-compiler#primaryimage\",\"url\":\"https:\\\/\\\/res.cloudinary.com\\\/nordsec\\\/f_auto,c_limit,w_1200,q_auto\\\/v1\\\/nord-security-web\\\/blog\\\/categories\\\/engineering\\\/2023\\\/11\\\/featured-image.png\",\"contentUrl\":\"https:\\\/\\\/res.cloudinary.com\\\/nordsec\\\/f_auto,c_limit,w_1200,q_auto\\\/v1\\\/nord-security-web\\\/blog\\\/categories\\\/engineering\\\/2023\\\/11\\\/featured-image.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/nordsecurity.com\\\/blog\\\/improving-nordvpn-android-compose-compiler#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9801\",\"item\":\"https:\\\/\\\/version-2.com\\\/zh\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/version-2.com\\\/zh\\\/#website\",\"url\":\"https:\\\/\\\/version-2.com\\\/zh\\\/\",\"name\":\"Version 2\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/version-2.com\\\/zh\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/version-2.com\\\/zh\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/version-2.com\\\/zh\\\/#organization\",\"name\":\"Version 2\",\"url\":\"https:\\\/\\\/version-2.com\\\/zh\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/version-2.com\\\/zh\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/version-2.com\\\/wp-content\\\/uploads\\\/2020\\\/08\\\/v2-hk-hor-4.png?fit=1795%2C335&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/version-2.com\\\/wp-content\\\/uploads\\\/2020\\\/08\\\/v2-hk-hor-4.png?fit=1795%2C335&ssl=1\",\"width\":1795,\"height\":335,\"caption\":\"Version 2\"},\"image\":{\"@id\":\"https:\\\/\\\/version-2.com\\\/zh\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/version-2.com\\\/zh\\\/#\\\/schema\\\/person\\\/011bc7c3731c930bcfeecd52fefb6365\",\"name\":\"tracylamv2\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/9d01d79cbfd8b2e878f5d701a362cc9fca466d33fec977b59706c23c1a2db15c?s=96&d=identicon&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/9d01d79cbfd8b2e878f5d701a362cc9fca466d33fec977b59706c23c1a2db15c?s=96&d=identicon&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/9d01d79cbfd8b2e878f5d701a362cc9fca466d33fec977b59706c23c1a2db15c?s=96&d=identicon&r=g\",\"caption\":\"tracylamv2\"},\"url\":\"https:\\\/\\\/version-2.com\\\/en\\\/author\\\/tracylamv2\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin - Version 2","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler","og_locale":"en_US","og_type":"article","og_title":"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin - Version 2","og_description":"At NordVPN, we&#8217;ve harnessed the power of Jetpack [&hellip;]","og_url":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler","og_site_name":"Version 2","article_published_time":"2023-12-13T07:29:58+00:00","og_image":[{"url":"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/featured-image.png","type":"","width":"","height":""}],"author":"tracylamv2","twitter_card":"summary_large_image","twitter_misc":{"Written by":"tracylamv2","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler#article","isPartOf":{"@id":"https:\/\/version-2.com\/2023\/12\/improving-nordvpns-android-performance-using-the-compose-compiler-plugin\/"},"author":{"name":"tracylamv2","@id":"https:\/\/version-2.com\/zh\/#\/schema\/person\/011bc7c3731c930bcfeecd52fefb6365"},"headline":"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin","datePublished":"2023-12-13T07:29:58+00:00","mainEntityOfPage":{"@id":"https:\/\/version-2.com\/2023\/12\/improving-nordvpns-android-performance-using-the-compose-compiler-plugin\/"},"wordCount":1095,"publisher":{"@id":"https:\/\/version-2.com\/zh\/#organization"},"image":{"@id":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/featured-image.png","keywords":["Nord Security","2023","NordLayer"],"articleSection":["Nord Security","2023","NordLayer","Press Release"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/version-2.com\/2023\/12\/improving-nordvpns-android-performance-using-the-compose-compiler-plugin\/","url":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler","name":"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin - Version 2","isPartOf":{"@id":"https:\/\/version-2.com\/zh\/#website"},"primaryImageOfPage":{"@id":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler#primaryimage"},"image":{"@id":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/featured-image.png","datePublished":"2023-12-13T07:29:58+00:00","breadcrumb":{"@id":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler#primaryimage","url":"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/featured-image.png","contentUrl":"https:\/\/res.cloudinary.com\/nordsec\/f_auto,c_limit,w_1200,q_auto\/v1\/nord-security-web\/blog\/categories\/engineering\/2023\/11\/featured-image.png"},{"@type":"BreadcrumbList","@id":"https:\/\/nordsecurity.com\/blog\/improving-nordvpn-android-compose-compiler#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9801","item":"https:\/\/version-2.com\/zh\/"},{"@type":"ListItem","position":2,"name":"Improving NordVPN\u2019s Android performance using the Compose Compiler plugin"}]},{"@type":"WebSite","@id":"https:\/\/version-2.com\/zh\/#website","url":"https:\/\/version-2.com\/zh\/","name":"Version 2","description":"","publisher":{"@id":"https:\/\/version-2.com\/zh\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/version-2.com\/zh\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/version-2.com\/zh\/#organization","name":"Version 2","url":"https:\/\/version-2.com\/zh\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/version-2.com\/zh\/#\/schema\/logo\/image\/","url":"https:\/\/i0.wp.com\/version-2.com\/wp-content\/uploads\/2020\/08\/v2-hk-hor-4.png?fit=1795%2C335&ssl=1","contentUrl":"https:\/\/i0.wp.com\/version-2.com\/wp-content\/uploads\/2020\/08\/v2-hk-hor-4.png?fit=1795%2C335&ssl=1","width":1795,"height":335,"caption":"Version 2"},"image":{"@id":"https:\/\/version-2.com\/zh\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/version-2.com\/zh\/#\/schema\/person\/011bc7c3731c930bcfeecd52fefb6365","name":"tracylamv2","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/9d01d79cbfd8b2e878f5d701a362cc9fca466d33fec977b59706c23c1a2db15c?s=96&d=identicon&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/9d01d79cbfd8b2e878f5d701a362cc9fca466d33fec977b59706c23c1a2db15c?s=96&d=identicon&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9d01d79cbfd8b2e878f5d701a362cc9fca466d33fec977b59706c23c1a2db15c?s=96&d=identicon&r=g","caption":"tracylamv2"},"url":"https:\/\/version-2.com\/en\/author\/tracylamv2\/"}]}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pbQRKm-jaJ","post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/posts\/73701","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/users\/149011790"}],"replies":[{"embeddable":true,"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/comments?post=73701"}],"version-history":[{"count":4,"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/posts\/73701\/revisions"}],"predecessor-version":[{"id":73705,"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/posts\/73701\/revisions\/73705"}],"wp:attachment":[{"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/media?parent=73701"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/categories?post=73701"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/version-2.com\/en\/wp-json\/wp\/v2\/tags?post=73701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}