Oauth 2.0示例无法启动活动ComponentInfo - NullPointerException

时间:2013-11-19 04:09:34

标签: java android oauth oauth-2.0 authorization

我找到了这个仅限应用程序授权(OAuth 2.0)和OAuth 1.0a的优秀示例。问题是示例都显示在同一个java文件中,我真的很想测试OAuth 2.0只读授权的功能。当我尝试注释并禁用0Auth 1.0a的部分时,它会爆炸。我也试图将按钮数量减少到最少两个(1a和1b)甚至没有,但我一直遇到错误。我将发布问题代码,但如果您想按原样使用完整的示例代码,可以在此处找到:http://ttlnews.blogspot.com/2013/07/integrating-twitter-11-api-in-android.html?m=1

错误日志:

11-18 22:32:22.355: E/AndroidRuntime(32334): FATAL EXCEPTION: main
11-18 22:32:22.355: E/AndroidRuntime(32334): java.lang.RuntimeException: Unable to   start activity     ComponentInfo{com.example.twitterclientoauth/com.example.twitterclientoauth.OAuthTwitterClient}: java.lang.NullPointerException
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2246)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2296)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.app.ActivityThread.access$700(ActivityThread.java:151)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1281)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.os.Handler.dispatchMessage(Handler.java:99)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.os.Looper.loop(Looper.java:137)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.app.ActivityThread.main(ActivityThread.java:5293)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at java.lang.reflect.Method.invokeNative(Native Method)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at java.lang.reflect.Method.invoke(Method.java:511)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at dalvik.system.NativeStart.main(Native Method)
11-18 22:32:22.355: E/AndroidRuntime(32334): Caused by: java.lang.NullPointerException
11-18 22:32:22.355: E/AndroidRuntime(32334):    at com.example.twitterclientoauth.OAuthTwitterClient.onCreate(OAuthTwitterClient.java:96)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.app.Activity.performCreate(Activity.java:5250)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1097)
11-18 22:32:22.355: E/AndroidRuntime(32334):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2210)
11-18 22:32:22.355: E/AndroidRuntime(32334):    ... 11 more

OAuthTwitterClient.java

package com.example.twitterclientoauth;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.HttpsURLConnection;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthProvider;
import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
import oauth.signpost.commonshttp.CommonsHttpOAuthProvider;
import oauth.signpost.exception.OAuthNotAuthorizedException;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.HTTP;
import org.json.simple.JSONValue;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
* Simple quite ugly looking UI to show usage of the new Twitter API v1.1.
* Includes tweeting and app-only authentication.
* 
*/
public class OAuthTwitterClient extends Activity {


private static final String USER_AGENT = "OAuthTwitterClient app-only auth";
/*private EditText tweetText = null;*/
private TextView buttonStatus = null;
/*private Button loginButton = null;*/
private Button bearerTokenButton = null;
/*private Button tweetButton = null;*/
private Button getTweetButton = null;
private TextView result = null;

private String twitterUsername= "twitter"; // The username to get the tweet for
private String tweet;

private LinearLayout wrappingLayout = null;

private static final String CALLBACK_URL = "Insert_Callback_URL"; // TO CHANGE!!
private static final String CONSUMER_KEY = "Consumer_Key"; // TO CHANGE!!
private static final String CONSUMER_SECRET = "Consumer_Secret"; // TO CHANGE!!

private OAuthProvider provider = null;
private CommonsHttpOAuthConsumer consumer = null;
//private HttpClient client = null;

private String bearerToken;


@Override
public void onCreate(Bundle savedInstance) {

    Log.d(getClass().getSimpleName(), "Entering");

    super.onCreate(savedInstance);

    setContentView(R.layout.activity_main);
    Log.d(getClass().getSimpleName(), "After setting content");

    //wrappingLayout = (LinearLayout) findViewById(R.id.wrappingLayout);
    //tweetText = (EditText) wrappingLayout.findViewById(R.id.tweetText);

    **buttonStatus = (TextView) wrappingLayout.findViewById(R.id.status);**

    //loginButton = (Button) wrappingLayout.findViewById(R.id.loginButton);
    //loginButton.setOnClickListener(loginOnClickListener);

    //tweetButton = (Button) wrappingLayout.findViewById(R.id.tweetButton);
    //tweetButton.setOnClickListener(tweetOnClickListener);

    bearerTokenButton = (Button) wrappingLayout.findViewById(R.id.getBearerTokenButton);
    bearerTokenButton.setOnClickListener(getBearerTokenOnClickListener);

    getTweetButton = (Button) wrappingLayout.findViewById(R.id.getTweetButton);
    getTweetButton.setOnClickListener(getTweetOnClickListener);

    result = (TextView) wrappingLayout.findViewById(R.id.result);

    // Setup oauth stuff that only needs to happen once.
    consumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);

