简体   繁体   中英

Android NDK - referencing C++ classes in native functions

I'm really new to Android NDK, and have the following issue.

I have a file within my JNI folder named 'get-raw-image.cpp' (trying to integrate from here ), and within it I've made a function to call natively with the Java android code.

extern "C"{
    void Java_com_example_ndksetup_MainActivity_testTest(JNIEnv * env, jobject ths){
        __android_log_print(ANDROID_LOG_DEBUG, "LOG_TAG", "Testing");

        ScreenshotClient screenshot;
        //screenshot.update();
    }
}

I'm trying to reference the ScreenshotClient class (within this same file) and create a new instance of it, but keep getting this error when I build/compile:

"undefined reference to 'android::ScreenshotClient::ScreenshotClient()' collect2: ld returned 1 exit status"

Here is what the ScreenshotClient class looks like, any help is appreciated, thanks.

class ScreenshotClient {
    /*
    sp<IMemoryHeap> mHeap;
    uint32_t mWidth;
    uint32_t mHeight;
    PixelFormat mFormat;
    */
    char data[1024]; //android 4.2 embed CpuConsumer::LockedBuffer here which cause more space
public:
    ScreenshotClient();

#if defined(TARGET_ICS)
    // frees the previous screenshot and capture a new one
    int32_t update();
#endif
#if defined(TARGET_JB)
    // frees the previous screenshot and capture a new one
    int32_t update(const sp<IBinder>& display);
#endif
    // pixels are valid until this object is freed or
    // release() or update() is called
    void const* getPixels() const;

    uint32_t getWidth() const;
    uint32_t getHeight() const;
    uint32_t getStride() const; //base + getStride()*bytesPerPixel will get start address of next row
    int32_t getFormat() const;
    // size of allocated memory in bytes
    size_t getSize() const;
};

#if defined(TARGET_JB)
class SurfaceComposerClient {
public:
    //! Get the token for the existing default displays.
    //! Possible values for id are eDisplayIdMain and eDisplayIdHdmi.
    static sp<IBinder> getBuiltInDisplay(int32_t id);
};
#endif

class ProcessState {
    char data[1024]; //please adjust this value when you copy this definition to your real source!!!!!!!!!!!!!!!!!!!!!!!
public:
    static sp<ProcessState> self();
    void startThreadPool();
};

} //end of namespace android

using android::ScreenshotClient;
using android::sp;
using android::IBinder;
#if defined(TARGET_JB)
using android::SurfaceComposerClient;
#endif
using android::ProcessState;

#endif //} end of "if defined(TARGET_ICS) || defined(TARGET_JB)"

edit: I made the native function static in the extern "C" section and now have the following errors when I call it, but it is compiling now at least.

06-17 16:47:09.365: E/AndroidRuntime(18566): FATAL EXCEPTION: main 06-17 16:47:09.365: E/AndroidRuntime(18566): java.lang.IllegalStateException: Could not execute method of the activity 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.view.View$1.onClick(View.java:3699) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.view.View.performClick(View.java:4223) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.view.View$PerformClick.run(View.java:17281) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.os.Handler.handleCallback(Handler.java:615) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.os.Handler.dispatchMessage(Handler.java:92) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.os.Looper.loop(Looper.java:137) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.app.ActivityThread.main(ActivityThread.java:4898) 06-17 16:47:09.365: E/AndroidRuntime(18566): at java.lang.reflect.Method.invokeNative(Native Method) 06-17 16:47:09.365: E/AndroidRun time(18566): at java.lang.reflect.Method.invoke(Method.java:511) 06-17 16:47:09.365: E/AndroidRuntime(18566): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1008) 06-17 16:47:09.365: E/AndroidRuntime(18566): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:775) 06-17 16:47:09.365: E/AndroidRuntime(18566): at dalvik.system.NativeStart.main(Native Method) 06-17 16:47:09.365: E/AndroidRuntime(18566): Caused by: java.lang.reflect.InvocationTargetException 06-17 16:47:09.365: E/AndroidRuntime(18566): at java.lang.reflect.Method.invokeNative(Native Method) 06-17 16:47:09.365: E/AndroidRuntime(18566): at java.lang.reflect.Method.invoke(Method.java:511) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.view.View$1.onClick(View.java:3694) 06-17 16:47:09.365: E/AndroidRuntime(18566): ... 11 more 06-17 16:47:09.365: E/AndroidRuntime(18566): Caused by: java.lang.UnsatisfiedLinkError: Native method not found: com.example.ndksetup.MainActivity.testTest: ()V 06-17 16:47:09.365: E/AndroidRuntime(18566): at com.example.ndksetup.MainActivity.testTest(Native Method) 06-17 16:47:09.365: E/AndroidRuntime(18566): at com.example.ndksetup.MainActivity.t(MainActivity.java:72) 06-17 16:47:09.365: E/AndroidRuntime(18566): ... 14 more

Here is my Android.mk make file:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_LDLIBS := -llog

LOCAL_MODULE    := ndksetup
LOCAL_MODULE := libgui 
LOCAL_SRC_FILES := fake_libgui.cpp
LOCAL_SRC_FILES := get-raw-image.cpp
LOCAL_SHARED_LIBRARIES += libgui

include $(BUILD_SHARED_LIBRARY)

second attempt : You cannot declare a JNI method static , because it must be "visible" to dynamic loader.

first attempt : On ICS or later, class ScreenshotClient is part of libgui.so (earlier versions had this class in the system library called libsurfaceflinger_client.so ) You can pull it from your device or emulator, with command

adb pull /system/lib/libgui.so c:\android\libs\libgui.so

Now in your Android.mk , add LOCAL_LDFLAGS += c:/android/libs/libgui.so to your module.

You will see a warning from ndk-build :

Android NDK: WARNING:jni/Android.mk: non-system libraries in linker flags: c:/android/libs/libgui.so

This is the price of using non-public APIs in ndk-build .

expecting the future questions : Make sure that your app has all relevant permissions to access the screen. See How to use ScreenShotClient in my android application for related discussion.

UPDATE

The referenced GitHub uses fake libs instead of pulling them from the device. It is easy to implement this with Android.mk . Get fake_libbinder.cpp to LOCAL_PATH directory, and add the following section into your Android.mk :

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE   := libibgui
LOCAL_SRC_FILES := fake_gui.cpp
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_MODULE    := ndksetup
LOCAL_SRC_FILES := get-raw-image.cpp
LOCAL_SHARED_LIBRARIES += libgui
include $(BUILD_SHARED_LIBRARY)

Now add LOCAL_SHARED_LIBRARIES += libgui to your main module.

You will probably need same for the fake libbinder . Still, your app will use the real libbinder and libgui libraries from the device /system/lib . Still, it depends on system calls not being changed.

I am the author of 'get-raw-image.cpp'. About your question, 1: You should be aware that your app will failed due to ScreenshotClient::update method require FRAME_BUFFER_ACCESS permission which normal java app does not have this permission. (Adb shell user is OK).

2: These native code need be linked to libgui.so and libbinder.so, but unfortunately, gcc will ask you for a lot of *.so referenced by them, I failed to do that, so i made a fake libgui.so, libbinder.so and link to get-raw-image. You can use my make file https://github.com/sjitech/sji-android-screen-capture/blob/master/src/android/ffmpeg/1_build_get-raw-image.sh to make these fake so files. (You need remove "rm libgui.so libbinder.so" in the script, then make, then extract so files).

Finally, you add libgui.so libbinder.so to your Android.mk for link.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM