@giorgionatili
@giorgionatili
Be advised...
If you are using Android Studio and need to integrate native libraries in your app, you may have had to use some complex methods before, involving maven and .aar/.jar packages… the good news is you don’t need these anymore
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
arguments '-DANDROID_STL=c++_static'
}
}
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/includes/HelloMario.cpp
src/main/cpp/native-lib.cpp )
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
(You already know how to do it)
class MainActivity : AppCompatActivity() {
companion object {
fun loadLibrary() {
System.loadLibrary("native-lib")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadLibrary()
}
}
class NativeWrapper {
external fun sayHelloToJNI(): String
external fun sayHelloFromMario(name: String): String
}
extern "C" JNIEXPORT jstring
JNICALL
Java_io_a2xe_experiments_simplejnitest_NativeWrapper_sayHelloFromMario(
JNIEnv *env,
jclass type,
jstring name) {
const char *toSayHello = env->GetStringUTFChars(name, 0);
env->ReleaseStringUTFChars(name, toSayHello);
return env->NewStringUTF(HelloMario::greet(toSayHello).c_str());
}
val wrapper = NativeWrapper()
sample_text.text = wrapper.sayHelloFromMario("Mary")
@Test
fun useTheCLuke() {
val wrapper = NativeWrapper()
assertNotEquals("whatever", wrapper.sayHelloFromMario("Mary"))
}
/home/giorgio/apps/android-studio/jre/bin/java -...
com.intellij.rt.execution.application.AppMainV2 com.intellij.rt.execution.junit.JUnitStarter
-ideVersion5 io.a2xe.experiments.simplejnitest.ExampleUnitTest,injectTheCLuke
java.lang.UnsatisfiedLinkError: io.a2xe.experiments.simplejnitest.NativeWrapper
.sayHelloToJNI()Ljava/lang/String;
at io.a2xe.experiments.simplejnitest.NativeWrapper.sayHelloToJNI(Native Method)
at io.a2xe.experiments.simplejnitest.ExampleUnitTest.injectTheCLuke(ExampleUnitTest.kt:22)
Process finished with exit code 255
interface NativeSayHello {
fun sayHelloFromMario(name: String): String
}
class NativeWrapperMock : NativeSayHello {
override fun sayHelloFromMario(name: String): String {
return "Hello $name!"
}
}
class NativeWrapper : NativeSayHello {
init {
System.loadLibrary("native-lib")
}
external override fun sayHelloFromMario(name: String): String
}
class NativeWrapperSolver : NativeSayHello {
override fun sayHelloFromMario(name: String): String {
return implementation.sayHelloFromMario(name)
}
companion object {
var implementation: NativeSayHello = try {
NativeWrapper()
} catch (e: Throwable) {
NativeWrapperMock()
}
}
}
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun itShouldContainTheNameOfThePersonToSayHello() {
val wrapper = NativeWrapper()
assertTrue(wrapper.sayHelloFromMario("Mary")
.contains("Mary"))
}
}
open class SimpleJNITestApplication : Application() {
override fun onCreate() {
super.onCreate()
initializeNativeWrappers()
}
open fun initializeNativeWrappers() {
NativeLibsManager()
}
}
class RobolectricApplication : SimpleJNITestApplication() {
init {
ShadowLog.stream = System.out //Android logcat output.
}
override fun initializeNativeWrappers() {
// Load external libraries
}
}
val libsBasePath = File(File("").absolutePath + "/src/test/libs")
.absolutePath
var os = System.getProperty("os.name")
os = if (!TextUtils.isEmpty(os)) os else ""
val soFileList = arrayListOf<File>()
if (os.contains("Linux")) {
val linuxSysSoBasePath = "$libsBasePath/arch_x86-64/"
soFileList.addAll(addLibs(linuxSysSoBasePath))
}
for (soFie in soFileList) {
System.load(soFie.absolutePath)
}
@Test
fun `it should contain some cheering text`() {
val textView = mainActivity
.findViewById<TextView>(R.id.sample_text)
val helloMessage = textView.text.toString()
assert.that(helloMessage, startsWith("Hello"))
}
#include "gtest/gtest.h"
#include "includes/HelloMario.h"
TEST(SalutationTestCase, test_hello_with_name) {
EXPECT_EQ(string("Hello World!"),
HelloMario::greet("World"));
}
#include <fakeit.hpp>
using namespace fakeit;
struct SomeInterface {
virtual int foo(int) = 0;
virtual int bar(int,int) = 0;
};
Mock<SomeInterface> mock;
// Stub a method to return a value once
When(Method(mock,foo)).Return(1);
$ANDROID_NDK_ROOT/ndk-build -C $NDK_PROJECT_PATH APP_BUILD_SCRIPT
=Android.mk NDK_APPLICATION_MK=Application_test.mk NDK_LIBS_OUT
=../../../libs V=1 $1 > /dev/null
adb push libs/x86/module_under_test.so /data/local/tmp/
adb push libs/x86/your_unit_test_module /data/local/tmp/
adb shell chmod 775 /data/local/tmp/your_unit_test_module
adb shell "LD_LIBRARY_PATH=/data/local/tmp
/data/local/tmp/your_unit_test_module"
https://github.com/harshvs/android-gtest-driver
@giorgionatili