【发布时间】:2014-08-12 01:52:07
【问题描述】:
我正在尝试使用 Universal Image Loader 将图像保存到外部 SD 卡。我在这里关注作者 nostra 的这个非常过时的代码片段:
Download Image stream from android universal image loader
我有兴趣成功地重新创建它,但是当我尝试时,我得到了
这个错误:
06-21 07:56:22.586 17647-17647/as;dlkfa E/libEGL﹕ call to OpenGL ES API with no current context (logged once per thread)
06-21 07:56:27.562 17647-17647/;alsdjf E/ViewRootImpl﹕ sendUserActionEvent() mView == null
06-21 07:56:30.965 17647-17647/;aldjf; E/ViewRootImpl﹕ sendUserActionEvent() mView == null
06-21 07:56:32.737 17647-17647/a;sdjfl E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NullPointerException
at com.nostra13.universalimageloader.utils.IoUtils.copyStream(IoUtils.java:73)
at com.nostra13.universalimageloader.utils.IoUtils.copyStream(IoUtils.java:50)
at ;a;ldsfkj.MediaDisplayActivity.saveToGallery(MediaDisplayActivity.java:344)
at as;dlkfj.MediaDisplayActivity.onMenuItemClick(MediaDisplayActivity.java:298)
at android.widget.PopupMenu.onMenuItemSelected(PopupMenu.java:142)
at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:735)
at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:874)
at com.android.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:175)
at android.widget.AdapterView.performItemClick(AdapterView.java:301)
at android.widget.AbsListView.performItemClick(AbsListView.java:1507)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3336)
at android.os.Handler.handleCallback(Handler.java:730)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5455)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
at dalvik.system.NativeStart.main(Native Method)
我把他原来的代码改成了这个。在菜单项单击时调用它:
private void saveToGallery()
{
String imageUrl = imageUrls.get(pager.getCurrentItem());
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File fileForImage = new File(path, imageUrl);
InputStream sourceStream = null;
File cachedImage = ImageLoader.getInstance().getDiskCache().get(imageUrl);
if (cachedImage.exists()) { // if image was cached by UIL
try {
sourceStream = new FileInputStream(cachedImage);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else { // otherwise - download image
ImageDownloader downloader = new BaseImageDownloader(this);
try {
sourceStream = downloader.getStream(imageUrl, null);
} catch (IOException e) {
e.printStackTrace();
}
}
// TODO : use try-finally to close streams
OutputStream targetStream = null;
try {
targetStream = new FileOutputStream(fileForImage);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
IoUtils.copyStream(sourceStream, targetStream, null);
} catch (IOException e) {
e.printStackTrace();
}
try {
targetStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
sourceStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
第 344 行是:
IoUtils.copyStream(sourceStream, targetStream, null);
读取 cmets 进行修复。基本上,我使用的是像 https**:**这样的坏字符
另外,这是我的新代码,它可以工作,但它仍然不会为您的应用创建相册,如果您希望用户快速浏览他们自己的相册,这是非常理想的。我将继续向 cmets 中的编码大神寻求帮助。另外,保存同一张图片的时候还不处理。
//TODO upon uninstall ensure the files are still there
//TODO make sure this janky stuff don't crash if you save the same stuff twice
private boolean saveToGallery() {
String imageUrl = imageUrls.get(pager.getCurrentItem());
int i = imageUrl.length()-1;
while(imageUrl.charAt(i) != '/')
{
i--;
}
String newImageUrl = imageUrl.substring(i+1);
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File fileForImage = new File(path, newImageUrl);
// if (!fileForImage.mkdirs()) {
// Log.e("shit", "Directory not created");
// }
boolean saved = false;
InputStream sourceStream = null;
File cachedImage = ImageLoader.getInstance().getDiskCache().get(imageUrl);
try {
if (cachedImage != null && cachedImage.exists()) { // if image was cached by UIL
sourceStream = new FileInputStream(cachedImage);
} else { // otherwise - download image
ImageDownloader downloader = new BaseImageDownloader(this);
sourceStream = downloader.getStream(imageUrl, null);
}
} catch (IOException e) {
L.e(e);
}
if (sourceStream != null) {
try {
System.out.println(fileForImage.getAbsolutePath());
OutputStream targetStream = new FileOutputStream(fileForImage);
IoUtils.copyStream(sourceStream, targetStream, null);
targetStream.close();
sourceStream.close();
saved = true;
} catch (IOException e) {
L.e(e);
}
}
//Updates the gallery, dunno how though lol, yay SO
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
return saved;
}
好的,我所要做的就是在附加 url 文件之前移动 mkdirs() 调用。我现在太累了,无法弄清楚事情的顺序,但肯定是因为这个。这是新代码。如果您使用通用图像加载器和文件名的部分 URL,这会将图像保存到应用程序的相册中,否则,您将无所适从:
//TODO upon uninstall ensure the files are still there
//TODO make sure this janky stuff don't crash if you save the same stuff twice
private boolean saveToGallery() {
String imageUrl = imageUrls.get(pager.getCurrentItem());
int i = imageUrl.length()-1;
while(imageUrl.charAt(i) != '/')
{
i--;
}
String newImageUrl = imageUrl.substring(i+1);
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES + "/Your app name here");
if (!path.mkdirs()) {
Log.e("shit", "Directory not created");
}
File fileForImage = new File(path, newImageUrl);
boolean saved = false;
InputStream sourceStream = null;
File cachedImage = ImageLoader.getInstance().getDiskCache().get(imageUrl);
try {
if (cachedImage != null && cachedImage.exists()) { // if image was cached by UIL
sourceStream = new FileInputStream(cachedImage);
} else { // otherwise - download image
ImageDownloader downloader = new BaseImageDownloader(this);
sourceStream = downloader.getStream(imageUrl, null);
}
} catch (IOException e) {
L.e(e);
}
if (sourceStream != null) {
try {
System.out.println(fileForImage.getAbsolutePath());
OutputStream targetStream = new FileOutputStream(fileForImage);
IoUtils.copyStream(sourceStream, targetStream, null);
targetStream.close();
sourceStream.close();
saved = true;
} catch (IOException e) {
L.e(e);
}
}
//Updates the gallery, dunno how though lol, yay SO
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
return saved;
}
【问题讨论】:
-
spanToGallery 中的第 344 行导致其中一个流出现 NullPointerException,您可以在 logcat 中读取。第 344 行是什么?
-
IoUtils.copyStream(sourceStream, targetStream, null);
-
好的。现在,正如我已经说过的那样,其中一个流是空的。你发现是哪一个了吗?你的代码有问题。实例化 targetStream 的 try 块有一个 catch 块,以防出现问题。如果有问题,您应该返回那里,因为在这种情况下继续使用空指针毫无意义。
-
奇怪的是,来自那个 catch 块的 e.printStackTrace() 没有出现在 logcat 中。无论如何,
targetStream=....是罪魁祸首。因此fileForImage为空或指示路径错误,或者您在清单中没有写入外部权限。请找出确切的完整路径。什么是 fileForImage.getAbsolutePath()? -
嗯,这是一条非常不寻常的道路。那不可能是您将其保存在那里的意图。首先,它不是有效路径,因为其中包含“:”。您已在存储目录上附加了以 http 开头的完整 url,您很可能只会附加以 .jpg 结尾的文件名。确实,您使用
imageUrl做到了。请先提取文件名。
标签: android exception-handling try-catch universal-image-loader fileoutputstream