Android基础第六天
移动互联网时代哪个app 不需要跟服务器进行交互呢?Android 给服务器提交数据的方式都有哪些呢?这正是今天讨论的话题,每一节介绍一种提交数据的方式,但是Android 提交数据的方式绝非仅仅这三种,这里给出的只是最基础的3种方式。将这些基础的方式学会了,其他再高级的方式对我们来说也不过是小菜一碟了。
由于这三种提交数据的方式都是由QQ登陆这个案列来进行讲解的,所以这里我们先将QQ登陆的案列需求给出。
QQ登陆-案例
首先看下整体界面的效果图:
案例需求:
界面整体采用垂直的线性布局,前两行为两个EditText,分别代表用户名和密码;第三行采用一个按钮Button,点击后采用get方式或post方式进行提交数据。数据提交成功后,服务器会有返回值,并将返回值通过最后一行登陆状态文本显示出来。
服务器搭建:
服务端采用Servlet 编写,名为LoginServlet,并使用Tomcat 作为其服务器。因为服务
器不是本文的重点,因此这里只简单介绍。以下是LoginServlet处理的核心代码:
/** tomcat容器默认的码表是 iso-8859-1的码表
* Servlet implementation classLoginServlet
*/
@WebServlet("/LoginServlet")
publicclass LoginServlet extends HttpServlet {
privatestaticfinallongserialVersionUID = 1L;
protectedvoid doGet(HttpServletRequest request, HttpServletResponseresponse) throws ServletException, IOException {
String qq = request.getParameter("qq");//采用的编码是iso-8859-1
String pwd = request.getParameter("pwd");
System.out.println("qq:"+new String(qq.getBytes("iso-8859-1"),"utf-8"));
System.out.println("pwd:"+new String(pwd.getBytes("iso-8859-1"),"utf-8"));
//查询数据库看qq和密码是否存在
if("10000".equals(qq)&&"abcde".equals(pwd)){
//tomcat容器如果发现字符串不识别就默认采用本地码表
response.getOutputStream().write("登陆成功".getBytes("utf-8"));
}else{
response.getOutputStream().write("登陆失败".getBytes("utf-8"));
}
}
@Override
protectedvoid doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("post过来的请求");
doGet(req, resp);
}
}
布局编写
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<EditText
android:text=""
android:id="@+id/et_qq"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入qq号码" />
<EditText
android:id="@+id/et_pwd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:inputType="textPassword"/>
<Button
android:onClick="login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登陆" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_status"
android:text="登陆状态:"
/>
</LinearLayout>
添加权限
凡是网络访问的操作,必须添加如下权限。
<uses-permissionandroid:name="android.permission.INTERNET"/>
使用HttpURLConnection 提交数据
Get请求
代码如下:
publicclass MainActivity extends Activity {
protectedstaticfinalintSUCCESS = 1;
protectedstaticfinalintERROR = 2;
private EditText et_qq;
private EditText et_pwd;
private TextView tv_status;
private Handler handler = new Handler(){
publicvoid handleMessage(android.os.Messagemsg) {
switch (msg.what) {
caseERROR:
Toast.makeText(MainActivity.this, "请求失败,请检查网络和服务器的状态", 0).show();
break;
caseSUCCESS:
Toast.makeText(MainActivity.this, (String)msg.obj, 0).show();
tv_status.setText("当前状态:"+ (String)msg.obj);
break;
}
};
};
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_qq = (EditText) findViewById(R.id.et_qq);
et_pwd = (EditText) findViewById(R.id.et_pwd);
tv_status = (TextView) findViewById(R.id.tv_status);
}
publicvoid login(View view){
//http://localhost:8080/web/LoginServlet?qq=10000&pwd=abcde
final String qq = et_qq.getText().toString().trim();
final String pwd = et_pwd.getText().toString().trim();
if(TextUtils.isEmpty(qq)||TextUtils.isEmpty(pwd)){
Toast.makeText(this, "qq或者密码不能为空", 0).show();
return;
}
new Thread(){
publicvoid run() {
try {
Stringpath = "http://192.168.1.103:8080/web/LoginServlet?qq="
+URLEncoder.encode(qq,"utf-8")+"&pwd="
+URLEncoder.encode(pwd,"utf-8");
URL url = new URL(path);
HttpURLConnectionconn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
int code = conn.getResponseCode();
if(code == 200){
InputStream is =conn.getInputStream();
String result =StreamTools.readStream(is);
Message msg =Message.obtain();
msg.what = SUCCESS;
msg.obj = result;
handler.sendMessage(msg);
}else{
Message msg =Message.obtain();
msg.what = ERROR;
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
Message msg =Message.obtain();
msg.what = ERROR;
handler.sendMessage(msg);
}
};
}.start();
}
}
上述涉及到控件初始化,以及handler消息处理的一些代码在这里先给贴了出来,在后面讲到其他请求方式上传数据的时候由于这些代码都是重复的就没必要再继续贴出来了。
Post请求
在子线程的run方法中是我们写请求网络的代码,这里我们使用post的方式操作代码如下:
URL url = new URL(path);
HttpURLConnectionconn = (HttpURLConnection) url.openConnection();
//1.设置请求方式为POST
conn.setRequestMethod("POST"); //注意单词必须大写.
conn.setConnectTimeout(5000);
//2.设置http请求数据的类型为表单类型
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
//3.设置给服务器写的数据的长度
//qq=10000&pwd=abcde
String data = "qq="+URLEncoder.encode(qq, "utf-8")+"&pwd="+URLEncoder.encode(pwd, "utf-8");
conn.setRequestProperty("Content-Length", String.valueOf(data.length()));
//4.记得指定要给服务器写数据
conn.setDoOutput(true);
//5.开始向服务器写数据
conn.getOutputStream().write(data.getBytes());
int code = conn.getResponseCode();
总结:
在http 协议中,get 请求协议没有请求体,只有请求头和请求行,因此如果想使用get 方式提交数据,只能通过在url 后面加上要提交的参数。这也意味着get 方式只能提交比较小的参数。
如果get 方式提交的参数有特殊字符或者中文字符那么必须对其进行URL 编码,不然会导致服务器接收到的数据乱码。
对于post 请求,提交的数据位于请求体中,因此需要设置connection.setDoOutput(true);参数,该参数设置后才允许将提交的数据通过输出流的形式提交。不过需要说明的是虽然采用post 方式提交,依然需要将参数进行URL 编码。
其实当我们使用浏览器进行form 表单提交的时候,浏览器内部已经帮我们实现了URL 编码。因为我们这里是使用代码模拟浏览器的提交数据过程,因此需要使用代码特殊处理。
使用HttpClient 提交数据
HttpClient是Apache Jakarta Common 下的子项目,提供了高效的、最新的、功能丰富的支持HTTP 协议的客户端编程工具包,并且它支持HTTP 协议最新的版本。HttpClient 被内置到Android SDK 中,因此可以在不添加任何额外jar 包情况下,直接使用。
Get方式提交
String path = "http://192.168.1.103:8080/web/LoginServlet?qq="
+URLEncoder.encode(qq, "utf-8")+"&pwd="
+URLEncoder.encode(pwd,"utf-8");
//1.打开浏览器
HttpClientclient = new DefaultHttpClient();
//2.输入地址或者数据
HttpGethttpGet = new HttpGet(path);
//3.敲回车
HttpResponseresponse = client.execute(httpGet);
//获取状态码
int code =response.getStatusLine().getStatusCode();
Post方式提交
String path = "http://192.168.1.103:8080/web/LoginServlet";
//1.打开浏览器
HttpClientclient = new DefaultHttpClient();
//2.输入地址或者数据
HttpPosthttpPost = new HttpPost(path);
List<NameValuePair>parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("qq", qq));
parameters.add(new BasicNameValuePair("pwd", pwd));
httpPost.setEntity(new UrlEncodedFormEntity(parameters, "utf-8"));
//3.敲回车
HttpResponseresponse = client.execute(httpPost);
//获取状态码
int code =response.getStatusLine().getStatusCode();
HttpClient的方式提交数据是面向对象的方式去设计,所以在理解上会更加容易记忆些。
使用AsyncHttpClient 框架提交数据
AsyncHttpClient 是开源免费的基于HttpClient的网络请求框架,其源码托管在githu 上。下载地址:
https://github.com/loopj/android-async-http
下图所示为AsyncHttpClient 在github 的网页,大家可以点击右边绿色的按钮,将源码下载下来
然后将下载好的压缩包解压,把src 目录中源码拷贝到自己的工程的src 目录下
Get方式提交
//请求路径
Stringpath = "http://192.168.1.103:8080/web/LoginServlet?qq="
+URLEncoder.encode(qq)+"&pwd="+URLEncoder.encode(pwd);
AsyncHttpClientclient = new AsyncHttpClient();
client.get(path,new AsyncHttpResponseHandler() {
@Override
publicvoid onSuccess(int statusCode, Header[] headers,
byte[] responseBody) {
//请求成功
tv_status.setText(new String(responseBody));
}
@Override
publicvoid onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
//请求失败
tv_status.setText("http请求失败"+new String(responseBody));
}
});
Post方式提交
String path = "http://192.168.1.103:8080/web/LoginServlet";
AsyncHttpClientclient = new AsyncHttpClient();
RequestParamsparams = new RequestParams();
params.put("qq", qq);
params.put("pwd", pwd);
client.post(path,params, new AsyncHttpResponseHandler(){
@Override
publicvoid onSuccess(int statusCode, Header[] headers,
byte[] responseBody) {
tv_status.setText("登陆结果:"+new String(responseBody));
}
@Override
publicvoid onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
tv_status.setText("请求失败请检查网络");
}
});
文件上传
需求:在文本框中输入文件路径,点击上传按钮,进行上传到服务器
界面实现如下:
布局代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<EditText
android:text=""
android:id="@+id/et_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入要上传的文件路径"/>
<Button
android:layout_below="@id/et_path"
android:text="上传"
android:onClick="upload"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</RelativeLayout>
MainActivity中实现的具体代码:
publicclass MainActivity extends Activity {
private EditText et_path;
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_path = (EditText) findViewById(R.id.et_path);
}
publicvoid upload(View view){
String path = et_path.getText().toString().trim();
File file = new File(path);
if(file.exists()&&file.length()>0){
//上传
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
try {
params.put("file", file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
client.post("http://192.168.1.103:8080/web/UploadServlet", params, new AsyncHttpResponseHandler() {
@Override
publicvoid onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Toast.makeText(MainActivity.this, "上传成功", 0).show();
}
@Override
publicvoid onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
Toast.makeText(MainActivity.this, "上传失败", 0).show();
}
});
}else{
Toast.makeText(this, "请检查文件是否存在", 0).show();
}
}
}