黑马程序员技术交流社区

标题: 【阳哥笔记】挑战年薪20W之Android笔记—Day04(网络编程) [打印本页]

作者: 阳哥忠粉    时间: 2015-6-27 23:48
标题: 【阳哥笔记】挑战年薪20W之Android笔记—Day04(网络编程)





~爱上海,爱黑马~



笔记总链接:http://bbs.itheima.com/thread-206454-1-1.html

Android基础

    网络编程

    请求网络图片

    网络交互就是基于HTTP协议请求和响应的过程。

    XMPP协议用于即时通讯。

    示例:

    res\layout\activity_main.xml
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     tools:context=".MainActivity" >

  6.     <Button
  7.         android:layout_width="wrap_content"
  8.         android:layout_height="wrap_content"
  9.         android:text="请求图片"
  10.         android:onClick="click"/>
  11.    
  12.     <ImageView
  13.         android:id="@+id/iv"
  14.         android:layout_width="wrap_content"
  15.         android:layout_height="wrap_content"
  16.         android:layout_centerInParent="true"
  17.         />

  18. </RelativeLayout>
复制代码

    src/cn.itcast.imageviewer/MainActivity.java
  1. package cn.itcast.imageviewer;

  2. import java.io.InputStream;
  3. import java.net.HttpURLConnection;
  4. import java.net.URL;

  5. import android.app.Activity;
  6. import android.graphics.Bitmap;
  7. import android.graphics.BitmapFactory;
  8. import android.os.Bundle;
  9. import android.view.View;
  10. import android.widget.ImageView;
  11. import android.widget.Toast;

  12. public class MainActivity extends Activity {

  13.         @Override
  14.         protected void onCreate(Bundle savedInstanceState) {
  15.                 super.onCreate(savedInstanceState);
  16.                 setContentView(R.layout.activity_main);
  17.         }

  18.         public void click(View v){
  19.                 //向服务器发送Http请求去请求图片
  20.                 String path = "http://192.168.1.100:8080/sh.jpg";
  21.                 try{
  22.                         //1.  把网址封装成url对象
  23.                         URL url = new URL(path);
  24.                         //2. 打开一个连接对象
  25.                         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  26.                         //3. 给连接对象做设置
  27.                         conn.setRequestMethod("GET");
  28.                         conn.setConnectTimeout(8000);
  29.                         conn.setReadTimeout(8000);
  30.                         //4. 发送请求,建立连接
  31.                         conn.connect();
  32.                         //5. 获取响应码,如果为200开头,说明请求成功
  33.                         if(conn.getResponseCode() == 200){
  34.                                 //获取服务器的流,服务器返回的数据是通过流写给客户端的,也就是说,流里就是请求的图片
  35.                                 InputStream is = conn.getInputStream();
  36.                                 //读取流里的数据,把数据构造成一个图片对象
  37.                                 Bitmap bm = BitmapFactory.decodeStream(is);
  38.                                 //把图片显示至屏幕
  39.                                 ImageView iv = (ImageView) findViewById(R.id.iv);
  40.                                 iv.setImageBitmap(bm);
  41.                         }else{
  42.                                 Toast.makeText(this, "请求失败啦啦啦", 0).show();
  43.                         }
  44.                 }catch(Exception e){
  45.                         e.printStackTrace();
  46.                 }
  47.         }
  48. }
复制代码

    添加权限:




    在Tomcat服务器webapps\ROOT存放图片:

   

    运行结果:

    双击start.bat,启动tomcat。





    启动2.3.3版本模拟器,图片显示成功:



    Handler机制让子线程刷新UI

    如果上面的示例运行在4.3版本的模拟器上,就会报警告:



    在Android中,主线程是绝对不能阻塞的。因为,网络请求是耗时操作,主线程处于阻塞状态,用户任何操作都无效,处于类似于死机的状态。此时,点击HOME键有效,点击返回键、菜单键都没有反应。因为,返回键和菜单键是由当前应用程序自行处理,HOME键是由系统处理。应用程序可以阻塞自己的主线程,不可能阻塞Android系统。

   为了保证用户体验良好,所有的耗时操作都不要写在主线程里,包括:请求网络、加载数据(数据库)和资源。

    ANR:Application Not Responding,应用长时间不能响应用户操作。





    查看出现ANR的原因,导出data/anr/traces.txt文件,打开。





    ANR无法调试,解决方法就是在主线程中不要做耗时操作。

    只有主线程可以刷新UI,主线程又称UI线程。这样做,是为了线程安全,只有一个线程可以刷新UI,如果有多个线程同时刷新UI,就可能出现线程安全导致UI刷新混乱的问题。

    如果我们在MainActivity.java中启动一个线程进行网络请求,再次执行该应用程序就会报警告,如下:





    通过异常可以看出来,刷新UI的代码必须在主线程中执行,但是请求图片的代码又由于不能引起阻塞,只能在子线程中执行,而且只有请求完图片之后才能刷新UI,这时候就形成了矛盾。Android里面提供了一种机制能够解决这个问题,也就是Handler机制。

    Handler机制



    主线程创建的时候,主线程中有一个消息队列MessageQueue,用来存放消息。还有一个Looper用来不断检测MessageQueue是否有消息。如果有消息就交给消息处理器Handler,Handler中有一个方法handleMessage,用来处理消息,这个方法是在主线程调用。那么,这个方法就可以刷新UI。如此,子线程想要刷新UI,只需调用Handler的sendMessag方法,将消息发送到MessageQueue即可。

    示例:

    src/cn.itcast.imageviewer2/MainActivity.java
  1. package cn.itcast.imageviewer2;

  2. import java.io.InputStream;
  3. import java.net.HttpURLConnection;
  4. import java.net.URL;

  5. import android.app.Activity;
  6. import android.graphics.Bitmap;
  7. import android.graphics.BitmapFactory;
  8. import android.os.Bundle;
  9. import android.os.Handler;
  10. import android.os.Message;
  11. import android.view.View;
  12. import android.widget.ImageView;
  13. import android.widget.Toast;

  14. public class MainActivity extends Activity {

  15.         Handler handler = new Handler(){
  16.                 //程序员需要重写handlerMessage方法,刷新UI
  17.                 @Override
  18.                 public void handleMessage(Message msg) {
  19.                         switch(msg.what){
  20.                         case 1:
  21.                                 //判断消息是成功消息还是失败消息
  22.                                 ImageView iv = (ImageView) findViewById(R.id.iv);
  23.                                 iv.setImageBitmap((Bitmap)msg.obj);
  24.                                 break;
  25.                         case 2:
  26.                                 Toast.makeText(MainActivity.this, "请求失败啦啦啦", 0).show();
  27.                                 break;
  28.                         }
  29.                 }
  30.         };
  31.         
  32.         @Override
  33.         protected void onCreate(Bundle savedInstanceState) {
  34.                 super.onCreate(savedInstanceState);
  35.                 setContentView(R.layout.activity_main);
  36.         }

  37.         public void click(View v){
  38.                 Thread t = new Thread(){
  39.                         public void run(){
  40.                                 String path = "http://192.168.1.100:8080/sh.jpg";
  41.                                 try{
  42.                                         URL url = new URL(path);
  43.                                         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  44.                                         conn.setRequestMethod("GET");
  45.                                         conn.setConnectTimeout(8000);
  46.                                         conn.setReadTimeout(8000);
  47.                                         conn.connect();
  48.                                         if(conn.getResponseCode() == 200){
  49.                                                 InputStream is = conn.getInputStream();
  50.                                                 Bitmap bm = BitmapFactory.decodeStream(is);
  51.                                                 //创建消息对象
  52.                                                 Message msg = new Message();
  53.                                                 //消息对象可以携带数据
  54.                                                 msg.obj = bm;
  55.                                                 msg.what = 1;
  56.                                                 //发送消息至主线程的消息队列
  57.                                                 handler.sendMessage(msg);
  58.                                         }else{
  59.                                                 Message msg = new Message();
  60.                                                 msg.what = 2;
  61.                                                 handler.sendMessage(msg);
  62.                                                 //由于Message没有携带任何数据,所以上面3句可以用下面1句替换。
  63.                                                 //handler.sendEmptyMessage(2);
  64.                                         }
  65.                                 }catch(Exception e){
  66.                                         e.printStackTrace();
  67.                                 }
  68.                         }
  69.                 };
  70.                 t.start();
  71.         }
  72. }
复制代码
    运行结果:



    添加缓存功能的图片查看器
   
    为了提升效率,查看图片后,缓存起来,便于下次查看图片不必在通过网络请求。

    代码:

    src/cn.itcast.imageviewer3/MainActivity.java
  1. package cn.itcast.imageviewer3;

  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.InputStream;
  5. import java.net.HttpURLConnection;
  6. import java.net.URL;

  7. import android.app.Activity;
  8. import android.graphics.Bitmap;
  9. import android.graphics.BitmapFactory;
  10. import android.os.Bundle;
  11. import android.os.Handler;
  12. import android.os.Message;
  13. import android.view.View;
  14. import android.widget.ImageView;
  15. import android.widget.Toast;

  16. public class MainActivity extends Activity {

  17.         Handler handler = new Handler(){
  18.                 @Override
  19.                 public void handleMessage(Message msg) {
  20.                         switch(msg.what){
  21.                         case 1:
  22.                                 ImageView iv = (ImageView) findViewById(R.id.iv);
  23.                                 iv.setImageBitmap((Bitmap)msg.obj);
  24.                                 break;
  25.                         case 2:
  26.                                 Toast.makeText(MainActivity.this, "请求失败啦啦啦", 0).show();
  27.                                 break;
  28.                         }
  29.                 }
  30.         };
  31.         
  32.         @Override
  33.         protected void onCreate(Bundle savedInstanceState) {
  34.                 super.onCreate(savedInstanceState);
  35.                 setContentView(R.layout.activity_main);
  36.         }

  37.         public void click(View v){
  38.                
  39.                 final String path = "http://192.168.1.100:8080/sh.jpg";
  40.                 final File file = new File(getCacheDir(),getFileName(path));

  41.                 if(file.exists()){
  42.                         System.out.println("从缓存获取");
  43.                         Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
  44.                         ImageView iv = (ImageView) findViewById(R.id.iv);
  45.                         iv.setImageBitmap(bm);
  46.                 }else{
  47.                         Thread t = new Thread(){
  48.                                 public void run(){
  49.                                         try{
  50.                                                 System.out.println("从网络获取");
  51.                                                 URL url = new URL(path);
  52.                                                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  53.                                                 conn.setRequestMethod("GET");
  54.                                                 conn.setConnectTimeout(8000);
  55.                                                 conn.setReadTimeout(8000);
  56.                                                 conn.connect();
  57.                                                 if(conn.getResponseCode() == 200){
  58.                                                         InputStream is = conn.getInputStream();
  59.                                                         
  60.                                                         //需要自己开启文件输出流,读取流里数据的同时,把数据写到本地
  61.                                                         byte[] b = new byte[1024];
  62.                                                         int len;
  63.                                                         FileOutputStream fos = new FileOutputStream(file);
  64.                                                         while((len = is.read(b)) != -1){
  65.                                                                 fos.write(b, 0, len);
  66.                                                         }
  67.                                                         fos.close();
  68.         
  69.                                                         //流里数据已经读取完毕,这行代码无法再构造图片了
  70.                                                         //Bitmap bm = BitmapFactory.decodeStream(is);
  71.                                                         
  72.                                                         Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
  73.                                                         
  74.                                                         Message msg = new Message();
  75.                                                         msg.obj = bm;
  76.                                                         msg.what = 1;
  77.                                                         handler.sendMessage(msg);
  78.                                                 }else{
  79.                                                         Message msg = new Message();
  80.                                                         msg.what = 2;
  81.                                                         handler.sendMessage(msg);
  82.                                                 }
  83.                                         }catch(Exception e){
  84.                                                 e.printStackTrace();
  85.                                         }
  86.                                 }
  87.                         };
  88.                         t.start();
  89.                 }
  90.         }
  91.         
  92.         public String getFileName(String path){
  93.                 int index = path.lastIndexOf("/");
  94.                 return path.substring(index+1);
  95.         }
  96. }
