I want to add a non-native shared library to Android so every application on the device would be able to use it. I mean using packaged classes just like a core library as if they were present in the application itself.
I studied Android source code to figure out a way to add a new path to the application ClassLoader
and found out that it is created during startup and there's no way to change the paths later. I can use my own ClassLoader
, but all I will get after loading a class will be a reference to a Class
object. This way I will be bound to work through reflection mechanism, which is slower than the native execution system.
Is there any way to organise a shared library on Android?
Update: just to be clear here, I don't need interaction between applications. I need classes which I can reuse in any application. Also static libraries don't really suit my needs because this will bloat the applications.
To create a dynamic library in Linux, simply type the following command: gcc *. c -c -fPIC and hit return. This command essentially generates one object file .o for each source file .
A dynamic library is loaded into the address space during execution runtime or launch. When loaded at execution runtime, a dynamic library is known as a "dynamically loaded library" or "dynamically linked library." When loaded at launch, a dynamic library is known as a "dynamic dependent library."
Similar to the traditional Linux model, shared libraries in Android are relocatable ELF files that map to the address space of the process when loaded. To save memory and avoid code duplication, all shared objects shipped with Android are dynamically linked against the Bionic libc library [23].
There are two tricks to create dynamic load library in Android
use sharedUserId
in AndroidManifest for your applications and library project
use dalvik.system.DexClassLoader
to load library
It contains just java code without any Android specific entry points. AndroidManifest.xml just contains this android:sharedUserId
attribute
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testlib"
android:sharedUserId="com.example.testlib"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15" />
<application android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:theme="@style/AppTheme">
</application>
</manifest>
package com.example.testlib;
public class TestCore implements ITestCore{
private int count = 0;
public String testString(String arg) {
String res = arg + " " + count;
count++;
return res;
}
}
Application that uses the library. Here is just the AndroidManifest.xml and TestApplication.java which do the trick. All other application staff is as usual.
Be carefull to use same android:sharedUserId value in AndroidManifest.xml as library one
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testapp"
android:sharedUserId="com.example.testlib"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name=".TestApplication" >
<activity
android:name=".MainActivity"
android:label="@string/title_activity_main" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="android.app.Activity" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
Library interface has to be declared in application to avoid use reflection
package com.example.testlib;
public interface ITestCore {
String testString(String arg);
}
In applicatiopn's onCreate handler there is real work happens
package com.example.testapp;
import com.example.testlib.ITestCore;
import dalvik.system.DexClassLoader;
import android.app.Application;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.util.Log;
public class TestApplication extends Application {
ClassLoader libClassLoader;
@Override
public void onCreate() {
PackageManager pm = getPackageManager();
String libSrcPath = null;
for (ApplicationInfo app : pm.getInstalledApplications(0)) {
if (app.packageName.equals("com.rhomobile.testlibrary")) {
libSrcPath = app.sourceDir;
Log.d("TestApplication", ">>>>>>>>>>>>>>> package: " + app.packageName + ", sourceDir: " + app.sourceDir);
}
}
try {
libClassLoader = new DexClassLoader(libSrcPath, getDir("dex", 0).getAbsolutePath(), null, getClassLoader());
Class<?> clazz = libClassLoader.loadClass("com.rhomobile.testlib.TestCore");
try {
ITestCore core = (ITestCore)clazz.newInstance();
String str = core.testString("TestApplication 1:");
Log.i("TestApplication", ">>>>>>>>>>>>>>> output: " + str);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
Log.e("TestApplication", libClassLoader.toString());
e.printStackTrace();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With