I am trying to build an application that runs under JavaSE and Android. Most of the code is the same between the two, but there are some specific functions that need to be separated. I use Eclipse. So I decided to put the shared code in a separate project, and then build one more project for Android and one for Java, which reference the shared project. I put all Java and Android specific functions in one class residing in the Java and Android specific projects. These classes are called UtilsJ (for Java) and UtilsA (for Android). The code in the shared project uses a factory to determine at runtime which version it needs to pick, and then calls the class loader to load the right class. Essentially: if property java.vm.name equals Dalvik, load UtilsA, else load UtilsJ (and of course cast to the Utils interface before returning). My question is simply if this is a good idea or is something going to eventually break? I've never used class loader before. Any other suggestions how to implement this sharing would also be appreciated.
-
Why can't you just put UtilsJ in Java project and UtilsA in the Android one? Call them both Utils, with no separate interface, and no classloader. – Alexei Kaigorodov Mar 19 '15 at 09:04
-
I can't do that because functions from Utils are used in the shared code. As a very simple example, imagine there is the code that reads the initialization file. The file location is different on Android and Java, so the file path is fetched from UtilsA or UtilsJ. The shared code does not reference Android and Java projects, so it doesn't know which function(s) to use to read the file. So it loads the Utils class at runtime, and then it knows what to do. – Branex Mar 19 '15 at 09:37
-
what I mean is to use shared code as a source folder, not as a compilable project. Both Java and Android projects refere that source folder, not a jar file. And both projects contain own version of Utils, with the same class name but different content. – Alexei Kaigorodov Mar 19 '15 at 11:25
-
I like to use Project -> Run As -> Android Application in Eclipse to run the app on the phone attached to the computer. I think I would not be able to use that if both projects (shared and Android) were not clean. – Branex Mar 19 '15 at 13:03
1 Answers
Generating an interface implementation dynamically is certainly a valid technique. For instance, having a data access interface that has multiple implementations; one each for flat files, MySQL and WebDAV. The program can pick an implementation at run time based on system/platform properties.
But this feels different. If I saw that I had a Java app and an Android app that had a lot of common code, my goal would be to create an Eclipse project that generates a jar file that I could just drop into the libraries of both projects. In that case, the jar file wouldn't contain any code that was incompatible with one platform or the other. So there wouldn't be any reason to have a platform-specific implementation.
Let's take your example some code reading an initialization file. If it's common code, you have an input parameter which is a file. On Android, maybe it's "/data/data/com.whatever.blahblahblah" and on Java you're getting the "user.dir" system parameter for the top level directories. But one way or another, it's a File, and you hand it to your common setup method. That's okay. But if your initialization file read code e.g. needs a Context to get a Resource to read the file for Android, then it's not common code. And it doesn't belong in a library jar for a JVM-hosted app.
So I think that in your case the platform-specific implementation classes are overkill. If it's common code, it's the same code — period.
Let's talk about another example in your comment. If you are using desktop Java, then you are probably using Swing or AWT, so you still have the same issue of running some network task off the UI thread, notifying when it completes, maybe even updating some progress indicator UI while it's processing. Same function, same operation, but the code is so different that I can't see how having it in the same library next to an AsyncTask version could be of any benefit.
And testing might get tricky. Obviously JUnit will work for everything, but some tests would need to run on a device or emulator.
I stated that it was a valid technique, and of course you may have other compelling reasons to choose the multi-platform option. You asked the question; is anything going to break? My answer is: Probably not, but why risk dealing with some heartburn down the road? Speaking for myself, I wouldn't do it. If I had to support multiple MVC apps, my common library would have nothing but M.

- 30,387
- 5
- 62
- 74
-
Reading a file was a trivial example. Let's say I need to run some network command doSomethingOnNetwork(). For JVM, I can just run it and for Android I need to wrap it inside an AsyncTask. In any case, the command is run from a class MyNetworkClass inside the shared code. How would you do it in your proposal? The way I have it right now is that Utils contains the function runNetwork(), which in UtilsJ simply calls doSomethingOnNetwork, and in UtilsA runs a task containing doSomethingOnNetwork. How would you minimize code duplication in this case while maintaining compilable shared library? – Branex Mar 19 '15 at 13:29
-
BTW, this would be an excellent question for the Programmers site on Stack Exchange. I would certainly be interested in hearing others' opinions on the pros and cons of this design. – kris larson Mar 20 '15 at 02:20