    provider = new CommonsHttpOAuthProvider("https://api.twitter.com/oauth/request_token", "https://api.twitter.com/oauth/access_token",
            "https://api.twitter.com/oauth/authorize");

    //client = new DefaultHttpClient();
    // If you get this error: "No trusted server certificate", try replacing the above statement with next statement to load own keystore with correct Twitter certificates:
    // client = new CrazyBobHttpClient(getApplicationContext());

}


/**
 * Listens to click on login button
 */
/* private OnClickListener loginOnClickListener = new OnClickListener() {

    public void onClick(View v) {

        Log.d(getClass().getSimpleName(), "Login button pressed");
        buttonStatus.setText("Loginbutton clicked");
        result.setText("");

        String authUrl = null;
        try {
            authUrl = provider.retrieveRequestToken(consumer, CALLBACK_URL);
        } catch (Exception ex) {
            Log.e(getClass().getSimpleName(), "Unable to retrieveRequestToken, exception  ", ex );
            throw new RuntimeException(ex);
        }

        // Note the singleInstance setting in the manifest.xml
        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));

    }
};
*/

/**
 * Listens to click on the tweet button
 */
/* private OnClickListener tweetOnClickListener = new OnClickListener() {

    public void onClick(View v) {

        Log.d(getClass().getSimpleName(), "Tweet button pressed");
        buttonStatus.setText("Tweetbutton clicked");
        result.setText("");

        // create a request that requires authentication
        HttpPost post = new HttpPost("https://api.twitter.com/1.1/statuses/update.json");

        // Set up the tweet contents
        final List<BasicNameValuePair> nvps = new ArrayList<BasicNameValuePair>();


        try {

            post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
            // set this to avoid 417 error (Expectation Failed)
            post.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
            // sign the request
            consumer.sign(post);

            // send the request
            final HttpResponse response = client.execute(post);
            // response status should be 200 OK
            int statusCode = response.getStatusLine().getStatusCode();
            final String reason = response.getStatusLine().getReasonPhrase();

            // Bit weird order, statusCode *can* be checked earlier, is there a special reason?
            if (statusCode != 200) {
                Log.e(getClass().getSimpleName(), "TwitterConnector failed, statusCode not 200 but " + statusCode +", reason = " + reason);
                throw new OAuthNotAuthorizedException();
            }     

            // release connection
            response.getEntity().consumeContent();

            result.setText("Succssfully tweeted");

        } catch (Exception ex) {
            Log.e(getClass().getSimpleName(), "Unable to post tweet update, exception = ", ex);
            result.setText(ex.getMessage());
        }
    }

};*/

/**
 * Listens to click on the get-tweet button
 */
private OnClickListener getTweetOnClickListener = new OnClickListener() {

    public void onClick (View v) {

        Log.d(getClass().getSimpleName(), "getTweet button pressed");
        buttonStatus.setText("getTweet button clicked");
        result.setText("");
        tweet = "";

        // Obtain a bearer token. Note here it's done a bit cleaner, not doing heavy processing in the UI thread.
        // Code heavily based upon: http://www.dreamincode.net/forums/blog/114/entry-4459-demo-of-twitter-application-only-oauth-authentication-using-java/
        try {

            tweet = fetchTimelineTweet("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + twitterUsername + "&count=2", bearerToken);

            result.setText("TWEET from user " + twitterUsername + " = " + tweet);
        } catch (Exception ex) {
            Log.e(getClass().getSimpleName(), "Unable to get bearer token, exception = ", ex);
            //result.setText(ex.getMessage());
        }

    }
};


/**
 * Listens to click on the get-tweet button
 */
private OnClickListener getBearerTokenOnClickListener = new OnClickListener() {

    public void onClick(View v) {

        Log.d(getClass().getSimpleName(), "getBearerToken button pressed");
        buttonStatus.setText("getBearerToken button clicked");
        result.setText("");
        tweet = "";

        // Obtain a bearer token. Note here it's done a bit cleaner, not doing heavy processing in the UI thread.
        // Code heavily based upon: http://www.dreamincode.net/forums/blog/114/entry-4459-demo-of-twitter-application-only-oauth-authentication-using-java/
        try {
            bearerToken = requestBearerToken("https://api.twitter.com/oauth2/token");
            Log.d(getClass().getSimpleName(), "bearerToken = " + bearerToken);
            if (TextUtils.isEmpty(bearerToken)) {
                result.setText("Unsuccessfully retrieved bearer token, empty!");
            } else {
                result.setText("Successfully retrieved bearer token");
            }
        } catch (Exception ex) {
            Log.e(getClass().getSimpleName(), "Unable to get bearer token, exception = ", ex);
            result.setText(ex.getMessage());
        }

    }
};





/**
 * Encodes the consumer key and secret to create the basic authorization key 
 */
private  String encodeKeys(String consumerKey, String consumerSecret) {
    try {
        String encodedConsumerKey = URLEncoder.encode(consumerKey, "UTF-8");
        String encodedConsumerSecret = URLEncoder.encode(consumerSecret, "UTF-8");

        String fullKey = encodedConsumerKey + ":" + encodedConsumerSecret;
        byte[] encodedBytes = Base64.encodeBase64(fullKey.getBytes());

        return new String(encodedBytes);  
    }
    catch (UnsupportedEncodingException e) {
        return new String();
    }
}


/**
 * Workaround to fix -1 responseCode on a second call via HttpsUrlConnection.
 * See: http://stackoverflow.com/questions/1440957/httpurlconnection-getresponsecode-returns-1-on-second-invocation 
 */
@SuppressWarnings("deprecation")
private void disableConnectionReuseIfNecessary() {
    // HTTP connection reuse which was buggy pre-froyo
    // When mindSdk >= 4 use: Build.VERSION.SDK_INT 
    if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.JELLY_BEAN) {
       System.setProperty("http.keepAlive", "false");
    }
}

