Search code examples
androidc++android-ndkjava-native-interface

Android JNI - 'raw' function fails to compile


I am trying to work out why the following code won't work under JNI / Android.

If I uncomment the 'raw_function()' from the code below, the code fails to compile with the following message:

Error:error: linker command failed with exit code 1 (use -v to see invocation)

I have two problems:

1) I have no idea how to use -v to see invocation - where would I do this?

2) I have no idea why the code is not compiling when I uncomment that specific line. Moving that function into the class (Header_Test) also fixes the problem, but why?

I have included pretty much everything from the project below. The only areas of interest should be header_test.h and native-lib.cpp.

I realize that putting a function in the global namespace is bad practice. This code is purely for test purposes.

Project Details:

  • Android Studio 3.0.1
  • CMake 3.6.4111459
  • NDK 15.2.4203891
  • C++ 14 Standard

header_test.h

#ifndef TESTCPLUSPLUSCHRONO_HEADER_TEST_H
#define TESTCPLUSPLUSCHRONO_HEADER_TEST_H
#include <string>

class Header_Test {
public:
    static std::string get_name() {
        return "Header Test";
    }
};

/*std::string raw_function() {
    return "Raw function";
}

#endif //TESTCPLUSPLUSCHRONO_HEADER_TEST_H

native-lib.cpp

#include <jni.h>
#include <string>
#include "header_test.h"

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_test_android_testrawfunctiontest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject) {
    std::string hello;
    hello = Header_Test::get_name();

    return env->NewStringUTF(hello.c_str());
}

Extra Details (This shouldn't affect the main code):

header_test.cpp

#include "header_test.h"

MainActivity.java

package com.test.android.testrawfunctiontest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        String jniText = stringFromJNI();
        tv.setText(jniText);
        String filler = "-------------------";
        System.out.println(filler);
        System.out.println("Text from JNI: " + jniText);
        System.out.println(filler);
    }

    public native String stringFromJNI();
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)

add_library(native-lib SHARED
             src/main/cpp/native-lib.cpp
             src/main/cpp/header_test.cpp )

find_library(log-lib log )

target_link_libraries(native-lib ${log-lib} 

I am sure if I understood what exactly I was asking, I would be able to word this a lot better. I've run a lot of searches trying to find an answer for this, but I don't even know how to phrase the search.

Any comments pointing me in the right direction for this would be appreciated.


Solution

  • Your raw_function() 'appears' twice for the linker: once, from file native-lib.cpp, and also, from header_test.cpp. To avoid the clash, you can declare it either inline or static. The result will be different: in the latter case, your native-lib will have two independent functions raw_function(), each visible from its own cpp file:

    inline std::string raw_function() {
        return "Raw function";
    }
    

    or

    static std::string raw_function() {
        return "Raw function";
    }