使用QQ賬號,新浪微博賬號登錄第三方應用

最近公司由於項目要求,在應用中集成了QQ和新浪微博登錄的功能,以前並沒有接觸過這方面的東西,前2天研究清楚,並實現了,寫點心得和大家分享,同時也作爲學習記錄保留下來。    廢話不說,直入正題了。

    一。使用QQ賬號登錄第三方應用

    這裏的第三方應用指的當然是我們自己開發的應用。

    騰訊開發平臺是一個比較大的開放平臺,它包括了騰訊微博開發平臺,微信平臺等諸多平臺,而我們所需要用到的東西,都在QQ互聯開放平臺。之所以在一開始就說明這一點是因爲,擼主之前因爲沒搞清楚他們之間的關係,走了不少彎路,所以希望大家能夠注意到。


   1. 在開發之前,首先要使用QQ號登錄平臺,然後完善開發者信息成爲開發者,之後,要在管理中心,創建你的應用,其中包括完善很多關於你的應用的息。(關於這部分的詳細信息,請查看官方在線文檔開發者註冊和android應用註冊)。


    2.當我們成功創建 了應用之後,會獲得APP ID,這個ID在開發中是必須的。


    3.QQ互聯SDK,前往該網頁,可以下載官方的sdk資料,其中 包括了jar文件,demo,以及詳細的開發說明文檔。


    4.(警醒)開發的第一步,首先要把所需要的jar文件導入到我們的工程中去,在上一步下載的SDK文檔中,我們會發現有一個open_sdk.jar文件,按照說明文檔導入進去,但是我們會發現在說明文檔中分明提到了2個jar文件,騰訊開發平臺SDK在這個鏈接中下載到相應的文件之後,我們會發現它和之前下載的QQ互聯文件幾乎一樣,區別在於開發說明文檔在細節上的區別,以及JAR文件的不同。我們剛纔提到的所缺少的一個jar文件,在這就可以找到了。但是需要特別注意的是,有些人一看,好像2個jar文件都在這裏,那就直接把這2個導入進去開發吧!恭喜你成功掉入陷阱了!我們仔細對比會發現,在騰訊SDK和QQ互聯sdk中的open_sdk.jar,他們的大小是不一樣的,這2個文件是有區別的。擼主也是在這裏糾結了很久,代碼完成後沒有提示錯誤,但是運行老是崩掉,後來發現這個問題之後,一陣無語。
    簡言之,將QQ互聯sdk中的open_sdk.jar以及騰訊sdk中的mta_sdk_XXX.jar導入到工程之後,可以按照開發文檔,繼續往下進行。
    
    5.關於AndroidManifest的配置,開發文檔比較詳盡,不贅述。

    6.鑑於官方demo的複雜性,以及文檔中的不完整和不一致性,我提供一個自己簡單封裝的類,參考一下:


/**
* 用於使用QQ互聯授權,登錄的輔助類

* @author totoro
* @since 2014-02-28
*/
public class TencentQQHelper {
        public static final String APP_ID = "101025815";
        public static final String SCOPE = "get_simple_userinfo";
        public Tencent tencent;
        public Context context;
        public UserInfo userInfo;
        
        public TencentQQHelper(Context context) {
                this.context = context;
                tencent = Tencent.createInstance(APP_ID, context.getApplicationContext());
        }
        
        /**
         * 登錄
         * @since 2014-02-28
         */
        public void login() {
                if (!tencent.isSessionValid()) {
                        tencent.login((Activity) context, SCOPE, new BaseUIListener() {

                                @Override
                                protected void doComplete(JSONObject obj) {
                                        TencentQQToken token = new TencentQQToken();                                        
                                        try {
                                                token.setOpenid(obj.getString("openid"));
                                                token.setAccess_token(obj.getString("access_token"));
                                                token.setExpires_in(obj.getString("expires_in"));
                                        } catch (JSONException e) {
                                                e.printStackTrace();
                                        }                                        
                                        
                                        QQTokenKeeper.writeAccessToken(context, token);
                                        userInfo = new UserInfo(context, tencent.getQQToken());                                
                                        getUserInfo();
                                }
                        });
                } else {
                        tencent.logout(context);
                }
        }
        
        /**
         * 獲取用戶信息
         * @since 2014-02-28
         */
        public void getUserInfo() {
                userInfo.getUserInfo(new BaseUIListener() {

                        @Override
                        protected void doComplete(JSONObject obj) {
                                Intent intent = new Intent(context,UserInfoActivity.class);
                Bundle extras = new Bundle();
                try {
                                        extras.putString("name", obj.getString("nickname"));
                                } catch (JSONException e) {
                                        e.printStackTrace();
                                }    
                intent.putExtras(extras);
                context.startActivity(intent);
                        }                
                });                
        }
        
        /**
         * 用戶授權和獲取用戶信息的回調
         * 
         * @author totoro
         */
        public class BaseUIListener implements IUiListener {
        
                @Override
                public void onComplete(Object response) {
                        doComplete((JSONObject) response);        
                }
                
                protected void doComplete(JSONObject obj) {
        
                }

