欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

Android的网络与通信

来源: 开发者 投稿于  被查看 32438 次 评论:6

Android的网络与通信


第一部分 Android网络基础
   Android平台浏览器采用了WeBKit引擎,这款名为Chorme Lite的Web浏览器拥有强大扩展特性,
每个开发者都以为编写自己的插件,使得浏览器的功能更加完善。
   目前Android平台有3种网络接口。
  第一种 java.net.*(标准java接口)
   此接口提供与联网有关的类,包括流和数据包套接字、Internet协议、常见HTTP处理。
如:创建URL以及URLConnection/HttpURLConnection对象、
设置连接参数、连接服务器、向服务器写数据、从服务器读取数据等通信。
   下例为常见java.net包的Http例子:
   try{
    URL url = new URL("http://www.google.com%22)//定义地址
    HttpURLConnection http = (HttpURLConnection) url.openConnection();//打开连接
    int nRC = http.getResponseCode();//得到连接状态
    if(nRC == HttpURLConnection.HTTP_OK){
     InputStream is = http.getInputStream();//取得数据
     .....//处理数据
    }
   }catch(Exception e){
    //因是连接网络,不免会出现一些异常,所以必须处理这些异常
   }
  第二种 Apache接口
   Android提供的Apache HttpClient,它是一个开源项目,功能更加完善,
   为客户端的Http编程提供高效、最新、功能丰富的工具包。
   Android目前使用的是HttpClient4.0(org.apache.http.*),
可将Apache看为目前流行的开源Web服务器,
主要包括创建HttpClient以及Get/Post、HttpRequest等对象,设置连接参数,执行HTTP操作,
   处理服务器返回结果等功能。
   下例为使用android.net.http.*包的例子:
   try{
    HttpClient hc = new DefaultHttpClient();//创建HttpClient,这里使用DefaultHttpClient表示默认属性
    HttpGet hg = new HttpGet("http://www.google.com%22);//HttpGet实例
    HttpResponse rp = hc.execute(hg);//连接
    if(rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
     InputStream is = rp.getEntity().getContent();
     .....//处理数据
    }
   }catch(IOEeception e){
   }
  第三中 Android网络接口
   android.net.*包实际是通过Apache中HttpClient的封装来实现的一个HTTP编程接口,
同时还提供了Http请求队列管理以及Http连接池管理,以提高并发请求情况下(如装载网页时)的效率
   还包括 网络状态监视等接口,网络访问的Socket,常用的Uri类以及有关WiFi相关的类等等。
   下例为最简单的Socket连接
   try{
    InetAddress ia = InetAddress.getByName("192.168.1.110");//IP地址
    Socket sk = new Socket(inetAddress,61203,true);//端口
    InputStream is =sk.getInputStream();//得到数据
    OutputStream os = sk.getOutputStream();
    .....//数据处理
    os.close();
    is.close();
    sk.close();
   }catch(UnknownHostException e){
   }catch(IOException e){
   }
第二部分 Http通信
   Android提供了HttpURLConnection和HttpClient接口来开发Http程序。
   Http(Hyper Text Transfer Protocol,超文本传输协议)用于传输WWW方式的数据。
   HTTP通信中客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。
从建立连接到关闭连接的过程为"一次连接"。
要保证客户端程序在线状态,需要不断地向服务器发送连接请求。
通常的做法是即使不需要获得任何数据,客户端也保持没隔一段固定的时间向服务器发送一次“保持连接”的请求,
服务器在收到该请求后对客户端进行回复,表明知道客户端在线。
若长时间无法收到客户端的请求,则认为客户端下线,若客户端长时间无法收到服务器的回复,则认为网络已经断开。
很多情况下,需要服务器端主动向客户端发送数据以保持客户端与服务器数据的实时与同步。
Http通信中,服务器需要等到客户端发送一次请求后才能将数据传回给客户端,
因此,客户端定时向服务器端发送连接请求,不仅可以保持在线,
同时也是在"询问"服务器是否有新数据,如果有就将数据传给客户端。
  
   Http采用请求/响应的模式。
客户端向服务器发送一个请求,
请求头包含了请求方法,URI,协议版本,以及包含请求修饰符,客户信息和内容,类似于MIME消息结构。
服务器以一个状态行作为响应,响应的内容包含消息协议的版本,成功或错误编码,
还包含服务器消息,实体元信息以及可能的实体内容。
它是一个属于应用层的面向对象的协议,适用于分布式超媒体信息系统。

   许多HTTP通信是由一个用户代理初始化的,并且包括一个申请在源服务器上资源的请求。
最简单的情况可能是在用户代理和服务器之间通过一个单独的连接来完成。
   在Internet上,Http通信通常发生在TCP/IP连接上,缺省端口是TCP80.其他端口也是可用的。
  第一种 HttpURLConnection接口
   Http通信中使用最多的是Get和Post.
   Get请求可用获得静态页面,也可以把参数放在URL字串的后面,传递给服务器。
   Post参数不是放在URL字串里面,而是放在http请求数据中。
   URLConnection与HttpURLConnection都是抽象类,无法直接实例化对象。
其对象主要通过URL的openConnection方法获得,
但openConnection方法只是创建URLConnection或者HttpURLConnection 的实例,并不是进行真正的连接操作。
因此在连接之前我们可用对一些属性进行设置
   对HttpConnection实例的属性设置:
    connection.setDoOutput(true);//设置输出流
    connection.setDoInput(true);//设置输出流
    connection.setRequestMethod("POST");//设置方式为POST
    connection.setUseCaches(false);//Post请求不能使用缓存
    urlConn.disconnect();//连接完成之后关闭HttpURLConnection连接
   
   首先在服务器上建立一个不需要传递参数的网页http1.jsp文件。代码如下:
   <HTML>
    <HEAD>
     <TITLE>
      Http Test
     </TITLE>
    </HEAD>
    <BODY>
     <% out.println("<h1>HTTP TEST<br>http test</h1>"); %>
    </BODY>
   </HTML>
   再创建一个适用Get和Post来传递参数的网页httpget.jsp 代码如下:
   <%@ page language="java" import = "java.util.*" pageEncoding = "gb2312" %>
   <HTML>
    <HEAD>
     <TITLE>
      Http Test
     </TITLE>
    </HEAD>
    <BODY>
     <%
      String type = request.getParameter("par");
      String result = new String(type.getBytes("iso-8859-1")),"gb2312");
      out.println("<h1>parameters:"+result+"</h1>");
     %>
    </BODY>
   </HTML>
   以下例中通过Android程序分别以不同的方式访问这两个页面。
   main.xm 文件
   <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent">
     <TextView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="通过下面的按钮进行不同方式的连接"/>
     <Button
      android:id="@+id/Button_HTTP"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="直接获取数据"/>
     <Button
      android:id="@+id/Button_Get"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="以GET方式传递数据"/>
     <Button
      android:id="@+id/Button_Post"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="以POST方式传递数据"/>
    </LinearLayout>

   http.xml文件
   <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     >
     <TextView 
      android:id="@+id/TextView_HTTP"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
     />
     <Button
      android:id="@+id/Button_Back"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="返回"/>
    </LinearLayout>
   
   public class Activity01 extends Activity{//进入界面实现3个控件的Activity
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
 
     Button button_http = (Button) findViewById(R.id.Button_HTTP);
     /* 监听button的事件信息 */
     button_http.setOnClickListener(new Button.OnClickListener() {
      public void onClick(View v){
       /* 新建一个Intent对象 */
       Intent intent = new Intent();
       /* 指定intent要启动的类 */
       intent.setClass(Activity01.this, Activity02.class);
       /* 启动一个新的Activity */
       startActivity(intent);
       /* 关闭当前的Activity */
       Activity01.this.finish();
      }
     });
     Button button_Get = (Button) findViewById(R.id.Button_Get);
     /* 监听button的事件信息 */
     button_Get.setOnClickListener(new Button.OnClickListener() {
      public void onClick(View v){
       /* 新建一个Intent对象 */
       Intent intent = new Intent();
       /* 指定intent要启动的类 */
       intent.setClass(Activity01.this, Activity03.class);
       /* 启动一个新的Activity */
       startActivity(intent);
       /* 关闭当前的Activity */
       Activity01.this.finish();
      }
     });
     Button button_Post = (Button) findViewById(R.id.Button_Post);
     /* 监听button的事件信息 */
     button_Post.setOnClickListener(new Button.OnClickListener() {
      public void onClick(View v){
       /* 新建一个Intent对象 */
       Intent intent = new Intent();
       /* 指定intent要启动的类 */
       intent.setClass(Activity01.this, Activity04.class);
       /* 启动一个新的Activity */
       startActivity(intent);
       /* 关闭当前的Activity */
       Activity01.this.finish();
      }
     });
    }
   }
  
   public class Activity02 extends Activity{//直接获取数据
    private final String DEBUG_TAG = "Activity02";
     public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.http);
    
     TextView mTextView = (TextView)this.findViewById(R.id.TextView_HTTP); 
     String httpUrl = "http://192.168.1.110:8080/http1.jsp%22;//http地址
     String resultData = "";//获得的数据
     URL url = null;
     try{ 
      url = new URL(httpUrl); //构造一个URL对象
     }catch (MalformedURLException e){
      Log.e(DEBUG_TAG, "MalformedURLException");
     }
     if (url != null){
      try{   
       HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();//使用HttpURLConnection打开连接   
       InputStreamReader in = new InputStreamReader(urlConn.getInputStream());//得到读取的内容(流)   
       BufferedReader buffer = new BufferedReader(in);// 为输出创建BufferedReader
       String inputLine = null;  
       while (((inputLine = buffer.readLine()) != null)){//使用循环来读取获得的数据   
        resultData += inputLine + "\n";//我们在每一行后面加上一个"\n"来换行
       }      
       in.close();//关闭InputStreamReader  
       urlConn.disconnect();//关闭http连接   
       if ( resultData != null ){
        mTextView.setText(resultData);//设置显示取得的内容
       }else {
        mTextView.setText("读取的内容为NULL");
       }
      }catch (IOException e){
       Log.e(DEBUG_TAG, "IOException");
      }
     }else{
      Log.e(DEBUG_TAG, "Url NULL");
     }

     Button button_Back = (Button) findViewById(R.id.Button_Back);//设置按键事件监听
      button_Back.setOnClickListener(new Button.OnClickListener(){//监听button的事件信息
       public void onClick(View v){   
        Intent intent = new Intent();
        intent.setClass(Activity02.this, Activity01.class);   
        startActivity(intent);
        Activity02.this.finish();
       }
      });
    }
   }

   public class Activity03 extends Activity{//以Get方式上传参数
    private final String DEBUG_TAG = "Activity03";
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.http);
 
     TextView mTextView = (TextView)this.findViewById(R.id.TextView_HTTP); 
     String httpUrl = "http://192.168.1.110:8080/httpget.jsp?par=abcdefg";//http地址"?par=abcdefg"是我们上传的参数
     String resultData = "";//获得的数据
     URL url = null;
     try{  
      url = new URL(httpUrl); //构造一个URL对象
     }catch (MalformedURLException e){
      Log.e(DEBUG_TAG, "MalformedURLException");
     }
     if (url != null){
      try{   
       HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();// 使用HttpURLConnection打开连接   
       InputStreamReader in = new InputStreamReader(urlConn.getInputStream());//得到读取的内容(流)  
       BufferedReader buffer = new BufferedReader(in); // 为输出创建BufferedReader
       String inputLine = null;   
       while (((inputLine = buffer.readLine()) != null)){//使用循环来读取获得的数据   
        resultData += inputLine + "\n";//我们在每一行后面加上一个"\n"来换行
       }       
       in.close();//关闭InputStreamReader   
       urlConn.disconnect();//关闭http连接  
       if ( resultData != null ){
        mTextView.setText(resultData);//设置显示取得的内容
       }else {
        mTextView.setText("读取的内容为NULL");
       }
      }catch (IOException e){
       Log.e(DEBUG_TAG, "IOException");
      }
     }else{
      Log.e(DEBUG_TAG, "Url NULL");
     }

     Button button_Back = (Button) findViewById(R.id.Button_Back);
      button_Back.setOnClickListener(new Button.OnClickListener() {
       public void onClick(View v){
        Intent intent = new Intent();
        intent.setClass(Activity03.this, Activity01.class);
        startActivity(intent);
        Activity03.this.finish();
       }
      });
    }
   }

   public class Activity04  extends Activity{//以Post方式上传参数
    private final String DEBUG_TAG = "Activity04";
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.http);
 
     TextView mTextView = (TextView)this.findViewById(R.id.TextView_HTTP);
     String httpUrl = "http://192.168.1.110:8080/httpget.jsp%22;//http地址"?par=abcdefg"是我们上传的参数 
     String resultData = "";//获得的数据
     URL url = null;
     try{ 
      url = new URL(httpUrl); //构造一个URL对象
     }catch (MalformedURLException e){
      Log.e(DEBUG_TAG, "MalformedURLException");
     }
     if (url != null){
      try{  
       HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();// 使用HttpURLConnection打开连接  
       urlConn.setDoOutput(true);//因为这个是post请求,设立需要设置为true
       urlConn.setDoInput(true);        
       urlConn.setRequestMethod("POST");// 设置以POST方式        
       urlConn.setUseCaches(false);// Post 请求不能使用缓存
       urlConn.setInstanceFollowRedirects(true);        
       urlConn.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); // 配置本次连接的Content-type,配置为application/x-www-form-urlencoded的
       // 连接,从postUrl.openConnection()至此的配置必须要在connect之前完成,
       // 要注意的是connection.getOutputStream会隐含的进行connect。
       urlConn.connect();   
       DataOutputStream out = new DataOutputStream(urlConn.getOutputStream());//DataOutputStream流         
       String content = "par=" + URLEncoder.encode("ABCDEFG", "gb2312");//要上传的参数        
       out.writeBytes(content); //将要上传的内容写入流中        
       out.flush();//刷新、关闭
       out.close();          
       BufferedReader reader = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));//获取数据
       String inputLine = null;   
       while(((inputLine = reader.readLine()) != null)){//使用循环来读取获得的数据   
        resultData += inputLine + "\n";//我们在每一行后面加上一个"\n"来换行
       }   
       reader.close();
       urlConn.disconnect();//关闭http连接   
       if ( resultData != null ){
        mTextView.setText(resultData);//设置显示取得的内容
       }else{
        mTextView.setText("读取的内容为NULL");
       }
      }catch (IOException e){
       Log.e(DEBUG_TAG, "IOException");
      }
     }else{
      Log.e(DEBUG_TAG, "Url NULL");
     }

     Button button_Back = (Button) findViewById(R.id.Button_Back);
      button_Back.setOnClickListener(new Button.OnClickListener(){
       public void onClick(View v){
        Intent intent = new Intent();
        intent.setClass(Activity04.this, Activity01.class);
        startActivity(intent);
        Activity04.this.finish();
       }
      });
    }
   }

   上面完成的是网络通信自是文本形式的,如果要显示网络上的一张图片,连接方式和前面相同,
只需要将连接之后得到数据流转换成Bitmap就可以了。
   下例为显示网络图片的方法
   GetNetBitmap方法
   //取得网络上的图片
   //url:图片地址
   public Bitmap GetNetBitmap(String url){
    URL imageUrl = null;
    Bitmap bitmap = null;
    try{
     imageUrl = new URL(url);
    }catch(MalformedURLException){
     Log.e(DEBUG_TAG,e.getMessage());
    }
    try{
     HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
     conn.setDoInput(true);
     conn.connect();
     InputStream is = conn.getInputStream();//将得到的数据转换成InputStream
     bitmap = BitmapFactory.decodeStream(is);//将InputStream转换成Bitmap
     id.close();
    }catch(IOException e){
     Log.e(DEBUG_TAG,e.getMessage());
    }
   }

  第二种 HttpClient接口
   与第一种相比HttpClient对java.net中的类做了封装和抽象,更适合我们在Android上开发互联网应用。
   需了解如下一些类:
   ClinetConnectionManager接口
    此接口是客户端连接管理器接口,有如下抽象方法:
    ClientConnectionManager  关闭所有无效、超时的连接
    closeIdleConnections  关闭空闲的连接
    releaseConnection   释放一个连接
    requestConnection   请求一个新的连接
    shutdown     关闭管理器并释放资源
   DefaultHttpClient
    是默认的一个HTTP客户端,可用它创建一个Http连接 代码如下:
    HttpClinet httpclinet = new HttpClient();
   HttpResponse
    是一个HTTP连接响应,当执行一个HTTP连接后,就会返回一个HttpResponse,可以通过HttpResponse获得一些响应的信息。
    下例为请求一个HTTP连接并获得该请求是否成功的代码:
    HttpResponse httpResponse = httpclient.execute(httpRequest);
    if(httpResponse.getStatusLine(),getStatusCode() == HttpStates.SC_OK){//判断是否连接成功
    }
   下例中分别使用Get和Post方式请求一个网页
   main.xml文件
   <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     >
     <TextView 
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="使用HttpClient来进行GET和POST连接"
     />
     <Button
      android:id="@+id/Button_Get"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="以GET方式传递数据"/>
     <Button
      android:id="@+id/Button_Post"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="以POST方式传递数据"/>
    </LinearLayout>

   http.xml文件
   <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     >
     <TextView 
      android:id="@+id/TextView_HTTP"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
     />
     <Button
      android:id="@+id/Button_Back"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="返回"/>
    </LinearLayout>
   
   public class Activity01 extends Activity{
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);

     Button button_Get = (Button) findViewById(R.id.Button_Get);
     /* 监听button的事件信息 */
     button_Get.setOnClickListener(new Button.OnClickListener() {
      public void onClick(View v){
       /* 新建一个Intent对象 */
       Intent intent = new Intent();
       /* 指定intent要启动的类 */
       intent.setClass(Activity01.this, Activity02.class);
       /* 启动一个新的Activity */
       startActivity(intent);
       /* 关闭当前的Activity */
       Activity01.this.finish();
      }
     });
     Button button_Post = (Button) findViewById(R.id.Button_Post);
     /* 监听button的事件信息 */
     button_Post.setOnClickListener(new Button.OnClickListener() {
      public void onClick(View v){
       /* 新建一个Intent对象 */
       Intent intent = new Intent();
       /* 指定intent要启动的类 */
       intent.setClass(Activity01.this, Activity03.class);
       /* 启动一个新的Activity */
       startActivity(intent);
       /* 关闭当前的Activity */
       Activity01.this.finish();
      }
     });
    }
   }

   public class Activity02 extends Activity{//Get方式请求例子
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.http);
     TextView mTextView = (TextView) this.findViewById(R.id.TextView_HTTP);
 
     String httpUrl = "http://192.168.1.110:8080/httpget.jsp?par=HttpClient_android_Get";// http地址 
     HttpGet httpRequest = new HttpGet(httpUrl);//HttpGet连接对象
     try{ 
      HttpClient httpclient = new DefaultHttpClient();//取得HttpClient对象
      HttpResponse httpResponse = httpclient.execute(httpRequest);//请求HttpClient,取得HttpResponse  
      if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){//请求成功   
       String strResult = EntityUtils.toString(httpResponse.getEntity());//取得返回的字符串
       mTextView.setText(strResult);
      }else{
       mTextView.setText("请求错误!");
      }
     }catch (ClientProtocolException e){
      mTextView.setText(e.getMessage().toString());
     }catch (IOException e){
      mTextView.setText(e.getMessage().toString());
     }catch (Exception e){
      mTextView.setText(e.getMessage().toString());
     }  
     Button button_Back = (Button) findViewById(R.id.Button_Back);//设置按键事件监听
      button_Back.setOnClickListener(new Button.OnClickListener() {
       public void onClick(View v){
        Intent intent = new Intent();
        intent.setClass(Activity02.this, Activity01.class);
        startActivity(intent);
        Activity02.this.finish();
       }
      });
    }
   }
 
   public class Activity03 extends Activity{//Post方式请求
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.http);
 
     TextView mTextView = (TextView) this.findViewById(R.id.TextView_HTTP); 
     String httpUrl = "http://192.168.1.110:8080/httpget.jsp%22;// http地址 
     HttpPost httpRequest = new HttpPost(httpUrl);//HttpPost连接对象
    
     List<NameValuePair> params = new ArrayList<NameValuePair>();//使用NameValuePair来保存要传递的Post参数 
     params.add(new BasicNameValuePair("par", "HttpClient_android_Post"));//添加要传递的参数
     try{
      HttpEntity httpentity = new UrlEncodedFormEntity(params, "gb2312");//设置字符集,Post需要设置所使用的字符集
      httpRequest.setEntity(httpentity);//请求httpRequest
      HttpClient httpclient = new DefaultHttpClient();//取得默认的HttpClient
      HttpResponse httpResponse = httpclient.execute(httpRequest);//取得HttpResponse 
      if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){//HttpStatus.SC_OK表示连接成功  
       String strResult = EntityUtils.toString(httpResponse.getEntity());//取得返回的字符串
       mTextView.setText(strResult);
      }else{
       mTextView.setText("请求错误!");
      }
     }catch (ClientProtocolException e){
      mTextView.setText(e.getMessage().toString());
     }catch (IOException e){
      mTextView.setText(e.getMessage().toString());
     }catch (Exception e){
      mTextView.setText(e.getMessage().toString());
     }   
     Button button_Back = (Button) findViewById(R.id.Button_Back);//设置按键事件监听
      button_Back.setOnClickListener(new Button.OnClickListener() {
       public void onClick(View v){
        Intent intent = new Intent();
        intent.setClass(Activity03.this, Activity01.class);
        startActivity(intent);
        Activity03.this.finish();
       }
      });
    }
   }
