练习项目——学生协作软件客户端(3)网络

来源:互联网 发布:linux终端连接服务器 编辑:程序博客网 时间:2024/06/03 01:42

继续写这个小项目,这次写网络部分

首先要获取权限,在 AndroidManifest.xml 里的根节点下加入如下片段

<!-- 互联网访问权限 --><uses-permission android:name="android.permission.INTERNET" /><!-- 读取手机网络状态的权限 --><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


之后先是说一下几个联网的方式,基本上分为直接使用Socket,使用HttpURLConnection,或者是使用HttpClient。

官方的参考:

http://developer.android.com/reference/java/net/Socket.html

http://developer.android.com/reference/java/net/HttpURLConnection.html

http://developer.android.com/reference/android/net/http/AndroidHttpClient.html


事实上,如果只是看网络连接这部分的话,是和一般的JAVA编程没有区别的。由于服务器端是用J2EE写的,所以就使用HTTP协议了,尽管很多Android教材都是偏重HttpClient,但从JAVA开始笔者个人就比较习惯于用HttpURLConnection,而且HttpClient存在一些问题,详见:

http://blog.csdn.net/androidzhaoxiaogang/article/details/8158122


下面以通知获取功能为例来说明具体的写法:


先是基础的HttpURLConnection

确定好URL的字符串之后创建一个URL对象,通过这个对象打开一个连接,此时还没发送数据,这时可以对将要发送的东西做出设定,比如通过setRequestProperty来添加Http请求头等等,这里为了简单起见就没有加太多了,加入cookie是因为服务器那边是通过这个方法验证的,所以这里就加上,这需要根据实际情况来自己选择。注意要调用setDoOutput和setDoInput方法,传入true,使得能支持输入和输出。然后就可以获得输出流和输入流读写数据了。