复制代码
   运行结果:




    第一次,从网络获取。第二次再次点击按钮,从缓存获取。




    获取开源代码

    在程序开发中,会遇到很多问题。例如,请求图片过程中遇到断网,网速慢,图片过大内存不够等等情况。由于这些情况大部分项目都会遇到,所以网上有很多写好的现成的模块。不要重新发明轮子,可以直接拿来用。

    好的开源网站:
    http://code.google.com
    http://github.com

    搜索,下载源码,解压,复制到我们自己的应用程序项目中。









    res\layout\activity_main.xml
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     tools:context=".MainActivity" >

  6.     <Button
  7.         android:layout_width="wrap_content"
  8.         android:layout_height="wrap_content"
  9.         android:text="请求网络图片"
  10.         android:onClick="click"
  11.         />

  12.     <com.loopj.android.image.SmartImageView
  13.         android:id="@+id/iv"
  14.         android:layout_width="wrap_content"
  15.         android:layout_height="wrap_content"
  16.         android:src="@drawable/ic_launcher"
  17.         android:layout_centerInParent="true"
  18.         />
  19. </RelativeLayout>
复制代码

    SmartImageView是自定义控件,以后会有专门的课程讲解。



    src/cn.itcast.smartimageview/MainActivity.java
  1. package cn.itcast.smartimageview;

  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.View;

  5. import com.loopj.android.image.SmartImageView;

  6. public class MainActivity extends Activity {

  7.         @Override
  8.         protected void onCreate(Bundle savedInstanceState) {
  9.                 super.onCreate(savedInstanceState);
  10.                 setContentView(R.layout.activity_main);
  11.         }

  12.     public void click(View v){
  13.             String path = "http://192.168.1.100:8080/sh.jpg";
  14.             
  15.             SmartImageView iv = (SmartImageView) findViewById(R.id.iv);
  16.             iv.setImageUrl(path);
  17.     }
  18. }  
复制代码
   
    添加权限:



    运行结果:



    Html源文件查看器

    代码:

    res\layout\activity_main.xml
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     tools:context=".MainActivity" >

  6.     <Button
  7.         android:layout_width="wrap_content"
  8.         android:layout_height="wrap_content"
  9.         android:text="请求网络"
  10.         android:onClick="click"/>

  11.     <ScrollView
  12.         android:layout_width="match_parent"
  13.         android:layout_height="match_parent"
  14.         >
  15.             <TextView
  16.                 android:id="@+id/tv"
  17.                 android:layout_width="match_parent"
  18.                 android:layout_height="match_parent"
  19.                 />
  20.     </ScrollView>
  21. </RelativeLayout>
复制代码

    src/cn.itcast.htmlviewer.tool/Tools.java
  1. package cn.itcast.htmlviewer.tool;

  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.InputStream;

  5. public class Tools {

  6.         public static String getTextFromStream(InputStream is){
  7.                
  8.                 try{
  9.                         byte[] b = new byte[1024];
  10.                         int len;
  11.                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
  12.                         
  13.                         while((len = is.read(b)) != -1){
  14.                                 bos.write(b,0,len);
  15.                         }
  16.                         
  17.                         //把输出流里的内容转换成字节数组
  18.                         String text = new String(bos.toByteArray());
  19.                         return text;
  20.                 }catch(IOException e){
  21.                         e.printStackTrace();
  22.                 }
  23.                
  24.                 return null;
  25.         }
  26. }
复制代码

    src/cn.itcast.htmlviewer/MainActivity.java
  1. package cn.itcast.htmlviewer;

  2. import java.io.InputStream;
  3. import java.net.HttpURLConnection;
  4. import java.net.URL;

  5. import android.app.Activity;
  6. import android.os.Bundle;
  7. import android.os.Handler;
  8. import android.os.Message;
  9. import android.view.View;
  10. import android.widget.TextView;
  11. import cn.itcast.htmlviewer.tool.Tools;

  12. public class MainActivity extends Activity {

  13.         Handler handler = new Handler(){
  14.                 @Override
  15.                 public void handleMessage(Message msg) {
  16.                         TextView tv = (TextView) findViewById(R.id.tv);
  17.                         tv.setText((String)msg.obj);
  18.                 }
  19.         };
  20.         
  21.         @Override
  22.         protected void onCreate(Bundle savedInstanceState) {
  23.                 super.onCreate(savedInstanceState);
  24.                 setContentView(R.layout.activity_main);
  25.         }

  26.         public void click(View v){
  27.                 Thread t = new Thread(){
  28.                         public void run(){
  29.                                 String path = "http://192.168.1.100:8080/baidu.html";
  30.                                 try{
  31.                                         URL url = new URL(path);
  32.                                         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  33.                                         conn.setRequestMethod("GET");
  34.                                         conn.setConnectTimeout(8000);
  35.                                         conn.setReadTimeout(8000);
  36.                                        
  37.                                         //先发送请求,再获取响应码,getResponseCode方法自身会发送请求消息
  38.                                         if(conn.getResponseCode() == 200){
  39.                                                 InputStream is = conn.getInputStream();
  40.                                                 String text = Tools.getTextFromStream(is);
  41.                                                 
  42.                                                 //如果消息池中没有消息,new一个,如果有,复用这条空闲消息
  43.                                                 Message msg = handler.obtainMessage();
  44.                                                 msg.obj = text;
  45.                                                 handler.sendMessage(msg);
  46.                                         }
  47.                                 }catch(Exception e){
  48.                                         e.printStackTrace();
  49.                                 }
  50.                         }
  51.                 };
  52.                 t.start();
  53.         }
  54. }
