| {namespace buck.exopackage} |
| |
| /***/ |
| {template .soyweb} |
| {call buck.page} |
| {param title: 'Exopackage' /} |
| {param subtitle: 'A technique for fast, iterative Android development.' /} |
| {param prettify: true /} |
| {param description} |
| Buck has an advanced feature to speed up iterative Android development |
| called exopackage. An exopackage is a small shell of an Android app that |
| contains the minimal code and resources needed to bootstrap loading the |
| code for a full-fledged Android application. |
| {/param} |
| {param content} |
| |
| {let $step2sha1: '6c809273e70428f2f465cdeef568e1f35c30b439' /} |
| {let $step3sha1: '5f7ce4934e9e8ea9a391a09d2093c79b64b7d207' /} |
| {let $step4sha1: 'fa2f6cb74e594d5a75c9b67da8b654fa09ecf3cf' /} |
| {let $step5sha1: '4856481f75b1f457ea489f2811b22f14402d747b' /} |
| {let $step6sha1: '340814588800efdd06d147ebfb04005dd4070f92' /} |
| |
| <p> |
| |
| Buck has an advanced feature to speed up iterative Android development |
| called exopackage. An <em>exopackage</em> is a small shell of an Android |
| app that contains the minimal code and resources needed to bootstrap |
| loading the code for a full-fledged Android application. Loading the |
| application code at runtime avoids a full reinstall of the app when testing |
| a Java change, which dramatically reduces the length of edit/refresh cycles. |
| Here are the performance improvements in build times for Buck vs. Gradle |
| in a real world Android application,{sp} |
| <a href="https://github.com/danieloeh/AntennaPod">AntennaPod</a>: |
| |
| <p> |
| |
| <table> |
| <tr> |
| <td> </td> |
| <th>Gradle</th> |
| <th>Buck</th> |
| <th>Speed Up</th> |
| </tr> |
| <tr> |
| <th>clean build</th> |
| <td class="measurement">31s</td> |
| <td class="measurement">6s</td> |
| <td class="measurement">5x</td> |
| </tr> |
| <tr> |
| <th>incremental build</th> |
| <td class="measurement">13s</td> |
| <td class="measurement">1.7s</td> |
| <td class="measurement">7.5x</td> |
| </tr> |
| <tr> |
| <th>no-op build</th> |
| <td class="measurement">3s</td> |
| <td class="measurement">0.2s</td> |
| <td class="measurement">15x</td> |
| </tr> |
| <tr> |
| <th>clean install</th> |
| <td class="measurement">7.2s</td> |
| <td class="measurement">7.2s</td> |
| <td class="measurement">1x</td> |
| </tr> |
| <tr> |
| <th>incremental install</th> |
| <td class="measurement">7.2s</td> |
| <td class="measurement">1.5s</td> |
| <td class="measurement">4.8x</td> |
| </tr> |
| </table> |
| |
| <p> |
| |
| (Note: These measurements were done on a MacBook Pro with a i7-3740QM CPU, |
| with HyperThreading enabled, using Oracle Java 1.7.0_45 for Linux. |
| We used 8 threads by running "./gradlew --parallel-threads 8". |
| Gradle's daemon, parallel, and configureondemand options were enabled, |
| as was Buck's daemon (which is enabled by default). |
| In all cases, the builds were run multiple times to allow |
| Java's JIT to warm up fully. |
| The incremental build was adding a single blank line to a Java file.) |
| |
| <p> |
| |
| As you might expect, using exopackage requires you to make some code changes |
| to your application. This article serves two purposes: it is both |
| a <strong>tutorial</strong> that shows how to migrate an Android app that builds with |
| Gradle over to Buck with exopackage, as well as |
| a <strong>technical explanation</strong> of how exopackage works. |
| |
| <p> |
| |
| For this tutorial, we will demonstrate how to use exopackage by adding Buck support |
| to <a href="https://github.com/danieloeh/AntennaPod">AntennaPod</a>, |
| an open source podcast management app for Android. |
| Each step in the process is documented as a separate commit in{sp} |
| <a href="https://github.com/facebookarchive/AntennaPod">our fork of AntennaPod</a>. |
| Note that most of the work in this tutorial is in adding Buck support to |
| AntennaPod. If your Android project already uses Buck, then you can jump |
| straight to <a href="#build-buck-support-library">Step 5</a>, which will require minimal |
| changes to your existing project. |
| |
| <p> |
| |
| <strong>Table of Contents</strong> |
| |
| <p> |
| |
| <ul class="tight-list"> |
| <li><a href="#check-out">Step 1: Check out AntennaPod</a> |
| <li><a href="#import-dependencies">Step 2: Import JARs for third party dependencies</a> |
| <li><a href="#refactor-r-constants">Step 3: Ensure R.* constants are not assumed to be final</a> |
| <li><a href="#build-rules">Step 4: Create BUCK files that define build rules to build AntennaPod with Buck</a> |
| <li><a href="#build-buck-support-library">Step 5: Build Buck's Android support library</a> |
| <li><a href="#use-exopackage">Step 6: Modify AntennaPod to use exopackage</a> |
| <li><a href="#profit">Step 7: Profit!</a> |
| <li><a href="#caveats">Caveats</a> |
| <li><a href="#incompatible-devices">Incompatible Devices</a> |
| </ul> |
| |
| <h2 id="check-out">Step 1: Check out AntennaPod</h2> |
| |
| <p> |
| |
| If you want to walk through this tutorial and make all of the changes yourself, |
| then the first step is to clone the AntennaPod application at the same |
| revision used to create this tutorial: |
| |
| <p> |
| |
| <pre>{literal} |
| git clone --recursive git@github.com:facebookarchive/AntennaPod.git |
| cd AntennaPod |
| git checkout c2080b1dfd17fc371e04ce1e7b39ebadaf3cb7a7 |
| {/literal}</pre> |
| |
| <p> |
| |
| If you just want to play with the final version of the tutorial after all |
| of the Buck/exopackage support has been added, then checkout to the appropriate revision |
| so you can build and run AntennaPod using Buck. |
| Note that you must add your own keystore before you can do a build. |
| (We do not check in <code>debug.keystore</code> for security reasons.) |
| |
| <p> |
| |
| <pre>{literal} |
| git checkout {/literal}{$step6sha1}{literal} |
| cp ~/.android/debug.keystore keystore/debug.keystore |
| buck install --run antennapod |
| {/literal}</pre> |
| |
| <h2 id="import-dependencies">Step 2: Import JARs for third party dependencies</h2> |
| |
| <p> |
| |
| {call .gitHubCommit} |
| {param sha1 : $step2sha1 /} |
| {/call} |
| |
| <p> |
| |
| Unlike Gradle, Buck requires that all files that contribute to the project |
| live under the project root (which is defined by the presence of |
| a <code>.buckconfig</code> file). Instead of downloading third party JARs from the |
| Maven Central Repository as part of the build process (like Gradle), |
| Buck expects such dependencies to live in version control, just like application code. |
| This ensures that builds are <em>reproducible</em> and <em>hermetic</em>. |
| |
| <p> |
| |
| For AntennaPod, we ran <code>./gradlew --debug assembleDebug</code> and inspected the |
| output to figure out which third party JAR files Gradle was using to build the app. |
| As a result, we ended up adding the following files to the <code>libs</code> directory, |
| which also includes an <a href="http://tools.android.com/tech-docs/new-build-system/aar-format">AAR</a>{sp} |
| file for the Android support library for v7 compatibility. |
| |
| <pre>{literal} |
| libs/appcompat-v7-19.1.0.aar |
| libs/commons-io-2.4.jar |
| libs/commons-lang3-3.3.2.jar |
| libs/flattr4j-core-2.10.jar |
| libs/library-2.4.0.jar |
| libs/support-v4-19.1.0.jar |
| {/literal}</pre> |
| |
| Note that we also removed the <code>libs</code> directory from <code>.gitignore</code> as |
| part of this change. |
| |
| <h2 id="refactor-r-constants">Step 3: Ensure R.* constants are not assumed to be final</h2> |
| |
| <p> |
| |
| {call .gitHubCommit} |
| {param sha1 : $step3sha1 /} |
| {/call} |
| |
| <p> |
| |
| If you have any code like the following: |
| |
| <p> |
| |
| <pre class="prettyprint lang-java">{literal} |
| int id = view.getId(); |
| switch (id) { |
| case R.id.button1: |
| action1(); |
| break; |
| case R.id.button2: |
| action2(); |
| break; |
| case R.id.button3: |
| action3(); |
| break; |
| } |
| {/literal}</pre> |
| |
| <p> |
| |
| You must convert it to use <code>if</code>/<code>else</code> blocks as follows: |
| |
| <p> |
| |
| <pre class="prettyprint lang-java">{literal} |
| int id = view.getId(); |
| if (id == R.id.button1) { |
| action1(); |
| } else if (id == R.id.button2) { |
| action2(); |
| } else if (id == R.id.button3) { |
| action3(); |
| } |
| {/literal}</pre> |
| |
| <p> |
| |
| As explained in the article <a href="http://tools.android.com/tips/non-constant-fields"> |
| Non-constant Fields in Case Labels</a>, the constants in the <code>R</code> class for an |
| Android library projects are not <code>final</code>, which means they cannot be used |
| as constant expressions in <code>case</code> statements. Because Buck treats the |
| code for an {call buck.android_library /} as if it were part of an Android library project, |
| this applies to all Android code built by Buck. The article explains how |
| you can leverage your IDE to automate this refactoring. |
| |
| <h2 id="build-rules">Step 4: Create BUCK files that define build rules to build AntennaPod with Buck</h2> |
| |
| <p> |
| |
| {call .gitHubCommit} |
| {param sha1 : $step4sha1 /} |
| {/call} |
| |
| <p> |
| |
| In Buck, build rules are defined in build files named <code>BUCK</code>. In this step, |
| we create a <code>BUCK</code> file and add the build rules necessary to build the |
| AntennaPod APK using Buck without touching any other files in the AntennaPod repository. |
| (Note that if you are creating a new Android project from scratch rather than retrofitting |
| an existing project, you may want to use {call buck.cmd_quickstart /} to create your project |
| and then skip ahead to <a href="#build-buck-support-library">Step 5</a>.) |
| |
| <p> |
| |
| We start by creating a <code>BUCK</code> file and defining an {call buck.android_library /} rule |
| that exposes all of the JARs in the <code>libs</code> directory as a single |
| dependency, <code>:all-jars</code>: |
| |
| <pre class="prettyprint lang-py">{literal} |
| import re |
| |
| jar_deps = [] |
| for jarfile in glob(['libs/*.jar']): |
| name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) |
| jar_deps.append(':' + name) |
| prebuilt_jar( |
| name = name, |
| binary_jar = jarfile, |
| ) |
| |
| android_library( |
| name = 'all-jars', |
| exported_deps = jar_deps, |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| We also wrap the AAR file for the Android support library with |
| an {call buck.android_prebuilt_aar /} rule: |
| |
| <pre class="prettyprint lang-py">{literal} |
| android_prebuilt_aar( |
| name = 'appcompat', |
| aar = 'libs/appcompat-v7-19.1.0.aar', |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| Next, we define some rules to generate <code>.java</code> files from <code>.aidl</code> files |
| and package them as an {call buck.android_library /}, as well: |
| |
| <pre class="prettyprint lang-py">{literal} |
| presto_gen_aidls = [] |
| for aidlfile in glob(['src/com/aocate/presto/service/*.aidl']): |
| name = 'presto_aidls__' + re.sub(r'^.*/([^/]+)\.aidl$', r'\1', aidlfile) |
| presto_gen_aidls.append(':' + name) |
| gen_aidl( |
| name = name, |
| aidl = aidlfile, |
| import_path = 'src', |
| ) |
| |
| android_library( |
| name = 'presto-aidls', |
| srcs = presto_gen_aidls, |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| Then we define an {call buck.android_build_config /}, which will generate |
| {sp}<code>de.danoeh.antennapod.BuildConfig</code> for us, compile it, and |
| expose it as a {call buck.java_library /}. As we will see, this class plays |
| an important role in creating an exopackage: |
| |
| <pre class="prettyprint lang-py">{literal} |
| android_build_config( |
| name = 'build-config', |
| package = 'de.danoeh.antennapod', |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| Before we can define an {call buck.android_library /} rule to compile the |
| primary sources for AntennaPod, we must define some rules to bundle the |
| resources and code for its dependent Android library projects: |
| |
| <pre class="prettyprint lang-py">{literal} |
| android_resource( |
| name = 'dslv-res', |
| package = 'com.mobeta.android.dslv', |
| res = 'submodules/dslv/library/res', |
| ) |
| |
| android_library( |
| name = 'dslv-lib', |
| srcs = glob(['submodules/dslv/library/src/**/*.java']), |
| deps = [ |
| ':all-jars', |
| ':dslv-res', |
| ], |
| ) |
| |
| android_library( |
| name = 'presto-lib', |
| srcs = glob(['src/com/aocate/**/*.java']), |
| deps = [ |
| ':all-jars', |
| ':presto-aidls', |
| ], |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| Now that the dependent Android library projects can be expressed as dependencies |
| in Buck, we define {call buck.android_resource /} and {call buck.android_library /} rules |
| that build the main AntennaPod code: |
| |
| <pre class="prettyprint lang-py">{literal} |
| android_resource( |
| name = 'res', |
| package = 'de.danoeh.antennapod', |
| res = 'res', |
| assets = 'assets', |
| deps = [ |
| ':appcompat', |
| ':dslv-res', |
| ] |
| ) |
| |
| android_library( |
| name = 'main-lib', |
| srcs = glob(['src/de/**/*.java']), |
| deps = [ |
| ':all-jars', |
| ':appcompat', |
| ':build-config', |
| ':dslv-lib', |
| ':presto-lib', |
| ':res', |
| ], |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| To package the Android code into an APK, we need |
| a keystore with which it should be signed, |
| a manifest that defines the app, |
| and a rule to package everything toegether. |
| Let's start with the keystore, as defining this rule requires an extra step |
| from the command line: |
| |
| <pre class="prettyprint lang-py">{literal} |
| keystore( |
| name = 'debug_keystore', |
| store = 'keystore/debug.keystore', |
| properties = 'keystore/debug.keystore.properties', |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| Note that a clean checkout of the AntennaPod repository includes |
| a <code>keystore/debug.keystore.properties</code> file, but |
| no <code>keystore/debug.keystore</code> file. |
| This is because the Android Developer Tools creates a keystore with |
| a common set of credentials under <code>~/.android/debug.keystore</code> on |
| your machine. Assuming you have not changed this default, |
| the values in <code>keystore/debug.keystore.properties</code> will be appropriate |
| for your <code>~/.android/debug.keystore</code>. Recall that Buck requires |
| all files it must know about to live under the project root, |
| so <strong>you must copy the keystore to your project where Buck expects it</strong>: |
| |
| <pre>cp ~/.android/debug.keystore keystore/debug.keystore</pre> |
| |
| <p> |
| |
| With the {call buck.keystore /} defined, now we can define |
| the {call buck.android_binary /} rule whose output will be the |
| AntennaPod APK. Note that the only item listed in its <code>deps</code> is |
| {sp}<code>:main-lib</code>, as {call buck.android_binary /} will package |
| {sp}<code>:main-lib</code> and its transitive dependencies into the APK. |
| |
| <p> |
| |
| <pre class="prettyprint lang-py">{literal} |
| android_binary( |
| name = 'antennapod', |
| manifest = 'AndroidManifest.xml', |
| target = 'Google Inc.:Google APIs:19', |
| keystore = ':debug_keystore', |
| deps = [ |
| ':main-lib', |
| ], |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| To facilitate building from the command line (and to leverage the build cache), create a |
| file named <code>.buckconfig</code> in the root of the repo with the following contents: |
| |
| <pre>{literal} |
| [alias] |
| antennapod = //:antennapod |
| [cache] |
| mode = dir |
| dir_max_size = 1GB |
| {/literal}</pre> |
| |
| Now you should be able to run <code>{call buck.cmd_build /} antennapod</code> to build the app, |
| or <code>{call buck.cmd_install /} antennapod</code> to install it if <code>adb devices</code> is |
| not empty. |
| |
| <h2 id="build-buck-support-library">Step 5: Build Buck's Android support library</h2> |
| |
| <p> |
| |
| {call .gitHubCommit} |
| {param sha1 : $step5sha1 /} |
| {/call} |
| |
| <p> |
| |
| In order for your app to use exopackage, it needs to use Buck's Java library |
| that provides support for it. You can easily build this library from source |
| from your checkout of Buck as follows: |
| |
| <pre>{literal} |
| <strong># Run this from the root of your checkout of Buck, not from AntennaPod.</strong> |
| buck build buck-android-support |
| {/literal}</pre> |
| |
| <p> |
| |
| Once you have built it, copy it over to AntennaPod's <code>libs</code> directory, |
| just like the other third party JAR files: |
| |
| <p> |
| |
| <pre>{literal} |
| cp `buck targets --show-output buck-android-support | awk '{print $2}'` \ |
| path/to/AntennaPod/libs |
| {/literal}</pre> |
| |
| <h2 id="use-exopackage">Step 6: Modify AntennaPod to use exopackage</h2> |
| |
| <p> |
| |
| {call .gitHubCommit} |
| {param sha1 : $step6sha1 /} |
| {/call} |
| |
| <p> |
| |
| On a high level, the main thing that you need to do to leverage exopackage is change the insertion point |
| of your app from the existing <code>android.app.Application</code> that your app uses |
| to an {call .ExopackageApplication /} that delegates to your original <code>Application</code>. |
| This level of indirection is what makes it possible for exopackage to dynamically |
| load the code for your application in debug mode. In release mode, {call .ExopackageApplication /} |
| {sp}expects all of the code for your app to be present in the APK, so it skips the |
| step where it tries to dynamically load code. |
| |
| <p> |
| |
| If your app has a class that subclasses <code>android.app.Application</code> that is |
| listed as the main app in <code>AndroidManifest.xml</code> via the{sp} |
| <code><application name></code> attribute, then the first thing that you need |
| to do is modify that class so it extends {call .DefaultApplicationLike /}{sp} |
| rather than <code>Application</code>: |
| |
| <p> |
| |
| <pre>{literal} |
| <span style="color:#A00">-public class PodcastApp extends Application {</span> |
| <span style="color:green">+public class PodcastApp extends DefaultApplicationLike {</span> |
| {/literal}</pre> |
| |
| <p> |
| |
| Further, your {call .DefaultApplicationLike /} must declare a constructor that takes |
| an <code>Application</code> as its only parameter. You most likely want to store it as a field: |
| |
| <p> |
| |
| <pre class="prettyprint lang-java">{literal} |
| private final Application appContext; |
| |
| public PodcastApp(Application appContext) { |
| this.appContext = appContext; |
| } |
| {/literal}</pre> |
| |
| <p> |
| |
| Now all methods that previously accessed the API of <code>Application</code> via inheritance can |
| delegate to the <code>appContext</code> instance instead: |
| |
| <p> |
| |
| <pre>{literal} |
| <span style="color:#A00">-LOGICAL_DENSITY = getResources().getDisplayMetrics().density;</span> |
| <span style="color:green">+LOGICAL_DENSITY = appContext.getResources().getDisplayMetrics().density;</span> |
| {/literal}</pre> |
| |
| <p> |
| |
| Now you must create your new <code>Application</code> class, which will be a subclass |
| of {call .ExopackageApplication /}. As you can see from its API, |
| it is an <code>abstract</code> class that does not have a default constructor, so you |
| must define a no-arg constructor as follows: |
| |
| <p> |
| |
| <pre class="prettyprint lang-java">{literal} |
| package de.danoeh.antennapod; |
| |
| import com.facebook.buck.android.support.exopackage.ExopackageApplication; |
| |
| public class AppShell extends ExopackageApplication { |
| |
| public AppShell() { |
| super( |
| // This is passed as a string so the shell application does not |
| // have a binary dependency on your ApplicationLike class. |
| /* applicationLikeClassName */ "de.danoeh.antennapod.PodcastApp", |
| |
| // The package for this BuildConfig class must match the package |
| // from the android_build_config() rule. The value of the flags |
| // will be set based on the "exopackage_modes" argument to |
| // android_binary(). |
| de.danoeh.antennapod.BuildConfig.EXOPACKAGE_FLAGS); |
| } |
| } |
| {/literal}</pre> |
| |
| <p> |
| |
| Alternatively, if your original app did not have a custom subclass |
| of <code>android.app.Application</code>, then you do not have to |
| create an implementation of <code>ApplicationLike</code>. You must still |
| create a subclass of <code>ExopackageApplication</code>, but now your |
| implementation can be simpler: |
| |
| <p> |
| |
| <pre class="prettyprint lang-java">{literal} |
| package de.danoeh.antennapod; |
| |
| import com.facebook.buck.android.support.exopackage.ExopackageApplication; |
| |
| public class AppShell extends ExopackageApplication { |
| |
| public AppShell() { |
| super(de.danoeh.antennapod.BuildConfig.EXOPACKAGE_FLAGS); |
| } |
| } |
| {/literal}</pre> |
| |
| <p> |
| |
| Now the more sophisticated changes will be in the <code>BUCK</code> file where |
| you defined your {call buck.android_binary /} rule. First, you will need to |
| create an {call buck.android_library /} that builds your {call .ExopackageApplication /}: |
| |
| <p> |
| |
| <pre class="prettyprint lang-py">{literal} |
| APP_CLASS_SOURCE = 'src/de/danoeh/antennapod/AppShell.java' |
| |
| android_library( |
| name = 'application-lib', |
| srcs = [APP_CLASS_SOURCE], |
| deps = [ |
| # This is the android_build_config() rule that you created in Step 4. |
| # If you jumped straight to Step 5 because your Android app was already |
| # configured to build with Buck, then go back to Step 4 and add this rule |
| # if you aren't already using an android_build_config(). |
| ':build-config', |
| |
| # This is the prebuilt_jar() rule that wraps buck-android-support.jar. |
| ':jars__buck-android-support', |
| ], |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| If you have an existing {call buck.android_library /} rule that {call buck.fn_glob /}s |
| your {call .ExopackageApplication /}'s source file, then make sure to exclude it: |
| |
| <pre>{literal} |
| <span style="color:#A00">- srcs = glob(['src/de/**/*.java']),</span> |
| <span style="color:green">+ srcs = glob(['src/de/**/*.java'], excludes = [APP_CLASS_SOURCE]),</span> |
| {/literal}</pre> |
| |
| <p> |
| |
| The biggest change to your <code>BUCK</code> file will be the new arguments |
| to your {call buck.android_binary /} rule (new lines are highlighted |
| in <span style="color:green">green</span>): |
| |
| <p> |
| |
| <pre>{literal} |
| android_binary( |
| name = 'antennapod', |
| manifest = 'AndroidManifest.xml', |
| target = 'Google Inc.:Google APIs:19', |
| keystore = ':debug_keystore', |
| <span style="color:green"> use_split_dex = True, |
| exopackage_modes = ['secondary_dex'], |
| primary_dex_patterns = [ |
| '^de/danoeh/antennapod/AppShell^', |
| '^de/danoeh/antennapod/BuildConfig^', |
| '^com/facebook/buck/android/support/exopackage/', |
| ],</span> |
| deps = [ |
| <span style="color:green"> ':application-lib',</span> |
| ':main-lib', |
| ], |
| ) |
| {/literal}</pre> |
| |
| <p> |
| |
| As you might have guessed, <code>primary_dex_patterns</code> is a pattern that |
| identifies which <code>.class</code> files from the transitive <code>deps</code> that |
| must be included in the shell app that bootstraps the rest of the app. |
| As such, these patterns match the transitive deps of <code>:application-lib</code>. |
| |
| <p> |
| |
| Setting <code>exopackage = ['secondary-dex']</code> is what ensures that |
| {sp}<code>BuildConfig.EXOPACKAGE_FLAGS</code> will be set correctly, in |
| addition to the other packaging changes that Buck makes to support exopackage. |
| This must used with <code>use_split_dex = True</code> because using |
| exopackage requires dividing the app into multiple dex files. |
| |
| <p> |
| |
| Finally, you must update your <code>AndroidManifest.xml</code> to refer to |
| the <code>ExopackageApplication</code> as the new entry point into your app: |
| |
| <p> |
| |
| <pre>{literal} |
| <span style="color:#A00">-android:name="de.danoeh.antennapod.PodcastApp"</span> |
| <span style="color:green">+android:name="de.danoeh.antennapod.AppShell"</span> |
| {/literal}</pre> |
| |
| <h2 id="profit">Step 7: Profit!</h2> |
| |
| Now your development cycle should be as follows: |
| |
| <p> |
| |
| <pre>{literal} |
| buck install --run antennapod |
| # Edit your application's Java code. |
| buck install --run antennapod |
| # Watch in amazement as your changes are loaded faster than ever before! |
| {/literal}</pre> |
| |
| <h2 id="caveats">Caveats</h2> |
| |
| <p> |
| |
| Currently, exopackage speeds up incremental install times for Java changes, |
| but changes to Android resources or native libraries require a full reinstall. |
| This is something we hope to improve in the future. |
| |
| <p> |
| |
| Be aware of the following limitations when using Buck and exopackage: |
| |
| <p> |
| |
| <ul class="tight-list"> |
| <li>You cannot use <code>adb install</code> for exopackages. You must use {call buck.cmd_install /}. |
| <li>You should use {call buck.cmd_uninstall /} instead of <code>adb uninstall</code> to |
| uninstall the app. Otherwise, unnecessary files will be left in <code>/data/local/tmp</code>. |
| You can remove them with <code>adb shell rm -r /data/local/tmp/exopackage/$PACKAGE_NAME</code>. |
| <li>Some devices are not compatible with the exopackage installer. |
| {sp}<a href="#incompatible-devices">See below</a>. |
| <li>Install to SD card does not work right now. |
| <li>Exopackages will not start up for non-primary users on a multi-user android device. |
| // (TODO: This might not be true any more.) |
| <li>When you do an install, system notifications and alarms will <strong>not</strong> be |
| cleared, so you might get an intent back from them with the old version of |
| your <code>Parcelable</code>, which could cause a crash or other confusing behavior. |
| <li>When you do an install on pre-ICS devices, the app will not be stopped. |
| </ul> |
| |
| <h2 id="incompatible-devices">Incompatible Devices</h2> |
| |
| <p> |
| |
| Empirically, we have determined that the following devices do not work with exopackage: |
| |
| <p> |
| |
| <ul class="tight-list"> |
| <li>Some AOSP builds between the KitKat release and L Preview. |
| </ul> |
| |
| You might want to keep two versions of your {call buck.android_binary /} rule in |
| your <code>BUCK</code> files: one that uses exopackage and one that does not. |
| That way, you will always have a way to test on devices that do not support exopackage. |
| |
| {/param} |
| {/call} |
| {/template} |
| |
| /***/ |
| {template .ExopackageApplication} |
| <a href="http://facebook.github.io/buck/javadoc/com/facebook/buck/android/support/exopackage/ExopackageApplication.html"> |
| <code>ExopackageApplication</code> |
| </a> |
| {/template} |
| |
| /***/ |
| {template .DefaultApplicationLike} |
| <a href="http://facebook.github.io/buck/javadoc/com/facebook/buck/android/support/exopackage/DefaultApplicationLike.html"> |
| <code>DefaultApplicationLike</code> |
| </a> |
| {/template} |
| |
| /** |
| * @param sha1 |
| */ |
| {template .gitHubCommit} |
| <span style="font-size: 80%"> |
| View on GitHub: <a href="https://github.com/facebookarchive/AntennaPod/commit/{$sha1}">{$sha1}</a> |
| </span> |
| {/template} |
| |