Android中许多应用需要从网络上获取内容,而如何从网络上获取内容,这就需要用到网络编程的知识。下面是Android中关于网络编程的一些知识。
一、使用httpURLConnection对象访问网络
使用UrlConnection请求一个url地址获取内容的一般步骤如下:
//1.创建一个Url对象
URL url = new URL(url_str);
//2.获取一个UrlConnection对象
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
//3.为UrlConnection对象设置一些请求的参数,请求方式,连接的超时时间
connection.setRequestMethod("GET");//设置请求方式
connection.setConnectTimeout(1000*10);//设置超时时间
//4.在获取url请求的数据前需要判断响应码,200 :成功,206:访问部分数据成功 300:跳转或重定向 400:错误 500:服务器异常
int code = connection.getResponseCode();
//5.获取有效数据,并将获取的流数据解析成String
InputStream inputStream = connection.getInputStream();
String result = StreamUtils.streamToString(inputStream);
注意访问网络需要设置android.permission.INTERNET 这个权限。
如果直接在主线程中做网络请求的话,可能会ANR:application not response 应用无响应的错误,这是因为Android中的耗时操作(请求网络,大文件的拷贝,数据库的操作等)需要在子线程中去做,但是在子线程中不能够更新控件(如Toast的展示及其他界面的修改操作)。这是可能就会产生问题,当即需要请求网络有需要更新UI时该怎么办呢?
这是后就需要handler了。
二、handler的使用
handler可以在子线程和主线程之间传递消息,当子线程中需要更新UI时,只需要把要更新的内容用handler传递到主线程中,在主线程中更新即可。下面来看一下handler的用法。
1.主线程中创建一个Handler
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
};
};
2.重写handler的handlermessage方法
3.子线程中创建一个Message对象,将获取的数据绑定给msg
Message msg = new Message();
//另一种方式:Message msg = Messge.obtain;
msg.obj = result;
4.主线程中的handler对象在子线程中将message发送给主线程
handler.sendMessage(msg);
5.主线程中handlermessage方法接受子线程发来的数据,就可以做更新UI的操作。
下面来看一段示例代码:
private EditText et_url;
private TextView tv_source;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
//一、获取控件
et_url = (EditText) findViewById(R.id.et_url);
Button bt_looksource = (Button) findViewById(R.id.bt_looksource);
tv_source = (TextView) findViewById(R.id.tv_source);
//二、绑定点击事件
bt_looksource.setOnClickListener(this);
System.out.println("oncreate方法线程:"+Thread.currentThread().getName());
}
//使用handler在子线程和主线程之间传递消息的步骤
//a.在主线程中创建一个handler对象
private Handler handler = new Handler(){
//b重写handler对象中的handlerMessage方法,用来接收子线程中发来的消息
@Override
public void handleMessage(Message msg) {
//e.接收子线程发来的消息,处理消息
String result = (String)msg.obj;
System.out.println("获取到信息");
//五、更新UI
tv_source.setText(result);
}
};
@Override
public void onClick(View view) {
//ctrl+alt+t快速添加try catch块
try {
//三、获取输入的url地址
final String url_str = et_url.getText().toString().trim();
if(TextUtils.isEmpty(url_str)){
//???
Toast.makeText(mContext,"url不能为空",Toast.LENGTH_SHORT).show();
return ;
}
System.out.println("onclick方法线程:"+Thread.currentThread().getName());
//四、创建一个子线程做网络请求
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("oclick方法runnable线程:"+Thread.currentThread().getName());
//使用UrlConnection请求一个url地址获取内容
//1.根据url地址创建一个URL对象
URL url = new URL(url_str);
//2.获取一个URLConnection对象
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(1000*10);
System.out.println("第一次请求的请求码"+connection.getResponseCode());
//3.获取头部信息,获取到重定向后的地址(为了解决返回码为302的问题)
String location = connection.getHeaderField("Location");
System.out.println("location "+location+"");
//4.为URLConnection设置请求参数(请求方式,连接的超时时间等)
url = new URL(location);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(1000*10);
//4.判断响应码
int code = connection.getResponseCode();
System.out.println(code);
if(code == 200){
//5.获取有效数据,并将数据解析为字符串
InputStream inputStream = connection.getInputStream();
String result = StreamUtils.streamToString(inputStream);
//c.子线程中创建一个Message对象,用于将子线程中的数传递给主线程
Message message = new Message();
message.obj = result;
//d.使用handler将数据从子线程传递到主线程
handler.sendMessage(message);
}else{
System.out.println("请求url失败");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
public class StreamUtils {
public static String streamToString(InputStream in){
String result = "";
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = 0;
while((length = in.read(buffer)) != -1){
out.write(buffer,0,length);
out.flush();
}
result = out.toString();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
在子线程中更新UI的其他集中方法:
1.使用activity的runOnUiThread方法更新ui,无论当前线程是否是主线程,都将在主线程执行。注意runOnUiThread不能在静态类中使用
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_simple.setText("我被更新了");
}
});
2.使用handler直接post到主线程,handler需要在主线程创建
//延迟多少毫米执行runnable。
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
tv_simple.setText("我被更新了");
}
}, 1000*5);
还有需要注意的是不能在子线程中更新UI并不是绝对的。以下集中情形可以在子线程中更新UI:SurfaceView :多媒体视频播放 ,可以在子线程中更新UI; Progress(进度)相关的控件:也是可以在子线程中更新Ui;审计机制:activity完全显示的时候审计机制才会去检测子线程有没有更新Ui。
三、get和post请求
在请求网络时需要设置请求方式,常用的请求方式有两种post和get。get的请求内容是跟在URL地址后面(不安全且大小有限制但是简单),post的请求内容是放在请求体当中(大小无限制,安全,但是繁琐)。使用post请求相比get请求还需要设置一下内容:
请求头的设置(不是必需的)
openConnection.setRequestProperty("Content-Length", body.length()+"");
openConnection.setRequestProperty("Cache-Control", "max-age=0");
openConnection.setRequestProperty("Origin", "http://192.168.13.83:8080");
请求内容的设置(必需)
//设置UrlConnection可以写请求的内容
openConnection.setDoOutput(true);
//获取一个outputstream,并将内容写入该流
openConnection.getOutputStream().write(body.getBytes());
使用post请求方式时还要注意编码要一致,否则可能产生乱码。可以使用URLEncode对数据进行编码。
四、用httpClient做网络请求
直接上代码,get方式:
try{
String path = "http://192.168.13.83:8080/itheima74/servlet/LoginServlet?username="+URLEncoder.encode(username,"utf-8")+"&pwd="+URLEncoder.encode(password,"utf-8");
//1.创建一个httpClient对象
HttpClient httpclient = new DefaultHttpClient();
//2.设置请求的方式
HttpGet httpget = new HttpGet(path);
//3.执行一个http请求
HttpResponse response = httpclient.execute(httpget);
//4.获取请求的状态码,
StatusLine statusLine = response.getStatusLine();
int code = statusLine.getStatusCode();
//5.判断状态码后获取内容
if(code == 200){
HttpEntity entity = response.getEntity();//获取实体内容,中封装的有http请求返回的流信息
InputStream inputStream = entity.getContent();
//将流信息转换成字符串
String result = StreamUtils.streamToString(inputStream);
Message msg = Message.obtain();
msg.what = 1;
msg.obj = result;
handler.sendMessage(msg);
}
}catch (Exception e) {
e.printStackTrace();
}
post方式:
String path = "http://192.168.13.83:8080/itheima74/servlet/LoginServlet";
AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put("username", username);
params.put("pwd", password);
//url: parmas:请求时携带的参数信息 responseHandler:是一个匿名内部类接受成功过失败
asyncHttpClient.post(path, params, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
//statusCode:状态码 headers:头信息 responseBody:返回的内容,返回的实体
//判断状态码
if(statusCode == 200){
//获取结果
try {
String result = new String(responseBody,"utf-8");
Toast.makeText(context, result, 0).show();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
}
});