Android 輕鬆實現網路互動模板
看完本文,您可以學到:
1.Android與後台互動的模板化方法
2.JSON的使用
3.檢查網路連接
4.AsyncTask的使用
我們簡單的以登入狀態例,來實現整個的流程。話不多說,先來看看:
一、通用類的編寫
首先,既然要實現互動模板化,最重要的就是要提取出儘可能多的可複用代碼。無論是與後台進行什麼操作,判斷網路是否正常串連、發送請求後得到資料、網路異常時的錯誤資訊提示都是必不可少的。所以我們編寫一個通用的CallService類:
/** * Created by Hyman on 2015/6/11. */public class CallService { /** * check net connection before call * * @param context * @return */ private static boolean checkNet(Context context) { ConnectivityManager connectivity = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivity != null) { // 擷取網路連接管理的對象 NetworkInfo info = connectivity.getActiveNetworkInfo(); if (info != null && info.isConnected()) { // 判斷當前網路是否已經串連 if (info.getState() == NetworkInfo.State.CONNECTED) { return true; } } } return false; } /** * call service by net * * @param urlString url * @param content a string of json,params * @return the result,a string of json */ public static String call(String urlString, String content, Context context) { if (!checkNet(context)) { return null; } try { URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setDoOutput(true); conn.setRequestMethod("POST"); conn.setRequestProperty("User-Agent", "Fiddler"); conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("Charset", "utf-8"); OutputStream os = conn.getOutputStream(); os.write(content.getBytes()); os.close(); int code = conn.getResponseCode(); if (code == 200) { BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8")); String retData; String responseData = ""; while ((retData = in.readLine()) != null) { responseData += retData; } in.close(); return responseData; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } public static void showNetErr(Context context){ new AlertDialog.Builder(context) .setTitle("網路錯誤") .setMessage("網路連接失敗,請確認網路連接") .setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { } }).show(); } }
其中,判斷網路連接狀態是藉助Android系統提供的ConnectivityManager的方法實現的,藉助這個類我們還可以擷取更多的串連狀態資訊,包括當前是用流量還是WIFI等等。
然後,在調用本類核心方法call()時,傳入了三個參數,一個是後台服務的url路徑,一個是已經組裝好的參數,第三個是上下文context。 在這個方法中,我們先去判斷網路連接,未串連就直接返回空。否則就使用HttpURLConnection方法向伺服器發送請求,再把伺服器的傳回值返回給調用者。
另一個showNetErr方法就只是簡單地跳出一個對話方塊進行提示。
二、利用Json以及AsyncTask進行互動
我們都知道,在安卓中進行網路操作等等這些耗時的操作,都不能在主線程(即UI線程中)操作,所以我們利用安卓提供的非同步機制AsyncTask(或者也可以自己寫new Thread + Handler)來進行網路操作。還不瞭解AsyncTask用法的朋友可以看我的另一篇部落格 Android AsyncTask詳解。 我們以登入狀態例:
/** * Created by Hyman on 2015/6/11. */public class Login { private static final String urlString = GetServerUrl.getUrl() + "index.php?r=period/login"; private static final String TAG = "Login"; private ProgressBar progressBar; private Context context; private String userName; private String password; public Login( Context context,ProgressBar progressBar) { this.progressBar=progressBar; this.context = context; } public void login(String userName,String password) { Log.i(TAG, "call login"); this.userName=userName; this.password=password; new LoginTask().execute(); } class LoginTask extends AsyncTask { @Override protected String doInBackground(Void... params) { JSONObject tosendsObject = new JSONObject(); Log.i(TAG, "start put json!"); try { //add account info tosendsObject.put("username", userName); tosendsObject.put("password", password); } catch (JSONException e) { e.printStackTrace(); } //change json to String String content = String.valueOf(tosendsObject); Log.i(TAG, "send :" + content); String responseData = CallService.call(urlString, content,context); if(responseData==null || responseData.equals("")){ return null; } Log.i(TAG, "res:" + responseData); JSONObject resultObject = null; String result=null; try { resultObject = new JSONObject(responseData); result = resultObject.getString("result"); Log.i(TAG, "result:" + result); } catch (JSONException e) { e.printStackTrace(); } return result; } @Override protected void onPreExecute() { progressBar.setVisibility(View.VISIBLE); //show the progressBar super.onPreExecute(); } @Override protected void onPostExecute(String result) { progressBar.setVisibility(View.GONE); //hide the progressBar if(result==null){ CallService.showNetErr(context); return; } Toast.makeText(context,"result:"+result,Toast.LENGTH_SHORT).show(); //here you can do anything you want after login } }}
我們在LoginActivity中初始化這個類的執行個體,傳入上下文以及ProgressBar(用於提高使用者體驗),再調用login方法傳入使用者名稱和密碼這兩個參數。在進行操作前,onPreExecute方法顯示出ProgressBar,在返回結果後,onPostExecute方法再隱藏ProgressBar。
然後我們再看doInBackGroud方法(這個方法聽名字就是非同步作業啊):我們建立一個JsonObject對象,再使用索引值對的方法(類似map)傳入參數,最後轉成String後一起傳給伺服器。在得到結果後把伺服器返回的json形式的字串轉成JsonObject。如果返回的是空,說明串連有問題,就調用通用類的showNetErr方法。 我把Log截了圖,此前不清楚Json格式的朋友可以管中窺豹:
如果有需要傳一個list給伺服器,還可以使用JsonArray類。比如:
JSONArray jsonArray = new JSONArray(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); try { for (PeriodPO peroid : localPeriods) { //這是一個我自訂的資料結構的list JSONObject periodObject = new JSONObject(); periodObject.put("date", sdf.format(peroid.getDate())); periodObject.put("tag", peroid.getTag()); periodObject.put("length", peroid.getLength()); jsonArray.put(periodObject); //把每一個對象轉成JsonObject,再把每個object放入Array } tosendsObject.put("periods", jsonArray); //add account info tosendsObject.put("username", "test"); } catch (JSONException e) { e.printStackTrace(); }
=============寫在後面========================
我寫完之後,覺得傳參數這件事情也可以放在通用類中,但對如何把那部分代碼巧妙提取出來始終找不到非常好的方法。希望各位朋友可以多提建議,不吝賜教,多謝了!