I tried to create a simple JNI application with new build system based on "experimental" com.android.model.application (com.android.tools.build:gradle-experimental:0.9.2).
./gradle/wrapper/gradle-wrapper.properties:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
./build.gradle:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.9.2'
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
./app/build.gradle:
apply plugin: 'com.android.model.application'
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def project_src_main = project.getRootDir().absolutePath + '/app/src/main'
model {
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
ndk {
moduleName = 'android-cpp-sandbox'
toolchainVersion "4.9"
platformVersion 16
stl 'gnustl_static'
abiFilters.addAll(['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'])
}
defaultConfig {
applicationId 'com.lamersoft.androidcppsandbox'
minSdkVersion.apiLevel 23
targetSdkVersion.apiLevel 25
}
sources {
main {
manifest {
source.srcDir project_src_main
}
java {
source.srcDir project_src_main + '/java'
}
res {
source.srcDir project_src_main + '/res'
}
jni {
source.srcDir project_src_main + '/jni'
}
}
}
productFlavors {
create("arm7") {
ndk.abiFilters.add('armeabi-v7a')
}
create("arm8") {
ndk.abiFilters.add('arm64-v8a')
}
create("x86") {
ndk.abiFilters.add('x86')
}
create("x86-64") {
ndk.abiFilters.add('x86_64')
}
create("all") {
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles.add(file('proguard-android.txt'))
}
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:25.3.1'
}
./app/src/main/res/layout/activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lamersoft.androidcppsandbox.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
./app/src/main/res/layout/content_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main"
tools:context="com.lamersoft.androidcppsandbox.MainActivity">
<TextView
android:id="@+id/sample_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
./app/src/main/java/com/lamersoft/androidcppsandbox/MainActivity.java:
package com.lamersoft.androidcppsandbox;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, stringFromJNI(), Snackbar.LENGTH_LONG).setAction("Action", null).show();
}
});
}
private native String stringFromJNI();
static {
System.loadLibrary("android-cpp-sandbox");
}
}
./app/src/main/jni/main.cpp:
#include <jni.h>
#include <string>
int counter = 1;
extern "C"
JNIEXPORT jstring JNICALL
Java_com_lamersoft_androidcppsandbox_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
char s[100];
snprintf(s, sizeof(s), "Hello from C++ #%d", counter++);
return env->NewStringUTF(s);
}
Modifications
To reproduce the issue I'll create a C++ class in a separate folder and include it to the project by adding the path with a cppFlag "-I".
./app/src/main/externalCPP/SampleClass.h:
#ifndef ANDROIDCPPSANDBOX_SAMPLECLASS_H
#define ANDROIDCPPSANDBOX_SAMPLECLASS_H
class SampleClass {
public:
SampleClass(int start);
int count();
private:
int counter = 0;
};
#endif //ANDROIDCPPSANDBOX_SAMPLECLASS_H
./app/src/main/externalCPP/SampleClass.cpp:
#include "SampleClass.h"
SampleClass::SampleClass(int start) {
counter = start;
}
int SampleClass::count() {
return counter++;
}
./app/src/main/jni/main.cpp:
#include <jni.h>
#include <string>
#include <SampleClass.h>
SampleClass *counter = new SampleClass(1);
extern "C"
JNIEXPORT jstring JNICALL
Java_com_lamersoft_androidcppsandbox_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
char s[100];
snprintf(s, sizeof(s), "Hello from C++ #%d", counter->count());
return env->NewStringUTF(s);
}
./app/build.gradle:
only one line added:
...
model {
android {
...
ndk {
...
cppFlags.add('-I' + project_src_main + '/externalCPP')
...
As you can see, the project does not compile. It fails with error: undefined reference to 'SampleClass::count()' and error: undefined reference to 'SampleClass::SampleClass(int)'.
The same happens when I try to use third-party prebuilt static library in the project by defining it this way:
...
model {
repositories {
libs(PrebuiltLibraries) {
ThirdPartyLibrary {
headers.srcDir project_src_main + '/externalCPP'
binaries.withType(StaticLibraryBinary) {
staticLibraryFile = file(project_src_main + '/externalCPP/lib/libSomeThirdPartyLibrary' + targetPlatform.getName() + '.a')
}
}
}
}
android {
...
sources {
main {
...
jni {
source.srcDir project_src_main + '/jni'
dependencies {
library 'ThirdPartyLibrary' linkage 'static'
}
}
}
}
...
Does anybody how to solve this with the experimental gradle plugin?