Java 8 Language features supported in Android N: Lambdas

One of the new additions the Android N brings is support for several Java 8 language features:
Lambda expressions
Default and static interface methods, and
Repeatable annotations

In this blog post we will take a look at the lambda expressions.

Setting up development environment

In order to start using these features however, the development environment needs some adjustments. The support of Java 8 language features requires a new compiler called Jack, which currently is supported only in Android Studio 2.1. So if you want to use Java 8 language features, you need to use Android Studio 2.1.

Android documentation describes in details how to setup your development environment for Java 8 support.

Enabling support for Java 8

To enable support for Java 8 in your Android project, the build.gradle file has to be adjusted by setting the compileOptions to version 1.8 and jackOptions enabled.

android {
  ...
  defaultConfig {
    ...
    jackOptions {
      enabled true
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

Lambda Expressions

Lambda expressions are a new feature included in the Java 1.8. A lambda expression basically is a block of code that can be passed around to be executed later. This, in fact, is very similar to anonymous classes. However, unlike anonymous classes, lambda expressions are succinct, as a result producing less verbose code.

Consider the following 3 examples:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Log.d(TAG, "Clicked.");
    }
});

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         Log.d(TAG, "Position: " + position);
         Log.d(TAG, "Id: " + id);
    }
 });

Runnable runnable = new Runnable() {
    @Override
     public void run() {
         Log.d(TAG, "From Runnable");
     }
};
new Thread(runnable).start();

Converting the above pieces of code to use lambda expressions will look like this:

button.setOnClickListener(view -> Log.d(TAG, "Clicked."));