复制代码

    添加权限:



    运行结果:



    如果html文件为gbk编码:



    那么Tools.java中代码只要进行如下修改即可:



    新闻客户端布局



    res\layout\item_listview.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="wrap_content" >
  5.    
  6.         <ImageView
  7.             android:id="@+id/iv"
  8.             android:layout_width="90dp"
  9.             android:layout_height="70dp"
  10.             android:src="@drawable/ic_launcher"
  11.             android:layout_centerVertical="true"
  12.             />
  13.         
  14.         <!-- android:singleLine是为了让过长的标题不会导致换行,影响美观 -->
  15.         <TextView
  16.             android:id="@+id/tv_title"
  17.             android:layout_width="wrap_content"
  18.             android:layout_height="wrap_content"
  19.             android:textSize="22sp"
  20.             android:text="这是标题"
  21.             android:singleLine="true"
  22.             android:layout_toRightOf="@+id/iv"
  23.             />
  24.         
  25.         <!-- android:lines表示如果文本过长,最多显示几行 -->
  26.         <TextView
  27.             android:id="@+id/tv_detail"
  28.             android:layout_width="wrap_content"
  29.             android:layout_height="wrap_content"
  30.             android:textSize="15sp"
  31.             android:text="这是详细"
  32.             android:lines="2"
  33.             android:textColor="@android:color/darker_gray"
  34.             android:layout_toRightOf="@+id/iv"
  35.             android:layout_below="@id/tv_title"
  36.             />
  37.         
  38.         <TextView
  39.             android:id="@+id/tv_comment"
  40.             android:layout_width="wrap_content"
  41.             android:layout_height="wrap_content"
  42.             android:text="12345条评论"
  43.             android:textColor="#ff0000"
  44.             android:layout_alignParentRight="true"
  45.             android:layout_below="@id/tv_detail"
  46.             />
  47.         
  48. </RelativeLayout>
复制代码
    效果:



    获取新闻信息

    将news.xml和images文件夹存放在Tomcat服务器中。



    News.xml
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <newslist>
  3.         <news>
  4.                 <title>西安一期就业快报</title>
  5.                 <detail>热烈祝贺西安一期平均薪水突破13k</detail>
  6.                 <comment>15687</comment>
  7.                 <image>http://192.168.1.100:8080/images/6.jpg</image>
  8.         </news>
  9.         <news>
  10.                 <title>程序员因写代码太乱被杀害</title>
  11.                 <detail>凶手是死者同事,维护死者代码时完全看不懂而痛下杀手</detail>
  12.                 <comment>16359</comment>
  13.                 <image>http://192.168.1.100:8080/images/7.jpg</image>
  14.         </news>
  15.         <news>
  16.                 <title>产品经理因频繁改需求被杀害</title>
  17.                 <detail>凶手是一名程序员,因死者对项目需求频繁改动而痛下杀手</detail>
  18.                 <comment>14112</comment>
  19.                 <image>http://192.168.1.100:8080/images/7.jpg</image>
  20.         </news>
  21.         <news>
  22.                 <title>3Q大战宣判: 腾讯获赔500万</title>
  23.                 <detail>最高法驳回360上诉, 维持一审宣判.</detail>
  24.                 <comment>6427</comment>
  25.                 <image>http://192.168.1.100:8080/images/1.jpg</image>
  26.         </news>
  27.         <news>
  28.                 <title>今日之声:北大雕塑被戴口罩</title>
  29.                 <detail>市民: 因雾霾起诉环保局; 公务员谈"紧日子": 坚决不出去.</detail>
  30.                 <comment>681</comment>
  31.                 <image>http://192.168.1.100:8080/images/2.jpg</image>
  32.         </news>
  33.         <news>
  34.                 <title>奥巴马见达赖是装蒜</title>
  35.                 <detail>外文局: 国际民众认可中国大国地位;法院: "流量清零"未侵权.</detail>
  36.                 <comment>1359</comment>
  37.                 <image>http://192.168.1.100:8080/images/3.jpg</image>
  38.         </news>
  39.         <news>
  40.                 <title>轻松一刻: 我要沉迷学习不自拔</title>
  41.                 <detail>放假时我醒了不代表我起床了, 如今我起床了不代表我醒了!</detail>
  42.                 <comment>11616</comment>
  43.                 <image>http://192.168.1.100:8080/images/4.jpg</image>
  44.         </news>
  45.         <news>
  46.                 <title>男女那些事儿</title>
  47.                 <detail>"妈, 我在东莞被抓, 要2万保释金, 快汇钱到xxx!"</detail>
  48.                 <comment>10339</comment>
  49.                 <image>http://192.168.1.100:8080/images/5.jpg</image>
  50.         </news>
  51.         <news>
  52.                 <title>赵帅哥语录一</title>
  53.                 <detail>少壮不努力,老大做IT</detail>
  54.                 <comment>14612</comment>
  55.                 <image>http://192.168.1.100:8080/images/8.jpg</image>
  56.         </news>
  57.         <news>
  58.                 <title>赵帅哥语录二</title>
  59.                 <detail>问君能有几多愁,恰似调完代码改需求</detail>
  60.                 <comment>13230</comment>
  61.                 <image>http://192.168.1.100:8080/images/8.jpg</image>
  62.         </news>
  63.         <news>
  64.                 <title>赵帅哥语录三</title>
  65.                 <detail>觉得我帅的人工资一般都比较高</detail>
  66.                 <comment>9928</comment>
  67.                 <image>http://192.168.1.100:8080/images/8.jpg</image>
  68.         </news>
  69.         <news>
  70.                 <title>今日之声:北大雕塑被戴口罩</title>
  71.                 <detail>市民: 因雾霾起诉环保局; 公务员谈"紧日子": 坚决不出去.</detail>
  72.                 <comment>681</comment>
  73.                 <image>http://192.168.1.100:8080/images/2.jpg</image>
  74.         </news>
  75.         <news>
  76.                 <title>奥巴马见达赖是装蒜</title>
  77.                 <detail>外文局: 国际民众认可中国大国地位;法院: "流量清零"未侵权.</detail>
  78.                 <comment>1359</comment>
  79.                 <image>http://192.168.1.100:8080/images/3.jpg</image>
  80.         </news>
  81. </newslist>
