Android Studio with experimental Gradle plug-in and CrystaX NDK
12/14/2015 06:20 AM

Earlier, we described how to use CrystaX NDK in Android Studio. Since that time Google announced a new (experimental) Gradle plug-in supporting NDK in Android Studio. Let's see how to use CrystaX NDK with this new plug-in.

To be consistent, we'll remaster the previous article with new conditions - i.e., this will again be a simple UI application where Boost.Serialization would be used. Using Boost is not necessary for understanding the new work scheme with the Gradle experimental plug-in; however, we've included Boost examples just to cover more use cases.

In the new Gradle plug-in, Google gave up on the old GNU Make-based build system (ndk-build). Now, Gradle's DSL used to describe every aspect of build process. It's good news for those who are not familiar with old-school GNU Make, and who want to do everything in IDE, asking IDE to manage the build process fully and not worrying about GNU Make files.

However, this approach has some drawbacks. The most important one is that, in this case, the developer is more involved in building the build file in Gradle DSL, and should know Groovy as well.

So let's see how it works.

Java part

First off, open Android Studio and create a new Android project there:

Select "Android 4.0.3" target:

Select blank activity:

Accept default names and press the "Finish" button:

Gradle

By default, Android Studio still uses the old Gradle plug-in. Let's switch to the new one (experimental).

First off, open and edit several files:

build.gradle.diff

gradle/wrapper/gradle-wrapper.properties.diff

app/build.gradle.diff

Now, we need to open local.properties and add the path to the CrystaX NDK, like below:

sdk.dir=/opt/android/android-sdk-mac
ndk.dir=/opt/android/crystax-ndk-10.3.1

For Windows users, backslashes and colons in the path should be escaped:

sdk.dir=C\:\\android\\android-sdk-mac
ndk.dir=C\:\\android\\crystax-ndk-10.3.1

And sync gradle changes:

Layout

Now, modify app/res/layout/content_main.xml so it looks like the following:

content_main.xml.diff
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
index ec3933f..077dacc 100644
--- a/app/src/main/res/layout/content_main.xml
+++ b/app/src/main/res/layout/content_main.xml
@@ -13,7 +13,9 @@
     tools:showIn="@layout/activity_main">

     <TextView
+        android:id="@+id/text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="Hello World!" />
+        />
+
 </RelativeLayout>
content_main.xml

MainActivity

Add the following lines into your MainActivity.onCreate():

TextView field = (TextView)findViewById(R.id.text);
field.setText(getGPSCoordinates(getFilesDir().getAbsolutePath()));

Add declaration of native method into MainActivity class:

private native String getGPSCoordinates(String rootPath);

Also, don't forget to add loading of the native library to the static initialization block:

static {
    System.loadLibrary("test-boost2");
}

Here is diff:

MainActivity.java.diff
diff --git a/app/src/main/java/net/crystax/examples/testboost2/MainActivity.java b/app/src/main/java/net/crystax/examples/testboost2/MainActivity.java
index f9c0821..0519122 100644
--- a/app/src/main/java/net/crystax/examples/testboost2/MainActivity.java
+++ b/app/src/main/java/net/crystax/examples/testboost2/MainActivity.java
@@ -8,9 +8,16 @@ import android.support.v7.widget.Toolbar;
 import android.view.View;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.widget.TextView;

 public class MainActivity extends AppCompatActivity {

+    static {
+        System.loadLibrary("test-boost2");
+    }
+
+    private native String getGPSCoordinates(String rootPath);
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -26,6 +33,9 @@ public class MainActivity extends AppCompatActivity {
                         .setAction("Action", null).show();
             }
         });
+
+        TextView field = (TextView) findViewById(R.id.text);
+        field.setText(getGPSCoordinates(getFilesDir().getAbsolutePath()));
     }

     @Override

Final content of MainActivity.java should be as below:

MainActivity.java

We have finished with the Java part of the application; let's switch to the native part now.

Native part

First we enable native build in the app/build.gradle:

app/build.gradle.diff

Then open MainActivity.java and add implementation of the native method:

This will create file app/src/main/jni/test-boost2.c. Now, rename it to the app/src/main/jni/test.cpp, and add two more files: app/src/main/jni/gps.hpp and app/src/main/jni/gps.cpp.

app/src/main/jni/gps.hpp
app/src/main/jni/gps.cpp
app/src/main/jni/test.cpp

Finally, we inject new tasks and helper functions to the app/build.gradle:

app/build.gradle.diff

File tree

The source file tree of the TestBoost2/app folder should look like the following:

.
├── app.iml
├── build.gradle
├── libs
├── proguard-rules.pro
└── src
    ├── androidTest
    │   └── java
    │       └── net
    │           └── crystax
    │               └── examples
    │                   └── testboost2
    │                       └── ApplicationTest.java
    ├── main
    │   ├── AndroidManifest.xml
    │   ├── java
    │   │   └── net
    │   │       └── crystax
    │   │           └── examples
    │   │               └── testboost2
    │   │                   └── MainActivity.java
    │   ├── jni
    │   │   ├── gps.cpp
    │   │   ├── gps.hpp
    │   │   └── test.cpp
    │   └── res
    │       ├── drawable
    │       ├── layout
    │       │   ├── activity_main.xml
    │       │   └── content_main.xml
    │       ├── menu
    │       │   └── menu_main.xml
    │       ├── mipmap-hdpi
    │       │   └── ic_launcher.png
    │       ├── mipmap-mdpi
    │       │   └── ic_launcher.png
    │       ├── mipmap-xhdpi
    │       │   └── ic_launcher.png
    │       ├── mipmap-xxhdpi
    │       │   └── ic_launcher.png
    │       ├── mipmap-xxxhdpi
    │       │   └── ic_launcher.png
    │       ├── values
    │       │   ├── colors.xml
    │       │   ├── dimens.xml
    │       │   ├── strings.xml
    │       │   └── styles.xml
    │       ├── values-v21
    │       │   └── styles.xml
    │       └── values-w820dp
    │           └── dimens.xml
    └── test
        └── java
            └── net
                └── crystax
                    └── examples
                        └── testboost2
                            └── ExampleUnitTest.java

Final result

That's it! Now build the project as usual (Build -> Make Project) and run it on the device. Here is a screenshot of the running application:

Sample project

To make it easier, we've uploaded a ready-to-use project to GitHub. Check it out and enjoy!

Back
Home
Map
Back
Home
Map

Our contributors: