«

Android加载大量图片时候的坑与改进方式

时间:2024-3-2 19:17     作者:韩俊     分类: Android


前言

近日需要在安卓上面显示图片,于是查了一些资料,下面可以参考一下:
Android高效显示图片详解(一)
Android高效显示图片详解(二)
Android高效显示图片详解(三)

但是问题在于,这个不是很科学,不踩一下坑不心熄,于是就有了这个文章。

使用原始方式加载显示本地图片列表

xml文件:imgtest_localimages.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请选择要显示的图片"/>
    <Spinner android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/spinner_images">

    </Spinner>
    <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="添加图片" android:id="@+id/btn_append_img"/>
    <ScrollView android:layout_width="fill_parent" android:layout_height="wrap_content">
        <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:id="@+id/img_list"></LinearLayout>
    </ScrollView>

</LinearLayout>

java后台代码:imgtest_localImages.java

package com.example.MyStudyAndroid;

import android.app.Activity;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.*;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Created by Administrator on 2015/6/30.
 */
public class imgtest_localImages extends Activity {

    List<String> _imageList;
    private Spinner spinner_images;
    private Button btn_append_img;
    private LinearLayout layout_imglist;
    @Override
    public void onCreate(Bundle savedInstanceState){

        super.onCreate(savedInstanceState);
        setContentView(R.layout.imgtest_localimages);

        initUI();
        initEvents();
        initDatas();

    }

    private void initUI(){
        spinner_images=(Spinner)findViewById(R.id.spinner_images);
        btn_append_img=(Button)findViewById(R.id.btn_append_img);
        layout_imglist=(LinearLayout)findViewById(R.id.img_list);

    }
    private void initEvents(){
        final BitmapFactory.Options bf_opts = new BitmapFactory.Options();
        bf_opts.inJustDecodeBounds = true;
        //BitmapFactory.decodeFile("/sdcard/picturetest/a1.jpg", opts);

        //bf_opts.inSampleSize = computeSampleSize(opts, -1, 480*800);
        bf_opts.inJustDecodeBounds = false;
        btn_append_img.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try{
                String imgpath=_imageList.get(spinner_images.getSelectedItemPosition());
                ImageView imageView=new ImageView(view.getContext());
                Bitmap bmp1 = BitmapFactory.decodeFile(imgpath, bf_opts);

                layout_imglist.addView(imageView);
                imageView.setImageBitmap(bmp1);
            } catch (OutOfMemoryError err) {
                    Toast.makeText(imgtest_localImages.this,err.toString(),Toast.LENGTH_SHORT);
            }
            }
        });
    }

    private void initDatas(){
        _imageList=scanImages();
        _imageList=_imageList.subList(0,50);

        ArrayAdapter adpt_chooser=new ArrayAdapter(this,android.R.layout.simple_spinner_item,_imageList);
        spinner_images.setAdapter(adpt_chooser);
        spinner_images.setOnItemSelectedListener(new Spinner.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                String path=_imageList.get(i);
                Log.e("fffffff","选择了:"+path);
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

                Log.e("xxxx","没有选中任何东西。");
            }
        });

    }

    //--浏览本地文件。
    private List<String> scanImages()
    {
        List<String> _imgInfos=new ArrayList<String>();
        HashMap<String,List<String>> imgInfos=new HashMap<String, List<String>>();
        Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        //Uri.parse("content://media/internal/images");
        String[] columns=new String[]{"*"};
        Cursor cur = managedQuery(mImageUri,columns,MediaStore.Images.Media.MIME_TYPE + "=? or "
                        + MediaStore.Images.Media.MIME_TYPE + "=?",
                new String[] { "image/jpeg", "image/png" }, MediaStore.Images.Media.DATE_MODIFIED);
        if(cur==null){
            return _imgInfos;
        }

        while (cur.moveToNext()){
            String imgPath=cur.getString(cur.getColumnIndex(MediaStore.Images.Media.DATA));
            String parentName=new File(imgPath).getParentFile().getName();
            if(imgInfos.containsKey(parentName)){
                List<String> childList=imgInfos.get(parentName);
                childList.add(imgPath);
            }
            else{
                ArrayList<String> childList=new ArrayList<String>();
                childList.add(imgPath);
                imgInfos.put(parentName,childList);
            }
            _imgInfos.add(imgPath);

        }
        cur.close();
        return _imgInfos;
    }

}

