Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I load Ruboto Core Dynamically in Runtime? #573

Closed
yizhi401 opened this issue Feb 19, 2014 · 12 comments
Closed

How can I load Ruboto Core Dynamically in Runtime? #573

yizhi401 opened this issue Feb 19, 2014 · 12 comments
Labels
Milestone

Comments

@yizhi401
Copy link

Hi, @donv and Ruboters:
I am brandly new to use Ruboto, and it is an amazing work! I've set up the development environment and wrote some demos of ruboto, but I encountered a problem now.

I know using ruboto have two ways, one is to put the jruby-core-jars in the project I need, the other is to install the Ruboto-core.apk in the phone.
But the problem is here, I don't want the user to install another app to use my app; I don't want to put the jars in my projects also becaus they are too large. So here is what I want to do: when the user open the page writes in ruboto, I would check if the ruboto-core exists, if it doesn't, then ask the user if s/he would like to download the core (but he would not need to install it). So at this situation, I need to load the classes.dex in Ruboto-core.apk dynamically. Here is what I tried:

First, I build a signed Ruboto-Core.apk from eclipse and pasted it in the /sdcard/RubotoCore.apk
I modified the JRubyAdapter.java as follows (only from line 198)
line 198...
classLoader = new PathClassLoader(apkName,
JRubyAdapter.class.getClassLoader());

            } catch (PackageManager.NameNotFoundException e2) {
                System.out.println("JRuby not found in local APK:");
                e1.printStackTrace();
                System.out.println("JRuby not found in platform APK:");
                e2.printStackTrace();

!!!!!!What I modified begins here!!!!!!!!!!!!!!
try {
String libPath = Environment
.getExternalStorageDirectory()
+ "/RubotoCore.apk";
File tmpDir = appContext.getDir("tempdex", 0);
classLoader = new DexClassLoader(libPath,
tmpDir.getAbsolutePath(),null,JRubyAdapter.class.getClass().getClassLoader());

                    apkName = Environment.getExternalStorageDirectory()
                            + "/RubotoCore.apk";
                    // apkName="/data/app/org.ruboto.core-1.apk";
                    RUBOTO_CORE_VERSION_NAME = "0.6.0";
                    // classLoader = new PathClassLoader(apkName,
                    // JRubyAdapter.class.getClassLoader());
                } catch (Exception e3) {
                    android.util.Log.i("mInfo",
                            "JRuby not found in SDcard:");
                    e3.printStackTrace();
                    return false;
                }
            }

            try {
                scriptingContainerClass = Class.forName(
                        "org.jruby.embed.ScriptingContainer", true,
                        classLoader);
            } catch (ClassNotFoundException e) {
                // FIXME(uwe): ScriptingContainer not found in the platform
                // APK...
                e.printStackTrace();
                return false;
            }
        }

!!!!!!!!!!!!!!Modification ends here!!!!!!!!!!!!!!!!!!!!!!!!!
try {
// ////////////////////////////////
//
// Set jruby.home
//
...........others are the same

However, when launching the quick_start app(the first ruboto app), I got this exception and app crashes:

FATAL EXCEPTION: ScriptLoader for Java::ComChenhaoExampleQuick_startQuickStartActivity
org.jruby.embed.EvalFailedException: (NameError) cannot load Java class com.chenhao.example.quick_start.R
at org.jruby.embed.internal.EmbedEvalUnitImpl.run(EmbedEvalUnitImpl.java:133)
...........
Caused by: org.jruby.exceptions.RaiseException: (NameError) cannot load Java class com.chenhao.example.quick_start.R
org.jruby.javasupport.JavaClass.for_name(org/jruby/javasupport/JavaClass.java:1242)
......

When I debug and track the programe, the app crashed down at when JRubyAdapter's method "runRubyMethod" is called, and the script is the "quick_start_activity.rb", it goes to the exception of java.lang.reflect.InvocationTargetException.

I would assume that you would get the same problem if tried what I did.

I've tried working on this for a whole day still don't know what to do. Please Help me, and I would like to try any suggestion without hesitation! Thanks!

@donv
Copy link
Member

donv commented Feb 19, 2014

Hi @yizhi401 !

Welcome to the project! This sounds like something we can make work 😄

Could you put it in a GitHub repository for us to try out?

@yizhi401
Copy link
Author

Thanks for noticing, @donv !
Sure, I've create a repository in my github and write the problem in the README file, I hope problem would be more clear there.
Here is the link:
https://github.com/yizhi401/rubotoQuickStart

with 13 hours' jet lag we are not at the same working time :)

@yizhi401
Copy link
Author

Updates:
I just try to use the PathClassLoader instead of DexClassLoader. I tried this before but failed because the app can't load the classes.dex in the RubotoCore.apk.

                apkName = Environment.getExternalStorageDirectory()
                        + "/RubotoCore.apk";
                RUBOTO_CORE_VERSION_NAME = "0.6.0";
                classLoader = new PathClassLoader(apkName,JRubyAdapter.class.getClassLoader());

But when I followed this thread in StackOverFlow

http://stackoverflow.com/questions/11453614/how-can-i-load-a-jar-file-dynamically-in-an-android-application-4-0-3

I change the /data/dalvik-cache's access control to 777
su chmod 777 /data/dalvik-cache
And then relunch, It works!
So I guess this might relates to the possible solution.

@donv
Copy link
Member

donv commented Feb 20, 2014

Great! This could get into Ruboto. We would like the RubotoCore package to be shared among apps, but in a secure way and with versioning. Is this something you could look into and contribute? If so, fork the project and send us a pull request. If you add tests as well, you will be invited to the team :)

