从URL加载Android位图

86

我有一个关于从网站加载图像的问题。我使用的代码是:

Display display = getWindowManager().getDefaultDisplay(); 
int width = display.getWidth();
int height = display.getHeight();
Bitmap bit=null;
try {
    bit = BitmapFactory.decodeStream((InputStream)new URL("http://www.mac-wallpapers.com/bulkupload/wallpapers/Apple%20Wallpapers/apple-black-logo-wallpaper.jpg").getContent());
} catch (Exception e) {}
Bitmap sc = Bitmap.createScaledBitmap(bit,width,height,true);
canvas.drawBitmap(sc,0,0,null);

但它总是返回一个空指针异常并导致程序崩溃。 URL 是有效的,似乎对其他人也有效。 我使用的是 2.3.1 版本。


1
你收到了什么崩溃信息?堆栈跟踪是什么?你知道哪一行导致了崩溃吗? - zeh
createScalesBitmap 抛出了 NullPointerException 异常,因为 bit 为空。 - Tyeomans
1
需要网络权限... 在 AndroidManifest.xml 中添加 <uses-permission android:name="android.permission.INTERNET" /> - Tyeomans
20个回答

184
public static Bitmap getBitmapFromURL(String src) {
    try {
        URL url = new URL(src);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoInput(true);
        connection.connect();
        InputStream input = connection.getInputStream();
        Bitmap myBitmap = BitmapFactory.decodeStream(input);
        return myBitmap;
    } catch (IOException e) {
        // Log exception
        return null;
    }
}

6
如何从 https 加载一个资源? - Abdul Wahab
13
注意避免阻塞主线程。这应该在AsyncTask派生的类中使用。 - Radim Halfar
19
代码没问题。但会出现“主线程上的网络”异常。尝试在“Async Task”中使用它。 - Vikrant_Dev
如何检测引发的IOException是否是由于缺乏Internet访问而引起的? - cprcrack
1
为什么我需要一个HTTP URL连接,而不是只做"InputStream in = url.openStream();"?你的方法和我的方法哪个更好呢?只是好奇。 - AntonioSanchez
显示剩余3条评论

38
如果您正在使用PicassoGlideUniversal-Image-Loader从URL加载图像,您只需执行以下操作即可简单地获取已加载的位图:

对于Picasso(当前版本为2.71828

Java代码

Picasso.get().load(imageUrl).into(new Target() {
    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        // loaded bitmap is here (bitmap)
    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) { }

    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {}
});

Kotlin代码

Picasso.get().load(url).into(object : com.squareup.picasso.Target { 
    override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
         // loaded bitmap is here (bitmap)
    }

    override fun onPrepareLoad(placeHolderDrawable: Drawable?) {}

    override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {}
})

对于Glide
请查看如何使用Glide将图像下载到位图中?

对于Universal-Image-Loader
Java代码

imageLoader.loadImage(imageUrl, new SimpleImageLoadingListener() 
{
    @Override
    public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) 
    {
         // loaded bitmap is here (loadedImage)
    }
});

Linh,非常感谢。这是一个伟大的答案,适用于那些使用 Picasso 加载他们的图像的人。 - M DEV

11

我更喜欢这个:

InputStream 中创建位图 并返回它:

    public static  Bitmap downloadImage(String url) {
        Bitmap bitmap = null;
        InputStream stream = null;
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inSampleSize = 1;

        try {
            stream = getHttpConnection(url);
            bitmap = BitmapFactory.decodeStream(stream, null, bmOptions);
            stream.close();
        }
        catch (IOException e1) {
            e1.printStackTrace();
            System.out.println("downloadImage"+ e1.toString());
        }
        return bitmap;
    }

  // Makes HttpURLConnection and returns InputStream

 public static  InputStream getHttpConnection(String urlString)  throws IOException {

        InputStream stream = null;
        URL url = new URL(urlString);
        URLConnection connection = url.openConnection();

        try {
            HttpURLConnection httpConnection = (HttpURLConnection) connection;
            httpConnection.setRequestMethod("GET");
            httpConnection.connect();

            if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                stream = httpConnection.getInputStream();
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("downloadImage" + ex.toString());
        }
        return stream;
    }

注意:

