1. 程式人生 > >Android客戶端實現註冊/登入詳解(二)

Android客戶端實現註冊/登入詳解(二)

上文中介紹了安卓客戶端與伺服器互動,實現註冊功能

本文將繼續介紹App與伺服器的互動實現登入和自動登入的功能,上文說到請求伺服器進行註冊主要是通過POST請求攜帶引數實現,起作用的程式碼主要是

    StringRequest request=new StringRequest(Method.POST, url, new Listener<String>() {

        //請求成功
        @Override
        public void onResponse(String s) {
            //執行請求成功的回撥
            callback.onSuccess()
        }

    }, new ErrorListener() {

        //請求錯誤
        @Override
        public void onErrorResponse(VolleyError volleyError) {
            //執行請求失敗的回撥
            callback.onFailure()
        }
    }){

        //攜帶引數(Map集合)
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {
            return parames;
        }
    };

    //將請求新增到請求佇列中
    Volley.newRequestQueue(context).add(request);

其實登入實現的原理也是一樣的,同樣是通過POST請求,而在本demo中則是把請求伺服器的方法封裝在一起了,所以登入的實現也是呼叫了RequestManager網路請求處理類中的post方法

/**
 * post 請求資料
 *
 * @param app_url     公共的介面字首 http://www.itlanbao.com/api/app/
 * @param tag_url     介面名稱,eg:users/user_register_Handler.ashx(註冊介面)
 * @param parameter  請求引數封裝物件
 * @param clazz      返回資料封裝物件,如果傳null,則直接返回String
 * @param callback   介面回撥監聽
 */
public static <T> void post(final String app_url, final String tag_url, final HashMap<String, String> parameter, Class<T> clazz,
                            final HttpResponeCallBack callback) {
    //傳送post請求伺服器
    post(app_url, tag_url, parameter, clazz, callback, Priority.NORMAL);
}

demo演示

實現程式碼

1.伺服器的資料格式

1.url:  http://www.itlanbao.com/api/app/users/user_login_handler.ashx

2.引數說明:
    email           必須有         郵箱
    password        必須有         密碼
    accesstoken     必須有         md5(email+password+"雙方平臺約定公鑰")      

3.請求方式:POST

4.返回值格式
    成功
    {
        "ret":0,
        "errcode":0,
        "msg":"登入使用者介面呼叫成功",
        "data":{
            "userid":"16489",
            "email":"
[email protected]
", "nickname":"duss", "userhead":"http://img.itlanbao.com/avatar.png" } } 失敗 { "ret":1, "errcode":1, "msg":"賬號或密碼錯誤" }

2.登入介面(LoginActivity),點選登入按鈕

    //點選登入按鈕
    loginBtn.setOnClickListener(new Button.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            String account = loginAccount.getText().toString();//賬號
            String password = loginPassword.getText().toString();//密碼
            if (!TextUtils.isEmpty(account) && !TextUtils.isEmpty(password)
                    && Utils.isEmail(account)) {
                RequestApiData.getInstance().getLoginData(account, password, UserBaseInfo.class, LoginActivity.this);
            } else {
                Toast.makeText(LoginActivity.this, "賬號或者密碼有誤", Toast.LENGTH_SHORT).show();
            }
        }
    });

核心程式碼為:

    //傳入賬號名,密碼,解析資料的bean物件和回撥(這裡傳入的是自身,所以LoginActivity也同樣實現了回撥介面HttpResponeCallBack)
    RequestApiData.getInstance().getLoginData(account, password, UserBaseInfo.class, LoginActivity.this);

3.網路介面類(RequestApiData)

    //建立介面物件
    public static RequestApiData getInstance() {
        if (instance == null) {
            instance = new RequestApiData();
        }
        return instance;
    }

    /**
     * 4.8登入使用者介面
     * @param email 郵箱
     * @param password 密碼
     * @param clazz 資料返回的解析物件
     * @param callback 回撥
     * 特別要注意引數位置不能變要根據文件來
     * 請求方式:POST
     */
    public void getLoginData(String email ,String password,
            Class<UserBaseInfo> clazz,
           HttpResponeCallBack callback) {
         mCallBack = callback;
         //這是每一個介面的唯一標示
         String tagUrl = UrlConstance.KEY_LOGIN_INFO;//登入介面
         HashMap<String, String> parameter = new HashMap<String, String>();
         parameter.put("email", email);
         parameter.put("password", password);

            //拼接引數資訊,郵箱,密碼,公鑰,並用md5進行加密
            StringBuilder builder = new StringBuilder();
            builder.append(email);
            builder.append(password);
            builder.append(UrlConstance.PUBLIC_KEY);

         parameter.put(UrlConstance.ACCESSTOKEN_KEY,MD5Util.getMD5Str(builder.toString()));

         //請求資料介面
         RequestManager.post(UrlConstance.APP_URL,tagUrl, parameter, clazz, callback);

    }

