Interfacing with platform specific code

Oftentimes it can become necessary to access platform specific APIs, e.g., adding advertisement services or a leaderboard functionality. This can be achieved by allowing specific implementation to be defined through a common API interface.

Take the following example, which tries to use a very simple leaderboard API that is only available on Android. For other targets we simply want to log invocations or provide mock return values.

The Android API looks like this:

/** Let's assume this is the API provided by Swarm **/
public class LeaderboardServiceApi {
   public void submitScore(String user, int score) { ... }
}

The first step is to create an abstraction of the API in form of an interface.

The interface is put into the core project:

public interface Leaderboard {
   public void submitScore(String user, int score);
}

Next we create specific implementations for each platform and put these into their respective projects.

The following would go into the Android project:

/** Android implementation, can access LeaderboardServiceApi directly **/
public class AndroidLeaderboard implements Leaderboard {
   private final LeaderboardServiceApi service;

   public AndroidLeaderboard() {
      // Assuming we can instantiate it like this
      service = new LeaderboardServiceApi();
   }

   public void submitScore(String user, int score) {
      service.submitScore(user, score);
   }
}

The following would go into the desktop project:

/** Desktop implementation, we simply log invocations **/
public class DesktopLeaderboard implements Leaderboard {
   public void submitScore(String user, int score) {
      Gdx.app.log("DesktopLeaderboard", "would have submitted score for user " + user + ": " + score);
   }
}

The following would go into the HTML5 project:

/** Html5 implementation, same as DesktopLeaderboard **/
public class Html5Leaderboard implements Leaderboard {
   public void submitScore(String user, int score) {
      Gdx.app.log("Html5Leaderboard", "would have submitted score for user " + user + ": " + score);
   }
}

Next, the ApplicationListener gets a constructor to which we can pass the concrete Leaderboard implementation:

public class MyGame implements ApplicationListener {
   private final Leaderboard leaderboard;

   public MyGame(Leaderboard leaderboard) {
      this.leaderboard = leaderboard;
   }

   // rest omitted for clarity
}

In each starter class we then simply instantiate MyGame, passing the corresponding Leaderboard implementation as an argument, e.g., on the desktop:

public static void main(String[] argv) {
   LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
   new LwjglApplication(new MyGame(new DesktopLeaderboard()), config);
}

Further threading

A forum discussion on this matter, also mentioning iOS specific things.