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

本文详细介绍了一个简单的Android项目中网络模块的实现过程,包括权限配置、使用HttpURLConnection进行网络请求及响应处理、子线程与主线程间的数据传递等关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

首先要获取权限,在 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.youkuaiyun.com/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 {
	// 如果没有新的通知默认返回的是null
	NotificationList 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() {
		@Override
		public 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 {
						// 尝试获取cookie
						String 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 {
	@Override
	public 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,笔者纯小白,代码难免不优雅,文章难免有错漏,欢迎各位指出。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值