复制代码

    src/cn.itcast.news.domain/News.java
  1. package cn.itcast.news.domain;

  2. public class News {

  3.         private String title;
  4.         private String detail;
  5.         private String comment;
  6.         private String imageUrl;
  7.         
  8.         public String getTitle() {
  9.                 return title;
  10.         }
  11.         public void setTitle(String title) {
  12.                 this.title = title;
  13.         }
  14.         public String getDetail() {
  15.                 return detail;
  16.         }
  17.         public void setDetail(String detail) {
  18.                 this.detail = detail;
  19.         }
  20.         public String getComment() {
  21.                 return comment;
  22.         }
  23.         public void setComment(String comment) {
  24.                 this.comment = comment;
  25.         }
  26.         public String getImageUrl() {
  27.                 return imageUrl;
  28.         }
  29.         public void setImageUrl(String imageUrl) {
  30.                 this.imageUrl = imageUrl;
  31.         }
  32. }
复制代码

    src/cn.itcast.news/MainActivity.java
  1. package cn.itcast.news;

  2. import java.io.InputStream;
  3. import java.net.HttpURLConnection;
  4. import java.net.URL;
  5. import java.util.ArrayList;
  6. import java.util.List;

  7. import org.xmlpull.v1.XmlPullParser;

  8. import android.app.Activity;
  9. import android.os.Bundle;
  10. import android.util.Xml;
  11. import cn.itcast.news.domain.News;

  12. public class MainActivity extends Activity {

  13.         List<News> newsList;
  14.         
  15.         @Override
  16.         protected void onCreate(Bundle savedInstanceState) {
  17.                 super.onCreate(savedInstanceState);
  18.                 setContentView(R.layout.activity_main);
  19.                
  20.                 getNewsInfo();
  21.         }
  22.         
  23.         private void getNewsInfo(){
  24.                 Thread t = new Thread(){
  25.                         public void run(){
  26.                                 String path = "http://192.168.1.100:8080/news.xml";
  27.                                 try{
  28.                                         URL url = new URL(path);
  29.                                         HttpURLConnection conn = (HttpURLConnection)url.openConnection();
  30.                                         conn.setRequestMethod("GET");
  31.                                         conn.setConnectTimeout(8000);
  32.                                         conn.setReadTimeout(8000);
  33.                                        
  34.                                         if(conn.getResponseCode() == 200){
  35.                                                 //获取服务器返回的流,流里就是xml文件
  36.                                                 InputStream is = conn.getInputStream();
  37.                                                 parserNewsInfo(is);
  38.                                         }
  39.                                 }catch(Exception e){
  40.                                         e.printStackTrace();
  41.                                 }
  42.                         }
  43.                 };
  44.                 t.start();
  45.         }
  46.         
  47.         private void parserNewsInfo(InputStream is){
  48.                 XmlPullParser xp = Xml.newPullParser();
  49.                 try{
  50.                         xp.setInput(is,"utf-8");
  51.                         
  52.                         int type = xp.getEventType();
  53.                         
  54.                         News news = null;
  55.                         
  56.                         while(type != XmlPullParser.END_DOCUMENT){
  57.                                 switch(type){
  58.                                         case XmlPullParser.START_TAG:
  59.                                                 if("newslist".equals(xp.getName())){
  60.                                                         newsList = new ArrayList<News>();
  61.                                                 }else if("news".equals(xp.getName())){
  62.                                                         news = new News();
  63.                                                 }else if("title".equals(xp.getName())){
  64.                                                         String title = xp.nextText();
  65.                                                         news.setTitle(title);
  66.                                                 }else if("detail".equals(xp.getName())){
  67.                                                         String detail = xp.nextText();
  68.                                                         news.setDetail(detail);
  69.                                                 }else if("comment".equals(xp.getName())){
  70.                                                         String comment = xp.nextText();
  71.                                                         news.setComment(comment);
  72.                                                 }else if("image".equals(xp.getName())){
  73.                                                         String image = xp.nextText();
  74.                                                         news.setImageUrl(image);
  75.                                                 }
  76.                                                 break;
  77.                                         case XmlPullParser.END_TAG:
  78.                                                 if("news".equals(xp.getName())){
  79.                                                         newsList.add(news);
  80.                                                 }
  81.                                                 break;
  82.                                 }
  83.                                 type = xp.next();
  84.                         }
  85.                 }catch(Exception e){
  86.                         e.printStackTrace();
  87.                 }
  88.         }
  89. }
