android网络编程
- 通过浏览器查看Tomcat服务器中的图片
1、将要查看的图片dd.jpg存放到Tomcat的webapps/ROOT/目录下
2、启动服务器
3、打开浏览器,输入:http://172.23.7.240:8080/dd.jpg
- 网络图片查看器(带有缓存功能)
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:id="@+id/activity_main" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:paddingBottom="@dimen/activity_vertical_margin" 8 android:paddingLeft="@dimen/activity_horizontal_margin" 9 android:paddingRight="@dimen/activity_horizontal_margin" 10 android:paddingTop="@dimen/activity_vertical_margin" 11 tools:context="com.ahu.lichang.scannetpicture.MainActivity"> 12 13 <Button 14 android:onClick="scan" 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" 17 android:text="查看网络图片(带缓存功能)" /> 18 <ImageView 19 android:id="@+id/iv" 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" /> 22 </RelativeLayout>
1、主线程不能被阻塞,所有很多的耗时操作都不能放在主线程中运行,否则会发生ANR异常(application not response)。
2、刷新UI界面的代码只能运行在主线程中,运行在子线程中是没有任何效果的。 如果需要在子线程总刷新UI,那么就要使用消息队列机制。
1 package com.ahu.lichang.scannetpicture; 2 3 import android.app.Activity; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.os.Bundle; 7 import android.os.Handler; 8 import android.os.Message; 9 import android.view.View; 10 import android.widget.ImageView; 11 import android.widget.Toast; 12 13 import java.io.File; 14 import java.io.FileOutputStream; 15 import java.io.InputStream; 16 import java.net.HttpURLConnection; 17 import java.net.URL; 18 19 public class MainActivity extends Activity { 20 private ImageView iv; 21 Handler handler = new Handler(){ 22 //此方法在主线程中调用,用来刷新UI 23 @Override 24 public void handleMessage(Message msg) { 25 switch (msg.what){ 26 case 1: 27 iv.setImageBitmap((Bitmap) msg.obj); 28 break; 29 case 0: 30 Toast.makeText(MainActivity.this,"请求失败!",Toast.LENGTH_SHORT).show(); 31 break; 32 } 33 } 34 }; 35 @Override 36 protected void onCreate(Bundle savedInstanceState) { 37 super.onCreate(savedInstanceState); 38 setContentView(R.layout.activity_main); 39 iv = (ImageView) findViewById(R.id.iv); 40 } 41 42 /** 43 * 点击按钮,查看网络图片(有缓存功能) 44 * @param v 45 */ 46 public void scan(View v){ 47 //图片的地址 48 final String path = "http://172.23.4.91:8080/dd.jpg"; 49 final File file = new File(getCacheDir(),getFileName(path)); 50 //判断是否有图片缓存 51 if(file.exists()){ 52 System.out.println("从缓存中获取。。。"); 53 //有缓存,直接从缓存中获取 54 Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath()); 55 iv.setImageBitmap(bm); 56 }else{ 57 System.out.println("从网络下载。。。"); 58 //没有缓存,就从网络上下载 59 //网络下载是个耗时操作,需在线程中完成 60 new Thread(){ 61 @Override 62 public void run() { 63 try { 64 URL url = new URL(path); 65 //获取连接对象,但还没建立连接 66 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 67 //设置连接超时 68 conn.setConnectTimeout(5000); 69 //设置读取超时 70 conn.setReadTimeout(5000); 71 //设置请求方法,必须大写 72 conn.setRequestMethod("GET"); 73 //建立连接,发送GET请求 74 conn.connect(); 75 //如果响应吗为200,说明请求服务器成功 76 if(conn.getResponseCode() == 200){ 77 //获取服务器响应头中的流,流中的数据就是客户端请求的数据 78 InputStream is = conn.getInputStream(); 79 //读取流里的数据,并写入到本地缓存起来 80 FileOutputStream fos = new FileOutputStream(file); 81 byte[] b = new byte[1024]; 82 int len = 0; 83 while((len = is.read(b)) != -1){ 84 fos.write(b,0,len); 85 } 86 fos.close(); 87 88 Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath()); 89 //发送消息 90 Message msg = new Message(); 91 msg.obj = bm; 92 msg.what = 1; 93 handler.sendMessage(msg); 94 }else{ 95 //请求服务器失败 96 Message msg = handler.obtainMessage(); 97 msg.what = 0; 98 handler.sendMessage(msg); 99 } 100 } catch (Exception e) { 101 e.printStackTrace(); 102 } 103 } 104 }.start(); 105 } 106 } 107 108 /** 109 * 根据地址,截取图片的名称 110 * @param path 111 * @return 112 */ 113 private String getFileName(String path) { 114 int index = path.lastIndexOf("/"); 115 return path.substring(index + 1); 116 } 117 }
要添加权限
1 <!--添加网络权限--> 2 <uses-permission android:name="android.permission.INTERNET"/>
- 消息处理机制
消息处理机制的原理:
所有使用UI界面的操作系统,后台都运行着一个死循环,在不停地监听和接受用户发出的指令,一旦接受指令就立即执行。
当我们的Android应用程序的进程一创建的时候,系统就给这个进程提供了一个Looper(轮询器),Looper是一个死循环,它内部维护着一个消息队列,Looper不停地从消息队列中取消息,取到消息就发送给Handler,最后Handler根据接收到的消息去修改UI。
主线程创建时,消息队列和轮询器对象就会被创建,但是消息处理器对象,需要使用时,自行创建。
步骤:
1、在主线程中创建Handler
2、在线程中得到handler的引用,调用发送消息的方法
3、handler执行handlerMessage方法去西湖里消息,并修改UI界面
- HTML源文件查看器
1 package com.ahu.lichang.scanhtml; 2 3 import android.os.Bundle; 4 import android.os.Handler; 5 import android.os.Message; 6 import android.support.v7.app.AppCompatActivity; 7 import android.view.View; 8 import android.widget.TextView; 9 10 import java.io.ByteArrayOutputStream; 11 import java.io.InputStream; 12 import java.net.HttpURLConnection; 13 import java.net.URL; 14 15 public class MainActivity extends AppCompatActivity { 16 17 private TextView tv; 18 Handler handler = new Handler(){ 19 @Override 20 public void handleMessage(Message msg) { 21 tv.setText((String)msg.obj); 22 } 23 }; 24 @Override 25 protected void onCreate(Bundle savedInstanceState) { 26 super.onCreate(savedInstanceState); 27 setContentView(R.layout.activity_main); 28 tv = (TextView) findViewById(R.id.tv); 29 } 30 public void scan(View view){ 31 new Thread(){ 32 @Override 33 public void run() { 34 String path = "http://172.23.4.91:8080/baidu.html"; 35 try { 36 URL url = new URL(path); 37 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 38 connection.setRequestMethod("GET"); 39 connection.setConnectTimeout(5000); 40 connection.setReadTimeout(5000); 41 connection.connect(); 42 if(connection.getResponseCode() == 200){ 43 InputStream is = connection.getInputStream(); 44 /** 45 * 要将字节流中的内容转换成字符串 46 */ 47 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 48 byte[] b = new byte[1024]; 49 int len = 0; 50 while ((len = is.read(b)) != -1){ 51 baos.write(b,0,len); 52 } 53 /** 54 * 注意转换的时候乱码的处理: 55 * 默认情况下是utf-8 56 * 也可以手动指定码表:text = new String(baos.toByteArray(),"gb2312"); 57 */ 58 String text = new String(baos.toByteArray()); 59 Message msg = handler.obtainMessage(); 60 msg.obj = text; 61 handler.sendMessage(msg); 62 } 63 } catch (Exception e) { 64 e.printStackTrace(); 65 } 66 } 67 }.start(); 68 } 69 }
- 使用GET方式向服务器端提交数据
1、把需要提交的参数组拼到URL地址的后面:http://192.168.22.136:8080/web/servlet/LoginServlet?username=123&password=1233
缺点:
1、提交数据的长度有限制:最大长度4kb,windows中提交数据时最大长度为1kb;
2、不安全
优点:代码简单
1 public class MainActivity extends Activity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 } 8 9 Handler handler = new Handler(){ 10 public void handleMessage(android.os.Message msg) { 11 Toast.makeText(MainActivity.this, (String)msg.obj, 0).show(); 12 } 13 }; 14 15 public void click(View v){ 16 EditText et_name = (EditText) findViewById(R.id.et_name); 17 EditText et_pass = (EditText) findViewById(R.id.et_pass); 18 19 final String name = et_name.getText().toString(); 20 final String pass = et_pass.getText().toString(); 21 22 Thread t = new Thread(){ 23 @Override 24 public void run() { 25 //提交的数据需要经过url编码,英文和数字编码后不变 26 @SuppressWarnings("deprecation") 27 String path = "http://192.168.13.13/Web2/servlet/LoginServlet?name=" + URLEncoder.encode(name) + "&pass=" + pass; 28 29 try { 30 URL url = new URL(path); 31 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 32 conn.setRequestMethod("GET"); 33 conn.setConnectTimeout(5000); 34 conn.setReadTimeout(5000); 35 36 if(conn.getResponseCode() == 200){ 37 InputStream is =conn.getInputStream(); 38 String text = Utils.getTextFromStream(is); 39 40 Message msg = handler.obtainMessage(); 41 msg.obj = text; 42 handler.sendMessage(msg); 43 } 44 } catch (Exception e) { 45 e.printStackTrace(); 46 } 47 } 48 }; 49 t.start(); 50 51 } 52 53 }
- 使用POST方式向服务器端提交数据(重点)
业务场景:
1、用户登录
2、文件上传
1、设置请求头信息POST、Content-Type、Content-Length:
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
//必须添加的两个请求头信息
conn.setRequestProperty("Content-Length", data.length()+"");
conn.setRequestMethod("POST");
2、设置把数据提交到服务器端:
//设置允许打开post请求的流
conn.setDoOutput(true);
//把数据写到服务器端
conn.getOutputStream().write(data.getBytes());
缺点:
1、代码复杂
优点:
1、安全;
2、提交大量的数据
1 public class MainActivity extends Activity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 } 8 9 Handler handler = new Handler(){ 10 public void handleMessage(android.os.Message msg) { 11 Toast.makeText(MainActivity.this, (String)msg.obj, 0).show(); 12 } 13 }; 14 15 public void click(View v){ 16 EditText et_name = (EditText) findViewById(R.id.et_name); 17 EditText et_pass = (EditText) findViewById(R.id.et_pass); 18 19 final String name = et_name.getText().toString(); 20 final String pass = et_pass.getText().toString(); 21 22 Thread t = new Thread(){ 23 @Override 24 public void run() { 25 //提交的数据需要经过url编码,英文和数字编码后不变 26 @SuppressWarnings("deprecation") 27 String path = "http://192.168.13.13/Web2/servlet/LoginServlet"; 28 29 try { 30 URL url = new URL(path); 31 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 32 conn.setRequestMethod("POST"); 33 conn.setConnectTimeout(5000); 34 conn.setReadTimeout(5000); 35 36 //拼接出要提交的数据的字符串 37 String data = "name=" + URLEncoder.encode(name) + "&pass=" + pass; 38 //添加post请求的两行属性 39 conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 40 conn.setRequestProperty("Content-Length", data.length() + ""); 41 42 //设置打开输出流 43 conn.setDoOutput(true); 44 //拿到输出流 45 OutputStream os = conn.getOutputStream(); 46 //使用输出流往服务器提交数据 47 os.write(data.getBytes()); 48 if(conn.getResponseCode() == 200){ 49 InputStream is = conn.getInputStream(); 50 String text = Utils.getTextFromStream(is); 51 52 Message msg = handler.obtainMessage(); 53 msg.obj = text; 54 handler.sendMessage(msg); 55 } 56 } catch (Exception e) { 57 e.printStackTrace(); 58 } 59 } 60 }; 61 t.start(); 62 63 } 64 65 }
- HttpClient(发送GET请求和POST请求)
1 public class MainActivity extends Activity { 2 3 Handler handler = new Handler(){ 4 @Override 5 public void handleMessage(android.os.Message msg) { 6 Toast.makeText(MainActivity.this, (String)msg.obj, 0).show(); 7 } 8 }; 9 @Override 10 protected void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12 setContentView(R.layout.activity_main); 13 } 14 15 16 public void get(View v){ 17 EditText et_name = (EditText) findViewById(R.id.et_name); 18 EditText et_pass = (EditText) findViewById(R.id.et_pass); 19 20 final String name = et_name.getText().toString(); 21 final String pass = et_pass.getText().toString(); 22 23 Thread t = new Thread(){ 24 @Override 25 public void run() { 26 String path = "http://192.168.13.13/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass; 27 //使用httpClient框架做get方式提交 28 //1.创建HttpClient对象 29 HttpClient hc = new DefaultHttpClient(); 30 31 //2.创建httpGet对象,构造方法的参数就是网址 32 HttpGet hg = new HttpGet(path); 33 34 //3.使用客户端对象,把get请求对象发送出去 35 try { 36 HttpResponse hr = hc.execute(hg); 37 //拿到响应头中的状态行 38 StatusLine sl = hr.getStatusLine(); 39 if(sl.getStatusCode() == 200){ 40 //拿到响应头的实体 41 HttpEntity he = hr.getEntity(); 42 //拿到实体中的内容,其实就是服务器返回的输入流 43 InputStream is = he.getContent(); 44 String text = Utils.getTextFromStream(is); 45 46 //发送消息,让主线程刷新ui显示text 47 Message msg = handler.obtainMessage(); 48 msg.obj = text; 49 handler.sendMessage(msg); 50 } 51 } catch (Exception e) { 52 // TODO Auto-generated catch block 53 e.printStackTrace(); 54 } 55 } 56 }; 57 t.start(); 58 59 } 60 61 public void post(View v){ 62 EditText et_name = (EditText) findViewById(R.id.et_name); 63 EditText et_pass = (EditText) findViewById(R.id.et_pass); 64 65 final String name = et_name.getText().toString(); 66 final String pass = et_pass.getText().toString(); 67 68 Thread t = new Thread(){ 69 @Override 70 public void run() { 71 String path = "http://192.168.13.13/Web/servlet/CheckLogin"; 72 //1.创建客户端对象 73 HttpClient hc = new DefaultHttpClient(); 74 //2.创建post请求对象 75 HttpPost hp = new HttpPost(path); 76 77 //封装form表单提交的数据 78 BasicNameValuePair bnvp = new BasicNameValuePair("name", name); 79 BasicNameValuePair bnvp2 = new BasicNameValuePair("pass", pass); 80 List<BasicNameValuePair> parameters = new ArrayList<BasicNameValuePair>(); 81 //把BasicNameValuePair放入集合中 82 parameters.add(bnvp); 83 parameters.add(bnvp2); 84 85 try { 86 //要提交的数据都已经在集合中了,把集合传给实体对象 87 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters, "utf-8"); 88 //设置post请求对象的实体,其实就是把要提交的数据封装至post请求的输出流中 89 hp.setEntity(entity); 90 //3.使用客户端发送post请求 91 HttpResponse hr = hc.execute(hp); 92 if(hr.getStatusLine().getStatusCode() == 200){ 93 InputStream is = hr.getEntity().getContent(); 94 String text = Utils.getTextFromStream(is); 95 96 //发送消息,让主线程刷新ui显示text 97 Message msg = handler.obtainMessage(); 98 msg.obj = text; 99 handler.sendMessage(msg); 100 } 101 } catch (Exception e) { 102 // TODO Auto-generated catch block 103 e.printStackTrace(); 104 } 105 } 106 }; 107 t.start(); 108 109 } 110 }
- 异步HttpClient框架(发送GET请求和发送POST请求)AsyncHttpClient
1 public class MainActivity extends Activity { 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 } 8 9 10 public void get(View v){ 11 EditText et_name = (EditText) findViewById(R.id.et_name); 12 EditText et_pass = (EditText) findViewById(R.id.et_pass); 13 14 final String name = et_name.getText().toString(); 15 final String pass = et_pass.getText().toString(); 16 String url = "http://192.168.13.13/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass; 17 //创建异步httpclient 18 AsyncHttpClient ahc = new AsyncHttpClient(); 19 20 //发送get请求提交数据 21 ahc.get(url, new MyResponseHandler()); 22 } 23 24 public void post(View v){ 25 EditText et_name = (EditText) findViewById(R.id.et_name); 26 EditText et_pass = (EditText) findViewById(R.id.et_pass); 27 28 final String name = et_name.getText().toString(); 29 final String pass = et_pass.getText().toString(); 30 String url = "http://192.168.13.13/Web/servlet/CheckLogin"; 31 32 //创建异步httpclient 33 AsyncHttpClient ahc = new AsyncHttpClient(); 34 35 //发送post请求提交数据 36 //把要提交的数据封装至RequestParams对象 37 RequestParams params = new RequestParams(); 38 params.add("name", name); 39 params.add("pass", pass); 40 ahc.post(url, params, new MyResponseHandler()); 41 } 42 43 class MyResponseHandler extends AsyncHttpResponseHandler{ 44 45 //请求服务器成功时,此方法调用 46 @Override 47 public void onSuccess(int statusCode, Header[] headers, 48 byte[] responseBody) { 49 Toast.makeText(MainActivity.this, new String(responseBody), 0).show(); 50 51 } 52 53 //请求失败此方法调用 54 @Override 55 public void onFailure(int statusCode, Header[] headers, 56 byte[] responseBody, Throwable error) { 57 Toast.makeText(MainActivity.this, "请求失败", 0).show(); 58 59 } 60 61 } 62 63 }