正文:在一个android应用中,网络部分的编程应该是整个APP里涉及到的最多的内容之一,可能也是最难的之一。说它多,毋庸置疑,当今的APP如果没有网络做支撑,任何内容将是死水一潭,就像我们在憋得不行的时候到处找卫生纸一样,现在的我们同样会因为某个地方没有WIFI而“憋得不行”,是的,我们需要上网!网络就像一道道连接世界的隐形光束,如果哪个地方没有被它照到,这个地方就是荒蛮之地,可见我们对网络的迫切地需求;说网络编程难,也不是我危言耸听:网络是变化的,是动态的,我们访问网络可能需要两拨人维护(android client和server端),需要通过OSI七层协议或TCP/IP四层协议,需要迎合各种各样的网络传输协议(ftp,telnet,http1.1,http/2 等),需要上传和下载各种类型的文件(字符串,图片流,文件流 等),不同格式的数据,打包和解析方式也不同(XML格式(需要PULL,SAX,DOM 等解析方式),JSON格式(需要JSONObject,JSONArray或GSON或JACKSON等解析方式),流文件(Httpmine解析))等。。。说了这么多,你可能很恼火:网络用处这么广,却又这么难,到底还学不学?当然要学,因为正是它比较难,一些公司推出了各种各样的第三方jar包开源框架,这个框架为我们做了很好的代码封装,让我们方便地进行网络编程,下面我结合几个demo跟各位浅谈一下android的网络编程。
网络编程的demo介绍
本文一共包含3个demo,他们分别是:
- 用户登录:用户在Android客户端输入用户名密码,上传至服务器,服务器通过查询数据库中的信息,给客户端返回一个正确性的提示。
- 用户注册:用户将自己输入的姓名和兴趣爱好,上传至服务器,服务器将新增用户添加至数据库中。
- 下载图片流
本文涉及的知识点:
- 在tomcat容器中搭建简单的Servlet,在doPost和doGet方法中通过参数HttpServletRequest对象和HttpServletResponse对象获取client端的内容或向client端发送内容。
- 在client端使用HttpClient对象或HttpURLConnection对象请求server,并使用这两个对象接收返回信息。
- 使用多线程、Handler、runOnUIThread等线程和异步知识,在主线程(UI线程)中更新UI,在子线程中访问网络,并利用Handler在线程之间传递信息。
- 利用JSONObject、JSONArray类封装、解析JSON格式的数据
- 弱引用
- 定制异常
- IO流
demo#1: 用户登录
本demo将实现从android客户端输入用户名密码,上传至服务器,服务器通过比对,返回客户端正确性信息。
- 服务器端使用tomcat容器装载Servlet web应用程序,通过doGet方式接收请求、处理、并返回客户端。
- 客户端通过HttpClient,以get/post方式请求server端
- 通过Handler实现UI更新
server端浅析
Server端需注意的几点:
- 由于tomcat容器最终运行的classes文件位于 /WebContent/WEB-INF/classes 中,然而创建Dynamic Web Project的时候,代码默认build成class文件的存放地址默认是 /工程名/build ,所以应把该存放地址改为 /WebContent/WEB-INF/classes 。
- 在tomcat中新建Servlet类时,默认的该Servlet的URL地址是 /Servlet类名,如果类名过长,可以在创建Servlet时,在URL mapping中修改一个虚拟映射的URL路径,方便访问。
- 无论客户端用哪种方式请求(get/post),在Server端用doGet和doPost方式都能接收,只需要在其中一个方法中调用另一个方法即可。
- 为了能够处理client端发送的中文信息,应设置字符的编码方式
request.setCharacterEncoding("UTF-8");
- 为了防止client端接收的消息乱码,应设置如下的编码方式
// 防止发送到client端乱码
response.setCharacterEncoding("UTF-8")
response.setContentType("text/html;charset=UTF-8")
服务器端代码如下:
@WebServlet("/login.do")
public class SecondVanpersieServletForAndroidLogin extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public SecondVanpersieServletForAndroidLogin() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
OutputStream out =null;
request.setCharacterEncoding("UTF-8");
String loginName = request.getParameter("LoginName");
String loginPassword = request.getParameter("LoginPassword");
System.out.println(loginName + "|" + loginPassword);
try {
out= response.getOutputStream();
if (loginName.equals("tom") && loginPassword.equals("123")) {
out.write("success!".getBytes("UTF-8"));
} else {
out.write("failed!".getBytes("UTF-8"));
}
} finally {
out.flush();
out.close();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
如上所示,request接收server传来的信息,为方便起见,直接判断用户名是否为tom,密码是否为123,若正确,则返回success!,否则返回failed!。
client端浅析
client端需注意的几点:
界面布局
界面布局很简单,就是两个输入框(EditText),一个清除button,一个注册button。不再做过多解释,代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="1"
android:text="@string/text_login" />
<EditText
android:id="@+id/text_logininput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:hint="@string/text_login_hint"
android:inputType="text"
android:selectAllOnFocus="true" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="1"
android:text="@string/text_password" />
<EditText
android:id="@+id/text_passwordinput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:hint="@string/text_password_hint"
android:inputType="textPassword"
android:selectAllOnFocus="true" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal" >
<Button
android:id="@+id/button_clear"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="#69696969"
android:text="@string/button_clear" />
<Button
android:id="@+id/button_login"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="#69696969"
android:text="@string/button_login" />
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
activity代码
本段代码主要用于初始化布局中的控件,绑定button监听器,开启一个线程用于访问server,在访问的过程中,可能会出现各种异常,可以通过try/catch块按照异常优先级进行捕捉,再强调一点,一定要在Handler中的handlerMessage中更新UI,代码如下:
@SuppressWarnings("deprecation")
public class LoginActivity extends Activity {
private EditText mEditLogin;
private EditText mEditPassword;
private Button mButtonClear;
private Button mButtonLogin;
private UserService mUserService = new UserServiceImplement();
private static final int FLAG_LOGIN_SUCCESS = 1;
private static final String MSG_LOGIN_ERROR = "登录出错!";
private static final String MSG_LOGIN_SUCCESS = "登录成功!";
public static final String MSG_LOGIN_FAILED = "登录名|密码出错";
public static final String MSG_SERVER_ERROR = "请求服务器错误";
public static final String MSG_REQUEST_TIMEOUT = "连接服务器超时";
public static final String MSG_RESPONSE_TIMEOUT = "服务器处理超时";
private static ProgressDialog mDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
init();
bindClickListener();
}
private void bindClickListener() {
mButtonLogin.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final String _editLoginName = mEditLogin.getText().toString();
final String _editPassword = mEditPassword.getText().toString();
if (mDialog == null) {
mDialog = new ProgressDialog(LoginActivity.this);
}
mDialog.setTitle("请等待...");
mDialog.setMessage("登陆中...");
mDialog.setCancelable(false);
mDialog.show();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
mUserService.userLogin(_editLoginName,
_editPassword);
handler.sendEmptyMessage(FLAG_LOGIN_SUCCESS);
}
catch (ConnectTimeoutException e) {
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putSerializable("ErrorMsg",
MSG_REQUEST_TIMEOUT);
msg.setData(bundle);
handler.sendMessage(msg);
}
catch (SocketTimeoutException e) {
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putSerializable("ErrorMsg",
MSG_RESPONSE_TIMEOUT);
msg.setData(bundle);
handler.sendMessage(msg);
}
catch (ServiceRulesException e) {
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putSerializable("ErrorMsg", e.getMessage());
msg.setData(bundle);
handler.sendMessage(msg);
}
catch (Exception e) {
e.printStackTrace();
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putSerializable("ErrorMsg", MSG_LOGIN_ERROR);
msg.setData(bundle);
handler.sendMessage(msg);
}
}
});
thread.start();
}
});
mButtonClear.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mEditLogin.setText("");
mEditPassword.setText("");
Toast.makeText(LoginActivity.this, "cleared!",
Toast.LENGTH_SHORT).show();
}
});
}
private void showTip(String str) {
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
};
private static class IHandler extends Handler {
private final WeakReference<Activity> mActivity;
public IHandler(LoginActivity activity) {
mActivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
if (mDialog != null) {
mDialog.dismiss();
}
int flag = msg.what;
switch (flag) {
case 0:
String errorMsg = (String) msg.getData().getSerializable(
"ErrorMsg");
((LoginActivity) mActivity.get()).showTip(errorMsg);
break;
case FLAG_LOGIN_SUCCESS:
((LoginActivity) mActivity.get()).showTip(MSG_LOGIN_SUCCESS);
break;
default:
break;
}
}
}
private IHandler handler = new IHandler(this);
private void init() {
mEditLogin = (EditText) findViewById(R.id.text_logininput);
mEditPassword = (EditText) findViewById(R.id.text_passwordinput);
mButtonClear = (Button) findViewById(R.id.button_clear);
mButtonLogin = (Button) findViewById(R.id.button_login);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
在上段代码中,继承了一个定制的Handler类,定义了一个弱引用类型的activity,用于处理当内存出现OOM时(Out Of Memory)时,系统及时调用GC机制(Garbage Collection),方便垃圾回收;另外程序会根据优先级对异常进行捕捉,如先捕捉连接服务器超时的异常(在规定的时间内没有连接到服务器,用ConnectTimeoutException类捕捉),在捕捉服务器响应超时异常(在规定的时间内服务器无响应,用SocketTimeoutException类捕捉),接着是业务异常(用户名或密码输入错误,用定制Exception类捕捉),最后是其他异常(用Exception类捕捉)。下面是定制的Exception类定义:
public class ServiceRulesException extends Exception {
/**
* 定制Exception类,捕捉业务异常
*/
private static final long serialVersionUID = 1L;
public ServiceRulesException(String message) {
super(message);
}
}
连接server端的client业务代码 (get请求)
定义一个接口,用于声明访问网络的方法并抛出异常,代码如下:
public interface UserService {
public void userLogin(String loginName, String loginPassword)
throws Exception;
程序使用HttpClient访问网络,用get方式请求,特别说明一点,HttpClient在Android2.3版本后就不推荐使用了,在Android6.0中更是直接被废弃了,若想访问网络可以使用HttpURLConnection,该类会在后面介绍,虽然被废弃了,不过还是有必要介绍一下,毕竟访问方式差不多,步骤如下:
- 定义HttpClient对象(HttpClient是个接口,只能new它的实现类DefaultHttpClient);
- 创建HttpGet/HttpPost对象,传入String类型的URL地址参数;
- 调用HttpClient类的execute方法,传入参数HttpGet/HttpPst对象,返回HttpResponse对象,该对象就是Server端返回给client端的信息;
- 判断返回的信息中携带的响应码是否为200,若不是200,说明出错,抛出异常;
- 通过entity实体解析HttpResponse对象,处理返回结果。
具体访问网络代码如下:
public class UserServiceImplement implements UserService {
@Override
public void userLogin(String loginName, String loginPassword)
throws Exception {
@SuppressWarnings("deprecation")
HttpClient client = new DefaultHttpClient();
String uri = "http://192.168.1.103:8080/test/login.do?LoginName="+ loginName + "&LoginPassword=" + loginPassword;
HttpGet get = new HttpGet(uri);
HttpResponse response = client.execute(get);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK)
{
throw new ServiceRulesException(LoginActivity.MSG_SERVER_ERROR);
}
String result = EntityUtils.toString(response.getEntity(), HTTP.UTF_8);
if (result.equals("success!")) {
} else {
throw new ServiceRulesException(LoginActivity.MSG_LOGIN_FAILED);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
注意一点:http协议的get请求方式如下
http://localhost:8080/test/login.do/?参数键=参数值&参数键=参数值 . . . . . .
每个参数的参数键都应与Server端接收到的键一致。
连接server端的client业务代码 (post请求)
与get请求不同,post请求将不会把参数键和参数值直接写在URL之后,而是写在请求的内容中,看得出来,**当传递数据的隐秘性不高、数据量比较小时,适合使用get请求访问server,当数据量比较大(>256bytes),且数据隐秘性比较高时(包含用户的密码等内容),应当考虑使用post请求。**post请求代码如下:
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
HttpConnectionParams.setConnectionTimeout(params, 3000);
HttpConnectionParams.setSoTimeout(params, 3000);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", PlainSocketFactory
.getSocketFactory(), 433));
registry.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
ClientConnectionManager conman = new ThreadSafeClientConnManager(
params, registry);
HttpClient client = new DefaultHttpClient(conman, params);
String url = "http://192.168.1.103:8080/test/login.do";
HttpPost post = new HttpPost(url);
NameValuePair paramLoginName = new BasicNameValuePair("LoginName",loginName);
NameValuePair paramLoginPassword = new BasicNameValuePair(
"LoginPassword", loginPassword);
List<NameValuePair> postParams = new ArrayList<NameValuePair>();
postParams.add(paramLoginName);
postParams.add(paramLoginPassword);
post.setEntity(new UrlEncodedFormEntity(postParams, HTTP.UTF_8));
HttpResponse response = client.execute(post);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
之后的代码与get方式相同。由于参数不能跟在URL后面,post请求使用NameValuePair对象存储需要传递的键值对,接着把这些对象存储在ArrayList中,最后调用GetPost的setEntity方法将list封装成一个实体,这样就可以将post作为execute方法中的参数传递出去了。而无论使用get请求还是post请求,Server端都不用修改代码。
至此第一个demo完成。
demo#2:用户注册
本demo用于将用户名和兴趣爱好数组上传至Server端,大部分内容与demo1相仿,不同点主要数据采用了JSON格式封装,server端需要用json-lib解析,server端所需json-lib.jar包及其依赖包如下:
commons-beanutils-1.8.0.jar
commons-collections-3.2.1.jar
commons-lang-2.5.jar
commons-logging-1.1.1.jar
ezmorph-1.0.6.jar
json-lib-2.4-jdk15.jar
界面布局
一个用户输入(EditText),一个兴趣爱好选择组(RaidioGroup),代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="1"
android:text="@string/text_register" />
<EditText
android:id="@+id/text_register_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:hint="@string/text_register_hint"
android:inputType="text"
android:selectAllOnFocus="true" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="@string/text_interesting" />
<CheckBox
android:id="@+id/checkbox_music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/checkbox_music" />
<CheckBox
android:id="@+id/checkbox_game"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/checkbox_game" />
<CheckBox
android:id="@+id/checkbox_swim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/checkbox_swim" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal" >
<Button
android:id="@+id/button_register_clear"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="#69696969"
android:text="@string/button_register_clear" />
<Button
android:id="@+id/button_register"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="#69696969"
android:text="@string/button_register" />
</LinearLayout>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
所见即所得
由于本demo的activity和demo1相仿,故不再给出。
client端发送请求封装JSON数据、接收响应解析JSON数据
client端采用HttpClient访问网络,使用post请求。
代码如下:
@Override
public void userRegister(String registerName, List<String> interestingList)
throws Exception {
HttpClient client = new DefaultHttpClient();
String uri = "http://localhost:8080/test/getstudent.do";
HttpPost post = new HttpPost(uri);
JSONObject obj = new JSONObject();
obj.put("RegisterName", registerName);
JSONArray arr = new JSONArray();
for (String _string : interestingList) {
arr.put(_string);
}
obj.put("Interesting", arr);
NameValuePair pair = new BasicNameValuePair("Data", obj.toString());
List<NameValuePair> data = new ArrayList<NameValuePair>();
data.add(pair);
post.setEntity(new UrlEncodedFormEntity(data, "UTF-8"));
HttpResponse response = client.execute(post);
int status = response.getStatusLine().getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new ServiceRulesException(RegisterActivity.MSG_SERVER_ERROR);
}
String result = EntityUtils.toString(response.getEntity(), HTTP.UTF_8);
JSONObject _obj = new JSONObject(result);
String _result = _obj.getString("result");
if (result.equals("success!")) {
} else {
String errorMsg = _obj.getString("errorMsg");
throw new ServiceRulesException(errorMsg);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
用户在界面上输入用户名,并勾选兴趣爱好,用户名以字符串的形式传入该方法,兴趣爱好以list的形式传入方法,则JSON格式的数据为如下形式:
var data={"RegisterName":"tom","Interesting":["swim","music","game"]};
JSON格式的数据也是以键值对的形式存在,对象中可以包含对象(JSONObject),也可以包含集合(JSONArray),反过来,集合中可以包含单个对象,也可以包含集合,即集合和对象可以相互嵌套。本例中,最外层是一个对象,里面包含了一个对象和一个简单集合,故封装JSON数据如上面代码所示。
client端接收server端的相应结果,首先用工具类UtilEntity对象把JSON数据解析成字符串,然后从Server端可知,封装的JSON数据格式为
var return={"result":"success!","errorMsg":"register success!" } {"result":"failed!","errorMsg":"register failed!"}
server端接收请求解析JSON数据、返回响应封装JSON数据
下面是Server端的代码:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
request.setCharacterEncoding("UTF-8")
// 解析client端传过来的json数据
String data = request.getParameter("Data")
// System.out.println(data)
JSONObject obj = JSONObject.fromObject(data)
String registerName = obj.getString("RegisterName")
System.out.println(registerName)
JSONArray arr = obj.getJSONArray("Interesting")
if (arr != null) {
for (Object object : arr) {
System.out.println(object)
}
}
// 封装json数据 向client端发送
response.setCharacterEncoding("UTF-8")
response.setContentType("text/html;charset=UTF-8")
PrintWriter out = null
try {
out = response.getWriter()
ResultJSONBean jsonBean = new ResultJSONBean()
// success
jsonBean.setResult("success!")
jsonBean.setErrorMsg("register success!")
// failed
// jsonBean.setResult("failed!")
// jsonBean.setErrorMsg("register failed!")
JSONObject _obj = JSONObject.fromObject(jsonBean)
System.out.println(_obj)
out.write(_obj.toString())
} finally {
out.flush()
out.close()
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
特别说明一下,Server端的JSONObject和JSONArray来自第三方框架json-lib.jar,client端来自org.json包。
至此第二个demo结束。
demo#3: 从server下载一个图片流
本demo将以HttpURLConnection请求server上的一个图片流,
采用HttpURLConnection访问网络步骤如下:
- 创建HttpURLConnection对象;
- 创建URL对象,传入URL对象地址参数;
- 调用URL对象的openConnection方法,打开连接(openConnection);
- 设置连接参数;
- 连接(设置本次连接的参数);
- 接收返回数据 对数据进行操作(connect)。
client端
@Override
public Bitmap getImage() throws Exception {
Bitmap _bitmap = null;
URL url = null;
HttpURLConnection _httpURLConnection = null;
InputStream _inputStream = null;
OutputStream out = null;
byte[] data = null;
try {
Map<String, String> _params = new HashMap<String, String>();
_params.put("id", "1");
data = setPostPassParams(_params).toString().getBytes();
url = new URL("http://127.0.0.1:8080/test/getImage.jpg");
_httpURLConnection = (HttpURLConnection) url.openConnection();
_httpURLConnection.setConnectTimeout(5000);
_httpURLConnection.setReadTimeout(5000);
_httpURLConnection.setDoInput(true);
_httpURLConnection.setDoOutput(true);
_httpURLConnection.setDefaultUseCaches(false);
_httpURLConnection.setRequestMethod("POST");
_httpURLConnection.connect();
int responceCode = _httpURLConnection.getResponseCode();
if (responceCode != HttpURLConnection.HTTP_OK) {
throw new ServiceRulesException("post请求服务器异常");
}
_inputStream = new BufferedInputStream(
_httpURLConnection.getInputStream());
if (_inputStream != null)
_bitmap = BitmapFactory.decodeStream(_inputStream);
out = _httpURLConnection.getOutputStream();
out.write(data);
out.flush();
} finally {
if (_inputStream != null) {
_inputStream.close();
}
if (_httpURLConnection != null) {
_httpURLConnection.disconnect();
}
}
return _bitmap;
}
private static StringBuffer setPostPassParams(Map<String, String> params) {
StringBuffer string = new StringBuffer();
for (Map.Entry<String, String> entry : params.entrySet()) {
try {
string.append(entry.getKey()).append("=")
.append(URLEncoder.encode(entry.getValue(), "UTF-8"))
.append("&");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
string.deleteCharAt(string.length() - 1);
}
return string;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
利用HttpURLConnection访问网络的几点注意事项:
- 该方式的get请求形式与HttpClient类似,都是在URL后加”?”并拼接键值对,中间用”&”隔开。
- post请求的实质也是拼接键值对,只是不能跟在URL后,将键值对拼接后转换成字节数组的形式传递。
- HttpURLConnection对象可以在打开连接后对本次连接做一些配置,比如设置连接server时限,server响应的时限,请求方式,读写server端的权限,是否使用缓冲等。
Server端
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("---get---");
String id = request.getParameter("id");
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(new File("F://图片素材/" + id + ".jpg"));
response.setContentLength(in.available());
response.setContentType("image/jpeg");
out = response.getOutputStream();
byte[] b = new byte[1024];
int read=0;
while((read=in.read(b))!=-1)
{
out.write(b, 0, read);
}
} catch (Exception _e) {
_e.printStackTrace();
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.flush();
out.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
代码首先从服务器磁盘中读取一张图片流到Servlet中,接着传到client端,由于是以流的形式传递,故不能一次传完,否则会比较占用空间,而应使用边存边发的形式。即先从server端读取1kb,再向client传递1kb,由于该图片流可能无法被1kb等分,故应调用outputStream含有三个参数的write方法,该方法的第三个参数将计算读到文件大小的偏移量,所以最后不会出现最后一段流按1kb计算的情况。
至此,demo#3结束。