android开发之javax.net.ssl.SSLPeerUnverifiedException处理办法

 更新时间:2016年9月20日 20:00  点击:1721
在编写云笔记时需要调用auth2.0协议,在调用是老出现javax.net.ssl.SSLPeerUnverifiedException: No peer certificate的错误,

根据有关资料解决如下:
1、编写SSLSocketFactoryEx,以代替原有的SSLSocketFactory,

代码如下:

 代码如下 复制代码

package com.nbcio.baishicha.yunbiji;

import java.io.IOException;

import java.net.Socket;

import java.net.UnknownHostException;

import java.security.KeyManagementException;

import java.security.KeyStore;

import java.security.KeyStoreException;

import java.security.NoSuchAlgorithmException;

import java.security.UnrecoverableKeyException;

 

import javax.net.ssl.SSLContext;

import javax.net.ssl.TrustManager;

import javax.net.ssl.X509TrustManager;

 

import org.apache.http.conn.ssl.SSLSocketFactory;

 

public class SSLSocketFactoryEx extends SSLSocketFactory {

 

SSLContext sslContext = SSLContext.getInstance("TLS");

 

public SSLSocketFactoryEx(KeyStore truststore)

throws NoSuchAlgorithmException, KeyManagementException,

KeyStoreException, UnrecoverableKeyException {

super(truststore);

 

TrustManager tm = new X509TrustManager() {

 

public java.security.cert.X509Certificate[] getAcceptedIssuers() {

return null;

}

 

@Override

public void checkClientTrusted(

java.security.cert.X509Certificate[] chain, String authType)

throws java.security.cert.CertificateException {

 
}
 

@Override

public void checkServerTrusted(

java.security.cert.X509Certificate[] chain, String authType)

throws java.security.cert.CertificateException {

 

}

};

sslContext.init(null, new TrustManager[] { tm }, null);

}


@Override

public Socket createSocket(Socket socket, String host, int port,

boolean autoClose) throws IOException, UnknownHostException {

return sslContext.getSocketFactory().createSocket(socket, host, port,

autoClose);

}

 

@Override

public Socket createSocket() throws IOException {

return sslContext.getSocketFactory().createSocket();

}

}

2、编写新的HttpClient  getNewHttpClient来代替原有的DefaultHttpClient,代码如下:
public static HttpClient getNewHttpClient() {
   try {
       KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
       trustStore.load(null, null);
 
       SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
       sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
 
       HttpParams params = new BasicHttpParams();
       HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
       HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
 
       SchemeRegistry registry = new SchemeRegistry();
       registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
       registry.register(new Scheme("https", sf, 443));
 
       ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
 
       return new DefaultHttpClient(ccm, params);
   } catch (Exception e) {
       return new DefaultHttpClient();
   }
}

3、在postRequest调用的时候如下方式就可以:

 代码如下 复制代码

HttpResponse httpResponse =  (HttpResponse) getNewHttpClient().execute(postMethod);
Related posts:

文章介绍了一篇关于android手机开发自定义标题栏,有需要的朋友可以参考一下下。

一、概述

      每一个应用程序默认的标题栏(注意与状态栏的区别)只有一行文字(新建工程时的名字),而且颜色、大小等都是固定的,给人的感觉比较单调。但当程序需要美化的时候,那么修改标题栏是就是其中一项内容,虽然Android已经定义了很多样式资源,但更多时候我们需要使用的是自己定义的样式。


二、要求

     使用自己定义的样式来修改程序的标题栏。


三、实现

     新建工程MyTitle,不用修改main.xml文件,在/res/layout目录下新建布局文件title.xml,在里面添加一个TextView和一个Button,完整的title.xml文件如下:

 代码如下 复制代码

<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="match_parent"
     android:orientation="horizontal"
     >
    
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="这是定制的标题栏"
         android:textStyle="bold"
         android:textColor="#FFFF0000"     
         />
    
     <Button
         android:id="@+id/button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="点我"
         />
 
 </LinearLayout>

在/res/values目录下新建titlestyle.xml文件,在里面定义两个style,一个用来修改标题栏的大小,一个用来修改标题栏的背景颜色,如下:

 代码如下 复制代码

<?xml version="1.0" encoding="utf-8"?>
 
 <resources>
    
     <style name="TitleBackgroundColor">
         <item name="android:background">#FF0000FF</item>
     </style>
    
     <style name="titlestyle" parent="android:Theme" >
         <item name="android:windowTitleSize">40dip</item>   
         <item name="android:windowTitleBackgroundStyle">@style/TitleBackgroundColor</item>
     </style>
    
 </resources>

修改AndroidManifest.xml文件,在application标签下添加一行:

 代码如下 复制代码
 android:theme="@style/titlestyle"

最后,修改MyTitleActivity.java文件,设置使用自定义的标题栏,实现Button按钮的监听,如下:

 代码如下 复制代码

package com.nan.title;
 
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.View;
 import android.view.Window;
 import android.widget.Button;
 import android.widget.Toast;
 
 public class MyTitleActivity extends Activity
 {
     private Button mButton = null;
    
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
         //使用自定义标题栏
         requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
         setContentView(R.layout.main);
         //使用布局文件来定义标题栏
         getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.title);
        
         mButton = (Button)this.findViewById(R.id.button);
         //按钮监听
         mButton.setOnClickListener(new View.OnClickListener()
         {
            
             @Override
             public void onClick(View v)
             {
                 // TODO Auto-generated method stub
                 displayToast("Clicked!");
             }
         });
             
     }
    
     //显示Toast函数
     private void displayToast(String s)
     {
         Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
     }
    
 }

注意上面程序的第20~23行的顺序不能调乱。

运行该程序:

 

点击一下“点我”按钮:

 

本文章分享一篇简单的android手机开发之实现图片循环播放实例教程,有需要了解的同学可以参考一下下。

