下载RecyclerView的图像时出现性能问题 [英] Performance issue downloading images for RecyclerView

查看:113
本文介绍了下载RecyclerView的图像时出现性能问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有自定义适配器的ListView.每行都有一个我使用位图呈现的ImageView,但是当我执行下载位图的AsyncTask之后,我当前的代码会阻塞UI线程,因为我正在使用get().我想更改代码并访问onPostExecute()中的imageViews或类似内容.这样就可以显示行,而无需等待所有精灵加载.

I have a ListView with a custom adapter. Every row has an ImageView that I render using a Bitmap, but my current code blocks the UI thread as I am using get() after executing my AsyncTask that downloads the bitmaps. I would like to change my code and access the imageViews in the onPostExecute() or something similar. So that the rows already display without waiting for all sprites to load.

适配器类(在此处触发下载)

Adapter class (download is triggered here)

public class PokemonAdapter extends ArrayAdapter<PokemonPOJO> implements View.OnClickListener{

private ArrayList<PokemonPOJO> dataSet;
Context mContext;
private int lastPosition = -1;

// View lookup cache
private static class ViewHolder {
    TextView txtName;
    TextView txtCP;
    TextView txtGenderShiny;
    ImageView sprite;
    Button btnDelete;
}

public PokemonAdapter(ArrayList<PokemonPOJO> data, Context context) {
    super(context, R.layout.row_pokemon, data);
    this.dataSet = data;
    this.mContext=context;
}

@Override
public void onClick(View v) {

    int position=(Integer) v.getTag();
    Object object= getItem(position);
    PokemonPOJO dataModel=(PokemonPOJO)object;

    switch (v.getId())
    {
        case R.id.btn_delete:
            FirebaseDatabase.getInstance().getReference("pokemons").child(dataModel.getUid()).removeValue();
            Toast.makeText(getContext(), "Pokemon removed!", Toast.LENGTH_SHORT).show();
            this.remove(dataModel);
            break;
    }
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // Get the data item for this position
    PokemonPOJO dataModel = getItem(position);
    // Check if an existing view is being reused, otherwise inflate the view
    ViewHolder viewHolder; // view lookup cache stored in tag

    final View result;

    if (convertView == null) {

        viewHolder = new ViewHolder();
        LayoutInflater inflater = LayoutInflater.from(getContext());
        convertView = inflater.inflate(R.layout.row_pokemon, parent, false);
        viewHolder.txtName = (TextView) convertView.findViewById(R.id.text_name);
        viewHolder.txtCP = (TextView) convertView.findViewById(R.id.text_cp);
        viewHolder.txtGenderShiny = (TextView) convertView.findViewById(R.id.text_gendershiny);
        viewHolder.sprite = (ImageView) convertView.findViewById(R.id.img_sprite);
        viewHolder.btnDelete = (Button)convertView.findViewById(R.id.btn_delete);
        result=convertView;

        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
        result=convertView;
    }

    lastPosition = position;

    viewHolder.txtName.setText(dataModel.getName());
    viewHolder.txtCP.setText("CP: " + Integer.toString(dataModel.getCP()));
    viewHolder.txtGenderShiny.setText(dataModel.getGender() + (dataModel.isShiny() ? " (Shiny)" : ""));
    viewHolder.btnDelete.setOnClickListener(this);

    try {
        Bitmap bm = new DownloadImageTask().execute(dataModel.getSpriteUrl()).get();
        viewHolder.sprite.setImageBitmap(bm);
    } catch (Exception e) {
        e.printStackTrace();
    }
    viewHolder.btnDelete.setTag(position);

    // Return the completed view to render on screen
    return convertView;
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {

    @Override
    protected Bitmap doInBackground(String... urls) {
        String urldisplay = urls[0];
        Bitmap bm = null;
        try {
            InputStream in = new java.net.URL(urldisplay).openStream();
            bm = BitmapFactory.decodeStream(in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bm;
    }

}

具有ListView的片段

Fragment with ListView

public class MyPokemonFragment extends Fragment {

    private FirebaseAuth auth;
    private DatabaseReference pokemonDb;
    private TextView text_noPokemon;
    private ListView listViewPokemon;
    private static PokemonAdapter adapter;
    private populateListViewTask populateListView;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_mypokemon,null);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

        super.onViewCreated(view, savedInstanceState);
        auth = FirebaseAuth.getInstance();

        listViewPokemon = view.findViewById(R.id.list_pokemon);
        text_noPokemon= view.findViewById(R.id.text_noPokemon);

        Query getUserPokemon = FirebaseDatabase.getInstance().getReference("pokemons").orderByChild("userUid").equalTo(auth.getCurrentUser().getUid());
        getUserPokemon.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot snapshot) {

                if(!snapshot.hasChildren()) {
                    text_noPokemon.setText("You have not added any Pokémon yet.");
                }
                else {
                    TreeMap<String, Pokemon> pokemons = new TreeMap<>();
                    for (DataSnapshot pokemon : snapshot.getChildren()) {
                        pokemons.put(pokemon.getKey(), pokemon.getValue(Pokemon.class));
                    }
                    populateListView = new populateListViewTask();
                    populateListView.execute(pokemons);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) { }
        });

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if(populateListView != null && populateListView.getStatus() == AsyncTask.Status.RUNNING)
            populateListView.cancel(true);
    }

    private class populateListViewTask extends AsyncTask<TreeMap<String, Pokemon>, Void, ArrayList<PokemonPOJO>> {

