java - 歌曲图像可以更快吗?

标签 java android android-fragments android-asynctask

我正在研究音乐播放器,它可以在 fragment 上显示来自本地设备的所有歌曲和相应歌曲的图像。

当我启动该应用程序时,它需要很长时间才能加载,我注意到是 getImagesForSongs() 处理缓慢

为什么这么慢?有没有更好的方法,我该怎么做?

My All Song fragment 类



import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.mikeinvents.musicbaze.R;
import com.mikeinvents.musicbaze.adapters.SongAdapter;
import com.mikeinvents.musicbaze.data_model.SongModel;
import com.mikeinvents.musicbaze.ui.MainActivity;

import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * A simple {@link Fragment} subclass.
 */
public class AllSongsFragment extends Fragment {
    RecyclerView recyclerView;
    ArrayList<SongModel> song;
    ArrayList<Bitmap> bitmaps;
    MediaMetadataRetriever mediaMetadataRetriever;
    static byte[] rawArt;
   static Bitmap art;
   static BitmapFactory.Options bfo;

    public AllSongsFragment() {
        // Required empty public constructor
    }

    @Override
    public void onStart() {
        super.onStart();

    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View rootView =  inflater.inflate(R.layout.fragment_all_songs, container, false);
       // setRetainInstance(true);
        recyclerView = rootView.findViewById(R.id.song_recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        new MyAsyncTask().execute();

        return rootView;
    }

    private void loadSongList() {
        ContentResolver contentResolver = Objects.requireNonNull(getActivity()).getContentResolver();


        Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        String selection = MediaStore.Audio.Media.IS_MUSIC;
        String sortOrder = MediaStore.Audio.Media.TITLE + " ASC";

        Cursor cursor = contentResolver.query(uri,null,selection,null,sortOrder);

        if(cursor != null && cursor.moveToFirst()){
            song = new ArrayList<>();

            //get columns
            do {
                String data = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
                String title = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
                String album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
                String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
                String timeDuration = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));

                //convert time
                int duration = Integer.parseInt(timeDuration);
                @SuppressLint("DefaultLocale") String time = String.format("%02d:%02d",
                        TimeUnit.MILLISECONDS.toMinutes(duration),
                                TimeUnit.MILLISECONDS.toSeconds(duration) -
                                        TimeUnit.MILLISECONDS
                                                .toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration)));
                //save to list
                song.add(new SongModel(data, title, artist, album, time));

                Log.i(MainActivity.TAG, " Added: "+"data = "+data+" title = "+title+
                        " album = "+album+" artist: "+artist+" duration = "+time);

            }while (cursor.moveToNext());

        }

        if(cursor !=null){
            cursor.close();
        }

    }

    private void getImagesForSongs(){
        ContentResolver contentResolver = Objects.requireNonNull(getActivity()).getContentResolver();


        Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        String selection = MediaStore.Audio.Media.IS_MUSIC;
        String sortOrder = MediaStore.Audio.Media.TITLE + " ASC";

        Cursor cursor = contentResolver.query(uri,null,selection,null,sortOrder);

        if(cursor != null && cursor.moveToFirst()){
            bitmaps = new ArrayList<>();
            do{
                String data = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));


                //get song image
                mediaMetadataRetriever = new MediaMetadataRetriever();

                mediaMetadataRetriever.setDataSource(data);
                Log.i(MainActivity.TAG, "Data source has been set");

                rawArt = mediaMetadataRetriever.getEmbeddedPicture();
                Log.i(MainActivity.TAG, "Raw art has been gotten");

                bfo = new BitmapFactory.Options();
                art = decodeSampledBitmapFromResource(100,100);


                //save to list
                bitmaps.add(art);

            }while (cursor.moveToNext());
        }

        if (cursor != null) {
            cursor.close();
        }

    }

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 4;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.

            while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
        Log.i(MainActivity.TAG,"Calculated in sample size");

        return inSampleSize;
    }


    public static Bitmap decodeSampledBitmapFromResource(int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        bfo.inJustDecodeBounds = true;

        if(rawArt != null){
            BitmapFactory.decodeByteArray(rawArt,0,rawArt.length,bfo);
            Log.i(MainActivity.TAG, "Gotten embedded picture");
            // Calculate inSampleSize
            calculateInSampleSize(bfo,reqWidth,reqHeight);

            // Decode bitmap with inSampleSize set
            bfo.inJustDecodeBounds = false;


            return BitmapFactory.decodeByteArray(rawArt,0,rawArt.length,bfo);
        }

        Log.i(MainActivity.TAG, "Gotten null embedded picture");

        return null;

    }

    private class MyAsyncTask extends AsyncTask<Void, Void, Void>{
        ProgressDialog pd;
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            pd = new ProgressDialog(getContext());
            pd.setTitle("Please wait...");
            pd.setMessage("Fetching songs");
            pd.setCancelable(false);
            pd.show();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            getImagesForSongs();
            loadSongList();
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            pd.dismiss();
            SongAdapter songAdapter = new SongAdapter(getContext(), song, bitmaps);
            Log.i(MainActivity.TAG,"Initialized adapter");
            recyclerView.setAdapter(songAdapter);
            Log.i(MainActivity.TAG,"Adapter has been set");

        }
    }

}

