📜 ⬆️ ⬇️

Porting to Android

Porting to Android



At the end of this article, I announced a plan to make a port for Android. Here I will try to describe the problems that I encountered and the methods for solving them. I just want to say that the experience with Android at the moment is exactly 2 months and maybe some solutions are dangerous or even not acceptable on this platform.

Engine

')
The engine (hobby) has been in development for 10 years.
The engine is written entirely in C / C ++, before starting the porting to Android, it supported iOS and Windows.
Logic, rendering, sound - all in C / C ++.


File system


An important feature that made porting to Android very simple is the engine file system.

Pseudo code:
 class IStream { void setName(string name ) = 0; open() = 0; write() = 0; … = 0; } class FileStream : public IStream {   fopen,fread... POSIX API. } class DataPackStream : public IStream {     .pak  Core::Meta* m_packFileStreamMeta;; /// m_packFileStreamMeta->Create() IStream*         .pak  } 


So: all work with files in the engine is based on the IStream interface, the engine architecture supports the factory of objects, and its customization.

Code example:
 Core :: FileStream :: Type () -> setFactoryMeta (DataPack :: DataPackStream :: Type ());

 FileStream * filestream1 = FileStream :: Create ();
 // the same will happen with new FileStream ()
 FileStream * filestream2 = FileStream :: CreateExact ();

 filestream1 will be a DataPackStream type
 filestream2 will be of type FileStream


Nothing new and everything is pretty standard.

Since all the resources are zaproprotserenny in the .pak file, then for Android the system had to write its own implementation of IStream specifically for working with Java.

Initializing this resource in Java
 AssetFileDescriptor RawAssetsDescriptor = this.getApplicationContext ()
				 .getResources (). openRawResourceFd (R.raw.data000);
 if (RawAssetsDescriptor! = null)
 {
     FileInputStream fis = RawAssetsDescriptor.createInputStream ();
     NativeMethods.dataPackChannel = fis.getChannel ();
     NativeMethods.dataPackOffset = RawAssetsDescriptor.getStartOffset ();
     NativeMethods.dataPackSize = RawAssetsDescriptor.getLength ();
 }


Native class all calls open, read, seek translates again in Java
 class AndroidPackStream: public IStream
 {
 // ....
     // read example
     size_t read (void * buffer, size_t size, size_t count)
     {
       if (JavaHelpers :: m_pClass)
       {
         jmethodID mid = JavaHelpers :: GetEnv () -> GetStaticMethodID (JavaHelpers :: m_pClass, "freadDataPack", "(I) I");
         int res = JavaHelpers :: GetEnv () -> CallStaticIntMethod (JavaHelpers :: m_pClass, mid, (int) size * count);
	 jfieldID field = JavaHelpers :: GetEnv () -> GetStaticFieldID (JavaHelpers :: m_pClass, "byteBuffer", "Ljava / nio / MappedByteBuffer;");
	 jobject obj = JavaHelpers :: GetEnv () -> GetStaticObjectField (JavaHelpers :: m_pClass, field);
	 uint8_t * pData = (uint8_t *) JavaHelpers :: GetEnv () -> GetDirectBufferAddress (obj);
	 memcpy (buffer, pData, size * count);
	 JavaHelpers :: GetEnv () -> DeleteLocalRef (obj);
	 return res / size;
     }
 }


Java implementation of reading this stream with returning data back to the nytiv
 public static int freadDataPack (int count)
 {
     long curPos = dataPackChannel.position ();
     int countReaded = count;
     byteBuffer = dataPackChannel.map (MapMode.READ_ONLY, dataPackChannel.position (), count);
     byteBuffer.load ();
     dataPackChannel.position (curPos + countReaded);
     return countReaded;
 }


The important point is that the apk collector compresses all resources, and we want to read the file as if it is just lying in the file system. In order for our .pak file not to be compressed inside apk - you must change the extension to one of those that will tell the packer not to compress these files when packing (details ponystyle.com/blog/2010/03/26/dealing-with-asset -compression-in-android-apps ). I chose .imy.
Downloading resources in this way is very fast, for example, on the Kindle Fire is faster than on the iPad 1
I will stipulate that such a trick can be turned on if you do not have a lot of data.
For a large amount of data - you can unpack the data on the internal media (for example, when you first start) and directly use them through the functions fopen fread (in my case, not through the replaced FileStream), etc.

Sound

For Windows and iOS, OpenAL was used. OpenAL for Android has been compiled thanks to pielot.org/2010/12/14/openal-on-android . He did not work immediately, but only after the corrections that are described in the comments on the website.
The build for Android is built only for Arm v7 - because the build of the port for Arm v6 OpenAL sometimes led to lags, in iOS OpenAL the mixer works faster and without lags (even on ArmV6, for example, on the iPod Touch 2G).

Interaction with native C / C ++ code

A very simple bundle of classes is responsible for handling calls to platform dependent implementations.
Pseudo code:

 class IPlatfomCommandFeedback
 {
     void onResponse (string &);
 };
 class IPlatfomCommand
 {
     string execute (string & command, IPlatfomCommandFeedback * feebback);
 };


For iOS, its implementation on Objective-C and for Android, its implementation on JNI-> Java.

Rendering

The nave part works using OpenGL, minimal changes have been made to Android. As it turned out later it was not enough. The fact is that on Windows and on iOS textures are not lost when the application goes into the background, but for Android it always happens. In the engine, there was already a texture manager (mainly for debugging) and adding reloading resources was not difficult.

Assembly

In the very first article I wrote about how I initially compiled iOS using the toolchain and I had customized makefiles. That's where it came in handy for me. Targets for assembling using the Android NDK were added, a step was added to Eclipse builders and everything took off. Yes, you can use the build system from Android NDK samples. I only used it to find out the parameters that are used for gcc calls.

Openfenit admob

The integration of both libraries went without problems - clearly according to the instructions of the developers

Proguard

Everything is more or less clear here - you just need to take care that your JNI Native binds don't fail.

Development tools

Iron: Kindle Fire, and a couple of tablets and mobile phones of friends (for testing).
Soft: Eclipse with plugins

Shock number 1

Android Market is not an AppStore. Everything is different there. There is no category New.
For comparison, on the AppStore on the first day of release there were 800+ races, in the second 2000+, on the Andoid Market the official first hundred races were received only on the third day. The promotion activity for both platforms was the same.

Shock number 2

If your application is free, you can freely distribute apk, anyone can install this application from any source. If you don’t do it, others will do it for you.

Shock number 3

The bestiary of devices is great. The number of problems, respectively.

Amazon

The review process on Amazon AppStore for Android takes a week, plus a few more days until the app appears on the AppStore list on Kindle Fire. The number of downloads and dynamics corresponds to the Android Market

Advertising

Rays of hatred of the Corporation of Good - already a week as an advertising banner was sent in April. Still silence.

Money

At the moment, the amount of money (let me remind you in the game is only advertising) that the iOS version brings is 40 (forty) times more than the Android version. It is clear that the comparison is not correct and we must wait at least another couple of months until the number of regular players per day stabilizes.

UPDATE: In the sound section, I described the reason for the assembly only for ArmV7 Android devices.

Source: https://habr.com/ru/post/139293/


All Articles