/*
 * Constructs the request for requesting a bearer token and returns that token as a string
 */
private String requestBearerToken(String endPointUrl) throws IOException {

    Log.d(getClass().getName(), "requestBearerToken(): entering");

    HttpsURLConnection connection = null;
    String encodedCredentials = encodeKeys(CONSUMER_KEY,CONSUMER_SECRET);

    try {
        URL url = new URL(endPointUrl); 
        connection = (HttpsURLConnection) url.openConnection();           
        connection.setDoOutput(true);
        connection.setDoInput(true); 
        connection.setRequestMethod("POST"); 
        connection.setRequestProperty("Host", "api.twitter.com");
        connection.setRequestProperty("User-Agent", USER_AGENT);
        connection.setRequestProperty("Authorization", "Basic " + encodedCredentials);
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); 
        connection.setRequestProperty("Content-Length", "29");
        connection.setUseCaches(false);

        disableConnectionReuseIfNecessary();

        writeRequest(connection, "grant_type=client_credentials");

        // Parse the JSON response into a JSON mapped object to fetch fields from.
        Object parse = JSONValue.parse(readResponse(connection));
        org.json.simple.JSONObject obj = (org.json.simple.JSONObject)parse;

        if (obj != null) {
            String tokenType = (String)obj.get("token_type");
            String token = (String)obj.get("access_token");

            return ((tokenType.equals("bearer")) && (token != null)) ? token : "";
        }
        return ""; // Bit ugly...

    } catch (MalformedURLException e) {
        throw new IOException("Invalid endpoint URL specified." +  e.getMessage());
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
    }
}




/**
 * Fetches the first tweet from a given user's timeline
 */
private String fetchTimelineTweet(String endPointUrl, String bearerToken) throws IOException {

    Log.d(getClass().getName(), "fetchTimelineTweet(): entering");
    HttpsURLConnection connection = null;

    try {
        URL url = new URL(endPointUrl); 
        connection = (HttpsURLConnection) url.openConnection();           
        // connection.setDoOutput(true); this line causes on (at least from) Android 4.x to return a HTTP 500 and gives a FileNotFoundException on reading the input stream
        // See for solution: http://stackoverflow.com/questions/9365829/filenotfoundexception-for-httpurlconnection-in-ice-cream-sandwich
        //      Similar: http://stackoverflow.com/questions/11810447/httpurlconnection-worked-fine-in-android-2-x-but-not-in-4-1-no-authentication-c
        connection.setDoInput(true); 
        connection.setRequestMethod("GET"); 
        connection.setRequestProperty("Host", "api.twitter.com");
        connection.setRequestProperty("User-Agent", USER_AGENT);
        connection.setRequestProperty("Authorization", "Bearer " + bearerToken);
        connection.setUseCaches(false);

        disableConnectionReuseIfNecessary();

        // Parse the JSON response into a JSON mapped object to fetch fields from.
        org.json.simple.JSONArray obj = (org.json.simple.JSONArray)JSONValue.parse(readResponse(connection));

        if (obj != null) {
            String tweet = ((org.json.simple.JSONObject)obj.get(0)).get("text").toString();

            return (tweet != null) ? tweet : "";
        }
        return ""; // Bit ugly...
    }
    catch (MalformedURLException e) {
        throw new IOException("Invalid endpoint URL specified." + e.getMessage());
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
    }
}



