com.prestongarno:trywithres-compat

A tool for backwards-compatible try-with-resources


Licenses
GPL-2.0-with-classpath-exception/Abstyles

Documentation

Try-With-Resources Compatibility: Java & Android API <19 (KitKat)

GPL License GitHub version

Standalone dependency to support migrating Android apps from retrolambda to Android Studio 2.4, which now comes with support for some Java 8 features out of the box. While Google is rumored to be providing support for try-with-resources 'hopefully sometime soon', I decided make a standalone tool to do this until Google provides a fix for Android Studio 2.4.


Quick note about the GPL license: with this being a compile only dependency with no runtime library/linkage, the same classpath exception applies here that covers javac: unless you extend this library, you are free in order to create an executable application, covered under whatever license you want. I.N.A.L., though:)

How to use with gradle:

trywithresources-compat will only work with source version 1.8. This is also a *compileOnly dependency. In your module build.gradle file add this to your module build.gradle:*

repositories {
  mavenCentral()
}

dependencies {
  compileOnly 'com.prestongarno:trywithres-compat:0.1'
}

NOTE: Android Studio may fail your build saying that "annotation processors must be explicitly declared". If this happens, simply replace "compileOnly" with "annotationProccessor" and build will proceed as normal.

Options:

  • Pretty-print: compile with the -g:source argument to print before/after processing. To do this with gradle:
    compileJava { 
      options.compilerArgs.add("-g:source")  
    }

Possible Issues:

  • Hotswap/InstantRun on emulators running Android <19 won't work after adding a try-with-resource and instant run before a clean build. A clean build will most likely be required if testing on <API 19 and you add a try-with-resource statement. Release APK won't be affected.



How it works:
  • trywithresources-compat leverages the Java ServiceLoader to attach itself to the javac compilation process and use the (admittedly, half internal/unsupported) API com.sun.tools.javac
  • After annotation processing, it runs a single pass over the the code structure and translates any try-with-resources blocks to standard try-catch blocks
  • The code generation process uses synthetic local variables in order to avoid naming conflicts with existing symbols. The transformed block follows the JLS canonical process (minus the call to Throwable#addSuppressed) for de-sugaring as shown below. This guarantees runtime stability with zero performance costs, and also provides that sweet, sweet, syntactical sugar.
Example code:
try(AutoCloseableImplImpl example = new AutoCloseableImpl()) {
  example.doRiskyThings();
} //optional catch/finally
Result:
{
  final AutoCloseableImplImpl example = new AutoCloseableImpl()
  Throwable primaryException0$ = null;

  try {
     example.doRiskyThings();
  } catch (synthetic final Throwable t$) {
      primaryException0$ = t$;
      throw t$;
  } finally {
      if (something != null) {
         if (primaryException0$ != null) 
           try { 
             something.close();
           } catch (synthetic final Throwable xI$) { } 
         else something.close();
      }
  }
} // if applicable: catch/finally blocks are inserted here