1. 程式人生 > >Android Volley 超實用簡易封裝

Android Volley 超實用簡易封裝

寫在前面

本文僅針對有一定volley基礎的朋友們。如果你之前完全沒有接觸過volley,推薦去看下郭霖大神的blog。Android Volley完全解析(一),初識Volley的基本用法

本文心法傳授於群裡大佬,大佬QQ:864009106,讓我對於volley以及封裝有了新的認識,十分感謝。

準備工作

  1. Manifest.xml配置網路許可權

    <!-- 網路請求許可權 -->
    <uses-permission android:name="android.permission.INTERNET" />
  2. JSON處理

    本文采用fastjson進行解析

    //fastJson
    compile 'com.alibaba:fastjson:1.2.33'
  3. 進度條顯示(可選)

    本文采用SweetAlertDialog

    //sweet-alert-dialog
    compile 'cn.pedant.sweetalert:library:1.3'

匯入SweetAlertDialog時,專案會報Manifest merge failed錯誤,在Manifest的application中加入tools:replace=”android:icon”即可解決。

初始化

  1. 確保RequestQueue單例。

    新建RequestQueueUtil工具類, 單例獲取RequestQueue物件。

    public class RequestQueueUtil {
    
        private static RequestQueue sRequestQueue;
    
        public static RequestQueue getRequestQueue(Context context) {
            if (sRequestQueue == null) {
                synchronized (RequestQueue.class) {
                    if (sRequestQueue == null) {
                    sRequestQueue = Volley.newRequestQueue(context);
                    }
                }
            }
            return
    sRequestQueue; } }
  2. 在Application中初始化請求佇列

    public class VolleyApplication extends Application {
    
        public static RequestQueue sRequestQueue;
    
        @Override
        public void onCreate() {
            super.onCreate();
            sRequestQueue = RequestQueueUtil.getRequestQueue(this);
        }
    
    }

開始封裝

開始之前,我們先來想一想,一個好的網路請求框架需要哪些東西?

首先要有可拓展性,可以根據專案的情況靈活修改。

其次要有統一的資料處理以及錯誤處理。

最後一定要結構整齊美觀,便於後期維護。

咱們先來看一個基本的Request用法:

private void getInfo() {

    StringRequest request = new StringRequest(Request.Method.GET, "url", new Response.Listener<String>() {
        @Override
        public void onResponse(String s) {
            Bean bean = JSON.parseObject(s, Bean.class);
            switch (bean.getStatus()) {
                //成功邏輯處理
                case 1000:
                    //do something...
                    break;
                //case xxxx:
                //其他邏輯處理...
                //break;
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {
            Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
        }
    });
    request.setRetryPolicy(new DefaultRetryPolicy(
            5 * 1000,//連結超時時間
            0,//重新嘗試連線次數
            DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
    ));
    requestQueue.addRequest(request);

}    

再來看一個我們封裝好的用法:

private void getNewInfo() {

    NetworkUtil.getInstance().get(MainActivity.this, "url", map, new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case TAG:
                    Log.i("result", msg.obj.toString());
                    //do something...
                    break;
            }
        }
    }, TAG);

}

