tomcat
获取Tomcat安装程序包
tar.gz文件是Linux操作系统下的安装版本
exe文件是Windows系统下的安装版本x86 x64
zip文件是Windows系统下的压缩版本 (建议)
Tomcat启动方式
使用Tomcat服务程序(安装版)
使用Tomcat程序组中的快捷菜单(安装版)
使用Tomcat.exe程序(安装版)
使用批处理程序(使用解压版)
双击 bin 目录下的 startup.bat 文件
输入 http://localhost:8080/,显示网页就成功
无论是开放式目录结构还是打包文件方式发布web应用,web应用的默认URL入口都是Web应用的根目录名。例如要访问MyApp应用,它的URL入口为/MyApp,如访问本地务
pc访问:http://localhost:8080/MyApp(http://127.0.0.1:8080/MyApp)本机ip访问、ip地址也可以
用模拟器加载本机的地址时,可以用 "http://10.0.2.2:8080/dd.jpg" 来替换
如果用手机:用iP地址访问
异步消息处理机制
主线程阻塞
WebView的用法
//1.调用WebView的getSettings()方法可以去设置一些浏览器的属性,这里我们并不去
// 设置过多的属性,只是调用了setJavaScriptEnabled()方法来让WebView支持JavaScript脚本。
webView.getSettings().setJavaScriptEnabled(true);
//2.调用WebView的setWebViewClient()方法,并传入了WebViewClient的匿名类作为参数,然后
// 重写了shouldOverrideUrlLoading()方法。这就表明当需要从一个网页跳转到另一个网页时,
// 我们希望目标网页仍然在当前WebView中显示,而不是打开系统浏览器。
webView.setWebViewClient(newWebViewClient(){
@Override
publicboolean shouldOverrideUrlLoading(WebView view,String url){
view.loadUrl(url);// 根据传入的参数再去加载新的网页,这是方法
returntrue;// 表示当前WebView可以处理打开新网页的请求,不用借助系统浏览器
}
});
//3.调用WebView的loadUrl()方法,并将网址传入,即可展示相应网页的内容
webView.loadUrl("http://www.baidu.com");
//4.程序使用到了网络功能,而访问网络是需要声明权限的
对于HTTP协议工作原理:就是客户端向服务器发出一条HTTP请求,服务器收到请求之后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理就可以了。是不是非常简单?一个浏览器的基本工作原理也就是如此了.WebView控件,其实也就是向百度的服务器发起了一条HTTP请求,接着服务器分析出我们想要访问的是百度的首页,于是会把该网页的HTML代码进行返回,然后WebView再调用手机浏览器的内核对返回的HTML代码进行解析,最终将页面展示出来。简单来说,WebView已经在后台帮我们处理好了发送HTTP请求、接收服务响应、解析返回数据,以及最终的页面展示这几步工作,不过由于它封装得实在是太好了,反而使得我们不能那么直观地看出HTTP协议到底是如何工作的。因此,接下来就让我们通过手动发送HTTP请求的方式,来更加深入地理解一下这个过程。
在Android上发送HTTP请求的方式一般有两种,HttpURLConnection和HttpClient
查看网络图片
publicclassMainActivityextendsActivity{
staticImageView iv;
staticMainActivity ma;
staticHandler handler =newHandler(){
//此方法在主线程中调用,可以用来刷新ui
publicvoid handleMessage(android.os.Message msg){
//处理消息时,需要知道到底是成功的消息,还是失败的消息
switch(msg.what){
case1:
//把位图对象显示至imageview
iv.setImageBitmap((Bitmap)msg.obj);
break;
//将handler设成静态是节省内存,防止内存溢出,那么imageview也的设成静态,
//而toast方法运行时上下文可能还没有,但是本例子的this是存在的,因为oncreat方法先运行
case0:
Toast.makeText(ma,"请求失败",0).show();
break;
}
}
};
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv =(ImageView) findViewById(R.id.iv);
ma =this;
}
publicvoid click(View v){
//下载图片
//1.确定网址
finalString path ="http://192.168.13.13:8080/dd.jpg";
finalFile file =newFile(getCacheDir(), getFileName(path));
//判断,缓存中是否存在该文件
if(file.exists()){
//如果缓存存在,从缓存读取图片
System.out.println("从缓存读取的");
Bitmap bm =BitmapFactory.decodeFile(file.getAbsolutePath());
iv.setImageBitmap(bm);
}
else{
//如果缓存不存在,从网络下载
System.out.println("从网上下载的");
Thread t =newThread(){
@Override
publicvoid run(){
try{
//2.把网址封装成一个url对象
URL url =new URL(path);
//3.获取客户端和服务器的连接对象,此时还没有建立连接
HttpURLConnection conn =(HttpURLConnection) url.openConnection();
//4.对连接对象进行初始化
//设置请求方法,注意大写
conn.setRequestMethod("GET");
//设置连接超时
conn.setConnectTimeout(5000);
//设置读取超时
conn.setReadTimeout(5000);
//5.发送请求,与服务器建立连接
conn.connect();
//如果响应码为200,说明请求成功
if(conn.getResponseCode()==200){
//获取服务器响应头中的流,流里的数据就是客户端请求的数据
InputStream is = conn.getInputStream();
//读取服务器返回的流里的数据,把数据写到本地文件,缓存起来
FileOutputStream fos =newFileOutputStream(file);
byte[] b =newbyte[1024];
int len =0;
while((len = is.read(b))!=-1){
fos.write(b,0, len);
}
fos.close();
//读取出流里的数据,并构造成位图对象
//流里已经没有数据了
// Bitmap bm = BitmapFactory.decodeStream(is);
Bitmap bm =BitmapFactory.decodeFile(file.getAbsolutePath());
Message msg =newMessage();
//消息对象可以携带数据
msg.obj = bm;
msg.what =1;
//把消息发送至主线程的消息队列
handler.sendMessag
}
else{
Message msg = handler.obtainMessage();
msg.what =0;
handler.sendMessage(msg);
}
}catch(Exception e){
// TODO Auto-generated catch block
e.printStackTrace();
}
finally { // if (connection != null) { // //最后可以调用disconnect()方法将这个HTTP连接关闭掉,如下所示: // connection.disconnect(); // }
}
};
t.start();
}
}
publicString getFileName(String path){
int index = path.lastIndexOf("/");
return path.substring(index +1);
}
}
注意权限 <uses-permission android:name="android.permission.INTERNET"/>
发消息如果消息不需要携带数据,可以发送空消息。例如,让主线程设置listview的适配器,listview处理的消息是list,而list是全局变量handler.sendEmptyMessage(1);
<com.loopj.android.image.SmartImageView/>
SmartImageView siv =(SmartImageView) findViewById(R.id.siv);
siv.setImageUrl("http://192.168.1.102:8080/dd.jpg");
URL url =new URL(path);
//获取连接对象
HttpURLConnection conn =(HttpURLConnection) url.openConnection();
//设置连接属性
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//建立连接,获取响应吗
if(conn.getResponseCode()==200){
}
byte[] b =newbyte[1024];
int len =0;
ByteArrayOutputStream bos =newByteArrayOutputStream();
while((len = is.read(b))!=-1){
//把读到的字节先写入字节数组输出流中存起来
bos.write(b,0, len);
}
//把字节数组输出流中的内容转换成字符串
//默认使用utf-8
text =newString(bos.toByteArray());
另一种读取的方法
BufferedReader reader =newBufferedReader(newInputStreamReader(is));
StringBuilder builder =newStringBuilder();
String line;
while((line=reader.readLine())!=null){
builder.append(line);
}
String text=builder.toString();
stringbuilder是个容器,长度的可变的,可以存储不同类型数据,可以对字符串进行修改,它是线程不同步的,通常用于单线程,效率高,当然也用于多线程,只不过是不安全了 乱码的处理
//手动指定码表
text =newString(bos.toByteArray(),"UTF-8");
finalString path ="http://192.168.1.104/Web/servlet/CheckLogin?name="+ name +"&pass="+pass;
URL url =new URL(path);
HttpURLConnection conn =(HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
if(conn.getResponseCode()==200){
}
String path ="http://192.168.1.104/Web/servlet/CheckLogin?name="+URLEncoder.encode(name)+"&pass="+pass;
POST方式提交数据
//给请求头添加post多出来的两个属性
String data ="name="+URLEncoder.encode(name)+"&pass="+pass;
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", data.length()+"");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(data.getBytes());
if(hr.getStatusLine().getStatusCode()==200){
//拿到响应头的实体
InputStreamis= hr.getEntity().getContent();
//拿到服务器返回的输入流
String text =Utils.getTextFromStream(is);
}
Thread t =newThread(){
@Override
publicvoid run(){
String path ="http://192.168.13.13/Web/servlet/CheckLogin";
//1.创建客户端对象
HttpClient hc =newDefaultHttpClient();
//2.创建post请求对象
HttpPost hp =newHttpPost(path);
//封装form表单提交的数据
BasicNameValuePair bnvp =newBasicNameValuePair("name", name);
BasicNameValuePair bnvp2 =newBasicNameValuePair("pass", pass);
List<BasicNameValuePair> parameters =newArrayList<BasicNameValuePair>();
//把BasicNameValuePair放入集合中
parameters.add(bnvp);
parameters.add(bnvp2);
try{
//要提交的数据都已经在集合中了,把集合传给实体对象
UrlEncodedFormEntity entity =newUrlEncodedFormEntity(parameters,"utf-8");
//设置post请求对象的实体,其实就是把要提交的数据封装至post请求的输出流中
hp.setEntity(entity);
//3.使用客户端发送post请求
HttpResponse hr = hc.execute(hp);
if(hr.getStatusLine().getStatusCode()==200){
InputStream is = hr.getEntity().getContent();
String text =Utils.getTextFromStream(is);
//发送消息,让主线程刷新ui显示text
Message msg = handler.obtainMessage();
msg.obj = text;
handler.sendMessage(msg);
}
}catch(Exception e){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
//创建异步的httpclient对象
AsyncHttpClient ahc =newAsyncHttpClient();
//发送get请求
ahc.get(path,newMyHandler());
classMyHandlerextendsAsyncHttpResponseHandler{
//http请求成功,返回码为200,系统回调此方法
@Override
publicvoid onSuccess(int statusCode,Header[] headers,
//responseBody的内容就是服务器返回的数据
byte[] responseBody){
Toast.makeText(MainActivity.this,newString(responseBody),0).show();
}
//http请求失败,返回码不为200,系统回调此方法
@Override
publicvoid onFailure(int statusCode,Header[] headers,
byte[] responseBody,Throwable error){
Toast.makeText(MainActivity.this,"返回码不为200",0).show();
}
}
发送post请求
//创建异步httpclient对象
AsyncHttpClient ahc =newAsyncHttpClient();
//创建RequestParams封装要携带的数据
RequestParams rp =newRequestParams();
rp.add("name", name);
rp.add("pass",pass);
//发送post请求
ahc.post(path, rp,newMyHandler());
原理:服务器CPU分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源
publicclassMainActivityextendsActivity{
staticintThreadCount=3;
staticint finishedThread =0;
int currentProgress;
String fileName ="QQPlayer.exe";
//确定下载地址
String path ="http://192.168.13.13:8080/"+ fileName;
privateProgressBar pb;
TextView tv;
Handler handler =newHandler(){
publicvoid handleMessage(android.os.Message msg){
//把变量改成long,在long下运算
tv.setText((long)pb.getProgress()*100/ pb.getMax()+"%");
}
};
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pb =(ProgressBar) findViewById(R.id.pb);
tv =(TextView) findViewById(R.id.tv);
}
publicvoid click(View v){
Thread t =newThread(){
@Override
publicvoid run(){
//发送get请求,请求这个地址的资源
try{
URL url =new URL(path);
HttpURLConnection conn =(HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
if(conn.getResponseCode()==200){
//拿到所请求资源文件的长度
int length = conn.getContentLength();
//设置进度条的最大值就是原文件的总长度
pb.setMax(length);
File file =newFile(Environment.getExternalStorageDirectory(), fileName);
//生成临时文件
RandomAccessFile raf =newRandomAccessFile(file,"rwd");
//设置临时文件的大小
raf.setLength(length);
raf.close();
//计算出每个线程应该下载多少字节
int size = length /ThreadCount;
for(int i =0; i <ThreadCount; i++){
//计算线程下载的开始位置和结束位置
int startIndex = i * size;
int endIndex =(i +1)* size -1;
//如果是最后一个线程,那么结束位置写死
if(i ==ThreadCount-1){
endIndex = length -1;
}
// System.out.println("线程" + i + "的下载区间是:" + startIndex + "---" + endIndex);
newDownLoadThread(startIndex, endIndex, i).start();
}
}
}catch(Exception e){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
classDownLoadThreadextendsThread{
int startIndex;
int endIndex;
int threadId;
publicDownLoadThread(int startIndex,int endIndex,int threadId){
super();
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
publicvoid run(){
//再次发送http请求,下载原文件
try{
File progressFile =newFile(Environment.getExternalStorageDirectory(), threadId +".txt");
//判断进度临时文件是否存在
if(progressFile.exists()){
FileInputStream fis =newFileInputStream(progressFile);
BufferedReader br =newBufferedReader(newInputStreamReader(fis));
//从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置
int lastProgress =Integer.parseInt(br.readLine());
startIndex += lastProgress;
//把上次下载的进度显示至进度条
currentProgress += lastProgress;
pb.setProgress(currentProgress);
//发送消息,让主线程刷新文本进度
handler.sendEmptyMessage(1);
fis.close();
}
System.out.println("线程"+ threadId +"的下载区间是:"+ startIndex +"---"+ endIndex);
HttpURLConnection conn;
URL url =new URL(path);
conn =(HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//设置本次http请求所请求的数据的区间
conn.setRequestProperty("Range","bytes="+ startIndex +"-"+ endIndex);
//请求部分数据,相应码是206
if(conn.getResponseCode()==206){
//流里此时只有1/3原文件的数据
InputStream is = conn.getInputStream();
byte[] b =newbyte[1024];
int len =0;
int total =0;
//拿到临时文件的输出流
File file =newFile(Environment.getExternalStorageDirectory(), fileName);
RandomAccessFile raf =newRandomAccessFile(file,"rwd");
//把文件的写入位置移动至startIndex
raf.seek(startIndex);
while((len = is.read(b))!=-1){
//每次读取流里数据之后,同步把数据写入临时文件
raf.write(b,0, len);
total += len;
System.out.println("线程"+ threadId +"下载了"+ total);
//每次读取流里数据之后,把本次读取的数据的长度显示至进度条
currentProgress += len;
pb.setProgress(currentProgress);
//发送消息,让主线程刷新文本进度
handler.sendEmptyMessage(1);
//生成一个专门用来记录下载进度的临时文件
RandomAccessFile progressRaf =newRandomAccessFile(progressFile,"rwd");
//每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中
progressRaf.write((total +"").getBytes());
progressRaf.close();
}
System.out.println("线程"+ threadId +"下载完毕-------------------小志参上!");
raf.close();
finishedThread++;
synchronized(path){
//三条线程都下载完毕之后,删除缓存文件
if(finishedThread ==ThreadCount){
for(int i =0; i <ThreadCount; i++){
File f =newFile(Environment.getExternalStorageDirectory(), i +".txt");
f.delete();
}
finishedThread =0;
}
}
}
}catch(Exception e){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
多线程断点续传 1.定义进度条,定义文字显示进度,按钮开始下载 2.加俩个权限,访问网络和读取sd卡 3.定义全局变量线程数和hander用于修改textview 4.在按钮监听事件中: ①开启子线程,请求网络,请求成功后获取返回资源的长度 ②设置进度条的最大值就是资源长度,并发送 ③生成临时文件,设置每个线程的开始和结束位置,再几条开启线程用来下载,将开始,结束和线程id成为它的构造函数的参数 5.在新线程中: ①读取进度文件的大小,如果存在就将开始位置改变 ②再次请求网络,读取资源并写入临时文件,写入的位置移动到开始位置,将读取的进度设置进进度条并发送,到这里下载完成了 ③断点续传需要文件记录住文件下载了的大小,生成专门记录文件大的进度文件,并写进去 ④下载完删除进度文件
HttpUtils本身就支持多线程断点续传,使用起来非常的方便
HttpUtils http =newHttpUtils();
http.download(url,//下载请求的网址
target,//下载的数据保存路径和文件名
true,//是否开启断点续传
true,//如果服务器响应头中包含了文件名,那么下载完毕后自动重命名
newRequestCallBack<File>(){//侦听下载状态
//下载成功此方法调用,不是请求成功,
@Override
publicvoid onSuccess(ResponseInfo<File> arg0){
tv.setText("下载成功"+ arg0.result.getPath());//弹出结果路径
}
//下载失败此方法调用,比如文件已经下载、没有网络权限、文件访问不到,方法传入一个字符串参数告知失败原因
@Override
publicvoid onFailure(HttpException arg0,String arg1){
tv.setText("下载失败"+ arg1);//下载失败的原因
}
//在下载过程中不断的调用,用于刷新进度条
@Override
publicvoid onLoading(long total,long current,boolean isUploading){
super.onLoading(total, current, isUploading);
//设置进度条总长度
pb.setMax((int) total);
//设置进度条当前进度
pb.setProgress((int) current);
tv_progress.setText(current *100/ total +"%");
}
});
访问网络写在公共的类
因为一个应用程序很可能会在许多地方都使用到网络功能, 而发送 HTTP请求的代码基本都是相同的,如果每次都去编写一遍发送 HTTP请求的代码,这显然是非常差劲的做法。通常情况下我们都应该将这些通用的网络操作提取到一个公共的类里,并提供一个静态方法,当想要发起网络请求的时候只需简单地调用一下这个方法即可。比如使用如下的写法:
1.首先需要定义一个接口,比如将它命名成 HttpCallbackListener,代码如下所示:
public interface HttpCallbackListener {
void onFinish(String response);
void onError(Exception e);
}
可以看到,我们在接口中定义了两个方法,onFinish()方法表示当服务器成功响应我们请求的时候调用, onError()表示当进行网络操作出现错误的时候调用。 这两个方法都带有参数,onFinish()方法中的参数代表着服务器返回的数据,而 onError()方法中的参数记录着错误的详细信息。
2.HTTPutils
conn.conntion连接不知要不要写,书上没写,视频里写了,如果要写就写在urlConn.setDoOutput(true);后面
publicclassHttpUtil{
publicstaticvoid sendHttpRequest(finalString address,
finalHttpCallbackListener listener){
newThread(newRunnable(){
@Override
publicvoid run(){
HttpURLConnection connection =null;
try{
URL url =new URL(address);
connection =(HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream in = connection.getInputStream();
BufferedReader reader =newBufferedReader(
newInputStreamReader(in));
StringBuilder response =newStringBuilder();
String line;
while((line = reader.readLine())!=null){
response.append(line);
}
if(listener !=null){
// 回调onFinish()方法
listener.onFinish(response.toString());
}
}catch(Exception e){
if(listener !=null){
// 回调onError()方法
listener.onError(e);
}
}finally{
if(connection !=null){
connection.disconnect();
}
}
}
}).start();
}
}
首先给 sendHttpRequest()方法添加了一个 HttpCallbackListener参数,并在方法的内部开启了一个子线程,然后在子线程里去执行具体的网络操作。注意子线程中是无法通过return语句来返回数据的,因此这里我们将服务器响应的数据传入了 HttpCallbackListener的onFinish()方法中,如果出现了异常就将异常原因传入到 onError()方法中。
3.现在 sendHttpRequest()方法接收两个参数了,因此我们在调用它的时候还需要将HttpCallbackListener的实例传入,如下所示:
HttpUtil.sendHttpRequest(address,newHttpCallbackListener(){
@Override
publicvoid onFinish(String response){
// 在这里根据返回内容执行具体的逻辑
}
@Override
publicvoid onError(Exception e){
// 在这里对异常情况进行处理
}
});
这样的话,当服务器成功响应的时候我们就可以在 onFinish()方法里对响应数据进行处理了,类似地,如果出现了异常,就可以在 onError()方法里对异常情况进行处理。如此一来,我们就巧妙地利用回调机制将响应数据成功返回给调用方了。
另外需要注意的是,onFinish()方法和 onError()方法最终还是在子线程中运行的,因此我们不可以在这里执行任何的 UI操作,如果需要根据返回的结果来更新 UI,则仍然要使用异步消息处理机制。