Loading 3rd party shared libraries from an Android native activity
Asked Answered
B

4

16

I've built the Assimp library as a shared library. I've included it in my Android ndk project and it builds fine but when I load it I get the following error: Unable to load native library: My-Native-Activity.

(Perhaps I should add that my activity works fine when the library is not included and that I've checked the apk and on the device; the library is being added to the libs folder and installed on the device in /data/data/my-app/lib.)

I've done a lot of reading and it seems that the only way to solve this is to load them using System.loadLibrary before launching my native activity. I think I'd prefer load them dynamically using dlopen before taking that approach.

Am I correct in assuming that Android wont automatically load the shared libraries my native activity(i.e. my shared library) depends on?

I would build it as a static library but it was over 54Mb which wont work.

This is my Android.mk: I've tried adding -lassimp to LOCAL_LDLIBS. I'm not sure if that would be correct but it didn't make any difference.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE                        := assimp
LOCAL_SRC_FILES                     := libassimp.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE                        := native-activity
LOCAL_SRC_FILES                     := main.cpp 
LOCAL_LDLIBS                        := -llog -landroid -lEGL -lGLESv2 
LOCAL_STATIC_LIBRARIES              := android_native_app_glue
LOCAL_SHARED_LIBRARIES              := assimp
LOCAL_CPPFLAGS                      += -march=armv7-a -mfloat-abi=softfp
LOCAL_CFLAGS                := $(LOCAL_CPPFLAGS)
TARGET_ARCH_ABI                     := armeabi-v7a
LOCAL_C_INCLUDES            += $(LOCAL_PATH)
include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)
Bathhouse answered 12/1, 2012 at 0:13 Comment(2)
I am working on this problem right now. I can see my shared .so library appearing in the data folder. However android_main never gets called, if I compile the main library linking the shared it just won't load.Genuine
Did you get assimp to work on Android? Could you put some example code on github? I'm having trouble loading from the assets directory. Assimp allows to implement your own IOSystem and IOStream but I can't get it to work! Txn!Scofflaw
R
22

Subclassing android.app.NativeActivity is the simplest way to solve this problem.

package com.you;

public class MyNativeActivity extends android.app.NativeActivity {

    static {
       System.loadLibrary("assimp");
    }
 }

Then change your AndroidManifest.xml. Replace android.app.NativeActivity with MyNativeActivity and remove the tag hasCode="false".

As a side note, Android does search for dependencies when loading a shared library. But the scope of the search is limited to /system/lib.

Ration answered 26/10, 2012 at 6:9 Comment(2)
+1 for the hasCode="false" removal. I couldn't figure out why it wasn't building my java code :PHeader
I'm guessing you wouldn't happen to know if there is any way to accomplish this without referencing the package name explicitly as done with the package com.you; line at the beginning? I ask because the code of course fails when the package name is forcibly changed in the APK file. Java noob here.Mammalogy
B
7

You want to start the NativeActivity with a java activity. This way you can load the shared libraries before NativeActivity.

AndroidManifest.xml:

<application android:label="@string/app_name" android:hasCode="true">
    <activity android:name="DummyActivity"
            android:label="@string/app_name"
            android:configChanges="orientation|keyboardHidden">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name="android.app.NativeActivity"
            android:label="@string/app_name"
            android:configChanges="orientation|keyboardHidden">
        <meta-data android:name="android.app.lib_name"
                android:value="native-activity" />
    </activity>
</application>

DummyActivity.java:

package com.example.native_activity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class DummyActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {       
        System.loadLibrary("some_shared_lib");
        System.loadLibrary("native-activity");

        super.onCreate(savedInstanceState);

        Intent intent = new Intent(DummyActivity.this, android.app.NativeActivity.class);
        DummyActivity.this.startActivity(intent);
    }
}
Billmyre answered 14/7, 2012 at 16:58 Comment(1)
This is great and works for me. One nit: the line `package com.example.native_activity;' is outside the code box and I missed it. Turns out this is critical. It will compile without it, but I spent the next hour chasing why the class wouldn't load! I tried to edit, but unless I change at least six characters, it won't let me. :-|Tother
A
1

Using System.loadLibrary is the way to go.

Android won't automatically load dependent shared libraries for you. So you need to do something like this:

static {
    System.loadLibrary("assimp");  // dependency .so first
    System.loadLibrary("native-activity"); // dependent .so second
}

This code usually goes in the class which contains the native Java methods (i.e. methods defined with keyword native, which map through to native code). Because this code is executed in a static block it is executed when the Java classloader loads the class -- i.e. before any code in the class actually gets executed.

You shouldn't have to add any reference to assimp to LOCAL_LDLIBS because you're already referencing assimp via the LOCAL_SHARED_LIBRARIES declaration.

This question may be relevant.

Autoclave answered 5/2, 2012 at 23:39 Comment(2)
The static block approach won't work unless you subclass the NativeActivity class (which can't be the main way to go), there should be another way of loading all required libraries at run time I think?Talanian
your picture makes me dizzyUnbelt
L
0

1: U could not use dlopen, since System.loadLibrary is the only method u load a native library from Java layer. 2: Ur library path looks not right, the location should be something like /data/data/urapp/lib/

U need to zip ur library to ur apk file, and while installing, android will unzip it and put it to /data/data/urapp/lib/ automatically.

Hope above information is useful for u.

Loppy answered 12/1, 2012 at 0:33 Comment(2)
Thanks for the suggestion. I don't know why they went to that folder but they were packaged into an apk file as part of the ndk-build, through eclipse (I checked inside the apk archive for the libs directory). The apk was then installed using adb (again through eclipse) and that's where they ended up. I ran find using adb shell to locate them... I'll investigate further.Bathhouse
Ah, navigated to /data/data/my-app/ and did an ls -al. the libs folder there is a symbolic link to /mnt/asec/my-app/lib. I'll update the main post to reflect this. Thanks again.Bathhouse

© 2022 - 2024 — McMap. All rights reserved.