很多时候,我们需要展示在客户端展示图片,而且是动态显示,即不停地自行切换图片。下面我们来看一下具体的实现方法。

首先,我们需要在XML文件中配置一下将要播放图片的控件(main.xml):

 代码如下 复制代码

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  android:layout_width = "fill_parent"

  android:layout_height= "fill_parent"

  android:orientation="vertical">

  <!--这里是要播放图片的控件,bofang是用来播放图片的View类-->

  <com.sunianjinshi.bofang

    android:layout_width="180dip"

    android:layout_height = "250dip"

  />

</LinearLayout>

好了,到这里需要用来播放图片的控件就配置好了,接下来我们就要来写实现类bofang.java。

 代码如下 复制代码

code of bofang.java:

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

 

public class bofang extends View

{

  int COMPONENT_WIDTH;//控件的宽度

  int COMPONENT_HEIGHT;//控件的高度

  boolean initflag = false;//是否已经初始化图片

  Bitmap[] bmp;//用来存放图片的数组

  int currPicIndex = 0;//当前播放图片的ID

  int[] bitmapId;//图片编号ID

  boolean workFlag = true;//播放图片的线程标识位

  public GGViewCX(Context father,AttributeSet as)//重写构造函数

  {

    //首先,要播放图片,就先要有图片,那就先给各个图片编号吧,这里的图片资源存放在了res下的drawable文件夹下了

    int[] bitmapId ={R.drawable.adv1, R.drawable.adv2, R.drawable.adv3};

    //好了,图片的编号现在已经搞定了,接下来该干什么呢?对,应该将资源里的图片塞进Bitmap数组了,那么我们先来确定将要播放的图片的数量,即Bitmap数组的长度

    bmp = new Bitmap[bitmapId.length];//这里不要直接将数值赋给bmp,因为我们可能会不定期地更换图片资源,这样我们就要修改多处代码,而我们这样根据

    //图片的ID来确定图片的数量,以减少不必要的麻烦,下面开始初始化图片,我们将初始化图片放在一个函数里

    initBitmap();//图片初始化完毕

    //图片初始化完毕了,接下来我们要做的就是播放图片了,但是播放图片之前,我们有一个问题,就是怎样让图片实现循环播放?这里我们另开一个新的线程来定时更改

    //要播放的图片的ID,以实现图片的循环播放,要实现循环播放图片的功能,我们需要覆写onDraw函数,首先,我们来新开一个线程

    new Thread()

    {

      //重写run方法

      public void run()
              {
                  // TODO Auto-generated method stub
                  while(workflag)//一直执行这个循环(死循环)
                  {
                      currIndex = (currIndex+1)%bitmapId.length;//更改图片的ID
                      bofang.this.postInvalidate();//刷新屏幕,导致屏幕重绘
                      try
                      {
                          Thread.sleep(3000);//到此处暂停3秒钟,然后继续执行run函数,即实现每隔3秒钟刷新屏幕一次
                      }
                      catch (InterruptedException e)
                      {
                          // TODO Auto-generated catch block
                          e.printStackTrace();
                      }
                  }
              }

    }.start();

  }

  //初始化图片

  public void initBitmap()

  {

    //获取资源图片

    Resources res = this.getResources();

    for(int i=0;i<bitmapId.length;i++)

    {

      bmp[i] = BitmapFactory.decodeResource(res, bitmapId[i]);

    }

  }

 

  //覆写onDraw方法

  @Override
    protected void onDraw(Canvas canvas)
    {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        if(!initflag)//检查是偶已经获取控件的宽和高,如果没有,那么就获取控件的宽和高
        {
            COMPONENT_WIDTH = this.getWidth();
            COMPONENT_HEIGHT = this.getHeight();
            initflag = true;
        }
        canvas.drawBitmap(bma[currIndex], 0, 0,paint);//绘制图片
    }

}

好了,到这里就全部完成了!


PS:这里要说明一下,上面的代码中其实有些地方还有更好的实现方法。

比如:

我们为了实现定时更改要播放的图片的ID,以实现循环播放,新开了了一个线程,并且开了一个死循环,但实际这样的写法可控性很低,JDK的java.util.concurrent中提供了大量的方法去控制一段代码定时执行,标准的改写上面的代码如下:

 代码如下 复制代码

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleWithFixedDelay(new runner(), 0, 1, TimeUnit.SECONDS);
//或者用scheduler.scheduleAtFixedRate(new runner(),0,1, TimeUnit.SECONDS);

//接着我们要实现Runnable方法,也就是定时更改目前播放图片的ID

public class runner implements Runnable
    {
        public void run()

  {

            // TODO Auto-generated method stub
            currIndex = (currIndex+1)%bitmapId.length;
            bofang.this.postInvalidate();//刷新屏幕
       }
    }

本文介绍在Android平台中实现对XML的三种解析方式。 XML在各种开发中都广泛应用,Android也不例外。作为承载数据的一个重要角色,如何读写XML成为Android开发中一项重要的技能。
  在Android中,常见的XML解析器分别为DOM解析器、SAX解析器和PULL解析器,下面,我将一一向大家详细介绍。
 
第一种方式:DOM解析器:
DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。Android完全支持DOM 解析。利用DOM中的对象,可以对XML文档进行读取、搜索、修改、添加和删除等操作。
DOM的工作原理:使用DOM对XML文件进行操作时,首先要解析文件,将文件分为独立的元素、属性和注释等,然后以节点树的形式在内存中对XML文件进行表示,就可以通过节点树访问文档的内容,并根据需要修改文档——这就是DOM的工作原理。
DOM实现时首先为XML文档的解析定义一组接口,解析器读入整个文档,然后构造一个驻留内存的树结构,这样代码就可以使用DOM接口来操作整个树结构。 
由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。 当然,如果XML文件的内容比较小,采用DOM是可行的。
常用的DoM接口和类:
  Document:该接口定义分析并创建DOM文档的一系列方法,它是文档树的根,是操作DOM的基础。  
Element:该接口继承Node接口,提供了获取、修改XML元素名字和属性的方法。
Node:该接口提供处理并获取节点和子节点值的方法。
NodeList:提供获得节点个数和当前节点的方法。这样就可以迭代地访问各个节点。
DOMParser:该类是Apache的Xerces中的DOM解析器类,可直接解析XML文件。
下面是DOM的解析流程:
 
 
第二种方式:SAX解析器:
  SAX(Simple API for XML)解析器是一种基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档的结束,不可暂停或倒退。它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。  
  SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。
SAX的工作原理:SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
  在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parser()方法来解析XML文档,并产生事件。事件处理器是org.xml.sax包中ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口。XMLReader通过相应事件处理器注册方法setXXXX()来完成的与ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口的连接。
常用的SAX接口和类:
Attrbutes:用于得到属性的个数、名字和值。  
ContentHandler:定义与文档本身关联的事件(例如,开始和结束标记)。大多数应用程序都注册这些事件。
DTDHandler:定义与DTD关联的事件。它没有定义足够的事件来完整地报告DTD。如果需要对DTD进行语法分析,请使用可选的DeclHandler。
DeclHandler是SAX的扩展。不是所有的语法分析器都支持它。
EntityResolver:定义与装入实体关联的事件。只有少数几个应用程序注册这些事件。
ErrorHandler:定义错误事件。许多应用程序注册这些事件以便用它们自己的方式报错。
DefaultHandler:它提供了这些接LI的缺省实现。在大多数情况下,为应用程序扩展DefaultHandler并覆盖相关的方法要比直接实现一个接口更容易。
 详见下表:
 
  
 可知,我们需要XmlReader 以及DefaultHandler来配合解析xml。
下面是SAX的解析流程:
 
 
 
第三种方式:PULL解析器: 
      Android并未提供对Java StAX API的支持。但是,Android附带了一个pull解析器,其工作方式类似于StAX。它允许用户的应用程序代码从解析器中获取事件,这与SAX解析器自动将事件推入处理程序相反。 
PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。
        读取到xml的声明返回 START_DOCUMENT;
 代码如下 复制代码
读取到xml的结束返回 END_DOCUMENT ;
读取到xml的开始标签返回 START_TAG
读取到xml的结束标签返回 END_TAG
读取到xml的文本返回 TEXT
  
PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器,Android官方推荐开发者们使用Pull解析技术。Pull解析技术是第三方开发的开源技术,它同样可以应用于JavaSE开发。
      PULL 的工作原理:XML pull提供了开始元素和结束元素。当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据。当解释到一个文档结束时,自动生成EndDocument事件。
      常用的XML pull的接口和类: 
XmlPullParser:XML pull解析器是一个在XMLPULL VlAP1中提供了定义解析功能的接口。
XmlSerializer:它是一个接口,定义了XML信息集的序列。
XmlPullParserFactory:这个类用于在XMPULL V1 API中创建XML Pull解析器。
XmlPullParserException:抛出单一的XML pull解析器相关的错误。
PULL的解析流程如下:
      [附加]第四种方式: Android.util.Xml类
在Android API中,另外提供了Android.util.Xml类,同样可以解析XML文件,使用方法类似SAX,也都需编写Handler来处理XML的解析,但是在使用上却比SAX来得简单 ,如下所示:
以android.util.XML实现XML解析 ,
 代码如下 复制代码
MyHandler myHandler=new MyHandler0;

android.util.Xm1.parse(ur1.openC0nnection().getlnputStream0,Xm1.Encoding.UTF-8,myHandler);

 下面是一个参考文档river.xml,放在assets目录.如下:
 代码如下 复制代码
<?xml version="1.0" encoding="utf-8"?>
<rivers>
 <river name="灵渠" length="605">
     <introduction>
      灵渠在广西壮族自治区兴安县境内,是世界上最古老的运河之一,有着“世界古代水利建筑明珠”的美誉。灵渠古称秦凿渠、零渠、陡河、兴安运河,于公元前214年凿成通航,距今已2217年,仍然发挥着功用。
     </introduction>
      <imageurl>
      http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
     </imageurl>
   </river>

   
   <river name="胶莱运河" length="200">
     <introduction>
      胶莱运河南起黄海灵山海口,北抵渤海三山岛,流经现胶南、胶州、平度、高密、昌邑和莱州等,全长200公里,流域面积达5400平方公里,南北贯穿山东半岛,沟通黄渤两海。胶莱运河自平度姚家村东的分水岭南北分流。南流由麻湾口入胶州湾,为南胶莱河,长30公里。北流由海仓口入莱州湾,为北胶莱河,长100余公里。
   

 代码如下 复制代码
  </introduction>
      <imageurl>
      http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
     </imageurl>
   </river>
  
   <river name="苏北灌溉总渠" length="168">
     <introduction>
      位于淮河下游江苏省北部,西起洪泽湖边的高良涧,流经洪泽,青浦、淮安,阜宁、射阳,滨海等六县(区),东至扁担港口入海的大型人工河道。全长168km。
     </introduction>
      <imageurl>
      http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
     </imageurl>
   </river>
 </rivers>


   
      采用DOM解析时具体处理步骤是:

1 首先利用DocumentBuilderFactory创建一个DocumentBuilderFactory实例
2 然后利用DocumentBuilderFactory创建DocumentBuilder

3 然后加载XML文档(Document),
4 然后获取文档的根结点(Element),
5 然后获取根结点中所有子节点的列表(NodeList),
6 然后使用再获取子节点列表中的需要读取的结点。

  当然我们观察节点,我需要用一个River对象来保存数据,抽象出River类

 代码如下 复制代码
public class River implements Serializable {    
   privatestaticfinallong serialVersionUID = 1L;
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
    public String getIntroduction() {
        return introduction;
    }
    public void setIntroduction(String introduction) {
        this.introduction = introduction;
    }
    public String getImageurl() {
        return imageurl;
    }
    public void setImageurl(String imageurl) {
        this.imageurl = imageurl;
    }
    private int length;
    private String introduction;
    private String imageurl;
}


 