大家可以试试不停选择任意一张图片不停添加图片,反正我这边会报错,而且假如图片大的话,会有卡顿现象:

结论
安卓程序里面的内存是有限的,没办法无限加载图片,这跟网页不一样,而且app里面加载图片会有卡顿现象,现在一种比较好的设想是,加载到图片时候或什么的,先给一张默认图片,然后另外开一个现成慢慢拉取图片,这样才比较好。

使用原始方式加载远程图片列表

上面用本地图片加载起来看了效果,发现卡顿及内存溢出的问题,那么,现在批量加载显示网上的远程图片看看效果。–这里先随便加载几个图片,不需要特意弄内存溢出了。
遗憾的是,测试得到的结果是报错,没办法重现阻塞ui线程的现象,这里说明一下,在4.0以后在主线程使用http请求都会出这种错。

代码内容如下

imgtest_remoteimages.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下面是远程列表图片。"/>
    <ScrollView android:layout_width="fill_parent" android:layout_height="wrap_content">
        <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:id="@+id/panel_imgList">

        </LinearLayout>
    </ScrollView>

</LinearLayout>

对应后台java文件

package com.example.MyStudyAndroid;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by Administrator on 2015/6/30.
 */
public class imgtest_remoteImages extends Activity {

    private LinearLayout panel_imgList;
    final BitmapFactory.Options bf_opts = new BitmapFactory.Options();

    @Override
    public void onCreate(Bundle savedInstanceState){

        super.onCreate(savedInstanceState);
        setContentView(R.layout.imgtest_remoteimages);

        initUI();
        initEvents();
        bf_opts.inJustDecodeBounds = false;
        fetchRemoteImages();

    }

    private void initUI(){
        panel_imgList=(LinearLayout)findViewById(R.id.panel_imgList);

    }
    private void initEvents(){}

    private void fetchRemoteImages(){
        appendRemoteImage("http://news.9duw.com/UploadPic/2013-4/201342922121240199.jpg");

    }
    public void appendRemoteImage(String path) {
        try{
            String imgpath=path;

            ImageView imageView=new ImageView(this);

            byte[] imgbytes=getimage(path);
            Bitmap bmp1 = BitmapFactory.decodeByteArray(imgbytes,0,imgbytes.length,bf_opts);

            panel_imgList.addView(imageView);
            imageView.setImageBitmap(bmp1);
        } catch (Exception err) {
            err.printStackTrace();
            Toast.makeText(this, err.toString(), Toast.LENGTH_SHORT).show();
        }
    }

    public  byte[] getimage(String path) throws Exception{ //连接远程网址
        URL url=new URL(path);
        HttpURLConnection conn=(HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setRequestMethod("GET");
        if(conn.getResponseCode()==200){
        InputStream instream=conn.getInputStream();
        return read(instream);
        }
        return null;
        }

    private  byte[] read(InputStream instream) throws Exception{ //  读取数据流,返回字节数据流
        ByteArrayOutputStream outstream=new ByteArrayOutputStream();
        byte[] buffer=new byte[1024];
        while( (instream.read(buffer))!=-1){
        outstream.write(buffer);
        }
        instream.close();
        return outstream.toByteArray();
        }
}

结论
看来,另开线程下载图片文件,然后将图片缓存到本地也是必须的。

改进图片图片列表的加载方式

改进方式有点多,下一篇详细说明。

        <p>版权声明:本文为博主原创文章,未经博主允许不得转载。</p>

标签: android

热门推荐