Reputation: 904
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/AndroidRuntime(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)
Upvotes: 1
Views: 4007
Reputation: 3185
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.
Upvotes: 1
Reputation: 57203
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.
Upvotes: 3