看完本文,您可以学到:
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(); } }
然后,在调用本类核心方法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<Void, Void, String> { @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(); }
=============写在后面========================
我写完之后,觉得传参数这件事情也可以放在通用类中,但对如何把那部分代码巧妙提取出来始终找不到非常好的方法。希望各位朋友可以多提建议,不吝赐教,多谢了!
ps:对文中代码有不理解或者有意见的朋友也欢迎留言!
- 2楼dbanzhu3435a昨天 13:59
- 厉害,nn可以向您学习吗 452982916 加群一起讨论
- Re: u012422829昨天 15:08
- 回复dbanzhu3435an谢谢夸奖,共同学习
- 1楼pengyu1801前天 22:25
- 可不可以把demo也上传一下哦
- Re: u012422829前天 22:42
- 回复pengyu1801n因为代码比较简单,所以没有像以前一样上传。如果需要的话可以留邮箱~