Keeping it cross-platform friendly

I hope you have managed to keep up with where we are at the moment with the third-party libraries. This sounds a little daunting at first, but once you have done it a couple of times, you will see that it is pretty straightforward.

What we are going to look at now is how you would interact with one of these libraries if you were to use one.

FriendFace for Android

Carrying on with our made-up social network called FriendFace, we are going to implement the social network's Android integration that it has. It doesn't offer anything fancy other than the ability for us to post a player's name and score. Then, FriendFace does whatever it is that it does with the name and score; perhaps it shows it to the player's friends?

The following is how we are going to define the FriendFaceAPI class:

public class FriendFaceAPI {
  public void postScore(String name, int score) {
    //Some code, the library may be opensource or not – who knows!
  }
}

Pretty straight forward!

FriendFace's Android integration comes in the form of a Gradle-based Android library project. This means that we will have to add it to our Android subproject.

I have already created a project that includes all this—refer to the code provided in this chapter.

Once you have imported it in your IDE, if you look inside the Android subproject, you will see a libs directory that contains the FriendFace library project!

Now, what will we do? For the sake of simplicity, we will use the default project and submit a score every time the space bar is pressed.

Hang on! I bet you are thinking that the game code is in the Core subproject and FriendFace only offers the capability available in the Android subproject; how on earth am I going to bridge that gap?

Well, you would be correct in thinking that. We can't have the FriendFace library in the Core subproject; for example, what will happen when we try to run it on iOS? So, what we need to do is create an interface and implementation-style approach.

In our Core subproject, we will create an interface class that we will call as ScoreHandler and it will have the following code:

public interface ScoreHandler {
  void postScore(String name, int score);
}

Looks familiar? Well it should, as it has the same API as FriendFace. I should point out that we can get away with this for now as the API is quite simplistic; however, as you integrate more complex third-party services across your different platforms, it may not be as easy to unify this API with the social network.

Next, we are going to update the game class; in my case, it is just called as the MyGdxGame class so that it refers to this new interface. The class ends up looking like this:

public class MyGdxGame extends ApplicationAdapter {
  SpriteBatch batch;
  Texture img;
  ScoreHandler scoreHandler;

  @Override
  public void create() {
    batch = new SpriteBatch();
    img = new Texture("badlogic.jpg");
  }

  @Override
  public void render() {
    if (Gdx.input.isKeyPressed(Input.Keys.SPACE)) {
      scoreHandler.postScore("James", 1000);
    }

    Gdx.gl.glClearColor(1, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.begin();
    batch.draw(img, 0, 0);
    batch.end();
  }
}

So, as we said before, on the tap of a space bar, we will call the method. The quick thinkers among you will probably be screaming at the book saying "There is going to be a NullPointerException exception!" and you are quite right—however, we are not done yet!

We need to provide an Android implementation of our interface; we can do this by creating a class in our Android subproject called ScoreHandlerAndroid, and it will implement our ScoreHandler interface class as:

public class ScoreHandlerAndroid implements ScoreHandler {
  @Override
  public void postScore(String name, int score) {
    
  }
}

Right now, this code doesn't do anything—however, as it is in the Android subproject, where the FriendFaceAPI class is, we can delegate the method call to a reference of it!

Now, our class becomes like this:

public class ScoreHandlerAndroid implements ScoreHandler {
  private final FriendFaceAPI friendFaceAPI;
  public ScoreHandlerAndroid(FriendFaceAPI friendFaceAPI) {
    this.friendFaceAPI = friendFaceAPI;
  }

  @Override
  public void postScore(String name, int score) {
    friendFaceAPI.postScore(name, score);
  }
}

Excellent! Now, we pass in our reference and then delegate the call. Next, we need to create this reference in our AndroidLauncher class:

public class AndroidLauncher extends AndroidApplication {
  @Override
  protected void onCreate (Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    FriendFaceAPI friendFaceAPI = new FriendFaceAPI();
    ScoreHandlerAndroid scoreHandlerAndroid = new ScoreHandlerAndroid(friendFaceAPI);
    AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
    initialize(new MyGdxGame(scoreHandlerAndroid), config);
  }
}

Finally, we just need to update the game class again to get a constructor that accepts a ScoreHandler reference and sets the local variable. Here is the snippet to do so from the updated class:

ScoreHandler scoreHandler;

public MyGdxGame(ScoreHandler scoreHandler) {
  this.scoreHandler = scoreHandler;
}

There you have it. Now, you can access the FriendFace API and post a score.

What you will find is that, after setting a constructor on the game class, you need to go around and update the reference on all platforms, such as desktop, for example. Here, you may want to create a dummy implementation for each platform. How the dummy will function is really based on what you, and the game, are expecting to happen from the integration. Of course, you may have a desktop library for FriendFace. If you wish to implement it, it's excellent; add that library to the project and create another implementation. Simple!

A potential trap! (Android)

OK, imagine that you want to a create a few games now. You are fairly confident about how LibGDX works and you are going to integrate more third-party services—perhaps you want to add an advertisement to your game, which is not an uncommon scenario if you want to make a little bit of revenue from it.

You select your advertisement platform that you wish to use and add their library to your project. You start creating interfaces and implementations and run your game on Android. Then, you get to the point where you expect an advertisement to pop up and, all of a sudden, it crashes. You may be thinking that this is a bit odd. You check your code and double-check the documentation for Android that the third-party offers, but find that they are all the same and correct. You finally come back to this book and you are now reading these words. What you saw now is an error when two worlds collide; chances are that your advertising platform is required to run on an Android UI thread. However, the calls you are making come from the game thread—the OpenGL-enabled LibGDX thread—and Android is upset because you are calling the code from the wrong thread!

To fix this, we can use an object called Handler. This class allows us to send messages between the threads. When we create a Handler, we can specify a handleMessage() method that we can use to handle these messages. So, we may want something like this in our AndroidLauncher class:

private final Handler handler = new Handler() {
  @Override
  public void handleMessage(Message message) {
    if (message.what == 1) ad.show();
  }
};

Next, we just need a way to send a message. This is pretty straightforward as the Handler class has lots of useful methods; one which we will use is to call sendEmptyMessage(). So in our code, where we toggle for our advertisement, we can have the following code:

public void displayAdvert() {
  handler.sendEmptyMessage(1);
}

It is that easy! Of course, there are many complex things we can do, but I just wanted to highlight this scenario, as I have tripped over such a situation many times before.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.68.18