4.網路請求處理類(RequestManager)中請求資料,和註冊執行了同樣的方法,只是這裡的傳入的tag_url為登入的介面

    /**
     * post 請求資料
     *
     * @param app_url     公共的介面字首 http://www.itlanbao.com/api/app/
     * @param tag_url     介面名稱,eg:users/user_login_handler.ashx(登入介面)
     * @param parameter  請求引數封裝物件
     * @param clazz      返回資料封裝物件,如果傳null,則直接返回String
     * @param callback   介面回撥監聽
     */
    public static <T> void post(final String app_url, final String tag_url, final HashMap<String, String> parameter, Class<T> clazz,
                                final HttpResponeCallBack callback) {
        //傳送post請求伺服器
        post(app_url, tag_url, parameter, clazz, callback, Priority.NORMAL);
    }

    /**
     * post 請求資料
     *
     * @param app_url    路徑
     * @param url        介面名稱
     * @param parameter  請求引數封裝物件
     * @param clazz      返回資料封裝物件,如果傳null,則直接返回String
     * @param callback   介面回撥監聽
     * @param priority   指定介面請求執行緒優先順序
     */
    public static <T> void post(final String app_url, final String url, final HashMap<String, String> parameter, final Class<T> clazz,
                                final HttpResponeCallBack callback, Priority priority) {
        if (callback != null) {
            callback.onResponeStart(url);//回撥請求開始
        }

        initRequestQueue();

        //將公共的介面字首和介面名稱拼接
        //eg:拼接成登入的介面  http://www.itlanbao.com/api/app/users/user_login_handler.ashx
        StringBuilder builder = new StringBuilder(app_url);
        builder.append(url);

        {// 檢查當前網路是否可用
            final NetworkUtils networkUtils = new NetworkUtils(ItLanbaoLibApplication.getInstance());

            if (!networkUtils.isNetworkConnected() && android.os.Build.VERSION.SDK_INT > 10) {
                if (callback != null) {
                    callback.onFailure(url, null, 0, "網路出錯");//回撥請求失敗
                    return;
                }
            }
        }

        /**
         * 使用Volley框架真正去請求伺服器
         * Method.POST:請求方式為post
         * builder.toString():請求的連結
         * Listener<String>:監聽
         */
        StringRequest request = new StringRequest(Method.POST, builder.toString(),
                new Listener<String>() {

                    @Override
                    public void onResponse(String response) {
                        // TODO Auto-generated method stub
//                          這個位置先公共解析處理共同異常
                        try {
                            if (response != null && callback != null) {
                                Gson gson = new Gson();
                                //回撥請求成功,同時url和解析的物件
                                callback.onSuccess(url, gson.fromJson(response, clazz));

                            }

                        } catch (Exception e) {
                            // TODO: handle exception
                            if (callback != null) {
                                //回撥請求失敗--解析異常
                                callback.onFailure(url, e, 0, "解析異常");
                                return;
                            }
                        }


                    }
                }, new ErrorListener() {
            //請求出錯的監聽
            @Override
            public void onErrorResponse(VolleyError error) {
                if (callback != null) {
                    if (error != null) {
                        callback.onFailure(url, error.getCause(), 0,
                                error.getMessage());
                    } else {
                        callback.onFailure(url, null, 0, "");
                    }
                }
            }
        }) {
            //post請求的引數資訊
            protected Map<String, String> getParams() {
                return getPostApiParmes(parameter);
            }
        };

        //新增請求到請求佇列中
        addRequest(request, url);
    }


    /*
     * post引數
     * 
     * ts:時間戳 sign: 介面簽名 parms = 按文件引數拼接 parm[0]+ … + parm[n-1] sign =
     * md5(parms+"雙方平臺約定公鑰")
     */
    private static ApiParams getPostApiParmes(final HashMap<String, String> parameter) {
        ApiParams api = new ApiParams();
        for (Entry<String, String> entry : parameter.entrySet()) {
            api.with(entry.getKey(), entry.getValue());
        }
        return api;
    }

5.同樣回到LoginActivity中執行回撥,失敗則提示,成功則將登入資訊儲存到SP中和Application中

@Override
public void onResponeStart(String apiName) {
    // TODO Auto-generated method stub

    if (UrlConstance.KEY_LOGIN_INFO.equals(apiName)) {
        Toast.makeText(LoginActivity.this, "正在載入資料中", Toast.LENGTH_SHORT).show();
    }
}

@Override
public void onLoading(String apiName, long count, long current) {
    // TODO Auto-generated method stub
}