我的 SongAdapter 类


import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;

import com.bumptech.glide.Glide;
import com.mikeinvents.musicbaze.R;
import com.mikeinvents.musicbaze.data_model.SongModel;
import com.mikeinvents.musicbaze.ui.MainActivity;

import java.util.ArrayList;

public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
    private Context ctx;
    private ArrayList<SongModel> mSong;
    private ArrayList<Bitmap> mBitmap;

    public SongAdapter(Context ct, ArrayList<SongModel> song, ArrayList<Bitmap> bitmap){
        ctx = ct;
        mSong = song;
        mBitmap = bitmap;
    }

    @NonNull
    @Override
    public SongAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        LayoutInflater inflater = LayoutInflater.from(ctx);
        View view = inflater.inflate(R.layout.song_row,viewGroup,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull SongAdapter.ViewHolder viewHolder, int i) {
        SongModel song = mSong.get(i);
        viewHolder.songTitle.setText(song.getTitle());
        viewHolder.songArtist.setText(song.getArtist());
        viewHolder.songAlbum.setText(song.getAlbum());
        viewHolder.songTime.setText(song.getSongTime());
       // Glide.with(ctx).load(mBitmap.get(i)).fallback(R.drawable.mojo_logo).into(viewHolder.songImage);

        if(mBitmap.get(i) == null){
            viewHolder.songImage.setImageResource(R.drawable.mojo_logo);
        }else if(mBitmap.get(i) !=null){
            viewHolder.songImage.setImageBitmap(mBitmap.get(i));
        }




        viewHolder.moreOptions.setOnClickListener(v -> {
            PopupMenu popupMenu = new PopupMenu(ctx,v);
            popupMenu.setOnMenuItemClickListener(item -> {
                switch (item.getItemId()){
                    case R.id.more_options_action_share:
                        Toast.makeText(ctx,"Implement Share Option", Toast.LENGTH_SHORT).show();
                        return true;
                    case R.id.more_options_action_add_fav:
                        Toast.makeText(ctx,"Implement Favorite Option", Toast.LENGTH_SHORT).show();
                        return true;
                    case R.id.more_options_action_delete:
                        Toast.makeText(ctx,"Implement Delete Option", Toast.LENGTH_SHORT).show();
                        return true;
                    default:
                        return false;
                }

            });

            popupMenu.inflate(R.menu.more_options_popup);
            popupMenu.show();
        });
    }

    @Override
    public int getItemCount() {
        return mSong.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        ImageView songImage, moreOptions;
        TextView songTitle, songArtist, songAlbum, songTime;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            songImage = itemView.findViewById(R.id.song_row_song_image);
            moreOptions = itemView.findViewById(R.id.song_row_more_options);
            songTitle = itemView.findViewById(R.id.song_row_name_of_song);
            songArtist = itemView.findViewById(R.id.song_row_artist_name);
            songAlbum = itemView.findViewById(R.id.song_row_album_name);
            songTime = itemView.findViewById(R.id.song_row_song_time);

            Log.i(MainActivity.TAG,"Views in song_row layout initialized");
        }

    }


}

谢谢。

最佳答案

不确定是否有具体的修复,但可以做一些事情来加快加载速度。

位图可能会很慢,所以如果可以获得 png 图像而不是这将加快该过程。在 decodeSampledBitmapFromResource 方法中,运行

BitmapFactory.decodeByteArray(rawArt,0,rawArt.length,bfo);

两次,这似乎无关紧要,但也许我错过了什么。

此外,您可以延迟加载图像而不是一次加载每首歌曲的所有图像,您可以在 onBindViewHolder 中获取图像。这样,每个图像也将以并行方式异步获取。

关于java - 歌曲图像可以更快吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58557807/

相关文章:

android - 设置 Sherlock ActionBar 的标题

java - 独立(并行)替换字符串中的一组子字符串

Android 2.1蓝牙SPP转LM058(串口线更换)问题

android - 如何在我的应用程序中添加第三个按钮

java - 如何在另一个类中调用 ListView 然后清除它

Android ImageView 在 GingerBread 下面的 FrameLayout 中不匹配_parent

java - Android 切换 Activity 后无法单击按钮

java - 如何为 IBM Watson Speech2text 服务指定语音关键字?

java - 整数 null 以检查数据库

android - Cordova 的 device.uuid 值可以用于发送 Firebase 推送通知吗?