Exception thrown when calling getFriends

462 Views Asked by At

I get this exception, "Can't create handler inside thread that has not called Looper.prepare()", thrown when I call either get or getFriends. Here is the code I use for getFriends. It is called from the main thread.

public void returnFriendIDs(final int pointer){
    Properties properties = new Properties.Builder()
                .add(Profile.Properties.ID)
                .add(Profile.Properties.FIRST_NAME)
                .add(Profile.Properties.LAST_NAME)
                .add(Profile.Properties.INSTALLED)
                .build();

    try{
        mSimpleFacebook.getFriends(properties,  new OnFriendsListener() {
            @Override
            public void onComplete(List<Profile> friends) {
                String resultStr = "";
                Log.d(LOGPREFIX, "Number of friends = " + friends.size());
                for(Profile p : friends){
                    resultStr += p.getId()+',';
                }
                if(friends.size() > 0) returnFriends(pointer, resultStr.substring(0,resultStr.length()-1));
                else returnFriends(pointer, "");
            }

            @Override
            public void onFail(String reason){
                returnFriends(pointer, "");
            }

            @Override
            public void onException(Throwable throwable){
                Log.w(LOGPREFIX, throwable.getMessage());
                returnFriends(pointer, "");
            }
        });
    }
    catch(Throwable throwable){
        Log.w(LOGPREFIX, "In getFriends"+throwable.getMessage());
    }
}

I've tried calling Looper.prepare() before calling getFriends, but that just leads to none of the callbacks ever getting called.

My onActivityResult function is

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        //Log.w(LOGPREFIX, "onActivityResult() called.");
        mSimpleFacebook.onActivityResult(this, requestCode, resultCode, data);

        // Handle InApp Purchase call results
        if(REQUEST_INAPP_PURCHASE == requestCode)
        {
             if (!mBillingHelper.handleActivityResult(requestCode, resultCode, data)) {
                 super.onActivityResult(requestCode, resultCode, data);
             }
        }
    }

The stacktrace is:

Pending exception java.lang.RuntimeException thrown by 'unknown throw location'
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
  at void android.os.Handler.<init>(android.os.Handler$Callback, boolean) (Handler.java:200)
  at void android.os.Handler.<init>() (Handler.java:114)
  at void com.facebook.RequestAsyncTask.onPreExecute() (RequestAsyncTask.java:146)
  at android.os.AsyncTask android.os.AsyncTask.executeOnExecutor(java.util.concurrent.Executor, java.lang.Object[]) (AsyncTask.java:587)
  at android.os.AsyncTask android.os.AsyncTask.execute(java.lang.Object[]) (AsyncTask.java:535)
  at void com.sromku.simple.fb.actions.GetAction.runRequest(com.facebook.Request) (GetAction.java:143)
  at void com.sromku.simple.fb.actions.GetAction.executeImpl() (GetAction.java:81)
  at void com.sromku.simple.fb.actions.AbstractAction.execute() (AbstractAction.java:17)
  at void com.sromku.simple.fb.SimpleFacebook.getFriends(com.sromku.simple.fb.entities.Profile$Properties, com.sromku.simple.fb.listeners.OnFriendsListener) (SimpleFacebook.java:594)
  at void com.fillmyblank.app.FillMyBlank.returnFriendIDs(int) (FillMyBlank.java:325)
  at void org.libsdl.app.SDLActivity.nativeInit() (SDLActivity.java:-2)
  at void org.libsdl.app.SDLMain.run() (SDLActivity.java:509)
  at void java.lang.Thread.run() (Thread.java:818)

It is called with this code from my main function(which I believe is the UI thread). I use the ndk so by main function I mean the actual function main.

    aEnv = (JNIEnv *)SDL_AndroidGetJNIEnv();

    aActivityClass = aEnv->FindClass(mClassPath.c_str());

    aStaticMid = aEnv->GetStaticMethodID(aActivityClass, "GetActivity", "()Lcom/fillmyblank/app/FillMyBlank;");
    aActivity =  aEnv->CallStaticObjectMethod(aActivityClass, aStaticMid);

    friends* fp = new friends;
    fp->waiting = SDL_CreateCond();

    aJavaMethodID = aEnv->GetMethodID(aActivityClass, "returnFriendIDs", "(I)V");

    aEnv->CallVoidMethod(aActivity, aJavaMethodID, fp);

EDIT: Added stacktrace

EDIT: Added the calling function.

1

There are 1 best solutions below

0
On BEST ANSWER

The explanation

The Exception is thrown in the onPreExecute part of an AsyncTask which is part of the Facebook library (com.facebook.RequestAsyncTask).

onPreExecute is executed in the same thread as the caller of the AsyncTask. If the caller is the ui thread, no issue, it will work. It the caller is not the ui thread, it could fail (there is a great chance). If any component of the ui or related to the ui is used, it will fail.

In your case, it is not executed in the ui thread but in in a regular thread which does not have a message loop so this famous Exception is thrown : Can't create handler inside thread that has not called Looper.prepare()

The solution

You have to use runOnUiThread method that will ensure that your code is executed in the ui thread. runOnUiThread takes a Runnable as a parameter.

public void returnFriendIDs(final int pointer)
{
    runOnUiThread(new Runnable()
    {
        @Override
        public void run()
        {
            Properties properties = new Properties.Builder()
            .add(Profile.Properties.ID)
            .add(Profile.Properties.FIRST_NAME)
            .add(Profile.Properties.LAST_NAME)
            .add(Profile.Properties.INSTALLED)
            .build();

            try{
                mSimpleFacebook.getFriends(properties,  new OnFriendsListener() {
                    @Override
                    public void onComplete(List<Profile> friends) {
                        String resultStr = "";
                        Log.d(LOGPREFIX, "Number of friends = " + friends.size());
                        for(Profile p : friends){
                            resultStr += p.getId()+',';
                        }
                        if(friends.size() > 0) returnFriends(pointer, resultStr.substring(0,resultStr.length()-1));
                        else returnFriends(pointer, "");
                    }

                    @Override
                    public void onFail(String reason){
                        returnFriends(pointer, "");
                    }

                    @Override
                    public void onException(Throwable throwable){
                        Log.w(LOGPREFIX, throwable.getMessage());
                        returnFriends(pointer, "");
                    }
                });
            }
            catch(Throwable throwable){
                Log.w(LOGPREFIX, "In getFriends"+throwable.getMessage());
            }
        }
    });
}