一.目标
将app客户端用户填写的地点和描述信息上传到服务器。
二.流程
1.界面设计,两个TextView用来提醒用户该输入什么信息,两个EditText让用户输入文本信息,一个Button用来提交用户输入的信息到服务器
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="地点" />
<EditText
android:id="@+id/addressEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="点击输入地点"
android:inputType="text" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="描述" />
<EditText
android:id="@+id/depictEdit"
android:layout_width="342dp"
android:layout_height="147dp"
android:hint="点击输入描述"
android:inputType="text"
/>
<Button
android:id="@+id/XianQingTiJiao"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:text="提交" />
2.给予app相应权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
3.初始化控件并通过id找到对应控件
private Button XianQingTiJiao;
private EditText addressEdit,depictEdit;
XianQingTiJiao = (Button) findViewById(R.id.XianQingTiJiao);
addressEdit = (EditText) findViewById(R.id.addressEdit);
depictEdit = (EditText) findViewById(R.id.depictEdit);
4.给提交按钮添加点击事件
XianQingTiJiao.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//这里面的代码参照第五步
}
}
5.在点击事件里开启一个线程,用来处理耗时操作
new Thread(new Runnable() {
@Override
public void run() {
//这里的代码参照第六步
}
}).start();
6.在线程里重写的run方法体里
1)定义一个字符串用来存放要访问的服务器程序地址
String url = "http://2k4154032o.qicp.vip:59770/MyServlet"+ "/XianQingShangBao";
2)定义一个map对象(暂时理解为一本字典)用来存放待传的参数,第一个参数类似于书签,第二个参数是真正的数据;分别将用户输入的两个参数放到map里面
Map<String, String> params = new HashMap<String, String>();
String address = addressEdit.getText().toString();
String depict = depictEdit.getText().toString();
params.put("address", address);
params.put("depict",depict);
3)定义一个方法getContextByHttp,目的是通过传入两个参数(服务器程序地址,待传输的map对象)以求获得从服务器传回的信息(是否上传成功)
public static String getContextByHttp(String urlStr,Map<String,String> parms){
//这里的代码见4)a步
try{
//这里的代码见4)b-j步
}
catch (Exception e){
return e.toString();
}
return sb.toString();
}
4)在方法体里写如下代码
a.新建一个可变字符床sb用来存储服务器传回的信息
StringBuilder sb = new StringBuilder();
b.定义一个url对象,将服务器地址传给它
URL url = new URL(urlStr);
c.新建一个HttpURLConnection对象,HttpURLConnection可用于向指定网站发送GET请求、POST请求,可以理解为一座桥梁,连接app与服务器的通讯
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
d.设置HttpURLConnection这道桥梁的参数
//设置连接的方式是post:post与get有什么区别
connection.setRequestMethod("POST");
//允许读取和连接最长时间为5秒
connection.setReadTimeout(5000);
connection.setConnectTimeout(5000);
//允许输入输出
connection.setDoInput(true);
connection.setDoOutput(true);
//设置本次连接是否自动处理重定向。
//设置成true,系统自动处理重定向;设置成false,则需要自己从http reply中分析新的url
//自己重新连接。
connection.setInstanceFollowRedirects(true);
e.定义一个OutputStream对象,getOutputStream方法得到的是一个输出流,客户端的上的getOutputStream方法得到的输出流其实就是发送给服务器端的数据。类似在connection这座桥梁行驶的物流车。
OutputStream outputStream = connection.getOutputStream();
f.定义一个BufferedWriter对象,缓存区,类似于快递车,什么是缓冲区?简单理解,缓冲区就是一块特殊的内存区域。为什么要使用缓冲区?因为如果一个程序频繁操作一个资源(文件或数据库),则性能会很低,为了提升性能,就可以将一部分数据暂时读入到内存的一块区域之中,以后直接从此区域读取数据即可,因为读取内存的速度要快于读取磁盘中文件内容的速度。
OutputStreamWriter是字符流对象,目的是在outputStream里用utf编码方式写入字符到outputStream里。因为用户输入的是字符,不好直接写到字节里面,通过先写到 OutputStreamWriter字符流对象里,然后它再按照指定方式转述给字节流。
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
g. 先定义一个方法getStringFromOutput将两个待输出参数的格式转换一下,然后写到 BufferedWriter对象中
private static String getStringFromOutput(Map<String,String> map) throws UnsupportedEncodingException {
StringBuilder sb = new StringBuilder();
boolean isFirst = true;
for(Map.Entry<String,String> entry:map.entrySet()){
if(isFirst)
isFirst = false;
else
sb.append("&");
sb.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
sb.append("=");
sb.append(URLEncoder.encode(entry.getValue(),"UTF-8"));
}
return sb.toString();
}
writer.write(getStringFromOutput(parms));
h.关闭此字符缓冲输出流,但要先刷新它;同时关闭输出流;
关闭字符流时会强制性地将缓冲区中的内容进行输出,但是如果没有关闭,缓冲区中的内容是无法输出的。此时已经把两个参数送走了。
writer.flush();
//关闭此字符缓冲输出流,但要先刷新它
writer.close();
outputStream.close();
i.判断是否连接成功,如果成功就用BufferedReader读取从服务器传来的InputStream,由于服务器传来的是字节,因此还要用InputStreamReader将字节转字符;如果连接失败就返回失败的原因。
if(connection.getResponseCode() == HttpURLConnection.HTTP_OK){
//如果连接上服务器端,客户端程序使用BufferedReader读取connection传来的数据
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
//在这里如果改为字符串数组的话,得知道数组长度
String temp;
//如果传入的数据不为空,则可变字符串添加该数据
while((temp = reader.readLine()) != null){
sb.append(temp);
break;
}
reader.close();
}else{
return "connection error:" + connection.getResponseCode();
}
j.断开连接
connection.disconnect();
5)定义一个字符串用来保存从服务器上反馈的结果
String result = HttpUtilsHttpURLConnection.getContextByHttp(url, params);
6)由于第五步的结果在子线程里,想要用它来更新主线程必须通过handler(消息处理者),而handler 传递消息的格式是Message,Message中放的是Bundle类型的数据。Bundle是一个载体,可以存放基本数据类型、对象等内容,相当于一辆汽车,可以装载很多东西,然后运到需要的地方
Message msg = new Message();
//指定message 的id
msg.what = 0x13;
//Bundle主要用于传递数据;它保存的数据,是以key-value(键值对)的形式存在的。
Bundle data = new Bundle();
data.putString("result", result);
//mes利用Bundle传递数据
msg.setData(data);
//发送信息到主线程
hander.sendMessage(msg);
7)定义一个消息发送器
Handler hander = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0x13) {
Bundle data = msg.getData();
String key = data.getString("result");//得到json返回的json
try {
if ("success".equals(key)) {
Toast.makeText(XianQingShangBao.this, "上传成功", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(XianQingShangBao.this, "上传失败", Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
};