我找到了这个仅限应用程序授权(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,因为我不是在寻找使用按钮监听器的操作,但是当应用程序运行时,我会自动感谢。