76

Is there any way to make an Android application to download and use a Java library at runtime?

Here is an example:

Imagine that the application needs to make some calculations depending on the input values. The application asks for these input values and then checks if the required Classes or Methods are available.

If not, it connects to a server, downloads the needed library, and loads it at runtime to calls the required methods using reflection techniques. The implementation could change depending on various criteria such as the user who is downloading the library.

Shlublu
  • 10,917
  • 4
  • 51
  • 70
llullulluis
  • 3,472
  • 3
  • 27
  • 30

7 Answers7

98

Sorry, I'm late and the question has already an accepted answer, but yes, you can download and execute external libraries. Here is the way I did:

I was wondering whether this was feasible so I wrote the following class:

package org.shlublu.android.sandbox;

import android.util.Log;

public class MyClass {
    public MyClass() {
        Log.d(MyClass.class.getName(), "MyClass: constructor called.");
    }

    public void doSomething() {
        Log.d(MyClass.class.getName(), "MyClass: doSomething() called.");
    }
}

And I packaged it in a DEX file that I saved on my device's SD card as /sdcard/shlublu.jar.

Then I wrote the "stupid program" below, after having removed MyClass from my Eclipse project and cleaned it:

public class Main extends Activity {

    @SuppressWarnings("unchecked")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        try {
            final String libPath = Environment.getExternalStorageDirectory() + "/shlublu.jar";
            final File tmpDir = getDir("dex", 0);

            final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader());
            final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass");

            final Object myInstance  = classToLoad.newInstance();
            final Method doSomething = classToLoad.getMethod("doSomething");

            doSomething.invoke(myInstance);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

It basically loads the class MyClass that way:

  • create a DexClassLoader

  • use it to extract the class MyClass from "/sdcard/shlublu.jar"

  • and store this class to the application's "dex" private directory (internal storage of the phone).

Then, it creates an instance of MyClass and invokes doSomething() on the created instance.

And it works... I see the traces defined in MyClass in my LogCat:

enter image description here

I've tried on both an emulator 2.1 and on my physical HTC cellphone (which is running Android 2.2 and which is NOT rooted).

This means you can create external DEX files for the application to download and execute them. Here it was made the hard way (ugly Object casts, Method.invoke() ugly calls...), but it must be possible to play with Interfaces to make something cleaner.

Wow. I'm the first surprised. I was expecting a SecurityException.

Some facts to help investigating more:

