The facebook SDK

posted Feb 1, 2013, 9:57 AM by Nick Martens
One day I decided ARM Launcher needed to have a Facebook widget. This would be a nice addition and in this post I will tell you about my experiences with the Facebook API. 

Note that even if I say something is not possible I may be completely wrong. I might have missed something in the documentation or misunderstood it completely. All I'd like to say in that case is: Dear Facebook please make the documentation of your API easier to understand.

The easiest way to get started is downloading the Facebook SDK, and set it up as a library project and make your Android application depend on it.

While it is quite nice and motivating that Facebook provides an SDK to access their services, it is also something that gave me a few headaches. Some parts of the code made me raise my eyebrows and had to be fixed or changed before using it in a production project.

I am going to mention a few of the issues I ran into and explain why, in my opinion, things are wrong and broken.

Facebook SDK code

I started by checking out the latest version (3.0) of the API from github.

Errors, I mean java.lang.Error?

First I found something awkward in the DialogListener interface. Some of the parameters received an instance of java.lang.Error. Yes really. Error handling in this interface has been implemented by calling onError and onFacebookError with subclasses of java.lang.Error. Which should, never be used in user code:

Error Class

When a dynamic linking failure or other hard failure in the Java virtual machine occurs, the virtual machine throws an Error. Simple programs typically do not catch or throw Errors. [java documentation]

Another side effect of creating an instance of these classes is that the virtual machine will fill the stack trace to the current point in code. This is terribly slow see Item 57 in Chapter 9 of Effective Java for more details. In ARM Launcher I 'fixed' these issues by overriding the initCause method and not calling super. Though this is not a fix it solves the overhead of initializing the stacktrace. 

StrictMode

When executing Facebook requests, StrictMode showed me violations about not closing streams. So upon investigating what was going on I found the following code in Util.java, in the method openUrl(String, String, Bundle)

String response = "";
try {
    response = read(conn.getInputStream());
} catch (FileNotFoundException e) {
    // Error Stream contains JSON that we can parse to a FB error
    response = read(conn.getErrorStream());
}
return response;

While this may not immediately seem wrong, any experienced java developer will notice that those two streams are indeed opened and never closed. If an application is running for a long time and performing a lot of requests this can lead to serious problems.

This was resolved by wrapping the parts that read the streams with try finally blocks like this:

String response = "";
try {
    InputStream in = conn.getInputStream();
    try {
        response = read(in);
    } finally {
        in.close();
    }
} catch (FileNotFoundException e) {
    // Error Stream contains JSON that we can parse to a FB error
    InputStream err = conn.getErrorStream();
    try {
        response = read(err);
    } finally {
        err.close();
    }
}
return response;

This could even be made a function to make it more readable.

Serious Lint warnings that should really be solved

When running Android Lint the following problems, among others that I don't care that much about, showed up.
  • Possible overdraw (not verified that there actually is overdraw)
  • Non default constructors for Fragments
  • Nested weights in layout files
  • Use of the default locale (Lint claims it to be a common source of bugs)
  • This LinearLayout should use android:layout_height="wrap_content". In fact it is using fill parent in a ScrollView. Which is really wrong.
  • Unused resources. Please don't include unused resources in production libraries.
  • This handler class should be static or leaks might occur (com.facebook.GetTokenClient.1)
Note that within eclipse it takes one click to run lint checks.

Relying on the Graph API

The concept behind the Graph API is quite nice. But certain parts are really inconsistent, not available or not working as expected. For example the is no way to simply share a post using the graph API (like the share button on the website does). 

When getting results from the wall or timeline paging though supported seems impossible to implement using limit and offset methods. 
One day the API broke and I was searching half a day what had changed in ARM Launcher before realizing it was a problem with Facebook.
I tried to make it possible to view subcomments (comments on comments) though i was able to retrieve them, it seemed impossible to post new ones through the API or to like any of them.
Facebook offers a way to execute batch requests. Great feature, but then you read the fine print:

Once both operations have been completed, Facebook sends a response which encapsulates the result of all operations. For each operation, the response includes a status code, header information, and body. This is equivalent to the response you expect from each operation if performed as raw requests against the Graph API. The body field contains a string encoded JSON object:

Wait a minute, a what? A String encoded Json Object? Why? What is wrong with encoding the result the same way as Multi-Query does? Multi-Query returns the results from each query as a normal Json Object. This is a bit weird, but what makes matters worse is that android has no native function to decode string encoded Json. So I decided not to use this feature even though it would improve performance of the requests.

Another thing I really do not understand is how I am supposed to get related posts from the API. I mean I get a result saying Person X likes shared a post by person Y. Now when the user clicks that post in ARM Launcher, I'd like to show the post by person Y. But it is not really clear how this is linked, sometimes it works by interpreting the post id and removing parts of it. But this is not documented. 

Same goes for FQL, it is nice but it is not always working the way you expect. I actually implemented everything to use FQL at first, but I started running into problems so I switched back to the normal Graph API. 
One issue that was really annoying was the stream table returning types that are not mentioned in the documentation.

Now unless I missed something I'd say: Come on Facebook, this can all be done much better can't it?
Comments