An overview of Java's history on Android and direction for the JVM on the future of Android, this talk compiles Java 8/9 APIs to Java 6, offering developers, back to Ice Cream Sandwich, more goodies to play with!
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Alive and Well with Java 8
1. Alive and Well with Java 8
Adam Pelsoczi
Android Developer - Rogers Digital Media
https://www.meetup.com/ToAndroidDev/
2. Agenda
● Implementing Java for Android
● Features and Language Levels
● Libraries to Backport API’s
● Introductory Knowledge
● More Bang For Your Buck
4. Apples, Oranges, and Virtual Machines
- Java API != Android API ; the two are disparate in comparison.
Source Code
“.java”
Java Compiler
“javac”
Bytecode
“.class”
Sun’s Traditional
Java Virtual Machine
- JAR not executed by Android
- Dalvik/ART compile Executable and Link
Format “ELF” executables
- Java VM = stack machine
- stack-based architecture
- Android VM = register machine
- register-based architecture
Source Code
“.java”
Bytecode
“.class”
Dalvik Virtual
Machine
Android Runtime
“ART”
5. Java’s Journey
Cupcake
Dalvik
Apache Harmony
KitKat
ART 1.6.0
OpenJDK (Preview)
Lollipop
Dalvik entirely
replaced
Nougat
OpenJDK commit
to AOSP
‘Dead’ Code Base
Apache fails JSE5 compl.
and retires Harmony
Lollipop
ART 2.1.0
Oreo
→
6. OpenJDK
- Official reference implementation of Java
- Harmony was a cleanroom implementation
- Licensed under more restrictive GNU General
Public License
- Code changes / fixes must be contributed
back to the community at large
- Initially based only on JDK 7 version of the
Java Platform
- Google wants to put more resources into
OpenJDK where the team can have a bigger
impact on new features and improvements.
"Google has long worked with and contributed to the
OpenJDK community and we look forward to making
even more contributions to the OpenJDK project in
the future."
- Google spokesperson
8. 5
- When Android was first released, Java 5 was
supported
- But that version is best left to history
- Generics, Autoboxing, Enum, Varargs, etc...
- Pillar of Android Application Development
- Never 100% supported - it couldn’t be
- re: Apache Harmony legal issues and further
market/product fit
- @Override annotations for interfaces
6
9. 7
- Usable Language Features
- Diamond Operator <> .
- String switch
- Multi Catch catch (Exc1 | Exc2 e) .
- Underscore in number literals 1_000 .
- Binary Literals 0b1100111 .
- These features cannot be used yet*
- try-with-resources
- requires non-existent interface
java.lang.AutoCloseable
- @SafeVarArgs
- java.lang.SafeVarargs does not exist
10. yet*
- Android library target 1.6
- Hidden because javadoc @hide
- android.jar not to include
- Android Source does contain interfaces
like AutoCloseable
https://stackoverflow.com/questions/7153989/
java-7-language-features-with-android/13550632#13550632
11. 5, 6, 7, 3.0?
re: Apples, Oranges, and Virtual Machines
3.0 and later supports all
Java 7 language features
Eclipse ADT hard coded Java 1.5 / 1.6 compliant
https://android.googlesource.com/platform/sdk/+/master/eclipse/plugins/com.android
.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
12. 8
- Subset of Java 8 features, varying by
platform version
- Lambdas ( ) → { } .
- Method References Object::methodName .
- Default and Static interface methods
- Type and repeating annotations
- Try-With-Resources
- Now Available on all API Levels
- More Java Language Features over time
- D8 Next-generation Dex Compiler (Preview)
- Jack compiler deprecated
- more restricted Java 8 support
- New default toolchain offers more
functionality than 3rd party
- javac/dx
14. “taking parts from a newer version of software and
porting them to an older version of the same software
Backporting
15. Calendar and Date (Java 6)
- Conceptually well envisioned
- Perhaps the best example of how not to do
something in any language
- Counter intuitive numeric representations
- Month, 0 - 11 indexed
- Year, 1900 based (2009 = year 109)
- Not necessarily thread-safe
- No classes for date/time concepts
- Non timezone dates or time, durations,
periods or intervals.
- Calendar stores state in two different ways
internally
- Date is mutable and often used incorrectly
- return Date.clone();
- Calendar, was designed to fix mutability
- Also mutable
- Bug 4639407
- Summer daylight savings can only gain 1h
- Historically, +2h (WWII era)
16. Recent Date and Time API’s
- JSR-310, a core Java 8 API
- inspired by Joda-Time
- ThreeTenBP curated by Stephen Colebourne
- also authored Joda-Time (deprecated)
- NOT an implementation of JSR-310
- Unnecessary hoops to jump through
- ThreeTenBP resolves Joda-Time design flaws
- Machine / Human timelines
- increment milliseconds, 1970-01-01T00:00Z
- vs. multiple calendars, timezones / DST are
always changing
- Pluggable Chronology
- date.getMonthOfYear(); // can return 13
- Joda-Time accept’s null
- underlying bugs, no exception throwing
- DST overlaps are clunky in Joda
ThreeTenBP:
“org.threeten:threetenbp:1.3.6”
ThreeTenABP:
“com.jakewharton.threetenabp:threetenabp:1.0.5”
17. If You Like Ice Cream…
- StreamSupport backports Java 8|9 to 6|7
- ‘optimized for pre-Java 8, which lacked
default and static interface methods’
- Ice Cream Sandwich minSdk
- RetroStreams is a fork of StreamSupport
targeted at Android Developers
- takes advantage of desugar
- Even more aligned to original Java 8|9
- default and static interface methods!
- Brings Java 8|9 to 6/7
- Streams, CompletableFuture, Parallel array
operations, Functional Interfaces
- Further java.util.concurrent enhancements from
Java 7|8 to Java 6
- Miscellaneous Java 8|9 goodies
- Optional, StringJoiner, …
api “net.sourceforge.streamsupport:android-retrostreams:1.5.6’’
api “net.sourceforge.streamsupport:android-retroatomic:1.5.6”
api “net.sourceforge.streamsupport:android-retroflow:1.5.6’”
api “net.sourceforge.streamsupport:android-retrofuture:1.5.6”
18. retrostreams
Utility Concurrent Function Stream
Classes and
implementations of static
and default interface
methods
Utility classes commonly
used in concurrent
programming
Pre-built functional
interfaces to provide target
types for lambda and
method references
Classes to support
functional-style operations
on streams of elements
StreamSupport retrofuture
Class providing low-level
utility methods for creating
and manipulating streams.
retroflow
20. Lambda Expressions
- Enables functional programming
- Readable and concise code
- Easier to use API’s and libraries
- Enables parallel processing
- Functional vs OOP
- Both achieve the same results
- Better, more maintainable code
- OOP, treats everything as an object, all code
blocks are associated with an object/class
- Lambda’s are functions, “Lambda Expression”
- String s = “Hello World”; // inline value
- Functions as values:
public final void runOnUiThread(Runnable action) {
throw new RuntimeException("Stub!");
}
runOnUiThread(new Runnable() {
@Override
public void run() {
print();
}
});
runOnUiThread(() -> print() );
21. Method Reference 1/2
- Shorthand syntax for a lambda expression
- Can’t be used for any/all method’s
- Replaces a single-method lambda expression
- Four types of Method References
- To use a method reference, you need a lambda
expression with one method
- To use a lambda expression, you first need a
functional interface (with one abstract method)
// Static Method
(args) -> Class.staticMethod(args)
Class::staticMethod
// Instance Method of an Object of a particular type
(args) -> obj.instanceMethod(args)
obj::instanceMethod
// Instance Method of an existing Object
(obj, args) -> obj.instanceMethod(args)
ObjectType::instanceMethod
// Constructor
(args) -> new ClassName(args)
ClassName::new
22. Instead of using an anonymous class, you can use
a lambda expression, and if this just calls one
method, you can use a method reference
List numbers = Arrays.asList("5", "3", "4", "1", "2");
// Anonymous Class
Collections.sort(numbers, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
Integer i1 = Integer.valueOf(s1);
Integer i2 = Integer.valueOf(s2);
return i1.compareTo(i2);
}
});
// Lambda Expression
Collections.sort(numbers, (Comparator<String>) (s1, s2) -> {
Integer i1 = Integer.valueOf(s1);
Integer i2 = Integer.valueOf(s2);
return i1.compareTo(i2);
});
// Method Reference
Collections.sort(numbers, this::compareAsInt);
Method Reference 2/2
23. Functional Interface 1/2
- Only one method declared in their definition
- Single Abstract Method “SAM”
- @FunctionalInterface with more than one
abstract method throws a compiler error
- Can declare abstract methods from Object
- considered SAM by definition
@FunctionalInterface
public interface Comparator<T> {
int compare(T var1, T var2);
boolean equals(Object var1);
default Comparator<T> reversed() {
/* .. */
}
/* .. */
}
@FunctionalInterface
public interface Runnable {
void run();
}
24. “I think the most important language change isn’t
lambdas, but static and default methods on
interfaces… The addition of default methods
removes many of the reasons to use abstract classes.
- Stephen Colebourne
Interface overhaul
25. Default Method’s
- Help extend interfaces without breaking
implementation classes
- Introduced to enhance Java 8 Collections API
- Any class in hierarchy can have matching
method signature
- Super.defaultMethod() irrelevant
- Can’t use method signatures from Object
- ‘Defender’ and ‘Virtual extension’ methods
- Is part of interface, can’t use for implementation
- Keeps security in mind
- Provide utility methods, checks, sort, etc…
- Can’t have class level static and instance method
with same signature
- “This static method cannot hide the instance
method from Object”
Static Method’s
26. Functional Interface 2/2
- Interface B extend A
- When A is @Functional… and doesn’t declare
any new abstract methods - B inherits
- re: SAM, one abstract method
- There are a lot of semantics and much more to
know!
// Lambda for big trade
ITrade bigTradeLambda = (Trade t) -> t.getQuantity() > 10000000;
// Lambda that checks if the trade is a new large google trade
ITrade issuerBigNewTradeLambda = (t) -> {
return t.getIssuer().equals("GOOG") &&
t.getQuantity() > 10000000 &&
t.getStatus().equals("NEW");
};
// Method that takes in list of trades and applies the
// lambda behavior for each of the trade in the collection
private List<Trade> filterTrades(ITrade tradeLambda, List<Trade>
trades) {
List<Trade> newTrades = new ArrayList<>();
for (Trade trade : trades) {
if (tradeLambda.check(trade)) {
newTrades.add(trade);
}
}
return newTrades;
}
@FunctionalInterface
public interface ITrade {
public boolean check(Trade t);
}
27. Summarizing Changes
- Abstract Classes aren’t irrelevant
- may use ‘access specifiers’ (public/private/…)
- Interface and fields are always public static final
- Pre-Java 8 interfaces practically written in stone
- Once declared/defined
- Interfaces needed to evolve to support lambda
- Add additional functionality to existing API
- Class.defaultMethod > Super.defaultMethod
- Immediate concrete class takes precedence
- Closest concrete implementation to subclass
wins inherited behaviour
- Use method references IF they make your code
cleaner!
- The real power is within recent API additions
- CompletableFuture, Streams, …
28. Augment Your Code
Optional<T>
Provide a type-level solution for representing optional values instead
of null references
Supplier<T> A supplier of results, accepting no args and returns <T>
Consumer<T>
BiConsumer<T,U>
Represent an operation (expected to operate via side-effects), accepts
one <T> or two <T,U> args, returns no result
Predicate<T>
BiPredicate<T,U>
Evaluates one <T> or two <T,U> args to a boolean result.
Function<T, R>
BiFunction<T,U,R>
‘Arity’ operation to encapsulate a method, evaluates one <T> or two
<T,U> args, and returns <R>
30. Two Sides Of The Same Coin
- Concurrency
- is designed for through awareness / decisions
- multiple tasks can run at the same time
- Parallelism
- tasks actually run at the same time
- In principle, you have two cores
- Are multiple threads concurrency or
parallelism?
- Converting to parallel is easy however, not
simple
- Build sequentially first, optimize after
- Without care, parallel can be seriously adverse
- Streams are divided into chunks, processed
independently, summarized after
31. Streams - A New Abstraction!
- A sequence of elements from a source that
supports aggregate operations
- No Storage
- Elements are conveyed from a source through a
pipeline of computational operations
- Data structure, generator functions, I/O channel
- Functional by nature
- Produce results, without modifying source
- Each operand produces a new stream
- Laziness seeking
- Most operations can be implemented lazily
- Divide into intermediate and terminal operations
- Intermediate operations always lazy
- Elements are consumable
- Visited once during the life of a stream
- Generate new streams to revisit same
- Don’t have a finite-size, can complete in
finite-time
32. aka. StreamSupport
- Chained operations can produce elements
“forever”
- Streams API will internally decompose query to
leverage multiple cores
- .sequential() vs .parallel(), is just a flag
- In effect once evaluated at terminal operation
- Parallel by default uses ForkJoinPool
- Just another ExecutorService
- Default pool size = runtime available processors +1
// Before
List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){
if(t.getType() == Transaction.GROCERY){
groceryTransactions.add(t);
}
}
Collections.sort(groceryTransactions, new Comparator(){
public int compare(Transaction t1, Transaction t2){
return t2.getValue().compareTo(t1.getValue());
}
});
List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
transactionsIds.add(t.getId());
}
// After
List<Integer> transactionsIds = StreamSupport.stream(transactionsIds)
.filter(t -> t.getType() == Transaction.GROCERY)
.sorted(comparing(Transaction::getValue).reversed())
.map(Transaction::getId)
.collect(toList());
33. Completable, the Future is
- Working with Future<T> may be blocking
- Managing multiple Future = “Callback Hell”
- CompletableFuture:
- implements Future, CompletionStage
- 50+ methods to orchestrate
- Supports dependant functions / triggers actions
- When multiple threads attempt to .complete,
.completeExceptionally, or .cancel - only one
succeeds
- .apply(Function f)
- .accept(Consumer c)
- .run(Runnable r)
- .supply(Supplier s)
- .then[apply/accept/run](), awaits completion
- combining complex tasks
- .either(), this one or that one, whichever is first
- .both(), awaits both
- .combine(), wait for first, take the result, then…
- ...async(), resubmits to thread pool
- Watch out for daemon threads!
34. That’s It Folks !
Adam Pelsoczi
apelsoczi@archlinux.us
https://github.com/apelsocz
https://github.com/eugenp/tutorials/tree/master/core-java-8