下面我们就开始读取xml文档对象,并添加进List中:

代码如下: 我们这里是使用assets中的river.xml文件,那么就需要读取这个xml文件,返回输入流。 读取方法为:inputStream=this.context.getResources().getAssets().open(fileName); 参数是xml文件路径,当然默认的是assets目录为根目录。

然后可以用DocumentBuilder对象的parse方法解析输入流,并返回document对象,然后再遍历doument对象的节点属性。
 

   //获取全部河流数据

 

 代码如下 复制代码

  /**  

     * 参数fileName:为xml文档路径
     */
    public List<River> getRiversFromXml(String fileName){
        List<River> rivers=new ArrayList<River>();
        DocumentBuilderFactory factory=null;
        DocumentBuilder builder=null;
        Document document=null;
        InputStream inputStream=null;
        //首先找到xml文件
        factory=DocumentBuilderFactory.newInstance();
        try {
            //找到xml,并加载文档
            builder=factory.newDocumentBuilder();
            inputStream=this.context.getResources().getAssets().open(fileName);
            document=builder.parse(inputStream);
            //找到根Element
             Element root=document.getDocumentElement();
             NodeList nodes=root.getElementsByTagName(RIVER);
            //遍历根节点所有子节点,rivers 下所有river
             River river=null;
             for(int i=0;i<nodes.getLength();i++){
                     river=new River();
                     //获取river元素节点
                     Element riverElement=(Element)(nodes.item(i));
                     //获取river中name属性值
                     river.setName(riverElement.getAttribute(NAME));
                     river.setLength(Integer.parseInt(riverElement.getAttribute(LENGTH)));
                     //获取river下introduction标签
                     Element introduction=(Element)riverElement.getElementsByTagName(INTRODUCTION).item(0);
                     river.setIntroduction(introduction.getFirstChild().getNodeValue());
                     Element imageUrl=(Element)riverElement.getElementsByTagName(IMAGEURL).item(0);
                     river.setImageurl(imageUrl.getFirstChild().getNodeValue());
                 rivers.add(river);
             }
        }catch (IOException e){
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        }
         catch (ParserConfigurationException e) {
            e.printStackTrace();
        }finally{
            try {
                inputStream.close();
            } catch (IOException e) {   
                e.printStackTrace();
            }
        }
        return rivers;
    }

 

在这里添加到List中, 然后我们使用ListView将他们显示出来。如图所示:


  采用SAX解析时具体处理步骤是:

1 创建SAXParserFactory对象

2 根据SAXParserFactory.newSAXParser()方法返回一个SAXParser解析器

3 根据SAXParser解析器获取事件源对象XMLReader

4 实例化一个DefaultHandler对象

5 连接事件源对象XMLReader到事件处理类DefaultHandler中

6 调用XMLReader的parse方法从输入源中获取到的xml数据

7 通过DefaultHandler返回我们需要的数据集合。

 

代码如下:

 代码如下 复制代码

  public List<River> parse(String xmlPath){
List<River> rivers=null;
        SAXParserFactory factory=SAXParserFactory.newInstance();
        try {
            SAXParser parser=factory.newSAXParser();
            //获取事件源
            XMLReader xmlReader=parser.getXMLReader();
            //设置处理器
            RiverHandler handler=new RiverHandler();
            xmlReader.setContentHandler(handler);
            //解析xml文档
            //xmlReader.parse(new InputSource(new URL(xmlPath).openStream()));
            xmlReader.parse(new InputSource(this.context.getAssets().open(xmlPath)));
            rivers=handler.getRivers();   
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
       
        return rivers;
    }


 

重点在于DefaultHandler对象中对每一个元素节点,属性,文本内容,文档内容进行处理。

前面说过DefaultHandler是基于事件处理模型的,基本处理方式是:当SAX解析器导航到文档开始标签时回调startDocument方法,导航到文档结束标签时回调endDocument方法。当SAX解析器导航到元素开始标签时回调startElement方法,导航到其文本内容时回调characters方法,导航到标签结束时回调endElement方法。

根据以上的解释,我们可以得出以下处理xml文档逻辑:

1:当导航到文档开始标签时,在回调函数startDocument中,可以不做处理,当然你可以验证下UTF-8等等。

2:当导航到rivers开始标签时,在回调方法startElement中可以实例化一个集合用来存贮list,不过我们这里不用,因为在构造函数中已经实例化了。

3:导航到river开始标签时,就说明需要实例化River对象了,当然river标签中还有name ,length属性,因此实例化River后还必须取出属性值,attributes.getValue(NAME),同时赋予river对象中,同时添加为导航到的river标签添加一个boolean为真的标识,用来说明导航到了river元素。

4:当然有river标签内还有子标签(节点),但是SAX解析器是不知道导航到什么标签的,它只懂得开始,结束而已。那么如何让它认得我们的各个标签呢?当然需要判断了,于是可以使用回调方法startElement中的参数String localName,把我们的标签字符串与这个参数比较下,就可以了。我们还必须让SAX知道,现在导航到的是某个标签,因此添加一个true属性让SAX解析器知道。

5:它还会导航到文本内标签,(就是<img></img>里面的内容),回调方法characters,我们一般在这个方法中取出就是<img></img>里面的内容,并保存。 6:当然它是一定会导航到结束标签</river> 或者</rivers>的,如果是</river>标签,记得把river对象添加进list中。如果是river中的子标签</introduction>,就把前面设置标记导航到这个标签的boolean标记设置为false. 按照以上实现思路,可以实现如下代码:


  /**导航到开始标签触发**/
        publicvoid startElement (String uri, String localName, String qName, Attributes attributes){
         String tagName=localName.length()!=0?localName:qName;
         tagName=tagName.toLowerCase().trim();
         //如果读取的是river标签开始,则实例化River
         if(tagName.equals(RIVER)){
             isRiver=true;
             river=new River();
                /**导航到river开始节点后**/
                river.setName(attributes.getValue(NAME));
                river.setLength(Integer.parseInt(attributes.getValue(LENGTH)));
         }
         //然后读取其他节点
          if(isRiver){
              if(tagName.equals(INTRODUCTION)){
                 xintroduction=true;
             }else if(tagName.equals(IMAGEURL)){
                 ximageurl=true;
             } 
         } 
        }
       
        /**导航到结束标签触发**/
        public void endElement (String uri, String localName, String qName){
         String tagName=localName.length()!=0?localName:qName;
         tagName=tagName.toLowerCase().trim();
        
        //如果读取的是river标签结束,则把River添加进集合中
         if(tagName.equals(RIVER)){
             isRiver=true;
             rivers.add(river);
         }
         //然后读取其他节点
          if(isRiver){
              if(tagName.equals(INTRODUCTION)){
                 xintroduction=false;
             }else if(tagName.equals(IMAGEURL)){
                 ximageurl=false;
             }
          }  
        }
       
        //这里是读取到节点内容时候回调
        public void characters (char[] ch, int start, int length){
            //设置属性值
                if(xintroduction){
                     //解决null问题
                     river.setIntroduction(river.getIntroduction()==null?"":river.getIntroduction()+new String(ch,start,length));
                 }else if(ximageurl){
                     //解决null问题
                     river.setImageurl(river.getImageurl()==null?"":river.getImageurl()+new String(ch,start,length));
                 }   
        }


 

运行效果跟上例DOM 运行效果相同。

 

采用PULL解析基本处理方式:

当PULL解析器导航到文档开始标签时就开始实例化list集合用来存贮数据对象。导航到元素开始标签时回判断元素标签类型,如果是river标签,则需要实例化River对象了,如果是其他类型,则取得该标签内容并赋予River对象。当然它也会导航到文本标签,不过在这里,我们可以不用。

根据以上的解释,我们可以得出以下处理xml文档逻辑:

1:当导航到XmlPullParser.START_DOCUMENT,可以不做处理,当然你可以实例化集合对象等等。

2:当导航到XmlPullParser.START_TAG,则判断是否是river标签,如果是,则实例化river对象,并调用getAttributeValue方法获取标签中属性值。

3:当导航到其他标签,比如Introduction时候,则判断river对象是否为空,如不为空,则取出Introduction中的内容,nextText方法来获取文本节点内容

4:当然啦,它一定会导航到XmlPullParser.END_TAG的,有开始就要有结束嘛。在这里我们就需要判读是否是river结束标签,如果是,则把river对象存进list集合中了,并设置river对象为null.

由以上的处理逻辑,我们可以得出以下代码:

 代码如下 复制代码


    public List<River> parse(String xmlPath){
List<River> rivers=new ArrayList<River>();
        River river=null;
        InputStream inputStream=null;   
        //获得XmlPullParser解析器
        XmlPullParser xmlParser = Xml.newPullParser();  
        try {
            //得到文件流,并设置编码方式
            inputStream=this.context.getResources().getAssets().open(xmlPath);
            xmlParser.setInput(inputStream, "utf-8");
            //获得解析到的事件类别,这里有开始文档,结束文档,开始标签,结束标签,文本等等事件。
            int evtType=xmlParser.getEventType();
         //一直循环,直到文档结束   
         while(evtType!=XmlPullParser.END_DOCUMENT){
            switch(evtType){
            case XmlPullParser.START_TAG:
                String tag = xmlParser.getName();
                //如果是river标签开始,则说明需要实例化对象了
                if (tag.equalsIgnoreCase(RIVER)) {
                   river = new River();
                  //取出river标签中的一些属性值
                  river.setName(xmlParser.getAttributeValue(null, NAME));
                  river.setLength(Integer.parseInt(xmlParser.getAttributeValue(null, LENGTH)));
                }else if(river!=null){
                    //如果遇到introduction标签,则读取它内容
                    if(tag.equalsIgnoreCase(INTRODUCTION)){
                    river.setIntroduction(xmlParser.nextText());
                    }else if(tag.equalsIgnoreCase(IMAGEURL)){
                        river.setImageurl(xmlParser.nextText());
                    }
                }
                break;
               
           case XmlPullParser.END_TAG:
             //如果遇到river标签结束,则把river对象添加进集合中
               if (xmlParser.getName().equalsIgnoreCase(RIVER) && river != null) {
                   rivers.add(river);
                   river = null;
               }
                break;
                default:break;
            }
            //如果xml没有结束,则导航到下一个river节点
            evtType=xmlParser.next();
         }
        } catch (XmlPullParserException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        return rivers;


 }

运行效果和上面的一样。

 

本文章介绍一下在手机上开的断点续传功能,有需要的同学可以参考一下下。

本示例介绍在Android平台下通过HTTP协议实现断点续传下载。

我们编写的是Andorid的HTTP协议多线程断点下载应用程序。直接使用单线程下载HTTP文件对我们来说是一件非常简单的事。那么,多线程断点需要什么功能?难在哪里?

1.多线程下载,

2.支持断点。

 

使用多线程的好处:使用多线程下载会提升文件下载的速度。那么多线程下载文件的过程是:

  (1)首先获得下载文件的长度,然后设置本地文件的长度。

     HttpURLConnection.getContentLength();//获取下载文件的长度

    RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd");

       file.setLength(filesize);//设置本地文件的长度

 

  (2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。

     如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示。

例如10M大小,使用3个线程来下载,

        线程下载的数据长度   (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M

        下载开始位置:线程id*每条线程下载的数据长度 = ?

        下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?

 

  (3)使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止,

        如:指定从文件的2M位置开始下载,下载到位置(4M-1byte)为止

           代码如下:HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");

 

  (4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。

 代码如下 复制代码

RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd");

threadfile.seek(2097152);//从文件的什么位置开始写入数据

  程序结构如下图所示:

   string.xml文件中代码:

 代码如下 复制代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, MainActivity!</string>
    <string name="app_name">Android网络多线程断点下载</string>
    <string name="path">下载路径</string>
    <string name="downloadbutton">下载</string>
    <string name="sdcarderror">SDCard不存在或者写保护</string>
    <string name="success">下载完成</string>
    <string name="error">下载失败</string>
</resources>

 main.xml文件中代码:


     AndroidManifest.xml文件中代码:

 代码如下 复制代码

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"      package="com.android.downloader"      android:versionCode="1"      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
   
    <!-- 在SDCard中创建与删除文件权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
   
    <!-- 往SDCard写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
   
    <!-- 访问internet权限 -->
    <uses-permission android:name="android.permission.INTERNET"/>


</manifest> 


 MainActivity中代码:

 代码如下 复制代码

package com.android.downloader;
import java.io.File;

import com.android.network.DownloadProgressListener;
import com.android.network.FileDownloader;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
    private EditText downloadpathText;
    private TextView resultView;
    private ProgressBar progressBar;
   
    /**
     * 当Handler被创建会关联到创建它的当前线程的消息队列,该类用于往消息队列发送消息
     * 消息队列中的消息由当前线程内部进行处理
     */
    private Handler handler = new Handler(){

        @Override
        public void handleMessage(Message msg) {           
            switch (msg.what) {
            case 1:               
                progressBar.setProgress(msg.getData().getInt("size"));
                float num = (float)progressBar.getProgress()/(float)progressBar.getMax();
                int result = (int)(num*100);
                resultView.setText(result+ "%");
               
                if(progressBar.getProgress()==progressBar.getMax()){
                    Toast.makeText(MainActivity.this, R.string.success, 1).show();
                }
                break;
            case -1:
                Toast.makeText(MainActivity.this, R.string.error, 1).show();
                break;
            }
        }
    };
   
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        downloadpathText = (EditText) this.findViewById(R.id.path);
        progressBar = (ProgressBar) this.findViewById(R.id.downloadbar);
        resultView = (TextView) this.findViewById(R.id.resultView);
        Button button = (Button) this.findViewById(R.id.button);
       
        button.setOnClickListener(new View.OnClickListener() {
           
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String path = downloadpathText.getText().toString();
                System.out.println(Environment.getExternalStorageState()+"------"+Environment.MEDIA_MOUNTED);
               
                if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                    download(path, Environment.getExternalStorageDirectory());
                }else{
                    Toast.makeText(MainActivity.this, R.string.sdcarderror, 1).show();
                }
            }
        });
    }
   
      /**
       * 主线程(UI线程)
       * 对于显示控件的界面更新只是由UI线程负责,如果是在非UI线程更新控件的属性值,更新后的显示界面不会反映到屏幕上
       * @param path
       * @param savedir
       */
    private void download(final String path, final File savedir) {
        new Thread(new Runnable() {           
            @Override
            public void run() {
                FileDownloader loader = new FileDownloader(MainActivity.this, path, savedir, 3);
                progressBar.setMax(loader.getFileSize());//设置进度条的最大刻度为文件的长度
               
                try {
                    loader.download(new DownloadProgressListener() {
                        @Override
                        public void onDownloadSize(int size) {//实时获知文件已经下载的数据长度
                            Message msg = new Message();
                            msg.what = 1;
                            msg.getData().putInt("size", size);
                            handler.sendMessage(msg);//发送消息
                        }
                    });
                } catch (Exception e) {
                    handler.obtainMessage(-1).sendToTarget();
                }
            }
        }).start();
    }


 

DBOpenHelper中代码:

 代码如下 复制代码

package com.android.service;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBOpenHelper extends SQLiteOpenHelper {
    private static final String DBNAME = "down.db";
    private static final int VERSION = 1;
   
    public DBOpenHelper(Context context) {
        super(context, DBNAME, null, VERSION);
    }
   
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS filedownlog (id integer primary key autoincrement, downpath varchar(100), threadid INTEGER, downlength INTEGER)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS filedownlog");
        onCreate(db);
    }
}

  

FileService中代码:

 代码如下 复制代码

package com.android.service;
import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class FileService {
    private DBOpenHelper openHelper;

    public FileService(Context context) {
        openHelper = new DBOpenHelper(context);
    }
   
    /**
     * 获取每条线程已经下载的文件长度
     * @param path
     * @return
     */
    public Map<Integer, Integer> getData(String path){
        SQLiteDatabase db = openHelper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select threadid, downlength from filedownlog where downpath=?", new String[]{path});
        Map<Integer, Integer> data = new HashMap<Integer, Integer>();
       
        while(cursor.moveToNext()){
            data.put(cursor.getInt(0), cursor.getInt(1));
        }
       
        cursor.close();
        db.close();
        return data;
    }
   
    /**
     * 保存每条线程已经下载的文件长度
     * @param path
     * @param map
     */
    public void save(String path,  Map<Integer, Integer> map){//int threadid, int position
        SQLiteDatabase db = openHelper.getWritableDatabase();
        db.beginTransaction();
       
        try{
            for(Map.Entry<Integer, Integer> entry : map.entrySet()){
                db.execSQL("insert into filedownlog(downpath, threadid, downlength) values(?,?,?)",
                        new Object[]{path, entry.getKey(), entry.getValue()});
            }
            db.setTransactionSuccessful();
        }finally{
            db.endTransaction();
        }
       
        db.close();
    }
   
    /**
     * 实时更新每条线程已经下载的文件长度
     * @param path
     * @param map
     */
    public void update(String path, Map<Integer, Integer> map){
        SQLiteDatabase db = openHelper.getWritableDatabase();
        db.beginTransaction();
       
        try{
            for(Map.Entry<Integer, Integer> entry : map.entrySet()){
                db.execSQL("update filedownlog set downlength=? where downpath=? and threadid=?",
                        new Object[]{entry.getValue(), path, entry.getKey()});
            }
           
            db.setTransactionSuccessful();
        }finally{
            db.endTransaction();
        }
       
        db.close();
    }
   
    /**
     * 当文件下载完成后,删除对应的下载记录
     * @param path
     */
    public void delete(String path){
        SQLiteDatabase db = openHelper.getWritableDatabase();
        db.execSQL("delete from filedownlog where downpath=?", new Object[]{path});
        db.close();
    }


DownloadProgressListener中代码:

package com.android.network;
public interface DownloadProgressListener {
    public void onDownloadSize(int size);


 

FileDownloader中代码:

 代码如下 复制代码

package com.android.network;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.android.service.FileService;

import android.content.Context;
import android.util.Log;

public class FileDownloader {
    private static final String TAG = "FileDownloader";
    private Context context;
    private FileService fileService;   
   
    /* 已下载文件长度 */
    private int downloadSize = 0;
   
    /* 原始文件长度 */
    private int fileSize = 0;
   
    /* 线程数 */
    private DownloadThread[] threads;
   
    /* 本地保存文件 */
    private File saveFile;
   
    /* 缓存各线程下载的长度*/
    private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>();
   
    /* 每条线程下载的长度 */
    private int block;
   
    /* 下载路径  */
    private String downloadUrl;
   
    /**
     * 获取线程数
     */
    public int getThreadSize() {
        return threads.length;
    }
   
    /**
     * 获取文件大小
     * @return
     */
    public int getFileSize() {
        return fileSize;
    }
   
    /**
     * 累计已下载大小
     * @param size
     */
    protected synchronized void append(int size) {
        downloadSize += size;
    }
   
    /**
     * 更新指定线程最后下载的位置
     * @param threadId 线程id
     * @param pos 最后下载的位置
     */
    protected synchronized void update(int threadId, int pos) {
        this.data.put(threadId, pos);
        this.fileService.update(this.downloadUrl, this.data);
    }
   
    /**
     * 构建文件下载器
     * @param downloadUrl 下载路径
     * @param fileSaveDir 文件保存目录
     * @param threadNum 下载线程数
     */
    public FileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum) {
        try {
            this.context = context;
            this.downloadUrl = downloadUrl;
            fileService = new FileService(this.context);
            URL url = new URL(this.downloadUrl);
            if(!fileSaveDir.exists()) fileSaveDir.mkdirs();
            this.threads = new DownloadThread[threadNum];                   
           
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5*1000);
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
            conn.setRequestProperty("Accept-Language", "zh-CN");
            conn.setRequestProperty("Referer", downloadUrl);
            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.connect();
            printResponseHeader(conn);
           
            if (conn.getResponseCode()==200) {
                this.fileSize = conn.getContentLength();//根据响应获取文件大小
                if (this.fileSize <= 0) throw new RuntimeException("Unkown file size ");
                       
                String filename = getFileName(conn);//获取文件名称
                this.saveFile = new File(fileSaveDir, filename);//构建保存文件
                Map<Integer, Integer> logdata = fileService.getData(downloadUrl);//获取下载记录
               
                if(logdata.size()>0){//如果存在下载记录
                    for(Map.Entry<Integer, Integer> entry : logdata.entrySet())
                        data.put(entry.getKey(), entry.getValue());//把各条线程已经下载的数据长度放入data中
                }
               
                if(this.data.size()==this.threads.length){//下面计算所有线程已经下载的数据长度
                    for (int i = 0; i < this.threads.length; i++) {
                        this.downloadSize += this.data.get(i+1);
                    }
                   
                    print("已经下载的长度"+ this.downloadSize);
                }
               
                //计算每条线程下载的数据长度
                this.block = (this.fileSize % this.threads.length)==0? this.fileSize / this.threads.length : this.fileSize / this.threads.length + 1;
            }else{
                throw new RuntimeException("server no response ");
            }
        } catch (Exception e) {
            print(e.toString());
            throw new RuntimeException("don't connection this url");
        }
    }
   
    /**
     * 获取文件名
     * @param conn
     * @return
     */
    private String getFileName(HttpURLConnection conn) {
        String filename = this.downloadUrl.substring(this.downloadUrl.lastIndexOf('/') + 1);
       
        if(filename==null || "".equals(filename.trim())){//如果获取不到文件名称
            for (int i = 0;; i++) {
                String mine = conn.getHeaderField(i);
               
                if (mine == null) break;
               
                if("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())){
                    Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase());
                    if(m.find()) return m.group(1);
                }
            }
           
            filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名
        }
       
        return filename;
    }
   
    /**
     *  开始下载文件
     * @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null
     * @return 已下载文件大小
     * @throws Exception
     */
    public int download(DownloadProgressListener listener) throws Exception{
        try {
            RandomAccessFile randOut = new RandomAccessFile(this.saveFile, "rw");
            if(this.fileSize>0) randOut.setLength(this.fileSize);
            randOut.close();
            URL url = new URL(this.downloadUrl);
           
            if(this.data.size() != this.threads.length){
                this.data.clear();
               
                for (int i = 0; i < this.threads.length; i++) {
                    this.data.put(i+1, 0);//初始化每条线程已经下载的数据长度为0
                }
            }
           
            for (int i = 0; i < this.threads.length; i++) {//开启线程进行下载
                int downLength = this.data.get(i+1);
               
                if(downLength < this.block && this.downloadSize<this.fileSize){//判断线程是否已经完成下载,否则继续下载   
                    this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1);
                    this.threads[i].setPriority(7);
                    this.threads[i].start();
                }else{
                    this.threads[i] = null;
                }
            }
           
            this.fileService.save(this.downloadUrl, this.data);
            boolean notFinish = true;//下载未完成
           
            while (notFinish) {// 循环判断所有线程是否完成下载
                Thread.sleep(900);
                notFinish = false;//假定全部线程下载完成
               
                for (int i = 0; i < this.threads.length; i++){
                    if (this.threads[i] != null && !this.threads[i].isFinish()) {//如果发现线程未完成下载
                        notFinish = true;//设置标志为下载没有完成
                       
                        if(this.threads[i].getDownLength() == -1){//如果下载失败,再重新下载
                            this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1);
                            this.threads[i].setPriority(7);
                            this.threads[i].start();
                        }
                    }
                }   
               
                if(listener!=null) listener.onDownloadSize(this.downloadSize);//通知目前已经下载完成的数据长度
            }
           
            fileService.delete(this.downloadUrl);
        } catch (Exception e) {
            print(e.toString());
            throw new Exception("file download fail");
        }
        return this.downloadSize;
    }
   
    /**
     * 获取Http响应头字段
     * @param http
     * @return
     */
    public static Map<String, String> getHttpResponseHeader(HttpURLConnection http) {
        Map<String, String> header = new LinkedHashMap<String, String>();
       
        for (int i = 0;; i++) {
            String mine = http.getHeaderField(i);
            if (mine == null) break;
            header.put(http.getHeaderFieldKey(i), mine);
        }
       
        return header;
    }
   
    /**
     * 打印Http头字段
     * @param http
     */
    public static void printResponseHeader(HttpURLConnection http){
        Map<String, String> header = getHttpResponseHeader(http);
       
        for(Map.Entry<String, String> entry : header.entrySet()){
            String key = entry.getKey()!=null ? entry.getKey()+ ":" : "";
            print(key+ entry.getValue());
        }
    }

    private static void print(String msg){
        Log.i(TAG, msg);
    }
}


DownloadThread 中代码:

 代码如下 复制代码

package com.android.network;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import android.util.Log;

public class DownloadThread extends Thread {
    private static final String TAG = "DownloadThread";
    private File saveFile;
    private URL downUrl;
    private int block;
   
    /* 下载开始位置  */
    private int threadId = -1;   
    private int downLength;
    private boolean finish = false;
    private FileDownloader downloader;
   
    public DownloadThread(FileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId) {
        this.downUrl = downUrl;
        this.saveFile = saveFile;
        this.block = block;
        this.downloader = downloader;
        this.threadId = threadId;
        this.downLength = downLength;
    }
   
    @Override
    public void run() {
        if(downLength < block){//未下载完成
            try {
                HttpURLConnection http = (HttpURLConnection) downUrl.openConnection();
                http.setConnectTimeout(5 * 1000);
                http.setRequestMethod("GET");
                http.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
                http.setRequestProperty("Accept-Language", "zh-CN");
                http.setRequestProperty("Referer", downUrl.toString());
                http.setRequestProperty("Charset", "UTF-8");
                int startPos = block * (threadId - 1) + downLength;//开始位置
                int endPos = block * threadId -1;//结束位置
                http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);//设置获取实体数据的范围
                http.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
                http.setRequestProperty("Connection", "Keep-Alive");
               
                InputStream inStream = http.getInputStream();
                byte[] buffer = new byte[1024];
                int offset = 0;
                print("Thread " + this.threadId + " start download from position "+ startPos);
                RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd");
                threadfile.seek(startPos);
               
                while ((offset = inStream.read(buffer, 0, 1024)) != -1) {
                    threadfile.write(buffer, 0, offset);
                    downLength += offset;
                    downloader.update(this.threadId, downLength);
                    downloader.append(offset);
                }
               
                threadfile.close();
                inStream.close();
                print("Thread " + this.threadId + " download finish");
                this.finish = true;
            } catch (Exception e) {
                this.downLength = -1;
                print("Thread "+ this.threadId+ ":"+ e);
            }
        }
    }
   
    private static void print(String msg){
        Log.i(TAG, msg);
    }
   
    /**
     * 下载是否完成
     * @return
     */
    public boolean isFinish() {
        return finish;
    }
   
    /**
     * 已经下载的内容大小
     * @return 如果返回值为-1,代表下载失败
     */
    public long getDownLength() {
        return downLength;
    }
}


