Android网络编程(一)
网络编程
网络图片查看
网络图片查看器
try {
// 2.把网址字符串封装成一个URL
URL url = new URL(path);
// 3.获取连接对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 4.做参数设置,注意大写
conn.setRequestMethod("GET");
// 5.设置连接超时时间
conn.setConnectTimeout(8000);
// 设置读取超时时间
conn.setReadTimeout(8000);
// 发送请求,建立连接
conn.connect();
// 6. 获取返回码,判断请求返回状态
if (conn.getResponseCode() == 200) {
// 请求成功
// 7.拿到服务器返回的流,里面的数据就是客户端请求的内容
InputStream is = conn.getInputStream();
// 在确定流中数据是图片的情况下可以使用Google提供的API直接生成图片,就不需要自己读取流了
//====================================================================================================================
//从流里读取数据就是在下载数据,如果网速过慢就会造成主线程阻塞(主线程阻塞在2.3可以,但是4.0+版本的Android会抛出异常)
Bitmap bm = BitmapFactory.decodeStream(is);
//====================================================================================================================
ImageView iv = (ImageView) findViewById(R.id.imageView1);
iv.setImageBitmap(bm);
} else {
Toast.makeText(this, "请求失败", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
以上代码,注意细节~
-
(应用)主线程阻塞
- 界面会停止刷新
- 应用停止响应用户任何操作
- 影响用户体验
-
规范
- 耗时的操作不要放在主线程((4.0以后的版本强制要求不可以放在主线程,要不就报错)放在子线程里面就可以了)
ANR异常
- application not responding(应用无响应)
- 主线程长时间阻塞,会爆出ANR异常
只有主线程才可以刷新UI(View)
消息队列
问题:怎么用子线程中处理过的数据去刷新UI
MessageQueue 消息队列
Looper 轮询器(会一直不停的检测消息队列里面是否有消息) * 消息队列没有数据,就什么都不干 * 消息队列有消息,就扔给Handler去处理
Handler * handleMessage()方法用来处理消息 - 重写该方法来处理我们的消息
在主线程创建时会同时创建MessageQueue和Looper对象,但是Handler对象在程序员需要使用是,(程序员)自行创建。 只要消息队列有消息,handleMessage()方法就会在主线程调用
子线程需要刷新UI,只需要往主线程的消息队列中发送一条数据即可
栗子:
public class MainActivity extends Activity {
// 创建消息处理器
Handler handler = new Handler() {
// 重写handleMessage方法
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0:
ImageView iv = (ImageView) findViewById(R.id.imageView1);
iv.setImageBitmap((Bitmap) msg.obj);
break;
case 1:
Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT)
.show();
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View V) {
Thread t = new Thread() {
@Override
public void run() {
// 网址
String path = "http://192.168.15.27:8080/dd.jpg";
try {
// 2.把网址字符串封装成一个URL
URL url = new URL(path);
// 3.获取连接对象
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 4.做参数设置,注意大写
conn.setRequestMethod("GET");
// 5.设置连接超时时间
conn.setConnectTimeout(8000);
// 设置读取超时时间
conn.setReadTimeout(8000);
// 发送请求,建立连接
conn.connect();
// 6. 获取返回码,判断请求返回状态
if (conn.getResponseCode() == 200) {
// 请求成功
// 7.拿到服务器返回的流,里面的数据就是客户端请求的内容
InputStream is = conn.getInputStream();
// 在确定流中数据是图片的情况下可以使用Google提供的API直接生成图片,就不需要自己读取流了
Bitmap bm = BitmapFactory.decodeStream(is);
// ImageView iv = (ImageView)
// findViewById(R.id.imageView1);
// iv.setImageBitmap(bm);
// 发送消息至主线程消息队列
Message msg = new Message();
// 利用消息对象携带数据
msg.obj = bm;
// 设置状态标识(这里设置的是成功获取返回0)
msg.what = 0;
handler.sendMessage(msg);
} else {
// Message msg = new Message();
// msg.what = 0;
// handler.sendMessage(msg);
// 不需要携带数据时也可以发送空消息,参数what
handler.sendEmptyMessage(1);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
}
数据缓存到本地
看代码
public class MainActivity extends Activity {
// 创建消息处理器
Handler handler = new Handler() {
// 重写handleMessage方法
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0:
ImageView iv = (ImageView) findViewById(R.id.imageView1);
iv.setImageBitmap((Bitmap) msg.obj);
break;
case 1:
Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT)
.show();
break;
}
};
};
…………
public void click(View V) {
// 网址
final String path = "http://192.168.15.27:8080/dd.jpg";
final File file = new File(getCacheDir(), getFileName(path));
// 判断文件是否已将缓存到本地
if (!file.exists()) {
// 是 直接读取显示
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
ImageView iv = (ImageView) findViewById(R.id.imageView1);
iv.setImageBitmap(bm);
} else {
Thread t = new Thread() {
@Override
public void run() {
try {
// 2.把网址字符串封装成一个URL
// 3.获取连接对象
// 4.做参数设置,注意大写
// 5.设置连接超时时间
// 设置读取超时时间
// 发送请求,建立连接
// 6. 获取返回码,判断请求返回状态
if (conn.getResponseCode() == 200) {
// 请求成功
// 7.拿到服务器返回的流,里面的数据就是客户端请求的内容
InputStream is = conn.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
fos.write(b, 0, len);
}
fos.close();
//生成图片
Bitmap bm = BitmapFactory.decodeFile(file
.getAbsolutePath());
// 在确定流中数据是图片的情况下可以使用Google提供的API直接生成图片,就不需要自己读取流了
// Bitmap bm = BitmapFactory.decodeStream(is);
// ImageView iv = (ImageView)
// 发送消息至主线程消息队列
// 利用消息对象携带数据
// 设置状态标识(这里设置的是成功获取返回0)
………………
} else {
handler.sendEmptyMessage(1);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
}
/**
* 获取文件名
*
* @param path
* @return
*/
public String getFileName(String path) {
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
获取开源代码的网站
- code.google.com
- github.com
- 在github搜索smart-image-view
- 下载开源项目smart-image-view
-
使用自定义组件时,标签名字要写包名
<com.loopj.android.image.SmartImageView/>
-
SmartImageView的使用
SmartImageView siv = (SmartImageView) findViewById(R.id.siv); siv.setImageUrl("http://192.168.1.102:8080/dd.jpg");
使用第三方ImageView(自己试)
测试用第三方资源:android-smart-image-view-master.zip
Html源文件查看器
-
发送GET请求
URL url = new URL(path); //获取连接对象 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //设置连接属性 conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //建立连接,获取响应吗 if(conn.getResponseCode() == 200){ }
-
获取服务器返回的流,从流中把html源码读取出来
byte[] b = new byte[1024]; int len = 0; ByteArrayOutputStream bos = new ByteArrayOutputStream(); while((len = is.read(b)) != -1){ //把读到的字节先写入字节数组输出流中存起来 bos.write(b, 0, len); } //把字节数组输出流中的内容转换成字符串 //默认使用utf-8 text = new String(bos.toByteArray());
新闻客户端实现
注意:线程异步问题
因为主线程不能执行网络下载等其他耗时的操作,而必须使用子线程去实现网络下载等操作,但是要注意这里面的线程异步问题。 如:主线程在刷新UI是调用了子线程没有处理完毕内容导致的空指针等异常
ViewHolder * 避免了重复生成大量的view对象 * 先把布局文件中的所有的组件封装到ViewHolder对象中 * ViewHolder的对象会与View一起被缓存起来 * 需要的时候直接xxx
乱码问题的解决
解决乱码问题非常简单: 统一两遍的编码集。 android下默认编码 utf-8
客户端: 中文和特殊字符 URLEncoder.encode(); 服务器端: 默认的编码 iso-8859-1 和 本地编码(gbk)
发现 数据
�� gbk->utf-8 ??? utf-8 -> iso-8859-1 编码不存在 锟脚达拷锟斤拷 无药可救 删除了重写。
提交数据(乱码问题)
GET方式提交数据
-
get方式提交的数据是直接拼接在url的末尾
final String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + name + "&pass=" + pass;
-
发送get请求,代码和之前一样
URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(5000); conn.setConnectTimeout(5000); if(conn.getResponseCode() == 200){ }
-
浏览器在发送请求携带数据时会对数据进行URL编码,我们写代码时也需要为中文进行URL编码
String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass;
GET方式提交数据的缺点: * 组拼http的URL的方式不安全。 * GET方式提交数据,对数据的长度是有要求的。http规范最大长度4K。
POST方式提交数据
- post提交数据是用流写给服务器的
-
协议头中多了两个属性
- Content-Type: application/x-www-form-urlencoded,描述提交的数据的mimetype
-
Content-Length: 32,描述提交的数据的长度
//给请求头添加post多出来的两个属性 String data = "name=" + URLEncoder.encode(name) + "&pass=" + pass; conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setRequestProperty("Content-Length", data.length() + "");
-
设置允许打开post请求的流
conn.setDoOutput(true);
-
获取连接对象的输出流,往流里写要提交给服务器的数据
OutputStream os = conn.getOutputStream(); os.write(data.getBytes());
POST方式提交数据的优缺点: * 代码写起来麻烦 注意4个细节。
优点: * 安全。 * 提交数据的长度没有限制。