第三部分 实时更新
   第二部分只是简单地一次性获取网页数据,
而在实际开发中更多的是需要我们实时获取最新数据,比如道路流量,实时天气信息等等。
   可通过一个线程来控制视图的更新,要实时的从网络获取数据,其实就是把获取网络数据的代码写到线程中,不停的进行更新。
   注意:Android中更新视图不能直接在线程中进行,所以需要使用Handler来实现更新。
   下例中我们创建一个网页来显示系统当前的时间,然后每隔5秒系统自动刷新一次视图。
   首先,创建一个显示当前系统时间的jsp网页文件如下:
   date.jsp
   <% page language="java" import="java.util.*" pageEncoding="gb2312"%>
    <HTML>
     <HEAD>
      <TITLE>
       Date Test
      </TITLE>
     </HEAD>
     <BODY>
      <%java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM--dd HH:mm:ss");>
     </BODY>
    </HTML>
  
   main.xml文件
   <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     >
     <TextView 
      android:id="@+id/TextView01"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@string/hello"
     />
     <Button
      android:id="@+id/Button01"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="刷新" />
    </LinearLayout>

   public class Activity01 extends Activity{
    private final String DEBUG_TAG = "Activity02";
     private TextView mTextView;
     private Button mButton;
     public void onCreate(Bundle savedInstanceState){
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
     
      mTextView = (TextView)this.findViewById(R.id.TextView01);
      mButton = (Button)this.findViewById(R.id.Button01);
      mButton.setOnClickListener(new Button.OnClickListener(){
       public void onClick(View arg0){
        refresh();//刷新
       }
      }); 
      new Thread(mRunnable).start();//开启线程
     }
     private void refresh(){//刷新网页显示
      String httpUrl = "http://192.168.1.110:8080/date.jsp";
      String resultData = "";
      URL url = null;
      try{  
       url = new URL(httpUrl);// 构造一个URL对象
      }catch (MalformedURLException e){
       Log.e(DEBUG_TAG, "MalformedURLException");
      }
      if (url != null){
       try{   
        HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();// 使用HttpURLConnection打开连接   
        InputStreamReader in = new InputStreamReader(urlConn.getInputStream());// 得到读取的内容(流)   
        BufferedReader buffer = new BufferedReader(in);// 为输出创建BufferedReader
        String inputLine = null;   
        while (((inputLine = buffer.readLine()) != null)){// 使用循环来读取获得的数据    
         resultData += inputLine + "\n";// 我们在每一行后面加上一个"\n"来换行
        }   
        in.close();// 关闭InputStreamReader  
        urlConn.disconnect();// 关闭http连接  
        if (resultData != null){
         mTextView.setText(resultData);// 设置显示取得的内容
        }else{
         mTextView.setText("读取的内容为NULL");
        }
       }catch (IOException e){
        Log.e(DEBUG_TAG, "IOException");
       }
      }else{
       Log.e(DEBUG_TAG, "Url NULL");
      }
     }
     private Runnable mRunnable = new Runnable(){
      public void run(){
       while (true){
        try{
         Thread.sleep(5 * 1000);          
         mHandler.sendMessage(mHandler.obtainMessage());//发送消息
        }catch (InterruptedException e){
         Log.e(DEBUG_TAG, e.toString());
        }
       }
      }
     };
     Handler mHandler = new Handler(){
      public void handleMessage(Message msg){
       super.handleMessage(msg);//接受消息      
       refresh();//刷新
      }
     };
   }

第四部分 Socket通信
   如果要开发一款多人联网的游戏,Http已经不能很好的满足要求了。这时就需要Socket通信了。
   Socket通常称为"套接字",用于描述IP地址和端口,是一个通信链的句柄。
应用程序通常通过“套接字”向网络发出请求或者应答网络请求。
它是通信的基石,是支持TCP/IP协议的网络
   通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必需的5种信息:
    连接使用的协议、本地主机的IP地址、本地进程的协议端口、远地主机的IP地址、远地进程的协议端口。
  1、Socket传输模式
   Socket有两种主要操作方式:面向连接的 和 无连接的
    面向连接的Socket操作就像一部电话机,必须要等对方接上之后才能通话。
所有的数据到达的顺序与它出发时的顺序是一样的。
面向连接的操作使用TCP协议,即此模式下必须先连接上目的地的Socket,
连接上后Socket就可以使用一个流接口进行打开、读、写、关闭等操作。
所有所发信息都会在另一端以同样的顺序被接收。安全性高,但效率低。
    无连接的就像是一个邮件投递,没有保证,多个邮件到达时的顺序可能与出发时的顺序不一样。
无连接的操作使用数据报协议,一个数据报是一个独立的单元,它包含了这次投递的所有信息。
可将其想象成一个信封,这个模式下的Socket不需要连接一个目的Socket,它只是简单的投出数据报。
无连接的操作时快速和高效的,但是数据安全性不高。
    到底用哪种由应用程序的需要决定。如:文件服务器需要数据的正确性和有序性,因选面向连接的。
  2、Socket编程原理
   Socket构造
    java.net包中提供两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务器端.
    两类中其构造方法如下:
    Socket(InetAddress address,int port);
    Socket(InetAddress address,int port,boolean stream);
    Socket(String host,int port);
    Socket(String host,int port,boolean stream);
    Socket(SocketImpl impl);
    Socket(String host,int port,InetAddress localAddr,int localPort);
    Socket(InetAddress address,int port,InetAddress localAddr,int localPort);
    ServerSocket(int port);
    ServerSocket(int port,int backlog);
    ServerSocket(int port,int backlog,InetAddress bindAddr);
    其中参数意义:
     address      双向连接中另一方的IP地址
     host   双向连接中另一方的主机名
     port   双向连接中另一方的端口号
     stream   指明Socket是流Socket还是数据报Socket
     localPort  本地主机的端口号
     localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址)
     impl   是Socket的父类,即可以用来创建ServerSocket,又可以用来创建Socket
    例:
    //count表示服务端所支持的最大连接数
    Socket client = new Socket("192.168.1.110",54321);
    ServerSocket server = new ServerSocket(54321);
    注意:在选择端口时每一个端口对应一个服务,只有给出正确的端口,才能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telent服务的端口号
    为21,ftp服务的端口号为23,所以选择端口号时最好选择一个大于1023的数 如上的54321,防止发生冲突。在创建Socket时如果发生错误,将产生IOException,所以在创建Socket
    和ServerSocket时必须捕获或抛出异常。
   Socket 客户端
    要想使用Socket来与一个服务器通信,就必须先在客户端创建一个Socket,并指出需要连接的服务器端的IP地址和端口,代码如下:
     try{
      Socket socket = new Socket("192.168.1.110",33221);//"192.168.1.110"是IP地址,33221是端口号
     }catch(IOException e){
     }
   ServerSocket 服务器端
    创建一个服务器端的代码:
     ServerSocket server = null;
     try{
      server = new ServerSocket(33221);//服务器端在33221端口号监听客户请求,在这里服务器端只能接收一个请求,接收后服务器端就退出了。实际的应用中总是让他不
               停地循环接收,一旦有客户请求,服务器端总是会创建一个服务器线程来服务新来的客户,而自己则继续监听。
     }catch(IOException e){    
     }
     try{
      Socket socket = new server.accpet();//accpet()为一个阻塞函数,即该方法被调用后将等待客户的请求,直到有一个客户启动并请求连接到相同的端口,然后accept
               返回一个对应于客户端的Socket.这时,客户方和服务方都建立了用于通信的Socket,接下来就由各个Socket分别打开各自的输入
               输出流。
     }catch(IOExcetion e){
     }
   输入、输出流
    Socket 提供了getInputStream()和getOutPutStream()来得到对应的输入(输出)流以进行读(写)操作,这两个方法分别返回InputStream和OutputStream类对象。
    为了便于读(写)数据,可以在返回输入、输出流对象上建立过滤流。如:DataInputStream、DataOutPutStream、或PrintStream类对象。对于文本方式流对象,可以采用
    InputStreamReader和OutputStreamWriter、PrintWirter处理 代码如下:
     PrintStream os = new PrintStream(new BufferedOutputStream(Socket.getOutputStream()));
     DataInputStream is = new DataInputStream(socket.getInputStream());
     PrintWriter out = new PrintWriter(socket.getOutStream(),true);
     BufferedReader in = new ButfferedReader(new InputStreamReader(Socket.getInputStream()));
   关闭Socket和流
    在Socket使用完毕后需要将其关闭,以释放资源。
    注意:在关闭Socket之前,应将与Socket相关的所有的输入、输出流先关闭,以释放资源。要注意关闭的顺序。
     os.close();//输出流先关闭
     is.close();//输入流其次
     socket.close();//最后关闭Socket
   下例中 实现一个服务器和客户端通信。客户端发送数据并接受服务器发回的数据。
    public class Server implements Runnable{//服务器实现 注意:该程序需要单独编译,并在命令行模式下启动
     public void run(){
      try{  
       ServerSocket serverSocket = new ServerSocket(54321);//创建ServerSocket 设置端口号为54321
       while (true){  
        Socket client = serverSocket.accept();//通过accept监听接受客户端请求
        System.out.println("accept");
        try{   
         BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));//通过BufferedReader对象接收客户端消息
         String str = in.readLine();
         System.out.println("read:" + str);    
         PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(client.getOutputStream())),true);  //通过PrintWriter向服务器发送消息,但
                                     需要通过Socket对象来取得其输出流
         out.println("server message");     
         out.close();//关闭流
         in.close();
        }catch (Exception e){
         System.out.println(e.getMessage());
         e.printStackTrace();
        }finally{    
         client.close();//关闭
         System.out.println("close");
        }
       }
      }catch (Exception e){
       System.out.println(e.getMessage());
      }
     }
     public static void main(String a[]){//main函数用来开启服务器
      Thread desktopServerThread = new Thread(new Server());
      desktopServerThread.start();//开启线程
     }
    }
    public class Activity01 extends Activity{//客户端
     private final String  DEBUG_TAG = "Activity01";
     private TextView mTextView=null;
     private EditText mEditText=null;
     private Button  mButton=null;
     public void onCreate(Bundle savedInstanceState){
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
 
      mButton = (Button)findViewById(R.id.Button01);
      mTextView=(TextView)findViewById(R.id.TextView01);
      mEditText=(EditText)findViewById(R.id.EditText01);
   
      mButton.setOnClickListener(new OnClickListener(){//登陆
       public void onClick(View v){
        Socket socket = null;
        String message = mEditText.getText().toString() + "\r\n";
        try {     
         socket = new Socket("192.168.1.110",54321);//创建Socket 连接服务器   
         PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())),true); //向服务器发送消息    
         out.println(message);
         BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //接收来自服务器的消息
         String msg = br.readLine(); //读取   
         if ( msg != null ){
          mTextView.setText(msg);//接受消息后更新显示到TextView中
         }else{
          mTextView.setText("数据错误!");
         }    
          out.close();//关闭流
          br.close();    
          socket.close(); //关闭Socket
        }catch (Exception e) {
         Log.e(DEBUG_TAG, e.toString());
        }
       }
      });
     }
    }
    通过上例总结了一下:
    使用Socket实现客户端的步骤;
     1、通过IP地址和端口实例化Socket,请求连接服务器
     2、获取Socket上的流以进行读写
     3、把流包装进BufferReader/PrintWriter的实例
     4、对Socket进行读写
     5、关闭打开的流
    创建服务器的步骤:
     1、指定端口实例化一个ServerSocket
     2、调用ServerSocket的accept()以在等待连接期间造成阻塞
     3、获取位于该层Socket的流以进行读写操作
     4、将数据封装成流
     5、对Socket进行读写
     6、关闭打开的流