@Override
public void onSuccess(String apiName, Object object) {
    // TODO Auto-generated method stub
    if (UrlConstance.KEY_LOGIN_INFO.equals(apiName)) {
        //郵箱登入返回資料
        if (object != null && object instanceof UserBaseInfo) {
            UserBaseInfo info = (UserBaseInfo) object;
            if (info.getRet().equals(Constant.KEY_SUCCESS)) {

                //登入成功,儲存登入資訊
                ItLanBaoApplication.getInstance().setBaseUser(info);//儲存到Application中

                //儲存到SP中
                UserPreference.save(KeyConstance.IS_USER_ID, String.valueOf(info.getUserid()));

                UserPreference.save(KeyConstance.IS_USER_ACCOUNT, info.getEmail());
                UserPreference.save(KeyConstance.IS_USER_PASSWORD, loginPassword.getText().toString());


                Intent intent = new Intent();
                intent.setClass(LoginActivity.this, MainActivity.class);
                startActivity(intent);
                overridePendingTransition(android.R.anim.slide_in_left,
                        android.R.anim.slide_out_right);
                finish();

            } else {
                Log.e("TAG", "info="+info.toString());
                if (info.getErrcode().equals(Constant.KEY_NO_REGIST)) {
                    Toast.makeText(LoginActivity.this, "登入失敗", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(LoginActivity.this, info.getMsg(), Toast.LENGTH_SHORT).show();
                    Log.e("TAG", "info.getMsg()="+info.getMsg());
                }

            }
        }
    }

}

@Override
public void onFailure(String apiName, Throwable t, int errorNo,
                      String strMsg) {
    // TODO Auto-generated method stub
    Toast.makeText(LoginActivity.this, "Failure", Toast.LENGTH_SHORT).show();   
}

6.自動登陸的實現,其實就是我們在歡迎頁面進行一個判斷:讀取SP中的資訊,如有登入的資訊,則取出,攜帶此資訊請求伺服器(同登入的請求),若成功,則直接跳轉到主頁面;如果登入不成功或者SP中沒有儲存的登入資訊,則跳轉到登入介面,程式碼如下(WelcomeActivity中)

            String userAccount = UserPreference.read(KeyConstance.IS_USER_ACCOUNT, null);//軟體還沒有保持賬號
            String userPassword = UserPreference.read(KeyConstance.IS_USER_PASSWORD, null);
            String userid = UserPreference.read(KeyConstance.IS_USER_ID, null);

            if (TextUtils.isEmpty(userAccount)) {//沒有儲存的登入資訊跳轉到登入介面
                //空的,表示沒有註冊,或者清除資料
                Intent intent = new Intent();
                intent.setClass(WelcomeActiviy.this, LoginActivity.class);
                startActivity(intent);
                overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
                finish();
            } else {
                //用儲存的資訊直接登入
                RequestApiData.getInstance().getLoginData(userAccount, userPassword,
                        UserBaseInfo.class, WelcomeActiviy.this);

            }

WelcomeActivity也同樣實現了HttpResponeCallBack介面,所以傳入的callback物件也是自身,我們在回撥方法中判斷是否登入成功

@Override
public void onResponeStart(String apiName) {

}

@Override
public void onLoading(String apiName, long count, long current) {

}

@Override
public void onSuccess(String apiName, Object object) {
    //當前介面是否是獲取使用者的基本資訊的介面
    if (UrlConstance.KEY_USER_BASE_INFO.equals(apiName)) {
        if (object != null && object instanceof UserBaseInfo) {
            UserBaseInfo info = (UserBaseInfo) object;
            ItLanBaoApplication.getInstance().setBaseUser(info);//把資料放入到Application裡面,全域性
            UserPreference.save(KeyConstance.IS_USER_ID, String.valueOf(info.getUserid()));

            Intent intent = new Intent();
            intent.setClass(WelcomeActiviy.this, MainActivity.class);
            startActivity(intent);
            overridePendingTransition(android.R.anim.slide_in_left,
                    android.R.anim.slide_out_right);
            finish();

        } else {
            Toast.makeText(WelcomeActiviy.this, "載入失敗", Toast.LENGTH_SHORT).show();
        }
    } else if (UrlConstance.KEY_LOGIN_INFO.equals(apiName)) {//當前介面是登入的介面
        //登入返回資料
        if (object != null && object instanceof UserBaseInfo) {
            UserBaseInfo info = (UserBaseInfo) object;
            if (Constant.KEY_SUCCESS.equals(info.getRet())) {

                ItLanBaoApplication.getInstance().setBaseUser(info);//將使用者資訊儲存在Application中
                UserPreference.save(KeyConstance.IS_USER_ID, String.valueOf(info.getUserid()));

                Intent intent = new Intent();
                intent.setClass(WelcomeActiviy.this, MainActivity.class);
                startActivity(intent);
                overridePendingTransition(android.R.anim.slide_in_left,
                        android.R.anim.slide_out_right);
                finish();

            } else {
                Toast.makeText(WelcomeActiviy.this, info.getMsg(), Toast.LENGTH_SHORT).show();
            }
        }
    }
}

@Override
public void onFailure(String apiName, Throwable t, int errorNo, String strMsg) {
    Toast.makeText(WelcomeActiviy.this, "Failure", Toast.LENGTH_SHORT).show();
}