                @Override
                public void onError(UiError e) {
                         Toast.makeText(context,e.errorMessage, Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onCancel() {
                         Toast.makeText(context,"取消操作", Toast.LENGTH_SHORT).show();
                }
        }
}
    關於其他相關的功能,比如註銷什麼的,應該相對簡單,可以在demo裏面找到,我就不說了。
    發帖BUG不少啊,一編輯我的鏈接全都不見了,感覺不會愛了!
    我就不貼了,騰訊開發平臺,QQ互聯開發平臺,所有相關東西都在裏面,大家自己去找吧。
    我也就不上傳相關文件了,畢竟下載好貴的,如果實在需要的,可以找我。
   再貼下上面代碼涉及到的類:
/**
* 存放登錄令牌的類
* @author totoro
*
*/
public class TencentQQToken {
        private String openid;
        private String access_token;
        private String expires_in;
        
        public TencentQQToken() {}
        
        public String getOpenid() {
                return openid;
        }
        public void setOpenid(String openid) {
                this.openid = openid;
        }
        public String getAccess_token() {
                return access_token;
        }
        public void setAccess_token(String access_token) {
                this.access_token = access_token;
        }
        public String getExpires_in() {
                return expires_in;
        }
        public void setExpires_in(String expires_in) {
                this.expires_in = expires_in;
        }
}

/**
* 該類定義了QQ互聯授權時所需要的參數

* @author totoro
* @since 2014-02-28
*/
public class QQTokenKeeper {
       private static final String PREFERENCES_NAME = "tencent_qq_token";
       private static final String KEY_OPENID           = "openid";
       private static final String KEY_ACCESS_TOKEN  = "access_token";
       private static final String KEY_EXPIRES_IN    = "expires_in";

    /**
     * 保存 Token 對象到 SharedPreferences。
     * 
     * @param context 應用程序上下文環境
     * @param token   Token 對象
     */
    public static void writeAccessToken(Context context, TencentQQToken token) {
        if (null == context || null == token) {
            return;
        }
        
        SharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_APPEND);
        Editor editor = pref.edit();
        editor.putString(KEY_OPENID, token.getOpenid());
        editor.putString(KEY_ACCESS_TOKEN, token.getAccess_token());
        String expires_in = token.getExpires_in();
        editor.putLong(KEY_EXPIRES_IN, System.currentTimeMillis() + Long.parseLong(expires_in) * 1000);
        editor.commit();
    }

    /**
     * 從 SharedPreferences 讀取 TencentQQToken 信息。
     * 
     * @param context 應用程序上下文環境
     * 
     * @return 返回 TencentQQToken 對象
     */
    public static TencentQQToken readAccessToken(Context context) {
        if (null == context) {
            return null;
        }
        
        TencentQQToken token = new TencentQQToken();
        SharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_APPEND);
        token.setOpenid(pref.getString(KEY_OPENID, ""));
        token.setAccess_token(pref.getString(KEY_ACCESS_TOKEN, ""));
        long expires_in = (pref.getLong(KEY_EXPIRES_IN, -1) - System.currentTimeMillis())/1000; 
        token.setExpires_in(Long.toString(expires_in));
        return token;
    }

    /**
     * 清空 SharedPreferences 中 Token信息。
     * 
     * @param context 應用程序上下文環境
     */
    public static void clear(Context context) {
        if (null == context) {
            return;
        }
        
        SharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_APPEND);
        Editor editor = pref.edit();
        editor.clear();
        editor.commit();
    }

}


 二。使用新浪微博賬號登錄第三方應用

相比來說,新浪微博畢竟省事一些。

    我們同樣需要註冊,創建應用,獲得APP_KEY(和QQ的APP_ID類似)。通過開發我們可以發現,他和QQ互聯的開發模式非常相似。

   1. http://open.weibo.com/wiki/SDK#Android_SDK 我們在這裏下載到所需要的文件後,可以找到一份比較詳細的開發文檔。同時有demo可供參考,不過有不同的是,這裏我們不通過導入jar文件的方式來引用,而是要將一個作爲library的工程WeiboSDk,導入到eclipse,然後add到自己的工程,具體見文檔。

   2.在登錄入口方面,QQ互聯是提供了所有接口,我們自己完成從按鈕到接口的實現;而新浪微博則是爲我們提供了封裝好的登錄按鈕,我們需要在佈局文件中添加新浪的登錄按鈕來實現登錄,登錄按鈕分爲數種,具體使用可見文檔。顯然這個比QQ省事的多。

   3.貌似沒什麼可說的,貼上自己的實現供參考:


/**
* 使用新浪微博賬號登錄時的SSO輔助類

* @author totoro
*
*/
public class SinaWeiboSSOHelper {
        /**
         * 用於使用新浪微博賬號登錄的APP KEY
         */
        public static final String APP_KEY = "809484557";
        /**
         * 使用新浪微博賬號登錄時的授權默認回調頁
         */
        public static final String REDIRECT_URL = "https://api.weibo.com/oauth2/default.html";
        /**
         * 用於新浪微博登錄的授權參數
         */
        public static final String SCOPE = "email";
        