复制代码

    把新闻信息显示至界面

    修改res\layout\item_listview.xml中的ImageView标签为SmartImageView标签,便于显示图片。





    src/cn.itcast.news/MainActivity.java
  1. package cn.itcast.news;

  2. import java.io.InputStream;
  3. import java.net.HttpURLConnection;
  4. import java.net.URL;
  5. import java.util.ArrayList;
  6. import java.util.List;

  7. import org.xmlpull.v1.XmlPullParser;

  8. import android.app.Activity;
  9. import android.os.Bundle;
  10. import android.os.Handler;
  11. import android.os.Message;
  12. import android.util.Xml;
  13. import android.view.View;
  14. import android.view.ViewGroup;
  15. import android.widget.BaseAdapter;
  16. import android.widget.ListView;
  17. import android.widget.TextView;
  18. import cn.itcast.news.domain.News;

  19. import com.loopj.android.image.SmartImageView;

  20. public class MainActivity extends Activity {

  21.         List<News> newsList;
  22.         Handler handler = new Handler(){
  23.                 @Override
  24.                 public void handleMessage(Message msg) {
  25.                         ListView lv = (ListView)findViewById(R.id.lv);
  26.                         lv.setAdapter(new MyAdapter());
  27.                 }
  28.         };
  29.         @Override
  30.         protected void onCreate(Bundle savedInstanceState) {
  31.                 super.onCreate(savedInstanceState);
  32.                 setContentView(R.layout.activity_main);
  33.                
  34.                 getNewsInfo();
  35.                 //由于getNewsInfo方法中是开启了一个子线程,与主线程并行。所以当listview显示内容时,newsList还没来得及创建,所以lv设置显示内容的代码不能运行在这里。要保证运行在xml解析完毕之后。
  36.                 //ListView lv = (ListView)findViewById(R.id.lv);
  37.                 //lv.setAdapter(new MyAdapter());
  38.         }
  39.         
  40.         class MyAdapter extends BaseAdapter{

  41.                 @Override
  42.                 public int getCount() {
  43.                         //之所以系统需要知道条目数量,是因为屏幕右侧的控制条显示基于此数据而定
  44.                         return newsList.size();
  45.                 }

  46.                 @Override
  47.                 public Object getItem(int position) {
  48.                         return null;
  49.                 }

  50.                 @Override
  51.                 public long getItemId(int position) {
  52.                         return 0;
  53.                 }

  54.                 //视图保存器
  55.                 class ViewHolder{
  56.                         TextView tv_title;
  57.                         TextView tv_detail;
  58.                         TextView tv_comment;
  59.                         SmartImageView siv;
  60.                 }
  61.                                 
  62.                 @Override
  63.                 public View getView(int position, View convertView, ViewGroup parent) {
  64.                         View v = null;
  65.                         
  66.                         ViewHolder vh = null;
  67.                         News news = newsList.get(position);
  68.                         
  69.                         if(convertView == null){
  70.                                 v = View.inflate(MainActivity.this, R.layout.item_listview, null);
  71.                                 
  72.                                 vh = new ViewHolder();
  73.                                 //如果缓存为空,那么需要填充新的View对象,同时找到布局文件中的所有组件,并封装至ViewHolder对象中
  74.                                 vh.tv_title = (TextView)v.findViewById(R.id.tv_title);
  75.                                 vh.tv_detail = (TextView)v.findViewById(R.id.tv_detail);
  76.                                 vh.tv_comment = (TextView)v.findViewById(R.id.tv_comment);
  77.                                 vh.siv = (SmartImageView) v.findViewById(R.id.iv);
  78.                                 
  79.                                 //把ViewHolder对象存入View对象中,缓存View对象,同时缓存了ViewHolder对象
  80.                                 v.setTag(vh);
  81.                         }else{
  82.                                 v = convertView;
  83.                                 //从缓存中取出ViewHolder对象,这个对象中就封装了布局文件中所有的组件对象,那么就不需要再次findViewById了
  84.                                 vh = (ViewHolder)v.getTag();
  85.                         }
  86.                         
  87.                         vh.tv_title.setText(news.getTitle());
  88.                         vh.tv_detail.setText(news.getDetail());
  89.                         vh.tv_comment.setText(news.getComment() + "条评论");
  90.                         vh.siv.setImageUrl(news.getImageUrl());
  91.                         
  92.                         return v;
  93.                 }
  94.         }
  95.         
  96.         private void getNewsInfo(){
  97.                 Thread t = new Thread(){
  98.                         public void run(){
  99.                                 String path = "http://192.168.1.100:8080/news.xml";
  100.                                 try{
  101.                                         URL url = new URL(path);
  102.                                         HttpURLConnection conn = (HttpURLConnection)url.openConnection();
  103.                                         conn.setRequestMethod("GET");
  104.                                         conn.setConnectTimeout(8000);
  105.                                         conn.setReadTimeout(8000);
  106.                                        
  107.                                         if(conn.getResponseCode() == 200){
  108.                                                 InputStream is = conn.getInputStream();
  109.                                                 parserNewsInfo(is);
  110.                                         }
  111.                                 }catch(Exception e){
  112.                                         e.printStackTrace();
  113.                                 }
  114.                         }
  115.                 };
  116.                 t.start();
  117.         }
  118.         
  119.         private void parserNewsInfo(InputStream is){
  120.                 XmlPullParser xp = Xml.newPullParser();
  121.                 try{
  122.                         xp.setInput(is,"utf-8");
  123.                         
  124.                         int type = xp.getEventType();
  125.                         
  126.                         News news = null;
  127.                         
  128.                         while(type != XmlPullParser.END_DOCUMENT){
  129.                                 switch(type){
  130.                                         case XmlPullParser.START_TAG:
  131.                                                 if("newslist".equals(xp.getName())){
  132.                                                         newsList = new ArrayList<News>();
  133.                                                 }else if("news".equals(xp.getName())){
  134.                                                         news = new News();
  135.                                                 }else if("title".equals(xp.getName())){
  136.                                                         String title = xp.nextText();
  137.                                                         news.setTitle(title);
  138.                                                 }else if("detail".equals(xp.getName())){
  139.                                                         String detail = xp.nextText();
  140.                                                         news.setDetail(detail);
  141.                                                 }else if("comment".equals(xp.getName())){
  142.                                                         String comment = xp.nextText();
  143.                                                         news.setComment(comment);
  144.                                                 }else if("image".equals(xp.getName())){
  145.                                                         String image = xp.nextText();
  146.                                                         news.setImageUrl(image);
  147.                                                 }
  148.                                                 break;
  149.                                         case XmlPullParser.END_TAG:
  150.                                                 if("news".equals(xp.getName())){
  151.                                                         newsList.add(news);
  152.                                                 }
  153.                                                 break;
  154.                                 }
  155.                                 type = xp.next();
  156.                         }
  157.                 }catch(Exception e){
  158.                         e.printStackTrace();
  159.                 }
  160.                
  161.                 //xml解析完毕,发送消息,通知主线程,设置lv的显示内容
  162.                 handler.sendEmptyMessage(1);
  163.         }
  164. }
复制代码
      添加权限:



    运行结果:



    使用get方式提交表单

    使用MyEclipse,新建一个Servlet:Login.java,把该Web项目部署到Tomcat服务器上。



    src/cn.itcast.login/Login.java
  1. package cn.itcast.login;

  2. import java.io.IOException;

  3. import javax.servlet.ServletException;
  4. import javax.servlet.ServletOutputStream;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;

  8. public class Login extends HttpServlet {

  9.         public void doGet(HttpServletRequest request, HttpServletResponse response)
  10.                         throws ServletException, IOException {
  11.                 String name = request.getParameter("name");
  12.                 String pass = request.getParameter("pass");
  13.                
  14.                 ServletOutputStream os = response.getOutputStream();
  15.                 if("asd".equals(name)&&"123".equals(pass)){
  16.                         os.write("登陆成功".getBytes("utf-8"));
  17.                 }else{
  18.                         os.write("登陆失败".getBytes("utf-8"));
  19.                 }
  20.         }

  21.         public void doPost(HttpServletRequest request, HttpServletResponse response)
  22.                         throws ServletException, IOException {

  23.                 doGet(request, response);
  24.         }
  25. }