        @Override
        protected ArrayList<PokemonPOJO> doInBackground(TreeMap<String, Pokemon>... maps) {

            ArrayList<PokemonPOJO> pojos = new ArrayList<>();
            HttpURLConnection connection = null;
            BufferedReader reader = null;

            Iterator it = maps[0].entrySet().iterator();
            while(it.hasNext()) {
                Map.Entry pair = (Map.Entry)it.next();
                Pokemon p = (Pokemon)pair.getValue();
                try {
                    URL url = new URL("https://pokeapi.co/api/v2/pokemon/" + p.getPokedexNr() + "/");
                    connection = (HttpURLConnection) url.openConnection();
                    connection.connect();
                    InputStream stream = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(stream));
                    StringBuffer buffer = new StringBuffer();
                    String line = "";
                    while ((line = reader.readLine()) != null) {
                        buffer.append(line + "\n");
                    }

                    JSONObject j = new JSONObject(buffer.toString());
                    String name = j.getString("name");
                    String spriteUrl = (p.isShiny() ? j.getJSONObject("sprites").getString("front_shiny") : j.getJSONObject("sprites").getString("front_default"));

                    PokemonPOJO pojo = new PokemonPOJO((String)pair.getKey(), p.getPokedexNr(), name, spriteUrl, p.isShiny(), p.getGender(), p.getCP());
                    pojos.add(pojo);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    connection.disconnect();
                    try {
                        if (reader != null)
                            reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return pojos;
        }

        @Override
        protected void onPostExecute (ArrayList < PokemonPOJO > pojos) {
            adapter = new PokemonAdapter(pojos, getContext());
            listViewPokemon.setAdapter(adapter);
        }
    }
}

口袋妖怪行XML

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/img_sprite"
            android:layout_width="96dp"
            android:layout_height="96dp"
            android:scaleType="fitCenter" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="left|center_vertical"
            android:orientation="vertical">

            <TextView
                android:id="@+id/text_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@android:color/black"
                android:textSize="20sp"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/text_cp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="18sp" />

            <TextView
                android:id="@+id/text_gendershiny"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="18sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="end"
            android:orientation="vertical">

            <Button
                android:id="@+id/btn_delete"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:backgroundTint="@color/colorPrimary"
                android:text="DELETE"
                android:textColor="#ffffff"
                android:textSize="16sp"
                android:textStyle="bold" />
        </LinearLayout>

    </LinearLayout>

</android.support.v7.widget.CardView>

推荐答案

由于在AsyncTask上调用get()方法,因此出现性能问题. get()方法基本上使主线程等待,直到AsyncTask中的代码完成执行,然后主线程才继续执行其他指令.至少可以说,为什么Google添加此方法很奇怪.因此,这样做可以修复您的代码.

You are having performance issues because you are calling the get() method on your AsyncTask. The get() method basically causes the main thread to wait until the code in the AsyncTask completes execution before the main thread continues executing other instructions. Why Google added this method is curious to say the least. So do this to fix your code.

创建一个新的Java类文件.将文件命名为"DownloadImageTask"并添加以下代码:

Create a new Java class file. Name the file "DownloadImageTask" and add this code:

public interface DownloadImageListener {
    void onCompletedImageDownload(Bitmap bm);
}


public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {

    private static final String TAG = DownloadImageTask.class.getSimpleName();

    private DownloadImageListener mListener;
    private String imageUrl = "";

    public DownloadImageTask(String imageUrl, DownloadImageListener listener){
        this.imageUrl = imageUrl;
        this.mListener = listener;
    }


    @Override
    protected Bitmap doInBackground(String... urls) {
        Bitmap bm = null;
        try {
            InputStream in = new java.net.URL(imageUrl).openStream();
            bm = BitmapFactory.decodeStream(in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bm;
    }

    protected void onPostExecute(Bitmap bm) {
        mListener.onCompletedImageDownload(bm);
    }
}


如果在将公共interface添加到"DownloadImageTask" Java文件时遇到任何问题,只需创建一个单独的Java文件名"DownloadImageListener",然后在其中放置interface代码即可.


If you have any issues adding the public interface to the "DownloadImageTask" Java file just create a separate Java file name "DownloadImageListener" and put the interface code in there.


设置代码以查询AsyncTask.
更改getView()中的Adapter代码:

Set your code to query the AsyncTask.
Change the Adapter code inside your getView() from this:

try {
    Bitmap bm = new DownloadImageTask().execute(dataModel.getSpriteUrl()).get();
    viewHolder.sprite.setImageBitmap(bm);
} catch (Exception e) {
    e.printStackTrace();
}

对此:

try {
    DownloadImageListener listener = new DownloadImageListener() {
        @Override
        public void onCompletedImageDownload(Bitmap bm) {
            if(bm != null){
                viewHolder.sprite.setImageBitmap(bm);
            }
        }
    };

    String imageUrl = dataModel.getSpriteUrl();

    DownloadImageTask downloadImageTask = new DownloadImageTask(imageUrl, listener);
    downloadImageTask.execute();

} catch (Exception e) {
    Log.e(TAG, e.getMessage());
}


这允许您的AsyncTask执行,当返回Bitmap时,将在onPostExecute()方法中触发侦听器,并在onCompletedImageDownload()回调方法中将Bitmap发送到您的ListView.

This allows your AsyncTask to execute and when the Bitmap is returned the listener is triggered in the onPostExecute() method sending the Bitmap to your ListView in the onCompletedImageDownload() callback method.

其他信息:
为了进一步提高性能,您可以创建一个缓存模型来保存和检索设备中的图像(如果您以前已经下载过这些图像).但这需要一些非常先进的技术-并且当您希望下载的图像可能不时更改时会变得非常棘手.

Additional Info:
To improve performance even further you could create a caching model to save and retrieve images from the device if you have already downloaded them in the past. But that requires some really advanced techniques--and gets really tricky when images you wish to download might change from time to time.

这篇关于下载RecyclerView的图像时出现性能问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