为了在下载文件时更新进度,我自定义了一个InputStreamToFileIterable类来使用rxjava:
public class InputStreamToFileIterable implements Iterable<Integer> {
private InputStream inputStream;
private FileOutputStream outputStream;
private byte[] buffer;
public InputStreamToFileIterable(InputStream inputStream, FileOutputStream outputStream) {
this.inputStream = inputStream;
this.outputStream = outputStream;
this.buffer = new byte[512];
}
public Iterator<Integer> iterator() {
return new InputStreamIterator();
}
class InputStreamIterator implements Iterator<Integer> {
int data;
boolean isClosed;
int read;
public InputStreamIterator() {
read = 0;
try {
data = inputStream.read(buffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
isClosed = false;
}
@Override
public boolean hasNext() {
return data != -1;
}
@Override
public Integer next() {
read += data;
try {
if (isClosed){
throw new NoSuchElementException();
}
if (!hasNext()) {
inputStream.close();
outputStream.close();
isClosed = true;
} else {
outputStream.write(buffer, 0, data);
data = inputStream.read(buffer);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return read;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}
然而,当我订阅像这样的Observable时:
subscriptionList.add(Observable.just(cachePath)
.concatMap(new Func1<String, Observable<Integer>>() {
@Override
public Observable<Integer> call(String s) {
try {
isDownloading = true;
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.connect();
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
file.createNewFile();
fileOutputStream = new FileOutputStream(cachePath);
inputStream = connection.getInputStream();
total = connection.getContentLength();
return Observable.from(new InputStreamToFileIterable(inputStream, fileOutputStream));
} catch (Exception e) {
Log.d(TAG, e.getMessage());
}
return Observable.just(null);
}
})
.filter(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer integer) {
return integer!=null;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getObserverDownloadPicture()));
如果inputStream中途关闭,我的观察者无法捕获InputStreamIterator.next()抛出的RuntimeException。我将得到如下异常:
04-16 22:39:18.393 16996-17031/me.wuke.weibo E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-2
Process: me.wuke.weibo, PID: 16996
Theme: themes:{default=overlay:system, iconPack:system, com.android.systemui=overlay:system, com.android.systemui.navbar=overlay:system}
java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:62)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.RuntimeException: java.net.SocketException: recvfrom failed: ETIMEDOUT (Connection timed out)
at me.wuke.weibo.utility.InputStreamToFileIterable$InputStreamIterator.next(InputStreamToFileIterable.java:60)
at me.wuke.weibo.utility.InputStreamToFileIterable$InputStreamIterator.next(InputStreamToFileIterable.java:27)
at rx.internal.operators.OnSubscribeFromIterable$IterableProducer.slowpath(OnSubscribeFromIterable.java:97)
at rx.internal.operators.OnSubscribeFromIterable$IterableProducer.request(OnSubscribeFromIterable.java:73)
at rx.internal.producers.ProducerArbiter.request(ProducerArbiter.java:66)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.requestFromChild(OperatorConcat.java:124)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.access$000(OperatorConcat.java:75)
at rx.internal.operators.OperatorConcat$ConcatProducer.request(OperatorConcat.java:70)
at rx.internal.operators.OperatorSubscribeOn$1$1$1$1.call(OperatorSubscribeOn.java:85)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.net.SocketException: recvfrom failed: ETIMEDOUT (Connection timed out)
at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:588)
at libcore.io.IoBridge.recvfrom(IoBridge.java:552)
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:481)
at java.net.PlainSocketImpl.-wrap0(PlainSocketImpl.java)
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:237)
at com.android.okhttp.okio.Okio$2.read(Okio.java:135)
at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:211)
at com.android.okhttp.okio.RealBufferedSource.read(RealBufferedSource.java:50)
at com.android.okhttp.internal.http.HttpConnection$FixedLengthSource.read(HttpConnection.java:418)
at com.android.okhttp.okio.RealBufferedSource.read(RealBufferedSource.java:50)
at com.android.okhttp.internal.http.HttpEngine$2.read(HttpEngine.java:942)
at com.android.okhttp.okio.RealBufferedSource$1.read(RealBufferedSource.java:349)
at java.io.InputStream.read(InputStream.java:162)
at me.wuke.weibo.utility.InputStreamToFileIterable$InputStreamIterator.next(InputStreamToFileIterable.java:57)
at me.wuke.weibo.utility.InputStreamToFileIterable$InputStreamIterator.next(InputStreamToFileIterable.java:27)
at rx.internal.operators.OnSubscribeFromIterable$IterableProducer.slowpath(OnSubscribeFromIterable.java:97)
at rx.internal.operators.OnSubscribeFromIterable$IterableProducer.request(OnSubscribeFromIterable.java:73)
at rx.internal.producers.ProducerArbiter.request(ProducerArbiter.java:66)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.requestFromChild(OperatorConcat.java:124)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.access$000(OperatorConcat.java:75)
at rx.internal.operators.OperatorConcat$ConcatProducer.request(OperatorConcat.java:70)
at rx.internal.operators.OperatorSubscribeOn$1$1$1$1.call(OperatorSubscribeOn.java:85)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: android.system.ErrnoException: recvfrom failed: ETIMEDOUT (Connection timed out)
at libcore.io.Posix.recvfromBytes(Native Method)
at libcore.io.Posix.recvfrom(Posix.java:189)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:250)
at libcore.io.IoBridge.recvfrom(IoBridge.java:549)
我没有忘记在我的观察者中实现onError()方法。
public Observer getObserverDownloadPicture() {
return Observers.create(
new Action1<Integer>() {
@Override
public void call(Integer integer) {
onProgressChanged(integer * 100 / total);
}
}
, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Log.e(TAG, throwable.getMessage());
try {
fileOutputStream.close();
isDownloading = false;
file.delete();
} catch (Exception e) {
ExceptionUtils.saveLog(e);
}
progressTextView.setText("载入失败");
subscriptionList.clear();
}
}
, new Action0() {
@Override
public void call() {
try {
fileOutputStream.close();
} catch (Exception e) {
Log.d(TAG, e.getMessage());
}
if (isDownloading) {
setupPicture();
}
isDownloading = false;
subscriptionList.clear();
}
}
);
}
如何处理异常?请帮助我。
答案 0 :(得分:0)
尝试使用具体异常替换catch块中的Exception
。像这样:
catch (IOException e) {
Log.d(TAG, e.getMessage());
}