复制代码

    WebRoot/index.jsp
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>

  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  3. <html>
  4.   <head>
  5.   </head>
  6.   
  7.   <body>
  8.     <form action="/Web/servlet/Login" method=get>
  9.             账号:<input type="text" name="name"><br/>
  10.             密码:<input type="text" name="pass"><br/>
  11.             <input type="submit" value="登陆"/>
  12.     </form>
  13.   </body>
  14. </html>
复制代码

    打开浏览器,输入地址,然后输入用户名和密码,提交。



    将浏览器编码改为UTF-8。



    结果如下:



    Android之Get方式提交数据:

    res\layout\activity_main.xml
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     tools:context=".MainActivity"
  6.     android:orientation="vertical">

  7.     <EditText
  8.         android:id="@+id/et_name"
  9.         android:layout_width="match_parent"
  10.         android:layout_height="wrap_content"/>

  11.     <EditText
  12.         android:id="@+id/et_pass"
  13.         android:layout_width="match_parent"
  14.         android:layout_height="wrap_content"/>
  15.    
  16.     <Button
  17.         android:layout_width="wrap_content"
  18.         android:layout_height="wrap_content"
  19.         android:text="登陆"
  20.         android:onClick="click"
  21.         />
  22. </LinearLayout>
复制代码

    src/cn.itcast.getmethod.tool/Tools.java
  1. package cn.itcast.getmethod.tool;

  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.InputStream;

  5. public class Tools {

  6.         public static String getTextFromStream(InputStream is){
  7.                
  8.                 try{
  9.                         byte[] b = new byte[1024];
  10.                         int len;
  11.                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
  12.                         
  13.                         while((len = is.read(b)) != -1){
  14.                                 bos.write(b,0,len);
  15.                         }
  16.                         
  17.                         //把输出流里的内容转换成字节数组
  18.                         String text = new String(bos.toByteArray());
  19.                         return text;
  20.                 }catch(IOException e){
  21.                         e.printStackTrace();
  22.                 }
  23.                
  24.                 return null;
  25.         }
  26. }
复制代码

    src/cn.itcast.getmethod/MainActivity.java
  1. package cn.itcast.getmethod;

  2. import java.io.InputStream;
  3. import java.net.HttpURLConnection;
  4. import java.net.URL;

  5. import android.app.Activity;
  6. import android.os.Bundle;
  7. import android.os.Handler;
  8. import android.os.Message;
  9. import android.view.View;
  10. import android.widget.EditText;
  11. import android.widget.Toast;
  12. import cn.itcast.getmethod.tool.Tools;

  13. public class MainActivity extends Activity {

  14.         Handler handler = new Handler(){
  15.                 @Override
  16.                 public void handleMessage(Message msg) {
  17.                         Toast.makeText(MainActivity.this, (String)msg.obj, 0).show();
  18.                 }
  19.         };
  20.         
  21.         @Override
  22.         protected void onCreate(Bundle savedInstanceState) {
  23.                 super.onCreate(savedInstanceState);
  24.                 setContentView(R.layout.activity_main);
  25.         }
  26.         
  27.         public void click(View v){
  28.                 EditText et_name = (EditText)findViewById(R.id.et_name);
  29.                 EditText et_pass = (EditText)findViewById(R.id.et_pass);
  30.                
  31.                 String name = et_name.getText().toString();
  32.                 String pass = et_pass.getText().toString();
  33.                
  34.                 //在url后面拼接要提交的数据
  35.                 final String path = "http://localhost:8080/Web/servlet/Login?name=" + name + "&pass=" + pass;
  36.                
  37.                 Thread t = new Thread(){
  38.                         public void run(){
  39.                                 URL url;
  40.                                 try {
  41.                                         url = new URL(path);
  42.                                         HttpURLConnection conn = (HttpURLConnection)url.openConnection();
  43.                                         conn.setRequestMethod("GET");
  44.                                         conn.setConnectTimeout(8000);
  45.                                         conn.setReadTimeout(8000);
  46.                                        
  47.                                         if(conn.getResponseCode() == 200){
  48.                                                 InputStream is = conn.getInputStream();
  49.                                                 String text = Tools.getTextFromStream(is);
  50.                                                 
  51.                                                 Message msg = handler.obtainMessage();
  52.                                                 msg.obj = text;
  53.                                                 handler.sendMessage(msg);
  54.                                         }
  55.                                 } catch (Exception e) {
  56.                                         e.printStackTrace();
  57.                                 }
  58.                         }
  59.                 };
  60.                 t.start();
  61.         }
  62. }
复制代码

    添加权限:



    运行结果:



    提交表单的乱码问题

    将Login.java中“zhangsan”修改为“张三”,并且打印出接收到的用户名及密码。

    src/cn.itcast.login/Login.java
  1. package cn.itcast.login;

  2. import java.io.IOException;

  3. import javax.servlet.ServletException;
  4. import javax.servlet.ServletOutputStream;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;

  8. public class Login extends HttpServlet {

  9.         public void doGet(HttpServletRequest request, HttpServletResponse response)
  10.                         throws ServletException, IOException {
  11.                 String name = request.getParameter("name");
  12.                 String pass = request.getParameter("pass");
  13.                
  14.                 System.out.println(name);
  15.                 System.out.println(pass);
  16.                
  17.                 ServletOutputStream os = response.getOutputStream();
  18.                 if("张三".equals(name)&&"123".equals(pass)){
  19.                         os.write("登陆成功".getBytes("utf-8"));
  20.                 }else{
  21.                         os.write("登陆失败".getBytes("utf-8"));
  22.                 }
  23.         }

  24.         public void doPost(HttpServletRequest request, HttpServletResponse response)
  25.                         throws ServletException, IOException {

  26.                 doGet(request, response);
  27.         }
  28. }