Android包含两个HTTP客户端HttpURLConnectionApache HTTP Client。从Gingerbread版本开始,HttpURLConnection是最好的选择。

从Android 3.x Honeycomb版本或更高版本开始,您不能在UI线程上执行网络IO,否则会抛出android.os.NetworkOnMainThreadException异常。必须使用如下所示的Asynctask来代替。

/**     AsyncTAsk for Image Bitmap  */
    private class AsyncGettingBitmapFromUrl extends AsyncTask<String, Void, Bitmap> {


        @Override
        protected Bitmap doInBackground(String... params) {

            System.out.println("doInBackground");

            Bitmap bitmap = null;

            bitmap = AppMethods.downloadImage(params[0]);

            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {

            System.out.println("bitmap" + bitmap);

        }
    }

如果我想加载多个位图并将它们显示为列表视图中列表项的背景,那么做到这一点的好方法是什么?我应该为每个位图调用Asynctask吗? - Tushar Kathuria
1
我有一个关于AsyncTask的问题。我该如何在我想要将url转换为位图的类中调用此方法,以及如何在这个类内部访问位图? - Marcos Guimaraes

6
public Drawable loadImageFromURL(String url, String name) {
    try {
        InputStream is = (InputStream) new URL(url).getContent();
        Drawable d = Drawable.createFromStream(is, name);
        return d;
    } catch (Exception e) {
        return null;
    }
}

6

这种方法可以使用Kotlin协程,以便不会阻塞UI主线程,并返回调整大小的圆形位图图像(如个人资料图片)。

 private var image: Bitmap? = null
 private fun getBitmapFromURL(src: String?) {
    CoroutineScope(Job() + Dispatchers.IO).launch {
        try {
            val url = URL(src)
            val bitMap = BitmapFactory.decodeStream(url.openConnection().getInputStream())
            image = Bitmap.createScaledBitmap(bitMap, 100, 100, true)
        } catch (e: IOException) {
            // Log exception
        }
    }
}

谢谢。它对我有用。但是它会给出警告不适当的阻塞方法调用。它可能阻塞UI主线程 - Rumit Patel

3

在Android中获取url到位图的方法是只需传递此图像的链接即可获得位图。

public static Bitmap getBitmapFromURL(String imgUrl) {
    try {
        URL url = new URL(imgUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoInput(true);
        connection.connect();
        InputStream input = connection.getInputStream();
        Bitmap myBitmap = BitmapFactory.decodeStream(input);
        return myBitmap;
    } catch (IOException e) {
        // Log exception
        return null;
    }
}

3

它在Pie操作系统中有效,请使用此方法

    @Override
    protected void onCreate() {
        super.onCreate();
        //setNotificationBadge();

        if (android.os.Build.VERSION.SDK_INT >= 9) {
            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
        }

    }

        BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
        Menu menu = bottomNavigationView.getMenu();
        MenuItem userImage = menu.findItem(R.id.navigation_download);
        userImage.setTitle("Login");

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                try {

                    URL url = new URL("https://rukminim1.flixcart.com/image/832/832/jmux18w0/mobile/b/g/n/mi-redmi-6-mzb6387in-original-imaf9z8eheryfbsu.jpeg?q=70");
                    Bitmap myBitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());

                    Log.e("keshav", "Bitmap " + myBitmap);
                    userImage.setIcon(new BitmapDrawable(getResources(), myBitmap));

                } catch (IOException e) {
                    Log.e("keshav", "Exception " + e.getMessage());
                }
            }
        });

3

使用 Kotlin 协程处理线程

代码崩溃的原因是因为 Bitmap 试图在 Main Thread 上创建,这是不允许的,因为它可能会导致 Android Not Responding (ANR) 错误。

所用概念

  • Kotlin 协程。请参阅此文档了解详情。
  • 下面使用了 Loading, Content, Error (LCE) 模式。如有兴趣,您可以在此演示和视频中了解更多信息。
  • 使用了 LiveData 返回数据。我将我的最爱 LiveData 资源编译在这些笔记中。
  • 奖励代码中,toBitmap() 是一个 Kotlin 扩展函数,需要将该库添加到应用依赖项中。

实现

代码

1. 在与 Main Thread 不同的线程中创建 Bitmap