listView.setOnItemClickListener((parent, view, position, id) -> {
     Log.d(TAG, "Position: " + position);
     Log.d(TAG, "Id: " + id); 
}

Runnable runnable = () -> Log.d(TAG, "From Runnable"); 
newThread(runnable).start(); 

 

It is clear that the code with the lambdas is shorter.  Specifically, this comes from:

– removal of class instantiation, eg.: new View.OnClickListener(),
– removal of method name, return type, and access modifier, eg.: public void onClick(View view)
– as well as removal of parameter types, eg.: only view, instead of View view.

And what is left, are the actual parameters and the method body.

Lambda Expression syntax

The basic format of a lambda expression is: a list of comma separated parameters, the “->” symbol, and the body.

param1, param2, paramN -> { // body }

1. Parameter types are optional, but they can be also specified in the expression:

// without parameter types
(parent, view, position, id) -> {
      Log.d(TAG, "Position: " + position);
      Log.d(TAG, "Id: " + title);
}

// with parameter types
(AdapterView<?> parent, View view, int position, long id) -> {
      Log.d(TAG, "Position: " + position);
      Log.d(TAG, "Id: " + title);
}

2. If the body of the method has only one line, then the curly brackets can be ommited:

// with curly brackets
(parent, view, position, id) -> { processItemClick(position); }

// without curly brackets
(parent, view, position, id) -> processItemClick(position)

3. If the expression has only one parameter, then the parenthesis around parameter can be omitted:

// with parenthesis
(view) -> Log.d(TAG, "Clicked.")

// without parenthesis
view -> Log.d(TAG, "Clicked.")

4. The return keyword is optional if the body has a single expression to return the value;

// example interface
public interface Calculator {
        int calculate(int a, int b);
}

// with return
Calculator calculator = (a, b) -> {return a + b;};

// without return
Calculator calculator = (a, b) -> a + b;

5. Lambda expression that does not take any arguments:

() -> Log.d(TAG, "From Runnable")

Functional interfaces

Lambdas are treated as instances of a special interface type called functional interface. A functional interface in fact is nothing but an interface with a single abstract method. We already saw several examples of such interfaces; Runnable, Callable, OnClickListener, OnItemClickListener are all examples of functional interfaces.

A new annotation called @FunctionalInterface was introduced to mark an interface as such.

@FunctionalInterface
public interface Calculator {
    int calculate(int a, int b);
}

The annotation is optional, but it is highly recommended; it clearly communicates the intent of the interface and it also allows the compiler to perform additional checks.

@FunctionalInterface
public interface Calculator {

    int calculate(int a, int b);

    // *** this will break compilation ***
    void print(String result);
}

@FunctionalInterface
public interface Calculator {

    int calculate(int a, int b);

    // this will NOT break the compilation,
    // because print is a default method
    default void print(String result){
        Log.d(TAG, result);
    }
}

Lambda expressions might led you to the wrong idea that they are something that is going to replace anonymous classes, and now seeing that a lambda expression can be converted from an interface with only one abstract method, you might wonder how useful is this. If this is so, then I would like to recall that a lambda expression is a mechanism that allows you to pack functionality and pass it around, in a succinct way. So no, lambdas, are not intended to replace anonymous classes.

Advertisements

Open-sourcing Explore

Explore is a simple app I wrote in my free time some time ago. The idea behind it is to help you find a place you would like to visit (eg.: country or city), by searching for information about that place in different sources and then making a summary of this information and presenting it to you.

More specifically, it searches for information in the following sources:

  • Youtube for videos
  • Flickr for photos
  • Wikipedia for description, and
  • Google Places for attractions (eg. museums, restaurants, etc)

For anyone interested in the source code, the source code is available on Github.

 

2013 in review

The WordPress.com stats helper monkeys prepared a 2013 annual report for this blog.

Here’s an excerpt:

The Louvre Museum has 8.5 million visitors per year. This blog was viewed about 220,000 times in 2013. If it were an exhibit at the Louvre Museum, it would take about 9 days for that many people to see it.

Click here to see the complete report.

I got interviewed by Android Dream Revised blog

Past week I gave an interview for Android Dream Revised blog. Bartek, the author of the blog, is an android developer who is making Android apps as a hobby. Periodically he interviews different android indie developers that share their experience and great tips about game engines, ad networks, tools they use, and how much do they make.

In this interview I talk about who I am, what do I do in my life, how do I got started with Android, what libraries and resources I use, and other info you may find useful.

Bartek interviews android indie developers on a regular basis, so make sure to subscribe to his blog and get the most out of it.

Your Google Play Publisher Console has been terminated, because of Copyright infringement..

This morning I found an email in my inbox that looked like this:
fake email google console
The first impression was like “What?!”. It’s true that recently Google Play Developer Policies were modified, and I heard stories from developers who had their accounts terminated because of violations of content policy. So in the current context this email seemed like true.

However, after a closer examination I noticed a few suspect things.
1. Firstly, the email address ivopi44@abv.bg was too suspicious to believe that it is from Google.
2. Secondly, the email was sent not only to me, but to a list of people.
3. All the links appearing in the email pointed to the same and one url.
4. And lastly, the real urls were masked behind goo.gl URL Shortener, which again I found it suspicious.

So I opened one of the links in the email in a private window, and the following page was displayed.
fake_google
The google Sign in box, the text that appears in the page, and the color scheme, makes it appear as something known for an Android developer.
However if you take a closer look at the page url, you may notice that the page is not hosted on Google servers, but on some address, savegaselectricity(dot)com. Also, it has a parameter named “continue” with the real google play page as value. Probably after successfully logging, to redirect you to real Google Play Developer Console page, so you won’t see notice anything strange.

The technique used by the attacker in fact is not new, it is one variation of impersonation attacks, where an attacker impersonates a legitimate site, tricking the users to log in and stealing their passwords.

So, if you will receive such an email, delete it, your google play account most probably is ok. If however you already used the fake page to login, change your password immediately.

As a rule of thumb, always be skeptical.
Do not click on links, download files or open attachments in emails from unknown senders. If you get one of these emails and are worried that there may be a real problem with your account, open up a new browser window, go directly to your Developer Console site and sign in there.

Caching Objects in Android Internal Storage

Android provides several options for persisting application data, such as SQLite Databases, SharedPreferences, internal and external storage.

In this post we’ll take a look how we can use the internal storage to persist application data. By default, files saved to internal storage are private to the application and they cannot be accessed by other applications. When the application is uninstalled, the files are removed.

To create and write a file to internal storage, openFileInput(); should be used. This opens a private file associated with the Context’s application package for writing. If the file does not already exits, then it is first created.

openFileInput() returns a FileInputStream, and we could use its write() method, which accepts a byte array as argument, to write the data. However, in the below example we will wrap the FileInputStream into an ObjectOutputStream which provides 2 convenient methods  for writing and reading objects: writeObject() and readObject().

So, lets  pretend that we need to save a List of some objects. The model class of our object looks like this:

public class Entry implements Serializable{
   private String name;

   public Entry(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }
}

Make sure that the model class implements Serializable, otherwise you may get a java.io.NotSerializableException when attempting to write the object on internal storage.

Below is the utility class that provides 2 methods, one for storing objects to internal storage, and another for retrieving objects from internal storage.

public final class InternalStorage{

   private InternalStorage() {}

   public static void writeObject(Context context, String key, Object object) throws IOException {
      FileOutputStream fos = context.openFileOutput(key, Context.MODE_PRIVATE);
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(object);
      oos.close();
      fos.close();
   }

   public static Object readObject(Context context, String key) throws IOException,
         ClassNotFoundException {
      FileInputStream fis = context.openFileInput(key);
      ObjectInputStream ois = new ObjectInputStream(fis);
      Object object = ois.readObject();
      return object;
   }
}

An this is how InternalStorage class can be used to persist and retrieve data from internal storage.

// The list that should be saved to internal storage.
List<Entry> entries = new ArrayList<Entry>();
entries.add(new Entry("House"));
entries.add(new Entry("Car"));
entries.add(new Entry("Job"));

try {
   // Save the list of entries to internal storage
   InternalStorage.writeObject(this, KEY, entries);

   // Retrieve the list from internal storage
   List<Entry> cachedEntries = (List<Entry>) InternalStorage.readObject(this, KEY);

   // Display the items from the list retrieved.
   for (Entry entry : cachedEntries) {
     Log.d(TAG, entry.getName());
   }
} catch (IOException e) {
   Log.e(TAG, e.getMessage());
} catch (ClassNotFoundException e) {
   Log.e(TAG, e.getMessage());
}