Beginning with j2objc

493 views
Skip to first unread message

Vincent Guo

unread,
Jun 23, 2017, 5:14:07 AM6/23/17
to j2objc-discuss
Hello !

So I'm working on a project where I'm trying to convert an Android app into iOS.
I'm only getting started so I first tried converting a helloworld java program and it worked perfectly fine, it also compiled sucessfuly with ./j2objcc and did what it was supposed to do with ./a.out

Now I'm trying to make it work with 2 java files with one extending the other, it works in java and I managed to translate the files with ./j2objc but it won't compile with ./j2objcc
I get undefined symbols for architecture as error and I've already searched my problem but couldn't find a solution.

I'd really appreciate if someone could help me.

Vincent Guo

unread,
Jun 23, 2017, 9:32:36 AM6/23/17
to j2objc-discuss
Problem solved, trying to use ./j2objcc with only the file that contains the main like we do in java did not work, but compiling with all files was the correct way to do it.

Tom Ball

unread,
Jun 23, 2017, 10:48:32 AM6/23/17
to j2objc-discuss
If you want the javac behavior of the compiler building all dependent source files, use the --build-closure flag. All classes needing conversion need their source on the -sourcepath; anything resolved on the -classpath will need to be separately built.

--
You received this message because you are subscribed to the Google Groups "j2objc-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to j2objc-discus...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Vincent Guo

unread,
Jun 26, 2017, 10:14:44 AM6/26/17
to j2objc-discuss
Thanks for the advice, it's clearer now

My project leader asked me to check if j2objc was able to translate the java libraries that he uses such as java.io.BufferedReader.
So I've been trying these libs out with little java programs that I convert but I have an issue with java.io.FileInputStream

But when executing the program I get a "LibraryNotLinkedError: Channel support not available"
I compiled without any flag so I tried with -ObjC -ljre_channels but I get a "ld: symbol(s) not found for architecture"

I don't know how to work this around so any help is much appreciated.

Tom Ball

unread,
Jun 26, 2017, 11:13:17 AM6/26/17
to j2objc-discuss
For initial development, the "-ljre_emul" is a better choice, since it includes the full JRE library. Since the JRE API is big on all Java platforms but not all of it is used by iOS apps, we split it into subset libraries like jre_channels to help engineers reduce app size. During development, though, avoiding potential link errors is more important than keeping the app small. Since libjre_emul.a contains all the objects in the other libjre*.a libraries, if "-ljre_emul" is specified then no other "-ljre_*" flags should be specified (or there will be duplicate symbol linker errors).

If you get any LibraryNotLinkedErrors thrown when linking with the jre_emul library, link with the -ObjC linker flag.

Vincent Guo

unread,
Jun 26, 2017, 11:32:02 AM6/26/17
to j2objc-discuss
I get the same error whether I use -ljre_emul or -ObjC, the error comes from the java function getChannel() since everything work perfectly fine when I comment the line that involves it.

Keith Stanger

unread,
Jun 26, 2017, 11:34:07 AM6/26/17
to j2objc-discuss
Could you include the details of the command line and errors you're getting? That would help to understand the problem.

Vincent Guo

unread,
Jun 26, 2017, 11:47:27 AM6/26/17
to j2objc-discuss
Sure,

So the Java program is :

import java.io.*;
//Java program demonstrating FileInputStream
class ReadFile
{
    public static void main(String args[]) throws IOException
    {
 
        //attach the file to FileInputStream
        FileInputStream fin= new FileInputStream("file1.txt");
 
        //illustrating getChannel() method
        System.out.println(fin.getChannel());
 
        //illustrating getFD() method
        System.out.println(fin.getFD());
 
        //illustrating available method
        System.out.println("Number of remaining bytes:"+fin.available());
 
        //illustrating skip method
        /*Original File content:
        * This is my first line
        * This is my second line*/
        fin.skip(4);
        System.out.println("FileContents :");
        //read characters from FileInputStream and write them
        int ch;
        while((ch=fin.read())!=-1)
            System.out.print((char)ch);
         
        //close the file
        fin.close();
    }
}

I compile it using javac and then I run ./j2objc -sourcepath x -classpath x -d x ReadFile.java
Then I use ./j2objcc ReadFile.m -ljre_emul
And then I try ./a.out ReadFile and that's where it fails

For errors I get :

Exception in thread "main" com.google.j2objc.LibraryNotLinkedError: Channel support is unavailable.

It then asks me to fix it either by adding -ljre_channels if I was using -ObjC or call JavaNioChannelFactoryImpl_class_()

Thank you in advance for your help.

Keith Stanger

unread,
Jun 26, 2017, 12:35:02 PM6/26/17
to j2objc-discuss
Thanks. I recommend adding -ObjC to your link command. The flag tells the linker to add all Objective-C symbols, regardless of whether they are reachable from your program. This flag is often required for Objective-C apps because of Objective-C's dynamic nature and is usually required when Objective-C categories are used.

Once you are further along in your development cycle, you may want to revisit whether to use -ObjC, since it may increase the binary size of your app.

The LibraryNotLinkedError indicates that a particular class is either not being linked, or is being stripped at link time. Adding -ljre_emul will ensure that it is linked, and adding -ObjC will ensure that it is not being stripped.

Tom Ball

unread,
Jun 26, 2017, 2:01:35 PM6/26/17
to j2objc-discuss
FWIW, the j2objcc script has a limitation where the -ljre_emul flag needs to be specified before any .m files, so perhaps that's the issue you're seeing. Here are the steps that worked for me:

$ cp ReadFile.java file1.txt  # or any other text
$ j2objc ReadFile.java         # no need for source or class paths, since there's no package declaration
$ j2objcc -ljre_emul ReadFile.m -ObjC    # -ljre_emul is before any .m files (j2objcc script limitation)
$ ./a.out ReadFile
<JavaNioFileChannelImpl_JreRetainedWith: 0x7fa81b002190>
FileDescriptor[3]
Number of remaining bytes:967
FileContents :
rt java.io.*;
//Java program demonstrating FileInputStream
class ReadFile
...

Tom Ball

unread,
Jun 26, 2017, 2:14:53 PM6/26/17
to j2objc-discuss
The -ljre_emul flag doesn't need to be specified in any order -- once I deleted the build-generated files, it works fine either way.

Vincent Guo

unread,
Jun 27, 2017, 3:28:27 AM6/27/17
to j2objc-discuss
Thank you very much both of you, everything is working as intended now.
It's really kind of you to take the time to help and explain to a beginner like me.

Vincent Guo

unread,
Jun 27, 2017, 11:16:05 AM6/27/17
to j2objc-discuss
Sorry to annoy again but I have an issue with the path of j2objc and I should've started by solving this but I found a way to work around so I did not do anything
So after I downloaded the zip file I've put the whole j2objc-2.0.1 folder in //Users/Vincent/Documents and all the tests I have done could only be made in that folder using ./j2objc (That's why I needed -sourcepath, since my java files were elsewhere)

I tried to put this in /usr/local and added the following line to my .bash_profile :
export PATH=$PATH:/usr/local/j2objc-2.0.1/bin

echo $PATH shows me that the path is correct but I'm still unable to call j2objc if I'm not in the j2objc-2.0.1 folder

It must be a dumb problem but I've read several topics but couldn't find a solution

Keith Stanger

unread,
Jun 27, 2017, 11:20:56 AM6/27/17
to j2objc-discuss
First thing that jumps out at me, why the /bin dir on your path? The j2objc and j2objcc scripts should be directly under j2objc-2.0.1.

Tom Ball

unread,
Jun 27, 2017, 11:27:45 AM6/27/17
to j2objc-discuss
In a distribution, the j2objc script is in the root directory, not bin, so try "/usr/local/j2objc-2.0.1". Bash commands that debug path locations include:
  • which j2objc   # what is the path for that command, no answer indicates it's not in the path
  • echo $PATH   # make sure that the shell currently running has the PATH set correctly
  • ls /usr/local/j2objc-2.0.1/bin   # what's in this path? If "No such file or directory", it's a bad path.
I encourage you to learn more about Bash in general, since it's a useful skill for developers regardless of what platform or project you're working on. Here's a list of useful references.

Vincent Guo

unread,
Jun 27, 2017, 1:31:37 PM6/27/17
to j2objc-discuss
Aaaah how dumb of me I didn't realize that and just followed things I saw on Internet

Again thank you for your answers

Vincent Guo

unread,
Jul 3, 2017, 10:02:47 AM7/3/17
to j2objc-discuss
Hello it's me again.

So now that I've managed to be a bit more familiar with using j2objc, my goal is to work on porting the Android app to iOS.
So there are files that can't be translated (files that import non supported android libraries) but most other files can.

Since j2objc requires every files that are linked to each others, the issue is that I have a file named "console.java" which imports different .java files among which there is a "InputDevice.java" file that imports "console.java" itself.
This "console.java" file also imports some android features so I can't translate it.

I thought about identifying those non-supported features and put them into comments, so I can keep the methods names and replace android with iOS specific code later.

Is that a viable method ? Is there a better solution for this kind of issue ?

Lukhnos Liu

unread,
Jul 3, 2017, 1:11:57 PM7/3/17
to j2objc-discuss
The two common strategies are super-sourcing (the approach you considered) and extracting the common interface from concrete, platform-independent implementation classes. Here's a previous discussion about them: https://github.com/google/j2objc/issues/576#issuecomment-125293374

Keith Stanger

unread,
Jul 4, 2017, 10:35:09 AM7/4/17
to j2objc-discuss
Note that separating out platform-specific components and putting them behind common interfaces will help to increase the maintainability and testability of your code.

Vincent Guo

unread,
Jul 7, 2017, 11:38:27 AM7/7/17
to j2objc-discuss
Hello,

I've seen on https://developers.google.com/j2objc/guides/writing-native-methods that there is a way to write objective C code in java files and the translator takes them into consideration but is there a feature that would allow some java code to be ignored during the translation ?

Keith Stanger

unread,
Jul 7, 2017, 12:14:45 PM7/7/17
to j2objc-discuss
Nope, if you need platform-specific implementations you'd need to write a separate .java file per platform.

Tom Ball

unread,
Jul 7, 2017, 1:07:24 PM7/7/17
to j2objc-discuss
How are planning on testing this? Having a shared interface or abstract class with separate platform-specific implementations/subclasses not only is a cleaner design, it supports easier unit testing using mocks. Testing is critical for mobile apps, since users don't always update quickly.

Vincent Guo

unread,
Jul 7, 2017, 1:44:08 PM7/7/17
to j2objc-discuss
I totally agree with you, but the issue is that the app was not thought to be ported to iOS when they first developped it, so the android-specific implementations are quite spread, it was a bit tedious to isolate them all.
When I told my "supervisor" that we could put objective-C code in java files he asked me if the other option was available.

I'd really like to put all the iOS-specific code in separate files but I must edit some of the translated files since they use some of the android implementations (like app.Activity or os.Bundle) as variables and parameters

I really want to ask for your help the least possible because I feel like I'm abusing your kindness and I'm not contributing to your project at all since I'm too newbish.

Tom Ball

unread,
Jul 7, 2017, 2:39:46 PM7/7/17
to j2objc-discuss
Another option is that j2objc supports a large subset of JNI, so cross-platform code can define native methods, then define Android and iOS libraries (we do for the java.util.regex and java.util.zip packages). That only makes sense for Android classes that already have native methods, of course.

Don't let your supervisor skimp on unit and integration testing, as testing is a not-so-secret competitive weapon (Google engineering is test-obsessed :-). We've found that investing in better unit test coverage helps portability, since hard to test code is frequently platform-specific (or needs rewriting). To increase coverage, refactoring out the simpler, cross-platform code into separate classes is often easier than a complicated test.

Vincent Guo

unread,
Jul 12, 2017, 5:00:24 AM7/12/17
to j2objc-discuss
Hi,

On my java project I use a cast that calls a method in a file that is in the package declaration but not in the import headers list, so when I tried to call its method on Xcode I noticed that the whole package wasn't included.
Should I manually include all the files of the package ?
If so would it create a cycle issue ?

Keith Stanger

unread,
Jul 12, 2017, 10:09:04 AM7/12/17
to j2objc-discuss
I don't understand the question. Could you clarify, perhaps with a brief example?

Vincent Guo

unread,
Jul 12, 2017, 11:16:00 AM7/12/17
to j2objc-discuss
The java file is named AndroidDraw.java, it is in a subfolder named GLES, which is itself in a folder named gfx, it starts out with :

package Engine.gfx.GLES;

import Engine.Math.Point2F;
import Engine.gfx.GFXDevice;


At some point it's trying to make a cast :
Canvas canvas = ((GLESDevice)mDevice).getCanvas();

getCanvas() is defined in the file GLESDevice.java which is in the GLES folder, when I try to make a cast on Xcode I don't have access to GLESDevice.m methods

Keith Stanger

unread,
Jul 12, 2017, 11:27:17 AM7/12/17
to j2objc-discuss
Are you editing your Java sources in XCode? I don't really know how well XCode supports editing Java sources, but I think most users prefer using a Java IDE like Eclipse or IntelliJ.

Vincent Guo

unread,
Jul 12, 2017, 11:31:03 AM7/12/17
to j2objc-discuss
I'm using jEdit for java files, but I only use it to comment code, I use Xcode to write in the translated file

Keith Stanger

unread,
Jul 12, 2017, 11:35:58 AM7/12/17
to j2objc-discuss
I think you will need to make sure you are including the necessary header (GLESDevice.h in this case) and also make sure that your translated headers are added to your XCode project so that XCode can index them.

Vincent Guo

unread,
Jul 12, 2017, 11:48:46 AM7/12/17
to j2objc-discuss
Ah so that's what I did for the moment, but in java it works because GLESDevice.java and AndroidDraw.java are in the same package, so I was able to call GLESDevice method in AndroidDraw.
But I can't do that with the translated files in Xcode so j2objc doesn't translate the package thing ?
Does the option --no-segmented-header have something to do with it ?

Keith Stanger

unread,
Jul 12, 2017, 11:59:21 AM7/12/17
to j2objc-discuss
Objective-C doesn't have the same concept of packages as Java does. Like in C, you need to include everything that you need.

Segmented headers are somewhat unrelated, but you can read about it here: https://developers.google.com/j2objc/guides/segmented-headers

Tom Ball

unread,
Jul 12, 2017, 12:24:43 PM7/12/17
to j2objc-discuss
Is GLESDevice defined in a GLESDevice.java file? I ask because we have an open issue with missing #includes when the referenced type doesn't have a matching source file.

Vincent Guo

unread,
Jul 12, 2017, 2:19:34 PM7/12/17
to j2objc-discuss
Yes we use GLESDevice declaration in the GLESDevice.java file

Vincent Guo

unread,
Jul 17, 2017, 5:58:56 AM7/17/17
to j2objc-discuss
Hello,

While working on my project, I figured out I could let j2objc translate part of a method I skipped before so I modified the java file and copy pasted the newly translated portion to my Xcode project, but I also noticed that some values in the J2ObjcMethodInfo changed
For instance some of the -1 became 22 or 19 and such.
What does is do ?
Can it work if I don't care about it ?

Keith Stanger

unread,
Jul 17, 2017, 11:29:17 AM7/17/17
to j2objc-discuss
It sounds like you're talking about changes to the generated "__metadata" method. This data provides J2ObjC's runtime the info it needs to support Java's reflection API, for example accessing methods and fields. It contains information like the names, types and modifiers for all the methods and fields in your class. If you change the contents of your .java source, the reflection information will change accordingly.

If you don't use reflection or don't reflect specifically on the class in question you probably won't notice any error in this data. In fact you can tell j2objc not to generate this data with the "--strip-refleciton" flag. This can help reduce your app size if you don't use lots of reflection.

We recommend making j2objc translation a part of your build process and consider the generated source files as intermediate artifacts in your build. This will avoid dealing with merging the contents of the generated sources as it sounds like you're doing.

Vincent Guo

unread,
Jul 17, 2017, 11:46:54 AM7/17/17
to j2objc-discuss
Thanks for the info !

I take note of your recommandations, but for this project I think I can't proceed differently, because there are platform-specific methods and variable declarations all over the place.
And if I decided to completely skip all the files that includes these methods and declarations, that would make a lot to translate by myself, and given my limited time on this project I don't think I would make it.

Vincent Guo

unread,
Jul 19, 2017, 8:50:24 AM7/19/17
to j2objc-discuss
Hello again,

I'm trying to test out a bit of the code and my goal is to display a bitmap image on the screen when the app launches.
The issue is that on the iOS version I'm getting SIGABRT because during many of the casts, the nil_chk detects a nil parameter and throws a NullPointerException
But when we look at the Android version, AndroidStudio shows that these parameters are not null
How can I address this issue ?

I've tried to ignore the nil check but several lines of code later I get EXC_BAD_ACCESS errors which are probably due to the fact I'm trying to access non allocated memory (parameters were nil, so I suppose it's not allocated)

Keith Stanger

unread,
Jul 19, 2017, 9:02:45 AM7/19/17
to j2objc-discuss
I think traditional debugging strategies apply here. I'm not an XCode expert so I can't add any tips for debugging in XCode.

Vincent Guo

unread,
Jul 27, 2017, 8:49:17 AM7/27/17
to j2objc-discuss
Hi,

We are using Xml.pullParser and store the InputStream on a variable, this is the java code :

InputStream inps = null;
inps = ((platformAndroid)console.mPlatform).getActivity().getAssets().open(filename.substring(7));
modelDir = console.getFilePath(filename);
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(inps, null);

Now the issue is that we couldn't translate the first line since it involves getActivity(), so I manually translated it, but I can only can use NSInputStream while the variable inps is a JavaIoInputStream, so it crashes at the last line with a NSInvalidArgumentException
Do you have any solution to work around it ?

Thanks in advance for helping.

Tom Ball

unread,
Jul 27, 2017, 10:55:20 AM7/27/17
to j2objc-discuss
If you created that NSInputStream using a NSData instance, you can instead use jre_emul's NSDataInputStream (implementation). If not, creating a similar wrapper should be easy to write since InputStream and NSInputStream have similar APIs.

However, it looks like you are trying to read application resources. If that's the case, I instead recommend reading How Java Resources Map to iOS Resources. Then you can use Class.getResourceAsStream() to get an InputStream to that data.

Vincent Guo

unread,
Jul 28, 2017, 3:21:43 AM7/28/17
to j2objc-discuss
Class.getResourceAsStream() is what I'm looking for ! However I don't seem to be able to use it, what do I need to import ? Also it only takes the name of file as parameter with its extension right ?

Vincent Guo

unread,
Jul 28, 2017, 3:55:17 AM7/28/17
to j2objc-discuss

Vincent Guo

unread,
Jul 28, 2017, 5:24:24 AM7/28/17
to j2objc-discuss
Actually getResourceAsStream returns nil for me ...
I tried many different things such as passing as parameter the absolute path, only the file name, the file name + extension, adding the file in the Copy Files Phase (I get "error : couldn't create directory ..." and "error : open ..." after that)
How do you properly do it ?

Tom Ball

unread,
Jul 28, 2017, 11:39:24 AM7/28/17
to j2objc-discuss
Look at JreEmulation.xcodeproj for an example project which uses resources. Basically, getResourceAsStream() requires that the resource have the same package path as the class, and if you specify a relative path for the resources, that gets added to it (this is the same for any Java platform, where the relative path is normally in a jar file). For example:

    InputStream smile = foo.bar.Emojis.getResourceAsStream("smile.gif") => resource path is "foo/bar/smile.gif"
or
    InputStream smile = foo.bar.Emojis.getResourceAsStream("graphics/smile.gif") => resource path is "foo/bar/graphics/smile.gif"

iOS projects copy all resources to the bundle's root, so they need to instead be copied to the relative path (such as foo/bar/graphics). If classes in different packages load resources, multiple copy steps need to be added to the project, at least one for each relative path.

Vincent Guo

unread,
Jul 28, 2017, 2:25:00 PM7/28/17
to j2objc-discuss
Thank you very much for the explanation, I did not understand that at all at the beginning
...

Vincent Guo

unread,
Aug 22, 2017, 9:04:08 AM8/22/17
to j2objc-discuss
Hello, back from a 3 week break,

So I ended my work session with this InputStream problem unsolved, and I'm still on it.

The files I want to read into are in a folder named assets, which is located in the Assets.xcassets folder of Xcode.
By "requires that the resource have the same package path as the class" do you mean that assets folder must be in the same folder as the class that tries to read them ?
I've tried with a copy of this assets folder and placed it under the same folder as the class Resources in which I try to use getResourceAsStream.

The translated piece of code in Resources is the following code :

JavaIoInputStream *EuMyrpgPlayerandroidResources_getAssetsWithNSString_(NSString *filename) {

  EuMyrpgPlayerandroidResources_initialize();

  JavaIoInputStream *is = nil;

  is = [JavaIoInputStream_class_() getResourceAsStream:filename];

  return is;

}

I have included the file in the copy file part, I don't really know what to put in the subpath field, I reckon there should be nothing since the parameter filename contains "assets/config_RA_FR.xml"


I have also tried with "assets" and "assets/" in the subpath field but the function still returns nil


I feel I didn't understand what to put before the getResourceAsStream, in the java code I wrote :

package eu.myrpg.playerandroid;


import java.io.InputStream;


public class Resources {


    public static InputStream getAssets(String filename) {

        InputStream is = null;

        is = InputStream.class.getResourceAsStream(filename);

        return is;

    }

}


Then I call this getAssets function when I need inputstream 

<blockquote style="margin:0;margin-left:0.8ex;border-left:1

Keith Stanger

unread,
Aug 22, 2017, 10:21:02 AM8/22/17
to j2objc-discuss
I'm not sure that Assets.xcassets is the right folder for your resources. I think that's normally meant for your app icon and launch images. I'm not sure exactly how to set up your resources bundle in XCode.

One error I see is that you're using InputStream.class to get the resource stream. The "getResourceAsStream" method (javadoc) will look for the file under the directory that matches the class package, in this case "/java/io/". You can avoid this by putting a slash in front of the file name. So InputStream.class.getResourceAsStream("foo.txt") will look for "/java/io/foo.txt" in your resource bundle, but InputStream.class.getResourceAsStream("/foo.txt") will look for "/foo.txt".

Tom Ball

unread,
Aug 22, 2017, 10:54:31 AM8/22/17
to j2objc-discuss
Cool, that leading slash trick can make access to iOS resources much easier. I'll look into updating our resources doc.
Reply all
Reply to author
Forward
0 new messages