  • My DEX shlublu.jar was signed, but not my app
  • My app was executed from Eclipse / USB connection. So this is an unsigned APK compiled in DEBUG mode
Shlublu
  • 10,917
  • 4
  • 51
  • 70
  • 4
    Great! This is really what I was looking for!! As you can see here (http://groups.google.com/group/android-security-discuss/browse_thread/thread/e928d79627867246) there isn't really a security problem because the code will execute with the permission of your app. – llullulluis Jul 28 '11 at 15:55
  • That makes complete sense, even if surprising at first glance. Thanks for the heads-up! – Shlublu Jul 28 '11 at 16:29
  • 3
    have a look at this too: http://android-developers.blogspot.com/2011/07/custom-class-loading-in-dalvik.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogspot%2FhsDu+%28Android+Developers+Blog%29 –  Jul 28 '11 at 20:18
  • 1
    Excellent, thank you! Yes, tis is exactly the same logic. It must be quite unusual to exceed 64k methods, so the valid use for this is definitely plugins. – Shlublu Jul 28 '11 at 21:12
  • MThis must be an important question because Google answered it on its own Android developer blog! ;P – llullulluis Aug 03 '11 at 09:22
  • 1
    I know this an old thread, but I was hoping someone here could help. I am trying to do the same thing (dynamically load a plugin). Has anyone made this work by casting to an interface? – cstrutton Apr 22 '12 at 04:54
  • This is something I am currently working on. It is a bit cumbersome for now, but I'm quite sure there is a way to make it smart. – Shlublu Apr 24 '12 at 10:16
  • how you have converted classes.dex to jar? – Dhrupal Mar 03 '14 at 11:56
  • @Dhrupal I simply saved my DEX as shlublu.jar – Shlublu Mar 11 '14 at 16:54
  • @Shlublu If your class implements the interface and supposing that this interface is a priori known (ie., no reflection), what would be the problem to cast `myInstance`? – VH-NZZ May 25 '14 at 13:47
  • @okiharaherbst Playing with interfaces is the right way to go. It is just that I wasn't there yet when answering this post :) – Shlublu Sep 29 '15 at 06:45
  • Hello i tried doing the same way..i used eclipse to make a demo project i.e with Myclass and renamed classes.dex to testjar.jar. But in my main code i.e Main class i always get classnotfound exception... Any help will be much appreciated.. – Harry Sharma Oct 16 '15 at 11:46
  • @Hardeep It might be a path issue, or a packaging issue... looking at your code would help. Why not openeing a question? – Shlublu Oct 16 '15 at 12:36
  • @Shublu Thanks a ton for the reply. I saw lot many questions already opened but without any solution.. :( i can post my code.can you please chack..i am trying from last two days.. – Harry Sharma Oct 16 '15 at 12:38
  • @Hardeep I don't know if I'll check it personnaly or if someone else will, but if you post a clear question with it, why not referencing this question, it will certainly receive answers. – Shlublu Oct 16 '15 at 15:05
  • @shublu Thanks for the reply.I have made it to work finally. Now i want to implement a interface from the dynamically loaded jar file.Can you please help me in it. Thanks – Harry Sharma Oct 19 '15 at 05:55
  • @shublu i have opened a question can you or anyone else please help me with it...http://stackoverflow.com/questions/33212623/interacting-with-dynamically-loaded-library-using-dexclassloader – Harry Sharma Oct 19 '15 at 11:27
  • can you explain more that how did you make shlublu.jar from the MyClass? – faraz khonsari Oct 06 '16 at 04:59
  • @farazkhonsari It is as explained above, I just saved my resulting .dex as shlublu.jar – Shlublu Oct 06 '16 at 08:05
  • Shlublu I am new to android and use xamarin to develop apps. I have a question: - I want to create dll (Class Library) project then dynamically use it in my main app at run-time. Imagine my main app has a feature that has a lot of activities and when user buy it, it is downloaded to main app and then user can uses it. Can I do this with DexClassLoader? – Mohammad Afrashteh Jan 14 '18 at 18:01
  • Hi All, your discussion is very helpful, but still i have doubt, we can not start an Activity from external jar, if any one has done this, please confirm. – User10001 Sep 18 '18 at 10:33
  • @User10001 I didn't try actually. Should this not feasible, you external jar can provide the caller with the name of an Activity to start anyway. But remember your Activity will have to be declared in the manifest file. – Shlublu Sep 18 '18 at 10:53
  • Hi, according to me we can not startActivity, even have declared it in the manifest file. But we can use fragment to load the UI but for this fragment must specify the ui dynamically(Java Code) because your fragment will not find layout file. – User10001 Sep 18 '18 at 11:03
  • If i am wrong then please correct, to launch an activity we need context and .jar file do not have any context. System always look it into the default location and at there this activity does not exists. – User10001 Sep 18 '18 at 11:21
  • @User10001 We are going to take long in this comments section. How about giving a try and asking a question here on StackOverflow ? ;) – Shlublu Sep 18 '18 at 11:36
  • @Shlublu - Can you please have alook my question - https://stackoverflow.com/questions/67667827/classnotfound-exception-while-dynamically-loading-dex-file-in-android – node_analyser May 28 '21 at 07:11
16

Shlublu's anwser is really nice. Some small things though that would help a beginner:

  • for library file "MyClass" make a separate Android Application project which has the MyClass file as only file in the src folder (other stuff, like project.properties, manifest, res, etc. should also be there)
  • in library project manifest make sure you have:
   <application android:icon="@drawable/icon" 
                android:label="@string/app_name">
        <activity android:name=".NotExecutable" 
                  android:label="@string/app_name">
        </activity>
    </application>

(".NotExecutable" is not a reserved word. It is just that I had to put something here)

  • For making the .dex file, just run the library project as android application (for the compiling) and locate .apk file from the bin folder of the project.
  • Copy the .apk file to your phone and rename it as shlublu.jar file (an APK is actually a specialization of a jar, though)

Other steps are the same as described by Shlublu.

  • Big thanks to Shlublu for cooperation.
iCantC
  • 2,852
  • 1
  • 19
  • 34
Pätris Halapuu
  • 165
  • 1
  • 6
  • You are welcome Pätris, and it was a pleasure to discuss with you. (I initially wrote this comment when I +1'ed your answer but I obviously forgot to press the "Add Comment" button...) – Shlublu Sep 30 '14 at 12:15
  • I appreciate the trick, but reproducing @Shlublu 's example above using your method results in a ~380kB apk whereas if I just create a library project, I get a 3kB jar. However, the library approach requires an extra command, `dx --dex --keep-classes --output=newjar.jar libproject.jar`. Maybe there's a way to make that dex step automatic when Eclipse builds the library. – Harvey Jan 23 '15 at 19:49
  • I'm confused why we use apk file?! I am new in xamarin android. I want to create library project dll (Class Library) and then use it in my main app at run-time. How is it possible to do that? – Mohammad Afrashteh Jan 14 '18 at 18:12
  • @Pätris - I tried your method (skipped 2nd step of including manifest entry) and did not change name to .jar. Instead kept the apk as default name, app-debug.apk and modified Shlublu's code (just changed file name). I am getting "ClassNotFound" Exception. I am running on Android 6.0. Am I missing anything ? – node_analyser May 23 '21 at 12:44
4

Technically should work but what about Google rules? From: play.google.com/intl/en-GB/about/developer-content-policy-pr‌​int

An app distributed via Google Play may not modify, replace or update itself using any method other than Google Play’s update mechanism. Likewise, an app may not download executable code (e.g. dex, JAR, .so files) from a source other than Google Play. This restriction does not apply to code that runs in a virtual machine and has limited access to Android APIs (such as JavaScript in a WebView or browser).

Marcin
  • 1,113
  • 1
  • 11
  • 33
  • 2
    That's absolutely right. However, the OP asked whether this is technically feasible and may want to do such a thing for his/her own purpose without using Google Play at all. Also, it looks like a Google Play app may embed jars that would be written on the local storage at deployment time and that would be loaded later by the app without breaking this rule (regardless whether it would be a good idea). But anyway, you are right to remind developers that an app downloaded from Google Play should not download code from elsewhere. This should be a comment though. – Shlublu Feb 19 '18 at 09:10
  • New URL as of 12/15/2021: https://support.google.com/googleplay/android-developer/answer/9888379 – Thach Van Dec 15 '21 at 21:46
1

sure, it is possible. apk which is not installed can be invoked by host android application.generally,resolve resource and activity's lifecircle,then,can load jar or apk dynamically. detail,please refer to my open source research on github: https://github.com/singwhatiwanna/dynamic-load-apk/blob/master/README-en.md

also,DexClassLoader and reflection is needed, now look at some key code:

/**
 * Load a apk. Before start a plugin Activity, we should do this first.<br/>
 * NOTE : will only be called by host apk.
 * @param dexPath
 */
public DLPluginPackage loadApk(String dexPath) {
    // when loadApk is called by host apk, we assume that plugin is invoked by host.
    mFrom = DLConstants.FROM_EXTERNAL;

    PackageInfo packageInfo = mContext.getPackageManager().
            getPackageArchiveInfo(dexPath, PackageManager.GET_ACTIVITIES);
    if (packageInfo == null)
        return null;

    final String packageName = packageInfo.packageName;
    DLPluginPackage pluginPackage = mPackagesHolder.get(packageName);
    if (pluginPackage == null) {
        DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
        AssetManager assetManager = createAssetManager(dexPath);
        Resources resources = createResources(assetManager);
        pluginPackage = new DLPluginPackage(packageName, dexPath, dexClassLoader, assetManager,
                resources, packageInfo);
        mPackagesHolder.put(packageName, pluginPackage);
    }
    return pluginPackage;
}

your demands is only partly of function in the open source project mentioned at the begining.

  • I got some error using you project, it still has the same problem with my previous project. ClassNotFound when startActivity.. – KDoX Sep 15 '17 at 01:18
  • I am new in android development. I don't know why we should use apk to load dynamically methods?! I want to load my dll that created by Class Library project dynamically at run-time. – Mohammad Afrashteh Jan 14 '18 at 18:07
1

If you're keeping your .DEX files in external memory on the phone, such as the SD card (not recommended! Any app with the same permissions can easily overwrite your class and perform a code injection attack) make sure you've given the app permission to read external memory. The exception that gets thrown if this is the case is 'ClassNotFound' which is quite misleading, put something like the following in your manifest (consult Google for most up to date version).

<manifest ...>

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="18" />
    ...
</manifest>
1

I think @Shlublu answer is correct but i just want to highlight some key points.

  1. We can load any classes from external jar and apk file.
  2. In Any way, we can load Activity from external jar but we can not start it because of the context concept.
  3. To load the UI from external jar we can use fragment. Create the instance of the fragment and embedded it in the Activity. But make sure fragment creates the UI dynamically as given below.

    public class MyFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup 
      container, @Nullable Bundle savedInstanceState)
     {
      super.onCreateView(inflater, container, savedInstanceState);
    
      LinearLayout layout = new LinearLayout(getActivity());
      layout.setLayoutParams(new 
     LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.MATCH_PARENT));
    Button button = new Button(getActivity());
    button.setText("Invoke host method");
    layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT);
    