        public AuthInfo authInfo;
        public Context context;
        public AuthListener authListener;
        
        public SinaWeiboSSOHelper(Context context) {
                this.context = context;
                // 創建授權認證信息
                authInfo = new AuthInfo(context, APP_KEY, REDIRECT_URL, SCOPE);
                authListener = new AuthListener();
        }
        
        /**
     * 登入按鈕的監聽器,接收授權結果。
     */
    @SuppressLint("SimpleDateFormat")
        private class AuthListener implements WeiboAuthListener {
        @Override
        public void onComplete(Bundle values) {
                Oauth2AccessToken accessToken = Oauth2AccessToken.parseAccessToken(values);
            if (accessToken != null && accessToken.isSessionValid()) {
     
                SinaAccessTokenKeeper.writeAccessToken(context, accessToken);
                UsersAPI usersAPI = new UsersAPI(accessToken);
                usersAPI.show(Long.parseLong(accessToken.getUid()), new UserRequstListener());
            }
        }

        @Override
        public void onWeiboException(WeiboException e) {
            Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onCancel() {
            Toast.makeText(context,"取消授權", Toast.LENGTH_SHORT).show();
        }
    }
    
    /**
     * 使用UsersAPI獲取用戶信息時需要的請求接口
     * 
     * 2014-02-26
     * @author totoro
     *
     */
    private class UserRequstListener implements RequestListener {

                @Override
                public void onComplete(String response) {
                        if (!TextUtils.isEmpty(response)) {
                try {
                    JSONObject obj = new JSONObject(response);
                    /**
                     * 將獲取到的用戶信息發送到即將跳轉到的個人界面
                     */
                    Intent intent = new Intent(context,UserInfoActivity.class);
                    Bundle extras = new Bundle();
                    extras.putString("name", obj.getString("screen_name"));
                    extras.putString("location", obj.getString("location"));
                    intent.putExtras(extras);
                    context.startActivity(intent);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }        
                }

                @Override
                public void onComplete4binary(ByteArrayOutputStream responseOS) {
                        // TODO Auto-generated method stub        
                }

                @Override
                public void onIOException(IOException e) {
                        Log.v("IOException",e.getMessage());        
                }

                @Override
                public void onError(WeiboException e) {
                        Log.v("Error",e.getMessage());        
                }        
    }
}


/**
* 該類定義了微博授權時所需要的參數。

* @author SINA
*/
public class SinaAccessTokenKeeper {
    private static final String PREFERENCES_NAME = "sina_weibo_token";

    private static final String KEY_UID           = "uid";
    private static final String KEY_ACCESS_TOKEN  = "access_token";
    private static final String KEY_EXPIRES_IN    = "expires_in";
    
    /**
     * 保存 Token 對象到 SharedPreferences。
     * 
     * @param context 應用程序上下文環境
     * @param token   Token 對象
     */
    public static void writeAccessToken(Context context, Oauth2AccessToken token) {
        if (null == context || null == token) {
            return;
        }
        
        SharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_APPEND);
        Editor editor = pref.edit();
        editor.putString(KEY_UID, token.getUid());
        editor.putString(KEY_ACCESS_TOKEN, token.getToken());
        editor.putLong(KEY_EXPIRES_IN, token.getExpiresTime());
        editor.commit();
    }

    /**
     * 從 SharedPreferences 讀取 Token 信息。
     * 
     * @param context 應用程序上下文環境
     * 
     * @return 返回 Token 對象
     */
    public static Oauth2AccessToken readAccessToken(Context context) {
        if (null == context) {
            return null;
        }
        
        Oauth2AccessToken token = new Oauth2AccessToken();
        SharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_APPEND);
        token.setUid(pref.getString(KEY_UID, ""));
        token.setToken(pref.getString(KEY_ACCESS_TOKEN, ""));
        token.setExpiresTime(pref.getLong(KEY_EXPIRES_IN, 0));
        return token;
    }

    /**
     * 清空 SharedPreferences 中 Token信息。
     * 
     * @param context 應用程序上下文環境
     */
    public static void clear(Context context) {
        if (null == context) {
            return;
        }
        
        SharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_APPEND);
        Editor editor = pref.edit();
        editor.clear();
        editor.commit();
    }
}

需要注意的是,以上APP_ID和APP_KEY都需要替換成自己的。
如有疑問或者建議,可以隨時留言。


    三。關於其他實現方式

其實我們可以使用其他第三方sdk來實現這些功能,比如ShareSDK,他提供了包括QQ,新浪微博,人人網,開心網等等一系列第三方分享和登錄等功能,而且有一個明顯的好處是,不用分別去學習各自平臺的開發方式,而是只需要學習ShareSDK的開發方式之後,就可以實現很多平臺的分享,尤其是當我們的應用中需要集成比較多的第三方平臺。

   但是當我們的應用比較少的集成第三方,或者比較少的集成第三方的功能,使用這種方式無疑會增加包的大小,因爲我們同時集成了其他很多用不到的功能。這時候如果能選擇本文介紹的方式開發的話,可以減少應用的大小。

   大概就這樣了,三克油。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章