Skip to content

JDOM2 Android Issue getSystemResource

rolfl edited this page Apr 23, 2012 · 4 revisions

JDOM JUnit tests use ClassLoader.getSystemResource("name") to create URLs that can be used as input to the various Build processes. When transferred to Android these tests have all been failing because the value returned from ClassLoader.getSystemResource(...) has been null.

The 'internet' is full of references to people complaining about this issue, but it is relatively sparse on clear references to a solution, and where solutions are given, there is no 'reasoning' for why the solution works.

While I have 'wasted' a lot of time on this problem, the actual problem is one of my understanding (PICNIC - problem in chair, not in computer). As a result of 'study', I can make the following observations of a simple case ... (first though, some assumptions: you have a resource 'Data.txt' in your jar file, in the 'package' "org.example"):

  1. ClassLoader.getSystemResource("org/example/Data.txt") will never likely give you what you want on Android.... the resources in your Jar files are not loaded by the Android equivalent of the 'System' class loader.
  2. this.getClass().getResource(...) will work on Android with limitations/expectations.
  3. this.getClass().getClassLoader().getResource(...) will also work on Android with limitations/expectations.

Here are some of the 'lessons' I have leared:

  • ClassLoader.getSystemResource(...) is essentially useless on Android.
  • When using an Android 'Test' project the rules are different to when using the main project.
  • You have to be very careful about whether you use relative or absolute paths (on both Java and Android), and you need to know which method expects which convention (Java and Android are consistent in this behaviour).

I have put together some test code to illustrate my education, but, first, a conclusion:

Conclusion

The methods this.getClass().getResource(...) and this.getClass().getClassLoader().getResource(...) work as expected, and seemingly identically, on both regular Java and Android. The first one works if you use an absolute path to the resource, or a relative path to the package the this class is in. The second one also works on Android and again you should be careful with your 'path' to the resource, it only effectively works with a relative path, relative to the 'top' of the classpath.

Example Code

.... the following methods:

	public final String getResource(final String res) {
		final StringBuilder sb = new StringBuilder();
		
		try {
			final String fmt = ("%s(%s%s)\n  %s\n");
			sb.append(String.format(fmt, "getSystemResource", "", res, ClassLoader.getSystemResource(res)));
			sb.append(String.format(fmt, "getSystemResource", "/", res, ClassLoader.getSystemResource("/" + res)));

			sb.append(String.format(fmt, "ClassLoader.getResource", "", res, this.getClass().getClassLoader().getResource(res)));
			sb.append(String.format(fmt, "ClassLoader.getResource", "/", res, this.getClass().getClassLoader().getResource("/" + res)));


			sb.append(String.format(fmt, "Class.getResource", "", res, this.getClass().getResource(res)));
			sb.append(String.format(fmt, "Class.getResource", "/", res, this.getClass().getResource("/" + res)));

		} catch (Throwable t) {
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw);
			sb.append("\n\nGot Exception!!!!\n");
			t.printStackTrace(pw);
			pw.flush();
			sb.append(sw.toString());
		}
		
		return sb.toString();
	}

        public void onClick(View v) {
        	final GetResource gr = new GetResource();
        	StringBuilder sb = new StringBuilder();
        	sb.append(gr.getResource("Data.txt"));
        	sb.append(gr.getResource("net/tuis/cltest/Data.txt"));
        	Log.i("RGLRES", sb.toString());
            mEditor.setText(sb.toString());
        }

produce the following results:

04-22 23:56:42.990: I/RGLRES(740): getSystemResource(Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   null
04-22 23:56:42.990: I/RGLRES(740): getSystemResource(/Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   null
04-22 23:56:42.990: I/RGLRES(740): ClassLoader.getResource(Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   null
04-22 23:56:42.990: I/RGLRES(740): ClassLoader.getResource(/Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   null
04-22 23:56:42.990: I/RGLRES(740): Class.getResource(Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   jar:file:/data/app/com.example.android.skeletonapp-1.apk!/net/tuis/cltest/Data.txt
04-22 23:56:42.990: I/RGLRES(740): Class.getResource(/Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   null


04-22 23:56:42.990: I/RGLRES(740): getSystemResource(net/tuis/cltest/Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   null
04-22 23:56:42.990: I/RGLRES(740): getSystemResource(/net/tuis/cltest/Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   null
04-22 23:56:42.990: I/RGLRES(740): ClassLoader.getResource(net/tuis/cltest/Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   jar:file:/data/app/com.example.android.skeletonapp-1.apk!/net/tuis/cltest/Data.txt
04-22 23:56:42.990: I/RGLRES(740): ClassLoader.getResource(/net/tuis/cltest/Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   null
04-22 23:56:42.990: I/RGLRES(740): Class.getResource(net/tuis/cltest/Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   null
04-22 23:56:42.990: I/RGLRES(740): Class.getResource(/net/tuis/cltest/Data.txt)
04-22 23:56:42.990: I/RGLRES(740):   jar:file:/data/app/com.example.android.skeletonapp-1.apk!/net/tuis/cltest/Data.txt

Running the same code on vanilla Java6

getSystemResource(Data.txt)
  null
getSystemResource(/Data.txt)
  null
ClassLoader.getResource(Data.txt)
  null
ClassLoader.getResource(/Data.txt)
  null
Class.getResource(Data.txt)
  file:/C:/workspace/ClassLoaderResource/ebuild/net/tuis/cltest/Data.txt
Class.getResource(/Data.txt)
  null


getSystemResource(net/tuis/cltest/Data.txt)
  file:/C:/workspace/ClassLoaderResource/ebuild/net/tuis/cltest/Data.txt
getSystemResource(/net/tuis/cltest/Data.txt)
  null
ClassLoader.getResource(net/tuis/cltest/Data.txt)
  file:/C:/workspace/ClassLoaderResource/ebuild/net/tuis/cltest/Data.txt
ClassLoader.getResource(/net/tuis/cltest/Data.txt)
  null
Class.getResource(net/tuis/cltest/Data.txt)
  null
Class.getResource(/net/tuis/cltest/Data.txt)
  file:/C:/workspace/ClassLoaderResource/ebuild/net/tuis/cltest/Data.txt
Clone this wiki locally