1.从网络上获取数据(重点)
1、发送请求:GET
2、接收服务器端返回的响应数据
使用代码实现的步骤:
1、创建URL,打开一个HTTP的连接;
2、设置请求头信息:GET(GET、POST)
3、接收服务器端返回的响应数据,响应码:200 ok,404没有找到资源 ,503服务器端内部错误
4、把接收的二进制数据转换成图片
使用代码实现的步骤:
1、创建URL,打开一个HTTP的连接;
2、设置请求头信息:GET(GET、POST)
3、接收服务器端返回的响应数据,响应码:200 ok,404没有找到资源 ,503服务器端内部错误
4、把接收的二进制数据转换成图片
模版代码:
1、创建一个URL对象,打开一个HTTP连接
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
2、设置请求头信息:GET(GET、POST)
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
3、接收服务器端返回的响应数据,响应码:200 ok,404没有找到资源 ,503服务器端内部错误
int code = conn.getResponseCode();
if(code == 200){
InputStream is = conn.getInputStream();
}
2.访问网络的Android应用都必须加上访问互联网的权限:
<uses-permission android:name="android.permission.INTERNET"/>
网络在主线程上的异常: android.os.NetworkOnMainThreadException
从Androi4.0开始,google更加UI界面运行的流畅性,强制要求访问网络的操作不能在主线程中进行,只能在子线程中进行。
activity中的oncreate方法和单击事件的方法都是运行在主线程中的。
只有创建UI界面的那个线程才能修改UI: Only the original thread that created a view hierarchy can touch its views.
主线程(UI线程),只有主线程才能修改UI。如果子线程修改UI,系统验证当前线程是不是主线程,如果不是主线程,就会终止运行。
消息处理(重点)
1、在主线程中创建handler
private Handler handler = new Handler(){
//接收消息并处理消息
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
}
};
//2、在子线程中得到handler的引用,并发送消息给主线程
Message msg = new Message();
msg.obj = bm;
handler.sendMessage(msg);
//3、在主线程中修改UI
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
Bitmap bm = (Bitmap) msg.obj;
iv.setImageBitmap(bm);
}
消息处理机制原理
所有使用UI界面的操作系统,后台都运行着一个死循环,在不停的监听和接收用户发出的指令,一旦接收指令就立即执行。
Looper Message Handler三者之间的关系:(重点)
当我们的Android应用程序的进程一创建的时候,系统就给这个进程提供了一个Looper,Looper是一个死循环,它内部维护这个一个消息队列,Loop不停地从消息队列中取消息(Message),取到消息就发送给了Handler,最后Handler根据接收到的消息去修改UI。
3.消息处理常用的API:
//第一个API
runOnUiThread
//第二个API
handler.postDelayed
//第三个API
//handler.postAtTime
网络请求数据
1、使用GET方式
使用GET方式向服务器端提交数据,是把提交的参数组频道url的后面:
http://192.168.12.28:8080/web/servlet/LoginServlet?username=232&password=sdfsd
最大提交4k,windows上最多允许提交1k。
2、使用POST方式
与GET方式的区别:
1、设置请求方式为POST
connection.setRequestMethod("POST");
connection.setConnectTimeout(5000);
2、设置表单类型及数据长度
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", data.length()+"");
3、设置连接允许向服务器写数据
connection.setDoOutput(true);
connection.getOutputStream().write(data.getBytes());
使用HttpURLConnection方式发送Get请求
public class MainActivity extends Activity {
private EditText et_qq;
private EditText et_pwd;
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
String result = (String) msg.obj;
Toast.makeText(MainActivity.this, result, 0).show();
};
};
@Override
protected void 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);
}
public void login(View view){
final String qq = et_qq.getText().toString().trim();
final String pwd = et_pwd.getText().toString().trim();
final String urlStr = "http://192.168.12.28:8080/web/servlet/LoginServlet";
if(TextUtils.isEmpty(qq) || TextUtils.isEmpty(pwd)){
Toast.makeText(this, "请输入qq号码或者密码", 0).show();
return;
}else{
String data = "?username="+URLEncoder.encode(qq)+"&password="+URLEncoder.encode(pwd);
final String path = urlStr + data;
new Thread(){
public void run() {
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
int code = connection.getResponseCode();
if(code == 200){
InputStream is = connection.getInputStream();
String result = StreamTools.readStream(is);
Message msg= Message.obtain();
msg.obj = result;
handler.sendMessage(msg);
}else
{
//
}
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}
}
}
/**
* 把输入流转换成一个字符串
* @param is 输入流
* @return 返回一个字符串,如果转换失败就返回一个空字符
*/
public static String readStream(InputStream is){
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while((len = is.read(buffer)) != -1){
baos.write(buffer, 0, len);
}
is.close();
return new String(baos.toByteArray());
} catch (Exception e) {
return "";
}
}
}
使用HttpURLConnection方式发送Post请求
public class MainActivity extends Activity {
private EditText et_qq;
private EditText et_pwd;
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
String result = (String) msg.obj;
Toast.makeText(MainActivity.this, result, 0).show();
};
};
@Override
protected void 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);
}
public void login(View view){
final String qq = et_qq.getText().toString().trim();
final String pwd = et_pwd.getText().toString().trim();
final String urlStr = "http://192.168.12.28:8080/web/servlet/LoginServlet";
if(TextUtils.isEmpty(qq) || TextUtils.isEmpty(pwd)){
Toast.makeText(this, "请输入qq号码或者密码", 0).show();
return;
}else{
final String data = "username="+URLEncoder.encode(qq)+"&password="+URLEncoder.encode(pwd);
final String path = urlStr ;
new Thread(){
public void run() {
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//设置请求方式为POST
connection.setRequestMethod("POST");
connection.setConnectTimeout(5000);
//设置表单类型及数据长度
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", data.length()+"");
//设置连接允许向服务器写数据
connection.setDoOutput(true);
connection.getOutputStream().write(data.getBytes());
int code = connection.getResponseCode();
if(code == 200){
InputStream is = connection.getInputStream();
String result = StreamTools.readStream(is);
Message msg= Message.obtain();
msg.obj = result;
handler.sendMessage(msg);
}else
{
//
}
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}
}
}
/**
* 把输入流转换成一个字符串
*
* @param is 输入流
* @return 返回一个字符串,如果转换失败就返回一个空字符
*/
public static String readStream(InputStream is) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
is.close();
return new String(baos.toByteArray(), "gb2312");
} catch (Exception e) {
return "";
}
}
}
async httpclient (重点)
使用GET方式向服务器端提交数据:
//1、创建一个浏览器
AsyncHttpClient client = new AsyncHttpClient();
//2、设置请求方式及访问路径,并处理服务器端返回的结果
client.get(path, new AsyncHttpResponseHandler() {
/**
* 处理成功的结果
*/
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Toast.makeText(MainActivity.this, "请求成功,"+new String(responseBody), 0).show();
}
/**
* 处理失败的结果
*/
@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
Toast.makeText(MainActivity.this, "请求失败", 0).show();
}
});
//
使用POST方式向服务器端提交数据的模版代码:
1、创建一个浏览器
AsyncHttpClient client = new AsyncHttpClient();
2、封装提交的参数
RequestParams params = new RequestParams();
params.add("username", qq);
params.add("password", pwd);
3、设置请求方式及访问路径,并处理响应结果
client.post(urlStr, params, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Toast.makeText(MainActivity.this, "请求成功,"+new String(responseBody), 1).show();
}
@Override
public void onFailure(int statusCode, Header[] headers,
byte[] responseBody, Throwable error) {
Toast.makeText(MainActivity.this, "请求失败", 1).show();
}
});
上传文件:
2、封装提交的参数
RequestParams params = new RequestParams();
params.put(“file”, file);
多线程下载的步骤
1、需要知道服务器端文件的小大;
2、等分文件为若干份;
3、知道每个线程下载数据的起始位置和结束位置;
4、创建子线程下载数据; conn.setRequestProperty(“Range”,”bytes=0-2”);
5、每个子线程什么时间下载完毕。循环更新一个数值
使用XUtils实现多线程下载,包括断点续传的功能(重点)
HttpUtils http = new HttpUtils();
HttpHandler handler = http.download("http://192.168.12.28:8080/tomcat.png",
"/mnt/sdcard/tomcat.png",
true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
new RequestCallBack<File>() {
@Override
public void onStart() {
tv.setText("正在下载...");
}
@Override
public void onLoading(long total, long current, boolean isUploading) {
tv.setText(current + "/" + total);
}
@Override
public void onSuccess(ResponseInfo<File> responseInfo) {
tv.setText("下载完成:" + responseInfo.result.getPath());
}
@Override
public void onFailure(HttpException error, String msg) {
tv.setText("下载失败");
}
});
多线程断点续传下载
多线程下载的实现过程:
1.首先得到下载文件的长度,然后设置本地文件的长度。
HttpURLConnection.getContentLength();
RandomAccessFile file = new RandomAccessFile(“FeiQ.exe”,”rwd”);
file.setLength(filesize); //设置本地文件的长度
2.根据文件长度和线程数计算每条线程下载的数据长度和下载位置。
如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如图所示。
3.使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止.
如:指定从文件的2M位置开始下载,下载到位置4M为止,代码如下:
HttpURLConnection.setRequestProperty(“Range”, “bytes=2097152-4194303”);
4.保存文件
使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。
RandomAccessFile threadfile = new RandomAccessFile(“QQWubiSetup.exe “,”rwd”);
threadfile.seek(2097152); //从文件的什么位置开始写入数据
public class DownloadUtils {
private int stopThreadCount;// 停止的线程数量
private int threadCount;// 线程总数
private Handler handler;
private LinearLayout llProgressBarGroup;
public void useMultiThreadDownloadFile(String path, int threadCount, Handler handler, LinearLayout llProgressBarGroup) throws Exception {
this.threadCount = threadCount;
this.handler = handler;
this.llProgressBarGroup = llProgressBarGroup;
stopThreadCount = 0;
// 1. 获取服务器端, 文件的长度.
int length = getFileLength(path);
if(length != -1) {
System.out.println("文件总长度: " + length);
// 2. 在本地创建一个文件长度为服务器返回的长度.
File file = new File(Environment.getExternalStorageDirectory(),
path.substring(path.lastIndexOf("/") + 1));
System.out.println(file.getName());
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(length);
// 3. 根据线程数量, 计算每个线程所要下载数据的开始,结束索引.
int blockSize = length / threadCount;
for (int i = 0; i < threadCount; i++) {
int startIndex = i * blockSize;
int endIndex = (i + 1) * blockSize - 1;
if(i == threadCount -1) {
endIndex = length -1;
}
// 4. 开始多线程下载.
new DownloadThread(path, startIndex, endIndex, i).start();
}
} else {
System.out.println("获取文件长度失败: " + length);
}
}
private int getFileLength(String path) throws Exception {
HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
conn.connect();
int responseCode = conn.getResponseCode();
if(responseCode == 200) {
int contentLength = conn.getContentLength();
conn.disconnect();
return contentLength;
}
return -1;
}
class DownloadThread extends Thread {
private String path;
private int startIndex;
private int endIndex;
private int threadID;
public DownloadThread(String path, int startIndex, int endIndex, int threadID) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadID = threadID;
this.path = path;
}
@Override
public void run() {
HttpURLConnection conn = null;
try {
// 开始下载前, 需要先判断是否是断点下载, 如果是. 继续下载
File progressFile = new File(Environment.getExternalStorageDirectory(), "线程" + threadID + ".cfg");
int total = 0;
if(progressFile.exists() && progressFile.length() > 0) {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(progressFile)));
total = Integer.valueOf(br.readLine());
br.close();
startIndex += total;
System.out.println(Thread.currentThread().getName() + "继续下载: " + startIndex);
}
conn = (HttpURLConnection) new URL(path).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
conn.connect();
int responseCode = conn.getResponseCode();
if(responseCode == 206) {
int contentLength = conn.getContentLength();
System.out.println(Thread.currentThread().getName() + "开始下载: length=" + contentLength);
InputStream is = conn.getInputStream();
File file = new File(Environment.getExternalStorageDirectory(),
path.substring(path.lastIndexOf("/") + 1));
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.seek(startIndex);
// 设置进度条
ProgressBar bar = (ProgressBar) llProgressBarGroup.getChildAt(threadID);
bar.setMax(contentLength);
bar.setProgress(total);
RandomAccessFile progressRAF = null;
byte[] buffer = new byte[1024 * 1024];
int len = -1;
while((len = is.read(buffer)) != -1) {
raf.write(buffer, 0, len);
total += len;
bar.setProgress(total);
// 终止下载时, 需要记住当前线程已下载了多少个字节. 便于下次下载时继续下载.
progressRAF = new RandomAccessFile(progressFile, "rwd");
progressRAF.write(String.valueOf(total).getBytes());
progressRAF.close();
}
is.close();
raf.close();
System.out.println(Thread.currentThread().getName() + ": 下载成功, startIndex="
+ startIndex + ", endIndex=" + endIndex);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(conn != null) {
conn.disconnect();
}
// 清除配置文件
stopThreadCount ++;
if(stopThreadCount == threadCount) {
handler.sendEmptyMessage(MainActivity.SUCCESS);
System.out.println("清除配置文件.");
File file = null;
for (int i = 0; i < threadCount; i++) {
file = new File(Environment.getExternalStorageDirectory(), "线程" + i + ".cfg");
if(file.delete()) {
System.out.println("删除成功: " + file.getName());
}
}
}
}
}
}
}