复制代码
    重新部署,运行。结果如下:



    将浏览器编码调整为UTF-8。







    可以看到,打印出来的为乱码。原因在于浏览器提交表单的时候,会把“张三”用UTF-8编码变成字节数组,然后传给服务器。服务器拿到这些字节以后,因为getParameter默认使用iso8859-1编码把读取到的字节数组构造成字符串,导致乱码。因此,解决方案为,首先使用iso8859-1把字符串重新转换成字节数组,然后再用utf-8构造成字符串即可。



    重新部署,运行,结果如下:







    尝试通过手机端发送中文的情况,如下:

    输入中文,首先选择谷歌拼音输入法。









    由上面的图片可以看到,依然存在乱码问题。原因在于浏览器提交的数据都是经过URL编码的,所以,通过代码提交的数据就需要手动编码。





    修改后,运行结果:





    使用post方式提交表单

    修改表单为post提交方式:







    通过代码实现post提交请求,一方面通过流的方式将数据传输给服务器,一方面是给post请求头添加额外属性。

    src/cn.itcast.postmethod/MainActivity.java
  1. package cn.itcast.postmethod;

  2. import java.io.InputStream;
  3. import java.io.OutputStream;
  4. import java.net.HttpURLConnection;
  5. import java.net.URL;
  6. import java.net.URLEncoder;

  7. import android.app.Activity;
  8. import android.os.Bundle;
  9. import android.os.Handler;
  10. import android.os.Message;
  11. import android.view.View;
  12. import android.widget.EditText;
  13. import android.widget.Toast;
  14. import cn.itcast.getmethod.tool.Tools;

  15. public class MainActivity extends Activity {

  16.         Handler handler = new Handler(){
  17.                 @Override
  18.                 public void handleMessage(Message msg) {
  19.                         Toast.makeText(MainActivity.this, (String)msg.obj, 0).show();
  20.                 }
  21.         };
  22.         
  23.         @Override
  24.         protected void onCreate(Bundle savedInstanceState) {
  25.                 super.onCreate(savedInstanceState);
  26.                 setContentView(R.layout.activity_main);
  27.         }
  28.         
  29.         public void click(View v){
  30.                 EditText et_name = (EditText)findViewById(R.id.et_name);
  31.                 EditText et_pass = (EditText)findViewById(R.id.et_pass);
  32.                
  33.                 final String name = et_name.getText().toString();
  34.                 final String pass = et_pass.getText().toString();
  35.                
  36.                 final String path = "http://192.168.1.100:8080/Web/servlet/Login";
  37.                
  38.                 Thread t = new Thread(){
  39.                         
  40.                         public void run(){
  41.                                 URL url;
  42.                                 try {
  43.                                         url = new URL(path);
  44.                                         HttpURLConnection conn = (HttpURLConnection)url.openConnection();
  45.                                         conn.setRequestMethod("POST");
  46.                                         conn.setConnectTimeout(8000);
  47.                                         conn.setReadTimeout(8000);
  48.                                        
  49.                                         //post请求头需要添加额外属性
  50.                                         conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
  51.                                        
  52.                                         String content = "name=" + URLEncoder.encode(name) + "&pass=" + pass;
  53.                                         conn.setRequestProperty("Content-Length", content.length() + "");
  54.                                        
  55.                                         //开启请求头的流,把要提交的数据写入流中
  56.                                         //设置打开连接对象输出流
  57.                                         conn.setDoOutput(true);
  58.                                         OutputStream os = conn.getOutputStream();
  59.                                        
  60.                                         os.write(content.getBytes());
  61.                                        
  62.                                         if(conn.getResponseCode() == 200){
  63.                                                 InputStream is = conn.getInputStream();
  64.                                                 String text = Tools.getTextFromStream(is);
  65.                                                 
  66.                                                 Message msg = handler.obtainMessage();
  67.                                                 msg.obj = text;
  68.                                                 handler.sendMessage(msg);
  69.                                         }
  70.                                 } catch (Exception e) {
  71.                                         e.printStackTrace();
  72.                                 }
  73.                         }
  74.                 };
  75.                 t.start();
  76.         }
  77. }
复制代码
   运行结果:



~END~



~爱上海,爱黑马~



作者: jwl245322883    时间: 2015-6-28 00:32
在论坛这么久,第一次抢到首席
作者: 遮天    时间: 2015-6-28 10:11
一直关注中............
作者: 黑马94那么拽    时间: 2015-6-28 10:42
先收藏了。。。。。
作者: keto    时间: 2015-6-28 16:04
慢慢学习。。。。。
作者: HM_HC    时间: 2015-6-28 16:21
慢慢学习
作者: javazhang    时间: 2015-6-28 17:36
每天总有收货就是最大的进步!
作者: tinibuzhi    时间: 2015-6-28 22:28
学习了:victory:
作者: qian0217wei    时间: 2015-6-28 23:32
支持阳哥!
作者: zhangcong    时间: 2015-6-29 12:27
阳哥的笔记真赞,后面得认真看看!
作者: javazhang    时间: 2015-7-1 10:45
不管刮风还是下雨打炸雷,我都会到帖子里看一看。
作者: ZhangHeng    时间: 2015-7-1 13:05
今天一小步,明天一大步。
作者: 黑马:二师兄    时间: 2015-7-7 18:04
小赞怡情,大赞伤身!
作者: 星痕-凌    时间: 2015-7-9 22:27
顶 顶 顶 顶 !!!!!!!!!!!!!!!!!!!!
作者: RichardM    时间: 2015-7-9 23:16
阳哥笔记,好资料呀,值得好好学习学习
作者: RichardM    时间: 2015-7-9 23:20
厉害呀,必须得好好学习一番。。。。
作者: 雪域雄鹰    时间: 2016-2-25 20:11
宝贵的资源,要好好学习!
作者: 雪域雄鹰    时间: 2016-2-27 13:56
好好学习!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2