比較起來,封裝之後是不是看起來更加的清晰明瞭呢?話不多說,咱們正式開始。

  1. 為了更好的應對專案的變化,我們來建立一個工具類來對請求進行封裝:

    public class NetworkUtil{
    
        private static NetworkUtil instance;
    
        //單例獲取工具類
        public static NetworkUtil getInstance() {
            if (instance == null) {
                synchronized (NetworkUtil.class) {
                    if (instance == null) {
                        instance = new NetworkUtil();
                    }
                }
            }
            return instance;
        }
    
        //獲取請求佇列
        private RequestQueue mRequestQueue = VolleyApplication.sRequestQueue;
    
    }
  2. 確定BaseBean:

    所有請求傳送成功之後的返回json都應該有著相同的結構,例如我們的專案中會返回:

    {
        "status": 1000,
        "desc": "success",
        "data": {
            "key": "value",
            "key2": 100,
            "key3": false
            //xxxx:yyyy ....
            }
    }
    

    為了統一資料,新建BaseBean類:

    public class BaseBean<T> {
    
        //返回碼
        private int status;
    
        //返回資訊
        private String desc;
    
        //我們需要的資料
        private T data;
    
        //getter and setter here...
    }

    這裡使用了泛型T來對data進行處理,這樣不管返回的結果如何,我們都不用再新建類去處理status以及desc,直接解析data即可。

  3. 完整方法:

    public void post(final Activity activity, String url, final Map<String, String> map, final Handler handler, final int tag) {
        final SweetAlertDialog dialog = new SweetAlertDialog(activity);
        dialog.getProgressHelper().setBarColor(Color.parseColor("#66CCFF"));
        dialog.setTitleText("Loading");
        dialog.setCancelable(false);
        if (!dialog.isShowing()) {
            dialog.show();
        }
    
        final Message message = Message.obtain();
        StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
            @Override
            public void onResponse(String s) {
                if (dialog.isShowing()) {
                    dialog.dismiss();
                }
                BaseBean baseBean = JSON.parseObject(s, BaseBean.class);
                int status = baseBean.getStatus();
                switch (status) {
                    case 1000:
                        message.what = tag;
                        message.obj = baseBean.getData();
                        handler.sendMessage(message);
                        break;
                        //case xxxx:
                        //處理其他邏輯,例如簽名錯誤、token失效、toast錯誤資訊等
                        //break;
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                if (dialog.isShowing()) {
                    dialog.dismiss();
                }
                handlerErrorMessage(activity, volleyError);
            }
        }) {
    
                //如有公參請求頭之類的在這裡設定就好
                //@Override
                //public Map<String, String> getHeaders() throws AuthFailureError {
                //return super.getHeaders();
                //}
    
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                return map;
            }
        };
        request.setRetryPolicy(new DefaultRetryPolicy(
                5 * 1000,//連結超時時間
                0,//重新嘗試連線次數
                DefaultRetryPolicy.DEFAULT_BACKOFF_MULT//曲線增長因子
        ));
        mRequestQueue.add(request);
    }
    

    先貼出一個完整的post請求方法,接下來我們逐步分析:

    1. 引數

      /**
       * @param activity 當前Activity Context
       * @param url 請求地址
       * @param map 請求引數
       * @param handler 返回結果處理
       * @param tag 請求標識
       */
      1. activity:

        傳入當前Activity作為Context,由於存在progress,必須依託在ActivityContext中,所以不能使用一般Context。另外在Log時也可以通過列印activity.getClass().getSimpleName()進行追蹤。

      2. url:

        請求的連結地址

      3. map:

        請求引數,通過Map進行引數處理

      4. handler:

        通過handler.sendMessage()傳送資料,在Activity中拿到msg.obj進行資料處理。

      5. tag:

        當前請求標識。由於存有一個頁面發起多個請求的情況,用一個自定義tag來對請求進行標記,在Activity的handler中,通過判斷tag來確保請求不發生混亂。

    2. 進度顯示

      此項為可選項,但從互動角度考慮需要加上進度條顯示。這裡使用了SweetAlertDialog,大家也可以選用自定義進度條或者其他控制元件。在請求發起時顯示,請求成功或失敗後關閉,並做對應的邏輯處理。

    3. 傳送請求

      一個基本的StringRequest,如果專案需求公參或者Header,在這裡統一設定。(最開始嫌麻煩沒有封裝,專案中期要求加公參,真的是一個一個改。。。都是淚)此外由於使用者操作,介面響應等多種因素,會造成傳送多次請求的情況,所以在將請求新增到佇列之前,使用setRetryPolicy來統一設定,確保不會重複傳送。

    4. 資料處理

      利用Message傳遞資料,成功響應後,將tag放入msg.what中,並將解析後的data放入msg.obj中,再由handler傳送。其中具體的邏輯按照具體的專案來修改。在失敗響應中,根據volleyError做了統一處理,直接在UI介面中Toast出相應資訊。

具體使用

一個基本的Button點擊發送請求,成功後在TextView中進行顯示。由於沒找到合適的post請求連結,就從網上找了個天氣api介面,但是不曉得為嘛返回亂碼。。。get請求的封裝稍後會在原始碼中放出。

public class MainActivity extends AppCompatActivity {

    private Button mButton;

    private TextView mContent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mButton = (Button) findViewById(R.id.test_button);

        mContent = (TextView) findViewById(R.id.test_content);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                NetworkUtil.getInstance().get(MainActivity.this, "http://wthrcdn.etouch.cn/weather_mini?citykey=101010100", null, new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        switch (msg.what) {
                            case WEATHER_TAG:
                                Log.i("result", msg.obj.toString());
                                mContent.setText(msg.obj.toString());
                                break;
                        }
                    }
                }, WEATHER_TAG);
            }
        });
    }
}

效果演示

gif

原始碼下載