@yizhi401
Copy link
Author

Well, @donv , my solution above doesn't solve the problem in fact ; because users or app without root authority cannot change the /data/dalvik-cache's access control to 777. So I cannot use PathClassLoader, but have to use the DexClassLoader which don't copy the classes.dex into /data/dalvik-cache but to the folder that you assign to.

And I think this is the problem: when using DexClassLoader the app would fail... But I have to use DexClassLoader NOT PathClassLoader. So the problem remains unsolved.

Yes, I am glad to look contribute; however, I am a new programmer (only started programming half a year) and don't know where to start on this project. Anyway, I am also a quick learner also. So, if you believe that I can help in someway and would like to give me some brief guidance about this, I am at your service, sir.

My e-mail is yizhi401@126.com. I am going to send you a pull request now.

@rscottm
Copy link
Member

rscottm commented Feb 20, 2014

I guess my thought on this is that we should think of RubotoCore as primarily for development (allowing quicker builds and installs).

It seems to me that any deployed app would be better off with it's own internal version of JRuby. Removing the need for a separate install of RubotoCore. Most of the Android limitations that made the separate RubotoCore more attractive (primarily limited app space) is long gone.

I'm not saying we get rid of it, but I don't see the need for another way to share it. Then again, I'm open to arguments I'm missing.

@yizhi401
Copy link
Author

Hi, @rscottm
Using a private Ruboto-Core within each app is reasonable; but that does not necessarily means conflickting with the "sharing" solution. Both ways can work, it depends on the app's requirements. And this is what I am doing: if I can put Ruboto-Core.apk in the SDcard, and load it dynamically when needed, you can choose whatever directories you need: your private app-space or the public Ruboto-Core directory. Both can work.
I need to figure out why DexClassLoader can't work while PathClassLoader can; if I succeed, all the needs would be easily satisfied!

@rscottm
Copy link
Member

rscottm commented Feb 21, 2014

I haven't tried it, but this blog post might be relevant:

http://android-developers.blogspot.com/2011/07/custom-class-loading-in-dalvik.html

Anyway, if the issue is that RubotoCore is to large to include, you can strip out the parts of the standard ruby libraries that you don't need. We currently include 1.8, 1.9, and 2.0. You can remove two of those (totaling ~10MB). You may be able to remove a lot more too.

@yizhi401
Copy link
Author

Hi, Guys! Hi, @donv and @rscottm
Finally I figured out the problem why DexClassPath won't work for me. The problem lies in this code:

  classLoader = new DexClassLoader(libPath,tmpDir.getAbsolutePath(),null,JRubyAdapter.class.getClass().getClassLoader());

Here I made a silly fault by writing

      class.getClass().getClassLoader();  

in fact, it should be

     class.getClassLoader() 

or

     getClass().getClassLoader().

So, PathClassLoader / DexClassLoader have no essential difference here.

As a result, I am now able to load the RubotoCore.apk whatever way you want!
The next thing to do, is what do we want the user experience be, and as @donv said, solve some security issues and version checking.

I will contribute my work when finished!

PS, @rscottm , reducing the RubotoCore libs is meaningful for me. In fact, if the core libs is small I wouldn't even try to load it dynamically. Thanks for your suggestion!

@AtomicPair
Copy link
Contributor

+1 for the interesting ideas being debated in this thread. :-)

Also as reference, most of the functionality being discussed here has been captured in issue #418. Unfortunately, I haven't had much time to work on that feature, so if anyone would like to help flesh out the idea and/or contribute some code, it would be most welcome!

@yizhi401
Copy link
Author

Hi, @Axianator!
I've read your thread yesterday. Well, we have come to the same thoughts, good! But I am not familiar with Ruby and JRuby; My major knowlege scope lies within Android and Java, but I am learning J/Ruby because I want to make use of Ruboto in my Android development. So hopefully I can contributes to Ruboto later. As for now, I can only help to write some wiki about it. So if you are going to write some code about it, just go ahead. I would ask to join the team when I am ready for it.

@donv donv modified the milestones: 1.0.2, 1.1 Feb 21, 2014
@donv
Copy link
Member

donv commented May 18, 2014

Hi @yizhi401 @rscottm @Axianator !

The new "large app" feature in #601 surely affects this issue also. It introduces a way to add jars with dexfiles to you project. The files are in the project as of now, but they can be downloaded or read from the sdcard.

The filtering has become better lately as well, and at least one issue is planned for Ruboto 1.1.1 to reduce app size even more.

@yizhi401 plese indicate if there is anything more to do on _this_ issue, or close it if you are satisfied for now.

@donv donv modified the milestones: 1.1.1, 1.1 May 18, 2014
@donv donv modified the milestones: 1.1.2, 1.1.1 Jul 1, 2014
@donv donv modified the milestones: 1.1.3, 1.1.2 Jul 9, 2014
@donv donv added the support label Oct 13, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants