I'm implementing a download application that works like android's 
DownloadProvider but based HttpCore NIO.

A fatal problem I encountered is this:
04-29 09:26:05.419: ERROR/AndroidRuntime(332): 
java.nio.channels.IllegalSelectorException
04-29 09:26:05.419: ERROR/AndroidRuntime(332):     at 
java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:158)
04-29 09:26:05.419: ERROR/AndroidRuntime(332):     at 
java.nio.channels.SelectableChannel.register(SelectableChannel.java:158)
04-29 09:26:05.419: ERROR/AndroidRuntime(332):     at 
org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processSessionRequests(DefaultConnectingIOReactor.java:244)
04-29 09:26:05.419: ERROR/AndroidRuntime(332):     at 
org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processEvents(DefaultConnectingIOReactor.java:97)
04-29 09:26:05.419: ERROR/AndroidRuntime(332):     at 
org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor.execute(AbstractMultiworkerIOReactor.java:317)
04-29 09:26:05.419: ERROR/AndroidRuntime(332):     at 
io.DownloadClient.run(DownloadClient.java:168)
04-29 09:26:05.419: ERROR/AndroidRuntime(332):     at 
java.lang.Thread.run(Thread.java:1096)

however it can work on real j2se6, is that unsupported on android at all?

And a minor problem: Will "ioReactor.execute(ioEventDispatch)" really throw out 
a InterruptedIOException?
I found it will be never even when I interrupt the thread explicitly. shutdown 
can't be done gracefully without this.


Thanks for help, my raw codes for testing:
//********** io.DownloadClient.java **********
package io;

import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.nio.DefaultClientIOEventDispatch;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.nio.ContentDecoder;
import org.apache.http.nio.FileContentDecoder;
import org.apache.http.nio.NHttpClientConnection;
import org.apache.http.nio.NHttpClientHandler;
import org.apache.http.nio.entity.ConsumingNHttpEntity;
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
import org.apache.http.nio.protocol.NHttpRequestExecutionHandler;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.util.HeapByteBufferAllocator;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.RequestConnControl;
import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;

public class DownloadClient implements Runnable {
 public static final String TAG = "nioDownload";

 Thread dispatcher;
 ExecutorService workers;

 ConnectingIOReactor reactor;
 IOEventDispatch eventDispatch;

 public DownloadClient() throws IOException {
  BasicHttpProcessor httpproc = new BasicHttpProcessor();
        httpproc.addInterceptor(new RequestContent());
        httpproc.addInterceptor(new RequestTargetHost());
        httpproc.addInterceptor(new RequestConnControl());
        httpproc.addInterceptor(new RequestUserAgent());
        httpproc.addInterceptor(new RequestExpectContinue());
  HttpParams params = new BasicHttpParams();
        params.setParameter(CoreProtocolPNames.USER_AGENT, 
"HttpComponents/1.1");
     params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000);
     params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
     params.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024);
     params.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, 
false);
     params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true);
  NHttpRequestExecutionHandler execHandler = new NHttpRequestExecutionHandler() 
{
   HttpRequest request = null;
   @Override
   public void initalizeContext(HttpContext context, Object attachment) {
    if(attachment != null && attachment instanceof DownloadCourse) {
     DownloadCourse course = (DownloadCourse)attachment;
     request = course.getRequest();
     context.setAttribute(TAG, attachment);
    }
   }
   @Override
   public HttpRequest submitRequest(HttpContext context) {
    HttpRequest result = this.request;
    if(result != null) {
     this.request = null;
    }
    return result;
   }
   @Override
   public ConsumingNHttpEntity responseEntity(HttpResponse response, 
HttpContext context)
     throws IOException {
    //nothing, handle in NHttpClientHandler.inputReady
    return null;
   }
   @Override
   public void handleResponse(HttpResponse response, HttpContext context)
     throws IOException {
    //nothing, handle in NHttpClientHandler.inputReady
   }
   @Override
   public void finalizeContext(HttpContext context) {
    context.removeAttribute(TAG);
   }
  };
  NHttpClientHandler handler = new AsyncNHttpClientHandler(
           httpproc, execHandler, new DefaultConnectionReuseStrategy(),
           new HeapByteBufferAllocator(), params) {
   @Override
   public void closed(NHttpClientConnection conn) {
    super.closed(conn);
    Object attachment = conn.getContext().getAttribute(TAG);
    if(attachment != null && attachment instanceof DownloadCourse) {
     DownloadCourse course = (DownloadCourse)attachment;
     course.interrupt();
    }
   }
   @Override
   public void exception(NHttpClientConnection conn, HttpException ex) {
    super.exception(conn, ex);
    Object attachment = conn.getContext().getAttribute(TAG);
    if(attachment != null && attachment instanceof DownloadCourse) {
     DownloadCourse course = (DownloadCourse)attachment;
     course.error(ex);
    }
   }
   @Override
   public void exception(NHttpClientConnection conn, IOException ex) {
    super.exception(conn, ex);
    Object attachment = conn.getContext().getAttribute(TAG);
    if(attachment != null && attachment instanceof DownloadCourse) {
     DownloadCourse course = (DownloadCourse)attachment;
     course.error(ex);
    }
   }
   @Override
   public void inputReady(NHttpClientConnection conn, ContentDecoder content) {
    //don't invoke super, handle by self
    log("inputReady: %s", content);
    Object attachment = conn.getContext().getAttribute(TAG);
    if(attachment != null && attachment instanceof DownloadCourse) {
     DownloadCourse course = (DownloadCourse)attachment;
     course.update((FileContentDecoder)content);
     if(content.isCompleted()) {
      course.complete(conn.getHttpResponse());
     }
    }
   }
   @Override
   public void responseReceived(NHttpClientConnection conn) {
    super.responseReceived(conn);
    Object attachment = conn.getContext().getAttribute(TAG);
    if(attachment != null && attachment instanceof DownloadCourse) {
     DownloadCourse course = (DownloadCourse)attachment;
     course.prepare(conn.getHttpResponse());
    }
   }
   @Override
   public void timeout(NHttpClientConnection conn) {
    super.timeout(conn);
    Object attachment = conn.getContext().getAttribute(TAG);
    if(attachment != null && attachment instanceof DownloadCourse) {
     DownloadCourse course = (DownloadCourse)attachment;
     course.interrupt();
    }
   }
  };
  eventDispatch = new DefaultClientIOEventDispatch(handler, params);
  reactor = new DefaultConnectingIOReactor(1, params);
 }

 @Override
 public void run() {
  log("DownloadClient run at %s", new Date());
  try {
   reactor.execute(eventDispatch);
  } catch(InterruptedIOException e) {
   //never thrown? even when being interrupted?
   log("DownloadClient interrupted");
  } catch(IOException e) {
   e.printStackTrace();
  } finally {
   try {
    reactor.shutdown();
   } catch(IOException e) {
    e.printStackTrace();
   }
  }
 }

 public void execute(DownloadCourse course) {
  HttpHost host = course.getHost();
  reactor.connect(
   new InetSocketAddress(host.getHostName(), host.getPort() > 0 ? 
host.getPort() : 80), null,
   course, null);
 }

 public void startup() {
  dispatcher = new Thread(this, "DownloadClient Thread");
  dispatcher.setDaemon(true);
  dispatcher.start();
  workers = Executors.newFixedThreadPool(2);
 }

 public void shutdown() {
  dispatcher.interrupt();
  workers.shutdown();
 }

 public void download(URI uri, File saveFile) throws IOException {
  DownloadCourse course = new DownloadCourse(this, uri, saveFile);
  workers.submit(course);
 }

 public static void log(Object s) {
  System.out.println(s);
 }

 public static void log(String format, Object... a) {
  System.out.println(String.format(format, a));
 }

 public static void main(String[] args) throws Exception {
  //startup client
  DownloadClient client = new DownloadClient();
  client.startup();

  //submit request
  File saveFile = new File("/sdcard/download.txt");
  if(!saveFile.exists()) saveFile.createNewFile();
  client.download(new URI("http://www.google.com/";), saveFile);

  //for a while...
  Thread.sleep(10000);

  //clear for end
  client.shutdown();
 }

}


