Malcolm
Malcolm

Reputation: 41498

How to add a dynamic library to Android?

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.

Upvotes: 12

Views: 18580

Answers (4)

Alexey
Alexey

Reputation: 146

There are two tricks to create dynamic load library in Android

  1. use sharedUserId in AndroidManifest for your applications and library project

  2. use dalvik.system.DexClassLoader to load library

Library Code:

It contains just java code without any Android specific entry points. AndroidManifest.xml just contains this android:sharedUserId attribute

AndroidManifest.xml

<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>

TestCore.java

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;
    }
}

Sample Application Code

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.

AndroidManifest.xml

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>

ITestCore.java

Library interface has to be declared in application to avoid use reflection

package com.example.testlib;

public interface ITestCore {
    String testString(String arg);
}

TestApplication.java

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();
        }
    }
}

Upvotes: 10

Malcolm
Malcolm

Reputation: 41498

Recent post on the Android blog explains exactly what I needed:

The Dalvik VM provides facilities for developers to perform custom class loading. Instead of loading Dalvik executable (“dex”) files from the default location, an application can load them from alternative locations such as internal storage or over the network.

The key is to use DexClassLoader to load the needed library. After that the code can be accessed either through interfaces, or by using reflection.

Upvotes: 3

adamp
adamp

Reputation: 28932

For static libraries of reusable classes you can use library projects. For dynamically interacting with other applications securely you can bind to a service.

Upvotes: 2

Yann Ramin
Yann Ramin

Reputation: 33197

Even if you get a Class object, the security model (binder) on Android will prohibit applications from reading into another application's directory.

The best you can do on a non-root phone is to create libraries via Intents.

Upvotes: 0

Related Questions