第五部分 Socket应用---简易聊天室
   第四部分例子中实现了一个客户端和一个服务器的单独通信,并且只能一次通信,
在实际中,往往需要在服务器上运行一个永久的程序,它可以接收来自其他多个客户端的请求,并提供相应服务。
这就需要多线程来实现多客户机制。
服务器总是在指定的端口上监听是否有客户请求,一旦监听到客户请求,
服务器就会启动一个专门的服务线程来响应该客户的请求,
而服务器本身在启动完线程后马上又进入监听状态,等待下一个客户。
  
   下例中使用Socket通信实现了一个简单的聊天室程序。
    下例我们需要启动两个客户端来同时连接服务器,一个客户端是Android程序,另一个是Java程序.
首先启动服务器---启动Android客户端---启动另一个PC客户端----Android客户端
    发送消息----pc客户端发送消息---Android客户端发送消息----pc客户端发送消息....
(当一个客户端发送消息(或连接服务器))后,服务器将向所有客户端发送一个消息,
这就需要服务器和客户端一直处于监听状态。
    main.xml文件
    <?xml version="1.0" encoding="utf-8"?>
     <AbsoluteLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="vertical"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      >
      <EditText
       android:id="@+id/EditText01"
       android:text="聊天记录:\n"
       android:layout_width="fill_parent"
       android:layout_height="200px">
      </EditText>
      <EditText
       android:id="@+id/EditText02"
       android:text="输入要发送的内容"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:layout_y="200px">
      </EditText>
      <Button
       android:id="@+id/Button_In"
       android:layout_width="80px"
       android:layout_height="wrap_content"
       android:text="登陆"
       android:layout_x="30px"
       android:layout_y="260px"
       /> 
      <Button
       android:id="@+id/Button_Send"
       android:layout_width="80px"
       android:layout_height="wrap_content"
       android:text="发送"
       android:layout_x="210px"
       android:layout_y="260px"
       />
     </AbsoluteLayout>

    public class Server{//服务器端 需要单独编译并在命令行模式下启动测试
     private static final int SERVERPORT = 54321; //服务器端口 
     private static List<Socket> mClientList = new ArrayList<Socket>(); //客户端连接 通过List来储存所有连接进来的客户端的Socket对象(也可用CopyOnWriteArrayList来储存)
     private ExecutorService mExecutorService;  //线程池 需要为每个客户端都开启一个线程   
     private ServerSocket mServerSocket;  //ServerSocket对象 
     public static void main(String[] args){ //main方法 开启服务器
      new Server();
     }
     public Server(){
      try{  
       mServerSocket = new ServerSocket(SERVERPORT);//设置服务器端口  
       mExecutorService = Executors.newCachedThreadPool();//创建一个线程池
       System.out.println("start...");  
       Socket client = null;//用来临时保存客户端连接的Socket对象
       while (true){   
        client = mServerSocket.accept(); //接收客户连接并添加到list中
        mClientList.add(client);   
        mExecutorService.execute(new ThreadServer(client));//开启一个客户端线程
       }
      }catch (IOException e){
       e.printStackTrace();
      }
     }   
     static class ThreadServer implements Runnable{//每个客户端单独开启一个线程
      private Socket   mSocket;
      private BufferedReader mBufferedReader;
      private PrintWriter  mPrintWriter;
      private String   mStrMSG;

      public ThreadServer(Socket socket) throws IOException{
       this.mSocket = socket;
       mBufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
       mStrMSG = "user:"+this.mSocket.getInetAddress()+" come total:" + mClientList.size();
       sendMessage();
      }
      public void run(){
       try{
        while ((mStrMSG = mBufferedReader.readLine()) != null){
         if (mStrMSG.trim().equals("exit")){
          //当一个客户端退出时
          mClientList.remove(mSocket);
          mBufferedReader.close();
          mPrintWriter.close();
          mStrMSG = "user:"+this.mSocket.getInetAddress()+" exit total:" + mClientList.size();
          mSocket.close();
          sendMessage();
          break;
         }else{
          mStrMSG = mSocket.getInetAddress() + ":" + mStrMSG;
          sendMessage();
         }
        }
       }catch (IOException e){
        e.printStackTrace();
       }
      } 
      private void sendMessage() throws IOException{//发送消息给所有客户端
       System.out.println(mStrMSG);
       for (Socket client : mClientList){
        mPrintWriter = new PrintWriter(client.getOutputStream(), true);
        mPrintWriter.println(mStrMSG);
       }
      }
     }
    }
   
    public class Client2{//需要单独编译并在命令行模式下启动测试
     private static final int  PORT = 54321;
     private static ExecutorService exec = Executors.newCachedThreadPool();

     public static void main(String[] args) throws Exception{
      new Client2();
     }
     public Client2(){
      try{
       Socket socket = new Socket("192.168.1.110", PORT);
       exec.execute(new Sender(socket));
       BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
       String msg;
       while ((msg = br.readLine()) != null){
        System.out.println(msg);
       }
      }catch (Exception e){
      }
     }    
     static class Sender implements Runnable{//客户端线程获取控制台输入消息
      private Socket socket;
      public Sender(Socket socket){
       this.socket = socket;
      }
      public void run(){
       try{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
        String msg;
        while (true){
         msg = br.readLine();
         pw.println(msg);
         if (msg.trim().equals("exit")){
          pw.close();
          br.close();
          exec.shutdownNow();
          break;
         }
        }
       }catch (Exception e){
        e.printStackTrace();
       }
      }
     } 
    }
   
    public class Activity01 extends Activity{//客户端, 客户端需要与服务器始终保持通信状态
注意:因Android是线程安全的,所以不能直接在线程中更新视图,需使用Handler来更新视图
           当点击”登陆“按钮时,连接服务器,并取得需要操作的流,
点击"发送"按钮时取出输入框中的内容发送向服务器,由服务器发送给每个客户端

     private final String  DEBUG_TAG = "Activity01";     
     private static final String SERVERIP = "192.168.1.110";//服务器IP、端口
     private static final int SERVERPORT = 54321;
     private Thread mThread = null;
     private Socket    mSocket  = null;
     private Button    mButton_In = null;
     private Button    mButton_Send= null;
     private EditText   mEditText01  = null;
     private EditText   mEditText02  = null;
     private BufferedReader  mBufferedReader = null;
     private PrintWriter mPrintWriter = null;
     private  String mStrMSG = "";

     public void onCreate(Bundle savedInstanceState){
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
 
      mButton_In = (Button)findViewById(R.id.Button_In);
      mButton_Send = (Button)findViewById(R.id.Button_Send);
      mEditText01=(EditText)findViewById(R.id.EditText01);
      mEditText02=(EditText)findViewById(R.id.EditText02);
 
      mButton_In.setOnClickListener(new OnClickListener(){//登陆按钮
       public void onClick(View v){
        try {    
         mSocket = new Socket(SERVERIP, SERVERPORT); //连接服务器    
         mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));//取得输入、输出流
         mPrintWriter=new PrintWriter(mSocket.getOutputStream(), true);  
        }catch (Exception e) {
         Log.e(DEBUG_TAG, e.toString());
        }
       }
      });
      mButton_Send.setOnClickListener(new OnClickListener(){//发送消息按钮
       public void onClick(View v){
        try {
         String str = mEditText02.getText().toString() + "\n";//取得编辑框中我们输入的内容
         mPrintWriter.print(str);//发送给服务器
         mPrintWriter.flush();
        }catch (Exception e) {
         Log.e(DEBUG_TAG, e.toString());
        }
       }
      });
      mThread = new Thread(mRunnable);
      mThread.start();
     }
     private Runnable mRunnable = new Runnable() { //线程:监听服务器发来的消息
      public void run(){
       while (true){
        try{
         if ( (mStrMSG = mBufferedReader.readLine()) != null ){     
          mStrMSG+="\n";//消息换行
          mHandler.sendMessage(mHandler.obtainMessage());// 发送消息
         }    
        }catch (Exception e){
         Log.e(DEBUG_TAG, e.toString());
        }
       }
      }
     };
     Handler mHandler = new Handler() {         
      public void handleMessage(Message msg){          
       super.handleMessage(msg);//接受消息 刷新
       try{     
        mEditText01.append(mStrMSG); //将聊天记录添加进来  
       }catch (Exception e){                    
        Log.e(DEBUG_TAG, e.toString());         
       }         
      }        
     };

 

相关文章

    暂无相关文章

用户评论