/** * 通过临时随机字符串来发送具体请求 *  * @param directUrl *            目标url * @param cookie *            注意这个直接放进了Cookie下,所以需要在传入参数时用cookie=xxx的格式 * @param request *            请求正文 * @return 响应正文字符串 * @throws Exception */public static final String normalRequestForText(String directUrl,String cookie, String request) throws Exception {Thread.sleep(2000);//为了模拟网络缓慢的情况URL url = new URL(directUrl);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setDoOutput(true);conn.setDoInput(true);if (cookie != null && (!cookie.equalsIgnoreCase(""))) {conn.setRequestProperty("Cookie", cookie);}PrintWriter pw = new PrintWriter(conn.getOutputStream());pw.write(request);pw.flush();pw.close();int length = conn.getContentLength();// System.out.println(length);InputStreamReader isr = new InputStreamReader(conn.getInputStream(),"UTF-8");char[] buf = new char[length];isr.read(buf);isr.close();conn.disconnect();return new String(buf);}
当然,通知不是光获取就算了,还要进行独自的解析,所以就写多一个方法专门来获取通知,这个方法调用了上面的方法,这里的NotificationList是自己写的一个类,用来存放通知和一些相关操作。
/** * 获取通知列表 *  * @param cookie *            表明身份的随机字符串 * @return * @throws Exception */public static final NotificationList getServerNotificationList(String cookie, String lasttime)throws Exception {// 如果没有新的通知默认返回的是nullNotificationList notis = null;String request = "time="+lasttime;String response = normalRequestForText(ENTIRE_NOTIFICATION_ADDRESS,"cookie="+cookie, request);Document doc = DocumentHelper.parseText(response);Element root = doc.getRootElement();Iterator iter = root.elementIterator();Element temp;for (;iter.hasNext();){if(notis==null)notis = new NotificationList();temp = (Element) iter.next();String date = temp.elementText("date");String title = temp.elementText("title");String text = temp.elementText("text");String id = temp.elementText("id");notis.addNotification(date, title, text, id);}return notis;}

Android不允许在UI主线程里做过多耗时的工作,网络操作就是一个典型的耗时操作,所以一定要在子线程里完成,而子线程要把数据返回给主线程,Android提供了一个Handler机制(http://developer.android.com/reference/android/os/Handler.html,注意是android.os.Handler,而不是java.util.logging.Handler),Handler允许其他线程调用sendMessage方法将消息加入消息队列,然后会有一个单独的线程把消息取出来处理。

要想使用Handler,就需要现在主线程里创建Handler对象,并重写handleMessage方法来,由于每个类的Handler都有自己对消息的具体处理机制,并不需要进行重用,所以不用单独把它抽离出来,直接写在类里面就可以了。这里以fragmentNotification为例,这个之前说过,是一个继承了ListFragment的类,在类里这样写:

@SuppressLint("HandlerLeak")Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case GET_NOTIFICATION_LIST:// 获取服务器发来的通知列表// ...逻辑一号break;case NETWORK_PROBLEM:// ...逻辑二号break;case GET_SERVER_COOKIE:// ...逻辑三号break;case FAIL:// ...逻辑四号break;default:break;}}};
为了清晰起见,先把内部具体的逻辑删掉,子线程调用Handler的sendMessage方法会发来一个Message类的对象(http://developer.android.com/reference/android/os/Message.html),这个类有两个最常用的成员,一个是Object obj,另一个是int what,使用的时候先创建一个Message对象,再将what赋值为一个自己喜欢的值,为了程序的可读性,一般会在Handler所属的类里定义好public static int的常量,以便赋值。

if (!cookie.equalsIgnoreCase("")) {// 如果cookie存在且cookie不为空字符串,发送cookie 获取数据new Thread() {public void run() {try {NotificationList notificationList = getLocalAndServerNotifications(cookie);// 如果notification list获取成功Message msg = new Message();msg.what = GET_NOTIFICATION_LIST;msg.obj = notificationList;handler.sendMessage(msg);} catch (Exception e) {e.printStackTrace();// 如果请求失败if ((!username.equalsIgnoreCase(""))&& (!password.equalsIgnoreCase(""))) {// 如果用户名和密码不为空try {// 尝试获取cookieString responseCookie = ServerConnector.getCookie(username, password);if (!responseCookie.equalsIgnoreCase("")) {// 如果成功获取了cookie则发送相应消息Message msg = new Message();msg.what = GET_SERVER_COOKIE;msg.obj = cookie;handler.sendMessage(msg);} else {// 如果获取cookie失败,则启动loginactivity由用户重新输入Intent intent = new Intent();intent.setAction("project.es.LoginActivity");intent.addCategory("android.intent.category.DEFAULT");getActivity().startActivity(intent);}} catch (Exception ee) {ee.printStackTrace();}}}}}.start();} else {}
如程序所示,what用来指定发送消息的目的,obj用来携带具体数据,调用sendMessage(Message msg)将msg 发送给主线程的消息队列。由于Object是所有类公有的父类,所以可以赋为任何值。

现在看一下在handleMessage里的具体逻辑,这里以逻辑一号为例:

case GET_NOTIFICATION_LIST:// 获取服务器发来的通知列表MainActivity.stopDialog();SimpleAdapter adapter = new SimpleAdapter(fragmentNotification.this.getActivity(),getData((NotificationList) msg.obj),R.layout.frame_notification_item, from, to);fragmentNotification.this.setListAdapter(adapter);break;
这个就是将数据填充给了ListView,这样就能呈现数据了。

注意到这里还调用了一下MainActivity里的stopDialog方法,这个方法是自己写在MainActivity里的,为了减小用户等待的焦急感,在用户点击通知列表的时候,会弹出一个ProgressDialog,在联网获取通知的时候就有一个进度圈在转。

MainActivity里这样写:

private class TabOnClickListener implements OnClickListener {@Overridepublic void onClick(View v) {transaction = manager.beginTransaction();switch (v.getId()) {case R.id.tv_local:tvBarLocal.setBackgroundColor(Color.rgb(0x33, 0x99, 0xff));tvBarServer.setBackgroundColor(Color.BLACK);tvBarNoti.setBackgroundColor(Color.BLACK);transaction.replace(R.id.content, new fragmentLocal());break;case R.id.tv_server:tvBarLocal.setBackgroundColor(Color.BLACK);tvBarServer.setBackgroundColor(Color.rgb(0x33, 0x99, 0xff));tvBarNoti.setBackgroundColor(Color.BLACK);progressDialog = ProgressDialog.show(MainActivity.this, "请稍候","正在获取文件列表...", true, false);transaction.replace(R.id.content, new fragmentServer());break;case R.id.tv_notification:tvBarLocal.setBackgroundColor(Color.BLACK);tvBarServer.setBackgroundColor(Color.BLACK);tvBarNoti.setBackgroundColor(Color.rgb(0x33, 0x99, 0xff));progressDialog = ProgressDialog.show(MainActivity.this, "请稍候","正在获取通知...", true, false);transaction.replace(R.id.content, new fragmentNotification());break;default:System.out.println("default");}transaction.commit();}}
/** * 供外部调用停止progressDialog的显示 */public static void stopDialog() {<span style="white-space:pre"></span>progressDialog.dismiss();}


至此,这个练习项目就介绍完了,这个软件基本上就是包含这几样东西。


PS,在结题答辩时看到笔者班上各路大神们的作品简直吓哭...各种做什么NFC充值,智能小车,人脸追踪风扇等等,瞬间觉得自己做的东西好没技术含量啊,当时就崩溃了...无比崇拜各位玩硬件的大神啊啊啊

PS2,笔者纯小白,代码难免不优雅,文章难免有错漏,欢迎各位指出。




0 0