//********** io.DownloadCourse.java **********
package io;

import static io.DownloadClient.log;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI;
import java.nio.channels.FileChannel;
import java.util.Date;

import org.apache.commons.io.FileUtils;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.nio.FileContentDecoder;

public class DownloadCourse implements Runnable {

 public static final int maxRetryTimes = 5;
 public static final byte QUEUING_STATUS = 0;
 public static final byte DOWNLOADING = 1;
 public static final byte PAUSED = 15;
 public static final byte FINISHED = 127;
 public static final byte VIOLATED = -1;
 public static final byte TERMINATED = -128;

 DownloadClient client;
 URI uri;
 File file;
 RandomAccessFile randomAccessFile;
 FileChannel fileChannel;
 long contentLength;
 long position;
 int status;
 int retryTimes;

 public DownloadCourse(DownloadClient client, URI uri, File file) {
  this.client = client;
  this.uri = uri;
  this.file = file;
  status = QUEUING_STATUS;
 }

 @Override
 public void run() {
  log("DownloadCourse run at %s", new Date());
  status = QUEUING_STATUS;
  client.execute(this);
 }

 public HttpHost getHost() {
  return uri.getPort() > 0 ?
   new HttpHost(uri.getHost(), uri.getPort()) :
   new HttpHost(uri.getHost());
 }

 public HttpRequest getRequest() {
  HttpRequest request = uri.getQuery() != null ?
   new BasicHttpRequest("GET", uri.getPath() + "?" + uri.getQuery()) :
   new BasicHttpRequest("GET", uri.getPath());
  request.setHeader("Host", getHost().toHostString());
  return request;
 }

 public void prepare(HttpResponse response) {
  Header contentLengthHeader = response.getFirstHeader("Content-Length");
  if(contentLengthHeader != null) {
   contentLength = Integer.parseInt(contentLengthHeader.getValue().trim());
  }
  try {
   randomAccessFile = new RandomAccessFile(file, "rw");
   if(contentLength > 0) {
    randomAccessFile.setLength(contentLength);
   }
   fileChannel = randomAccessFile.getChannel();
  } catch(IOException e) {
   throw new RuntimeException(e);
  }
  status = DOWNLOADING;
 }

 public void update(FileContentDecoder content) {
  long length = 0;
  try {
   length = content.transfer(fileChannel, position, contentLength);
  } catch(IOException e) {
   throw new RuntimeException(e);
  }
  position += length;
  log("download progress: %d/%d", position, contentLength);
 }

 public void complete(HttpResponse response) {
  try {
   randomAccessFile.close();
   log(response.getStatusLine().toString());
   log(new String(FileUtils.readFileToByteArray(file)));
  } catch(IOException e) {
   throw new RuntimeException(e);
  }
  status = FINISHED;
 }

 public void error(Exception ex) {
  try {
   randomAccessFile.close();
  } catch(IOException e) {
   throw new RuntimeException(e);
  }
  if(++retryTimes < maxRetryTimes) {
   status = VIOLATED;
  } else {
   status = TERMINATED;
  }
 }

 public void interrupt() {
  try {
   randomAccessFile.close();
  } catch(IOException e) {
   throw new RuntimeException(e);
  }
  status = PAUSED;
 }

 public String toString() {
  return String.format("%s\n\turi: %s\n\tfile: %s\n\tstatus: 
%d\n\tcontentLength: %d\n\tposition:%d", super.toString(), uri, file, status, 
contentLength, position);
 }

}

Reply via email to