歌曲图像可以更快吗?

Posted

技术标签:

【中文标题】歌曲图像可以更快吗?【英文标题】:Can the song image be gotten faster? 【发布时间】:2020-02-21 18:20:24 【问题描述】:

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

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

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

My All Song 片段类



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");
        

    



谢谢。

【问题讨论】:

你在哪里获取歌曲图片? 从本地设备 【参考方案1】:

不确定是否有具体修复,但可以采取一些措施来加快加载速度。

位图可能会很慢,因此如果可以获取 png 图像,这将加快处理速度。在 decodeSampledBitmapFromResource 方法中,你运行

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

两次,这似乎是不必要的,但也许我错过了一些东西。

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

【讨论】:

谢谢,但我有点困惑。 additionally you could lazyload images 是的。应该这样做。

以上是关于歌曲图像可以更快吗?的主要内容,如果未能解决你的问题,请参考以下文章

我可以使用 MPMediaQuery 来获取歌曲的年份吗?

我们可以使用java中的Clip库以不同的频率播放歌曲吗

ImageMagick 是覆盖图像的最快方法吗?我怎样才能做得更快,或者有没有我不知道的更快的技术?

在 GPU 上计算积分图像真的比在 CPU 上更快吗?

我可以在 iOS 中使用 iPodMusicPlayer 播放文档目录中的歌曲吗?

有啥音乐软件VIP歌曲可以免费下载?