SDK下載 (最新3.1):http://connect.qq.com/intro/login
1.自己創建應用
首先我們打開騰訊開發平臺這個網頁,點擊—->移動應用—->創建應用,成功創建應用後,可以產生我們需要的App ID和App Key,如下圖所示:
2.運行官方Demo(官方Demo比較全,可以根據自己需求使用,我們在這裏只分析登入的代碼)
下面是主要的登錄代碼,在Demo的MainActivity裏面
/**
*
* 通過調用Tencent類的login函數發起登錄/校驗登錄態。
*
* 該API具有兩個作用:
* (1)如果開發者沒有調用mTencent實例的setOpenId,setAccessToken API,則該API執行正常的登錄操作;
* (2)如果開發者先調用mTencent實例的setOpenId、setAccessToken
* API,則該API執行校驗登錄態的操作。如果登錄態有效,則返回成功給應用,
* 如果登錄態失效,則會自動進入登錄流程,將最新的登錄態數據返回給應用
*
* @author super bear
*
*/
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getName();
public static String mAppid;
private Button mNewLoginButton;
private Button mServerSideLoginBtn;
private TextView mUserInfo;
private ImageView mUserLogo;
private UserInfo mInfo;
private EditText mEtAppid = null;
public static Tencent mTencent;
private static Intent mPrizeIntent = null;
private static boolean isServerSideLogin = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "-->onCreate");
// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//
// 固定豎屏
setContentView(R.layout.activity_main_new);
initViews();
//setBarTitle("demo菜單");
if (TextUtils.isEmpty(mAppid)) {
mAppid = "222222";
mEtAppid = new EditText(this);
mEtAppid.setText(mAppid);
try {
new AlertDialog.Builder(this).setTitle("請輸入APP_ID")
.setCancelable(false)
.setIcon(android.R.drawable.ic_dialog_info)
.setView(mEtAppid)
.setPositiveButton("Commit", mAppidCommitListener)
.setNegativeButton("Use Default", mAppidCommitListener)
.show();
} catch (Exception e) {
}
} else {
if (mTencent == null) {
mTencent = Tencent.createInstance(mAppid, this);
}
}
// 獲取有獎分享的intent信息
if (null != getIntent()) {
mPrizeIntent = getIntent();
}
}
/**
* 有獎分享處理,未接入有獎分享可以不考慮
*/
private void handlePrizeShare() {
// -----------------------------------
// 下面的註釋請勿刪除,編譯lite版的時候需要刪除, 注意//[不要有空格。
//[liteexludestartmeta]
if (null == mPrizeIntent || null == mTencent) {
return;
}
// 有獎分享處理
boolean hasPrize = mTencent.checkPrizeByIntent(this, mPrizeIntent);
if (hasPrize) {
Util.showConfirmCancelDialog(this, "有獎品領取", "請使用QQ登錄後,領取獎品!", prizeShareConfirmListener);
}
//[liteexludeendmeta]
}
// -----------------------------------
// 下面的註釋請勿刪除,編譯lite版的時候需要刪除, 注意//[不要有空格。
//[liteexludestart_flag_one]
private DialogInterface.OnClickListener prizeShareConfirmListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
boolean isLogin = mTencent.isSessionValid();
if (isLogin) {
// 本地已經有保存openid和accesstoken的情況下,先調用mTencent.setAccesstoken().
// 也可在獎品列表頁,主動調用此接口獲取未領取的獎品
if (null != mPrizeIntent) {
mTencent.queryUnexchangePrize(MainActivity.this, mPrizeIntent.getExtras(),
prizeQueryUnexchangeListener);
}
} else {
// 未登陸提示用戶使用QQ號登陸
onClickLogin();
}
break;
default:
break;
}
}
};
private IUiListener prizeQueryUnexchangeListener = new IUiListener() {
@Override
public void onError(UiError e) {
Util.toastMessage(MainActivity.this, "onError: " + e.errorDetail);
Util.dismissDialog();
}
@Override
public void onCancel() {
Util.toastMessage(MainActivity.this, "onCancel: ");
Util.dismissDialog();
}
@Override
public void onComplete(Object response) {
Util.showConfirmCancelDialog(MainActivity.this, "兌換獎品", response.toString(),
new PrizeClickExchangeListener(response.toString()));
// 兌換獎品後,mPrizeIntent 置爲空
mPrizeIntent = null;
}
};
private IUiListener prizeExchangeListener = new IUiListener() {
@Override
public void onError(UiError e) {
Util.toastMessage(MainActivity.this, "onError: " + e.errorDetail);
Util.dismissDialog();
}
@Override
public void onCancel() {
Util.toastMessage(MainActivity.this, "onCancel: ");
Util.dismissDialog();
}
@Override
public void onComplete(Object response) {
Util.showResultDialog(MainActivity.this, response.toString(), "兌換信息");
}
};
private class PrizeClickExchangeListener implements DialogInterface.OnClickListener {
String response = "";
PrizeClickExchangeListener(String strResponse) {
response = strResponse;
}
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
if (null != mTencent) {
Bundle params = new Bundle();
ArrayList<String> shareIdList = handlePrizeResponse(response);
if (null != shareIdList) {
ArrayList<String> list = new ArrayList<String>();
// 後臺測試環境目前只支持一個shareid的兌換,正式環境會支持多個shareid兌換。
list.add(shareIdList.get(0));
params.putStringArrayList("shareid_list", list);
mTencent.exchangePrize(MainActivity.this, params, prizeExchangeListener);
}
}
break;
default:
break;
}
}
};
private ArrayList<String> handlePrizeResponse(String response) {
ArrayList<String> shareIdList = new ArrayList<String>();
if (TextUtils.isEmpty(response)) {
return null;
}
try {
JSONObject obj = new JSONObject(response);
int code = obj.getInt("ret");
int subCode = obj.getInt("subCode");
if (code == 0 && subCode == 0) {
JSONObject data = obj.getJSONObject("data");
JSONArray prizeList = data.getJSONArray("prizeList");
int size = prizeList.length();
JSONObject prize = null;
for (int i = 0; i < size; i++) {
prize = prizeList.getJSONObject(i);
if (null != prize) {
shareIdList.add(prize.getString("shareId"));
}
}
} else {
return null;
}
} catch (Exception e) {
return null;
}
return shareIdList;
}
//[liteexludeend_flag_one]
@Override
protected void onStart() {
Log.d(TAG, "-->onStart");
super.onStart();
}
@Override
protected void onResume() {
Log.d(TAG, "-->onResume");
// 有獎分享處理
handlePrizeShare();
super.onResume();
}
@Override
protected void onPause() {
Log.d(TAG, "-->onPause");
super.onPause();
}
@Override
protected void onStop() {
Log.d(TAG, "-->onStop");
super.onStop();
}
@Override
protected void onDestroy() {
Log.d(TAG, "-->onDestroy");
super.onDestroy();
}
private void initViews() {
mNewLoginButton = (Button) findViewById(R.id.new_login_btn);
mServerSideLoginBtn = (Button) findViewById(R.id.server_side_login_btn);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.main_container);
OnClickListener listener = new NewClickListener();
for (int i = 0; i < linearLayout.getChildCount(); i++) {
View view = linearLayout.getChildAt(i);
if (view instanceof Button) {
view.setOnClickListener(listener);
}
}
mUserInfo = (TextView) findViewById(R.id.user_nickname);
mUserLogo = (ImageView) findViewById(R.id.user_logo);
updateLoginButton();
}
private void updateLoginButton() {
if (mTencent != null && mTencent.isSessionValid()) {
if (isServerSideLogin) {
mNewLoginButton.setTextColor(Color.BLUE);
mNewLoginButton.setText("登錄");
mServerSideLoginBtn.setTextColor(Color.RED);
mServerSideLoginBtn.setText("退出Server-Side賬號");
} else {
mNewLoginButton.setTextColor(Color.RED);
mNewLoginButton.setText("退出帳號");
mServerSideLoginBtn.setTextColor(Color.BLUE);
mServerSideLoginBtn.setText("Server-Side登陸");
}
} else {
mNewLoginButton.setTextColor(Color.BLUE);
mNewLoginButton.setText("登錄");
mServerSideLoginBtn.setTextColor(Color.BLUE);
mServerSideLoginBtn.setText("Server-Side登陸");
}
}
private void updateUserInfo() {
if (mTencent != null && mTencent.isSessionValid()) {
IUiListener listener = new IUiListener() {
@Override
public void onError(UiError e) {
}
@Override
public void onComplete(final Object response) {
Message msg = new Message();
msg.obj = response;
msg.what = 0;
mHandler.sendMessage(msg);
new Thread(){
@Override
public void run() {
JSONObject json = (JSONObject)response;
if(json.has("figureurl")){
Bitmap bitmap = null;
try {
bitmap = Util.getbitmap(json.getString("figureurl_qq_2"));
} catch (JSONException e) {
}
Message msg = new Message();
msg.obj = bitmap;
msg.what = 1;
mHandler.sendMessage(msg);
}
}
}.start();
}
@Override
public void onCancel() {
}
};
mInfo = new UserInfo(this, mTencent.getQQToken());
mInfo.getUserInfo(listener);
} else {
mUserInfo.setText("");
mUserInfo.setVisibility(android.view.View.GONE);
mUserLogo.setVisibility(android.view.View.GONE);
}
}
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0) {
JSONObject response = (JSONObject) msg.obj;
if (response.has("nickname")) {
try {
mUserInfo.setVisibility(android.view.View.VISIBLE);
mUserInfo.setText(response.getString("nickname"));
} catch (JSONException e) {
e.printStackTrace();
}
}
}else if(msg.what == 1){
Bitmap bitmap = (Bitmap)msg.obj;
mUserLogo.setImageBitmap(bitmap);
mUserLogo.setVisibility(android.view.View.VISIBLE);
}
}
};
private void onClickLogin() {
if (!mTencent.isSessionValid()) {
mTencent.login(this, "all", loginListener);
isServerSideLogin = false;
Log.d("SDKQQAgentPref", "FirstLaunch_SDK:" + SystemClock.elapsedRealtime());
} else {
if (isServerSideLogin) { // Server-Side 模式的登陸, 先退出,再進行SSO登陸
mTencent.logout(this);
mTencent.login(this, "all", loginListener);
isServerSideLogin = false;
Log.d("SDKQQAgentPref", "FirstLaunch_SDK:" + SystemClock.elapsedRealtime());
return;
}
mTencent.logout(this);
updateUserInfo();
updateLoginButton();
}
}
private void onClickServerSideLogin() {
if (!mTencent.isSessionValid()) {
mTencent.loginServerSide(this, "all", loginListener);
isServerSideLogin = true;
Log.d("SDKQQAgentPref", "FirstLaunch_SDK:" + SystemClock.elapsedRealtime());
} else {
if (!isServerSideLogin) { // SSO模式的登陸,先退出,再進行Server-Side模式登陸
mTencent.logout(this);
mTencent.loginServerSide(this, "all", loginListener);
isServerSideLogin = true;
Log.d("SDKQQAgentPref", "FirstLaunch_SDK:" + SystemClock.elapsedRealtime());
return;
}
mTencent.logout(this);
isServerSideLogin = false;
updateUserInfo();
updateLoginButton();
}
}
public static String getAppid() {
if (TextUtils.isEmpty(mAppid)) {
mAppid = "222222";
}
return mAppid;
}
public static boolean ready(Context context) {
if (mTencent == null) {
return false;
}
boolean ready = mTencent.isSessionValid()
&& mTencent.getQQToken().getOpenId() != null;
if (!ready) {
Toast.makeText(context, "login and get openId first, please!",
Toast.LENGTH_SHORT).show();
}
return ready;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "-->onActivityResult " + requestCode + " resultCode=" + resultCode);
if (requestCode == Constants.REQUEST_LOGIN ||
requestCode == Constants.REQUEST_APPBAR) {
Tencent.onActivityResultData(requestCode,resultCode,data,loginListener);
}
super.onActivityResult(requestCode, resultCode, data);
}
public static void initOpenidAndToken(JSONObject jsonObject) {
try {
String token = jsonObject.getString(Constants.PARAM_ACCESS_TOKEN);
String expires = jsonObject.getString(Constants.PARAM_EXPIRES_IN);
String openId = jsonObject.getString(Constants.PARAM_OPEN_ID);
System.out.println("token"+token+"-------------"+openId);
if (!TextUtils.isEmpty(token) && !TextUtils.isEmpty(expires)
&& !TextUtils.isEmpty(openId)) {
mTencent.setAccessToken(token, expires);
mTencent.setOpenId(openId);
}
} catch(Exception e) {
}
}
IUiListener loginListener = new BaseUiListener() {
@Override
protected void doComplete(JSONObject values) {
Log.d("SDKQQAgentPref", "AuthorSwitch_SDK:" + SystemClock.elapsedRealtime());
initOpenidAndToken(values);
updateUserInfo();
updateLoginButton();
}
};
private class BaseUiListener implements IUiListener {
@Override
public void onComplete(Object response) {
if (null == response) {
Util.showResultDialog(MainActivity.this, "返回爲空", "登錄失敗");
return;
}
JSONObject jsonResponse = (JSONObject) response;
if (null != jsonResponse && jsonResponse.length() == 0) {
Util.showResultDialog(MainActivity.this, "返回爲空", "登錄失敗");
return;
}
Util.showResultDialog(MainActivity.this, response.toString(), "登錄成功");
// 有獎分享處理
handlePrizeShare();
doComplete((JSONObject)response);
}
protected void doComplete(JSONObject values) {
}
@Override
public void onError(UiError e) {
Util.toastMessage(MainActivity.this, "onError: " + e.errorDetail);
Util.dismissDialog();
}
@Override
public void onCancel() {
Util.toastMessage(MainActivity.this, "onCancel: ");
Util.dismissDialog();
if (isServerSideLogin) {
isServerSideLogin = false;
}
}
}
private DialogInterface.OnClickListener mAppidCommitListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mAppid = AppConstants.APP_ID;
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
// 用輸入的appid
String editTextContent = mEtAppid.getText().toString().trim();
if (!TextUtils.isEmpty(editTextContent)) {
mAppid = editTextContent;
}
break;
case DialogInterface.BUTTON_NEGATIVE:
// 默認appid
break;
}
mTencent = Tencent.createInstance(mAppid, MainActivity.this);
// 有獎分享處理
handlePrizeShare();
}
};
private void onClickIsSupportSSOLogin() {
if (mTencent.isSupportSSOLogin(MainActivity.this)) {
Toast.makeText(MainActivity.this, "支持SSO登陸", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "不支持SSO登陸", Toast.LENGTH_SHORT).show();
}
}
class NewClickListener implements OnClickListener {
@Override
public void onClick(View v) {
Context context = v.getContext();
Animation shake = AnimationUtils.loadAnimation(context,
R.anim.shake);
Class<?> cls = null;
boolean isAppbar = false;
switch (v.getId()) {
case R.id.new_login_btn:
onClickLogin();
v.startAnimation(shake);
return;
case R.id.server_side_login_btn:
onClickServerSideLogin();
v.startAnimation(shake);
return;
case R.id.main_sso_btn:
onClickIsSupportSSOLogin();
return;
case R.id.main_getInfo_btn:
cls = AccountInfoActivity.class;
break;
case R.id.main_qqShare_btn:
cls = QQShareActivity.class;
break;
case R.id.main_qzoneShare_btn:
cls = QZoneShareActivity.class;
break;
case R.id.main_social_api_btn:
cls = SocialApiActivity.class;
break;
//-----------------------------------
// 下面的註釋請勿刪除,編譯lite版的時候需要刪除, 注意//[不要有空格。
//[liteexludestart]
case R.id.game_add_friend:
cls = GameLogicActivity.class;
break;
case R.id.main_qzonePic_btn:
cls = QzonePicturesActivity.class;
break;
case R.id.main_tqqInfo_btn:
cls = TQQInfoActivity.class;
break;
case R.id.main_wap_btn:
cls = WPAActivity.class;
break;
case R.id.main_others_btn:
cls = OtherApiActivity.class;
break;
case R.id.main_avatar_btn:
cls = AvatarActivity.class;
break;
case R.id.main_appbar_btn:
cls = SocialAppbarActivity.class;
isAppbar = true;
break;
case R.id.main_qqgroup_btn:
cls = QQGroupActivity.class;
break;
//[liteexludeend]
}
v.startAnimation(shake);
if (cls != null) {
Intent intent = new Intent(context, cls);
if (isAppbar) { //APP內應用吧登錄需接收登錄結果
startActivityForResult(intent, Constants.REQUEST_APPBAR);
} else {
context.startActivity(intent);
}
}
}
}
}
在主程序裏面實現登錄和獲取用戶信息,主要起作用的語句:
mTencent.login(MainActivity.this, scope, loginListener); //登錄
userInfo = new UserInfo(MainActivity.this, mTencent.getQQToken()); //獲取用戶信息
userInfo.getUserInfo(userInfoListener);
3.配置AndroidManifest
在應用的AndroidManifest.xml增加配置的<application>節點下增加以下配置(注:不配置將會導致無法調用API);
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<activity
android:name="com.tencent.tauth.AuthActivity"
android:noHistory="true"
android:launchMode="singleTask" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tencent你的AppId" />
</intent-filter>
</activity>
<application>
其中,如果你已經添加了"android.permission.INTERNET"和"android.permission.ACCESS_NETWORK_STATE"權限,則無需重複添加。
而"你的AppId"則要替換成具體應用的AppId,例如你的AppId是"222222",則<data>標籤應該是這樣的:
<data android:scheme="tencent222222" />
4.初始化實例
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Tencent類是SDK的主要實現類,開發者可通過Tencent類訪問騰訊開放的OpenAPI。
// 其中APP_ID是分配給第三方應用的appid,類型爲String。
mTencent = Tencent.createInstance(APP_ID, this.getApplicationContext());
// 1.4版本:此處需新增參數,傳入應用程序的全局context,可通過activity的getApplicationContext方法獲取
// 初始化視圖
initViews();
}
5.登錄按鈕被點擊的代碼
private void onClickLogin() {
if (!mTencent.isSessionValid()) {
mTencent.login(this, "all", loginListener);
isServerSideLogin = false;
Log.d("SDKQQAgentPref", "FirstLaunch_SDK:" + SystemClock.elapsedRealtime());
} else {
if (isServerSideLogin) { // Server-Side 模式的登陸, 先退出,再進行SSO登陸
mTencent.logout(this);
mTencent.login(this, "all", loginListener);
isServerSideLogin = false;
Log.d("SDKQQAgentPref", "FirstLaunch_SDK:" + SystemClock.elapsedRealtime());
return;
}
mTencent.logout(this);
updateUserInfo();
updateLoginButton();
}
}
6.實現回調
//這個是登錄時傳遞進去的接口
IUiListener loginListener = new BaseUiListener() {
@Override
protected void doComplete(JSONObject values) {
Log.d("SDKQQAgentPref", "AuthorSwitch_SDK:" + SystemClock.elapsedRealtime());
//去獲取Token與OpenId
initOpenidAndToken(values);
}
};
--------------------華麗的分割線--------------------------------
private class BaseUiListener implements IUiListener {
@Override
public void onComplete(Object response) {
if (null == response) {
Util.showResultDialog(MainActivity.this, "返回爲空", "登錄失敗");
return;
}
JSONObject jsonResponse = (JSONObject) response;
if (null != jsonResponse && jsonResponse.length() == 0) {
Util.showResultDialog(MainActivity.this, "返回爲空", "登錄失敗");
return;
}
Util.showResultDialog(MainActivity.this, response.toString(), "登錄成功");
// 有獎分享處理
handlePrizeShare();
doComplete((JSONObject)response);
}
protected void doComplete(JSONObject values) {
}
@Override
public void onError(UiError e) {
Util.toastMessage(MainActivity.this, "onError: " + e.errorDetail);
Util.dismissDialog();
}
@Override
public void onCancel() {
Util.toastMessage(MainActivity.this, "onCancel: ");
Util.dismissDialog();
if (isServerSideLogin) {
isServerSideLogin = false;
}
}
}
--------------------華麗的分割線--------------------------------
//去拿到ACCESS_TOKEN與PARAM_OPEN_ID與EXPIRES_IN(有效時間)
public static void initOpenidAndToken(JSONObject jsonObject) {
try {
String token = jsonObject.getString(Constants.PARAM_ACCESS_TOKEN);
String expires = jsonObject.getString(Constants.PARAM_EXPIRES_IN);
String openId = jsonObject.getString(Constants.PARAM_OPEN_ID);
System.out.println("token"+token+"-------------"+openId);
if (!TextUtils.isEmpty(token) && !TextUtils.isEmpty(expires)
&& !TextUtils.isEmpty(openId)) {
mTencent.setAccessToken(token, expires);
mTencent.setOpenId(openId);
}
} catch(Exception e) {
}
}
7.使用access_token和openid
應用在每次登錄之後,都會獲取到openid、access_token和expires_in,在調用SDK提供的接口時,後臺會根據這三個參數來驗證請求的合法性。
(1)如果應用已經走過登錄流程,調用應用分享、邀請等接口,是不需要再將這三個參數傳入到請求參數中去的,這是因爲在登錄成功後,SDK會自動將這幾個參數保存在SDK的上下文中,在發送請求時,會自動爲請求加上這些參數。
(2)如果應用不希望每次都走登錄流程來使用SDK的功能,可以通過以下步驟來實現:
Step1:在首次登錄成功後,將返回的openid、access_token、expires_in三個參數保存在本地(比如保存在sharedPreferrence)。其中expires_in參數在存儲前需進行如下計算:
1.System.currentTimeMillis() + Long.parseLong(expires_in) * 1000;
這樣得出的就是token的失效日期。
Step2:在用戶下次進入應用,發起應用分享等SDK調用之前,首先創建Tencent實例,然後取出之前保存的openid、access_token、expires_in(前面計算出來的值)的值。
Step3:調用Tencent類的setOpenId和setAccessToken方法。其中setOpenId的參數傳入上一步取出的openid,setAccessToken的第一個參數傳入上一步取出的access_token,第二個參數傳入(上一步保存的token失效日期-當前系統時間)/1000。這裏計算出的結果是當前保存的token的有效時間,如果結果小於或等於0,表示token已經過期,應該提示用戶重新走登錄流程。
免登錄流程調用SDK接口的示例代碼如下(省去了獲取存儲的變量的過程):
String openid = "1234567896ASDFGHJKLLIUYT";
String access_token = "2C0884DC4B930010D852D8D504FC9F4D";
String expires_in = "7776000"; // 實際值需要通過上面介紹的方法來計算
mTencent = Tencent.createInstance(APP_ID);
mTencent.setOpenId(openid);
mTencent.setAccessToken(access_token, expires_in);
8.混淆
-keep class com.tencent.open.TDialog$*
-keep class com.tencent.open.TDialog$* {*;}
-keep class com.tencent.open.PKDialog
-keep class com.tencent.open.PKDialog {*;}
-keep class com.tencent.open.PKDialog$*
-keep class com.tencent.open.PKDialog$* {*;}
微信授權登錄可以參考下面博客
http://blog.csdn.net/qq247890212/article/details/40822481