/**
 * Writes a request to a connection
 */
private static boolean writeRequest(HttpsURLConnection connection, String textBody) {

    try {
        BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
        wr.write(textBody);
        wr.flush();
        wr.close();

        return true;
    } catch (IOException e) {
        // TODO: needs better error handling
        return false; 
    }
}


/** 
 * Reads a response for a given connection and returns it as a string.
 */
private static String readResponse(HttpsURLConnection connection) {

    try {
        StringBuilder str = new StringBuilder();

        int responseCode = connection.getResponseCode();
        Log.d(OAuthTwitterClient.class.getName(), "readResponse(): Response code = " + responseCode);
        if (responseCode != HttpStatus.SC_OK) {

            if (responseCode != -1) {
                InputStream errorStream = connection.getErrorStream();
                InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
                BufferedReader br = new BufferedReader(inputStreamReader);
                String line = "";
                while((line = br.readLine()) != null) {
                    str.append(line + System.getProperty("line.separator"));
                }
            } else {
                String errorMsg = "Response code = -1, so can't get error stream";
                Log.e(OAuthTwitterClient.class.getSimpleName(), errorMsg);
                str.append(errorMsg);
            }
            Log.e(OAuthTwitterClient.class.getSimpleName(), "Error stream output = " + str.toString());
            return str.toString();
        }
        InputStream inputStream = connection.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader br = new BufferedReader(inputStreamReader);
        String line = "";
        while((line = br.readLine()) != null) {
            str.append(line + System.getProperty("line.separator"));
        }

        Log.d(OAuthTwitterClient.class.getSimpleName(), "Returning as response string: " + str.toString());

        return str.toString();

    } catch (IOException e) {

        // TODO: better error handling
        e.printStackTrace(); 
        throw new IllegalArgumentException(e); 
    }
}





/**
 * Invoked: when browser calls that callback URL then your app is brought forward and activity’s onResume() method is called
 * See: http://dev.bostone.us/2009/07/16/android-oauth-twitter-updates/#awp::2009/07/16/android-oauth-twitter-updates/
 * 
 */
@Override
protected void onResume() {

    Log.d(getClass().getSimpleName(), "onResume entered");

    Uri uri = this.getIntent().getData();
    if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {

        Log.d(getClass().getSimpleName(), "Yup it's talking to us! callback_url is ok.");
        String verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);
        Log.d(getClass().getSimpleName(), "Retrieved query param verifier = " + verifier );
        // this will populate token and token_secret in consumer
        try{
            provider.retrieveAccessToken(consumer, verifier);
        } catch (Exception ex) {
            Log.e(getClass().getSimpleName(), "Unable to retrieveAccessToken, exception  ", ex );
            throw new RuntimeException(ex);
        }

        // At this point you can call consumer#getToken() and consumer#getTokenSecret() to get and save token/secret for subsequent calls. According to Twitter – access token will not expire unless revoked by user
    }

    super.onResume();
}


/**
 * See: http://dev.bostone.us/2009/07/16/android-oauth-twitter-updates/#IDComment29500241
 */
@Override
protected void onNewIntent(Intent intent) {

    Log.d(getClass().getSimpleName(), "onNewIntent entered");

    Uri uri = intent.getData();
    if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {

        Log.d(getClass().getSimpleName(), "Yup it's talking to us! callback_url is ok.");
        String verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);
        Log.d(getClass().getSimpleName(), "Retrieved query param verifier = " + verifier );
        // this will populate token and token_secret in consumer
        try{
            provider.retrieveAccessToken(consumer, verifier);
        } catch (Exception ex) {
            Log.e(getClass().getSimpleName(), "Unable to retrieveAccessToken, exception  ", ex );
            throw new RuntimeException(ex);
        }

        // At this point you can call consumer#getToken() and consumer#getTokenSecret() to get and save token/secret for subsequent calls. According to Twitter – access token will not expire unless revoked by user
        result.setText("Authorized!!");
    }

    super.onNewIntent(intent);
}


}

非常感谢任何帮助。此外,如果有人知道如何交换OnClick,因为我不是在寻找使用按钮监听器的操作,但是当应用程序运行时,我会自动感谢。

0 个答案:

没有答案