在本示例中,使用 Kotlin 协程 将函数执行在线程 Dispatchers.IO 中,该线程用于基于 CPU 的操作。该函数加上了前缀 suspend ,这是协程语法。

奖励 - 在创建 Bitmap 后,还将其压缩成一个 ByteArray,以便稍后通过 Intent 传递,详细信息请参阅此完整示例

Repository.kt

suspend fun bitmapToByteArray(url: String) = withContext(Dispatchers.IO) {
    MutableLiveData<Lce<ContentResult.ContentBitmap>>().apply {
        postValue(Lce.Loading())
        postValue(Lce.Content(ContentResult.ContentBitmap(
            ByteArrayOutputStream().apply {
                try {                     
                    BitmapFactory.decodeStream(URL(url).openConnection().apply {
                        doInput = true
                        connect()
                    }.getInputStream())
                } catch (e: IOException) {
                   postValue(Lce.Error(ContentResult.ContentBitmap(ByteArray(0), "bitmapToByteArray error or null - ${e.localizedMessage}")))
                   null
                }?.compress(CompressFormat.JPEG, BITMAP_COMPRESSION_QUALITY, this)
           }.toByteArray(), "")))
        }
    }

ViewModel.kt

//Calls bitmapToByteArray from the Repository
private fun bitmapToByteArray(url: String) = liveData {
    emitSource(switchMap(repository.bitmapToByteArray(url)) { lce ->
        when (lce) {
            is Lce.Loading -> liveData {}
            is Lce.Content -> liveData {
                emit(Event(ContentResult.ContentBitmap(lce.packet.image, lce.packet.errorMessage)))
            }
            is Lce.Error -> liveData {
                Crashlytics.log(Log.WARN, LOG_TAG,
                        "bitmapToByteArray error or null - ${lce.packet.errorMessage}")
            }
        }
    })
}

奖励 - 将 ByteArray 转换为 Bitmap

Utils.kt

fun ByteArray.byteArrayToBitmap(context: Context) =
    run {
        BitmapFactory.decodeByteArray(this, BITMAP_OFFSET, size).run {
            if (this != null) this
            // In case the Bitmap loaded was empty or there is an error I have a default Bitmap to return.
            else AppCompatResources.getDrawable(context, ic_coinverse_48dp)?.toBitmap()
        }
    }

2

传递您的图像URL: 尝试使用以下方法:

private Bitmap getBitmap(String url) 
    {
        File file=fileCache.getFile(url);
        Bitmap bm = decodeFile(file);
        if(bm!=null) 
            return bm;
        try {
            Bitmap bitmap=null;
            URL ImageUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection)ImageUrl.openConnection();
            conn.setConnectTimeout(50000);
            conn.setReadTimeout(50000);
            conn.setInstanceFollowRedirects(true);
            InputStream is = conn.getInputStream();
            OutputStream os = new FileOutputStream(file);
            Utils.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(file);
            return bitmap;
        } catch (Exception ex){
           ex.printStackTrace();
           return null;
        }
    }
    private Bitmap decodeFile(File file){
        try {
            BitmapFactory.Options opt = new BitmapFactory.Options();
            opt.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(file),null,opt);
            final int REQUIRED_SIZE=70;
            int width_tmp=opt.outWidth, height_tmp=opt.outHeight;
            int scale=1;
            while(true){
                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale*=2;
            }
            BitmapFactory.Options opte = new BitmapFactory.Options();
            opte.inSampleSize=scale;
            return BitmapFactory.decodeStream(new FileInputStream(file), null, opte);
        } catch (FileNotFoundException e) {}
        return null;
    }

创建 Utils 类:

public class Utils {
    public static void CopyStream(InputStream is, OutputStream os)
    {
        final int buffer_size=1024;
        try
        {
            byte[] bytes=new byte[buffer_size];
            for(;;)
            {
              int count=is.read(bytes, 0, buffer_size);
              if(count==-1)
                  break;
              os.write(bytes, 0, count);
            }
        }
        catch(Exception ex){}
    }
}

2
如果你喜欢 Coil 而不是 Glide。
val imageRequest = ImageRequest.Builder(context)
        .data(imageUrl)
        .target { drawable ->
            val bitmap = drawable.toBitmap() // This is the bitmap 
        }
        .build()
    ImageLoader(context).enqueue(imageRequest)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接