onActivityResult called twice

2019-09-18 06:04发布

问题:

I have a simple activity with a facebook button to use login (using Facebook Android SDK:

package com.example.adminn.facebooktest;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import android.content.Intent;
import android.view.View;
import android.app.Activity;
import android.support.v4.app.FragmentActivity;

import com.facebook.FacebookException;
import com.facebook.FacebookSdk;
import com.facebook.CallbackManager;
import com.facebook.login.LoginManager;
import com.facebook.login.widget.LoginButton;
import com.facebook.login.LoginResult;
import com.facebook.FacebookCallback;

import java.util.Arrays;


public class MainActivity extends FragmentActivity {
    CallbackManager callbackManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FacebookSdk.sdkInitialize(this.getApplicationContext());
        setContentView(R.layout.activity_main);

        callbackManager = CallbackManager.Factory.create();


        //LoginButton loginButton = (LoginButton) findViewById(R.id.login_button);
        LoginManager.getInstance().registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                //Toast toast = Toast.makeText(MainActivity.this, "OnSuccess:" + loginResult.getAccessToken().toString(), Toast.LENGTH_LONG);
                //toast.show();

            }

            @Override
            public void onCancel() {
                Toast toast = Toast.makeText(MainActivity.this, "Cancel!!", Toast.LENGTH_LONG);
                toast.show();
            }

            @Override
            public void onError(FacebookException e) {
                Toast toast = Toast.makeText(MainActivity.this, "Error!", Toast.LENGTH_LONG);
                toast.show();
            }
        });




    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    protected  void  onActivityResult(final int requestCode, final int resultCode,final Intent data){
        super.onActivityResult(requestCode,resultCode,data);
        callbackManager.onActivityResult(requestCode,resultCode,data);

    }

    public void facebookLogin(View v){

        LoginManager.getInstance().logInWithReadPermissions(this, Arrays.asList("public_profile","user_friends"));

    }
}

The code above crashes once i press the button, with the following error:

Process: com.example.adminn.facebooktest, PID: 8830
    java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=64206, result=-1, data=Intent { (has extras) }} to activity {com.example.adminn.facebooktest/com.example.adminn.facebooktest.MainActivity}: java.lang.NullPointerException: Argument 'context' cannot be null
            at android.app.ActivityThread.deliverResults(ActivityThread.java:3626)
            at android.app.ActivityThread.handleSendResult(ActivityThread.java:3669)
            at android.app.ActivityThread.access$1300(ActivityThread.java:148)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1341)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5312)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)
     Caused by: java.lang.NullPointerException: Argument 'context' cannot be null
            at com.facebook.internal.Validate.notNull(Validate.java:67)
            at com.facebook.appevents.AppEventsLogger.<init>(AppEventsLogger.java:652)
            at com.facebook.appevents.AppEventsLogger.newLogger(AppEventsLogger.java:417)
            at com.facebook.login.LoginLogger.<init>(LoginLogger.java:67)
            at com.facebook.login.LoginManager.getLogger(LoginManager.java:397)
            at com.facebook.login.LoginManager.logCompleteLogin(LoginManager.java:415)
            at com.facebook.login.LoginManager.onActivityResult(LoginManager.java:190)
            at com.facebook.login.LoginManager$1.onActivityResult(LoginManager.java:140)
            at com.facebook.internal.CallbackManagerImpl.onActivityResult(CallbackManagerImpl.java:82)
            at com.example.adminn.facebooktest.MainActivity.onActivityResult(MainActivity.java:88)
            at android.app.Activity.dispatchActivityResult(Activity.java:6161)
            at android.app.ActivityThread.deliverResults(ActivityThread.java:3622)
            at android.app.ActivityThread.handleSendResult(ActivityThread.java:3669)
            at android.app.ActivityThread.access$1300(ActivityThread.java:148)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1341)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5312)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)

I have done some digging through the SDK's source code using breakpoints and i realized that the onActivityResult is being called twice (when i press the button and another one for no reason). The first time it goes smooth and retrieves the token and session information. When it finishes, the context field inside LoginManager.java is set to null. Since callbackManager.onActivityResult calls, at some point, logCompleteLogin, which in turn creates a LoginLogger based on the nulled context, the application crashes.

Why is onActivityResult being called twice? As far as i know, there is only another activity besides MainActivity, which is FacebookActivity.

回答1:

Both problems are solved and they are related.

What happens and i failed to understand was that the facebook button has an underlying event that triggers the login process. I set another event on MainActivity to act upon click. So, internally, what was happening was that, the facebookactivity was being created twice which lead to two calls to onActivityResult. The first call to onActivityResult leaves the internal state of LoginManager with a nulled context field (which was previously the MainActivity). A second call assumes that the context is not null, crashing the application if it is (there is a Validate verification internally).

Falling back to a previous version of the SDK worked because LogginManager.context was not being nullified when calling logInWithReadPermissions, rendering the second call "valid".



回答2:

In the latest v4.2.0 of the Facebook android sdk the NPE you are seeing looks to be a result of this commit.

It shouldn't be an issue in the next version of the sdk since a pull request has already been accepted which mitigates this issue pr430. And for now you could fallback to v4.1.2 of the sdk.

I'm not sure why the onActivityResult is called twice