看看在手机上的效果

 

[!--infotagslink--]

相关文章

  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • Windows批量搜索并复制/剪切文件的批处理程序实例

    这篇文章主要介绍了Windows批量搜索并复制/剪切文件的批处理程序实例,需要的朋友可以参考下...2020-06-30
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • BAT批处理判断服务是否正常运行的方法(批处理命令综合应用)

    批处理就是对某对象进行批量的处理,通常被认为是一种简化的脚本语言,它应用于DOS和Windows系统中。这篇文章主要介绍了BAT批处理判断服务是否正常运行(批处理命令综合应用),需要的朋友可以参考下...2020-06-30
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • PHP file_get_contents设置超时处理方法

    file_get_contents的超时处理话说,从PHP5开始,file_get_content已经支持context了(手册上写着:5.0.0 Added the context support. ),也就是说,从5.0开始,file_get_contents其实也可以POST数据。今天说的这篇是讲超时的,确实在...2013-10-04
  • vscode搭建STM32开发环境的详细过程

    这篇文章主要介绍了vscode搭建STM32开发环境的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-05-02
  • C#多线程中的异常处理操作示例

    这篇文章主要介绍了C#多线程中的异常处理操作,涉及C#多线程及异常的捕获、处理等相关操作技巧,需要的朋友可以参考下...2020-06-25
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • postgresql 中的时间处理小技巧(推荐)

    这篇文章主要介绍了postgresql 中的时间处理小技巧(推荐),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
  • Android 实现钉钉自动打卡功能

    这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
  • Android 开发之布局细节对比:RTL模式

    下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
  • Python同时处理多个异常的方法

    这篇文章主要介绍了Python同时处理多个异常的方法,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-29