    return layout;
     }
    }
    
User10001
  • 1,295
  • 2
  • 18
  • 30
1

I am not sure if you can achieve this by dynamically loading java code. May be you can try embedding a script engine your code like rhino which can execute java scripts which can be dynamically downloaded and updated.

Naresh
  • 1,336
  • 9
  • 14
  • Thanks. I will investigate this approach (didn't know about scripting on Android before today). More information (if someone needs it) can be found here: http://google-opensource.blogspot.com/2009/06/introducing-android-scripting.html – llullulluis Jul 28 '11 at 12:41
  • 1
    @Naresh Yes, you can also download and execute Java compiled code. See my response in this page. I wouldn't have expected that actually. – Shlublu Jul 28 '11 at 14:37
  • 1
    Thanks @shlublu seems interesting :). The only problem i can think of is it relies heavily on reflections or you need to start writing wrappers i see lot of management problems, if we have to write whole code that way. That's why i thought scripts are easy and good way managing business logic. – Naresh Jul 28 '11 at 18:10
  • Also how would you start generating standalone dex jar files? you would create seperate android application projects? as of now there is no concept of static libraries in android rite? – Naresh Jul 28 '11 at 18:16
  • As I did here: I have exported a java project that was only containing MyClass. This generated a valid Dex file compliant with the Dalvik's format. – Shlublu Jul 28 '11 at 18:24
  • 1
    Look at the the /system/framework directory of your device, it is full of jars made that way. (no root right needed) – Shlublu Jul 28 '11 at 18:34