首页 文章

Android:应用程序可能在其主线程上做了太多工作

提问于
浏览
0

我正在尝试将网址中的视频缩略图加载到网格视图中 . 一切正常,但我收到这条消息“/编舞:跳过46帧!应用程序可能在其主线程上做了太多工作 . ”

我知道它与主线程中的缩略图加载有关,但我无法找到如何解决这个问题 .

我也在使用设备进行测试 .

public class ThumbAdapter  extends ArrayAdapter<Videos> {

    Context context;
    int ressource;
    ThumbAdapter_Holder holder=new ThumbAdapter_Holder();

    public ThumbAdapter(Context context, int resource, ArrayList<Videos> videoList) {
        super(context, resource, videoList);
        this.context=context;
        this.ressource=resource;

    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View view=convertView;
        if (view==null){

            LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(ressource,parent,false);

            holder.video_thum = (ImageView) view.findViewById(R.id.video_thum);

            view.setTag(holder);
        }
        else {
            holder=(ThumbAdapter_Holder) view.getTag();
        }

        try {
            holder.video_thum.setImageBitmap(retriveVideoFrameFromVideo(getItem(position).getURL()));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return view;
    }


    class ThumbAdapter_Holder{
       ImageView video_thum ;

    }

    public static Bitmap retriveVideoFrameFromVideo(String videoPath) throws Throwable {
        Bitmap bitmap = null;
        MediaMetadataRetriever mediaMetadataRetriever = null;
        try
        {
            mediaMetadataRetriever = new MediaMetadataRetriever();
            if (Build.VERSION.SDK_INT >= 14)
                mediaMetadataRetriever.setDataSource(videoPath, new HashMap<String, String>());
            else
                mediaMetadataRetriever.setDataSource(videoPath);
            //   mediaMetadataRetriever.setDataSource(videoPath);
            bitmap = mediaMetadataRetriever.getFrameAtTime();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw new Throwable(
                    "Exception in retriveVideoFrameFromVideo(String videoPath)"
                            + e.getMessage());

        } finally {
            if (mediaMetadataRetriever != null) {
                mediaMetadataRetriever.release();
            }
        }
        return bitmap;
    }
}

1 回答

  • 1

    问题是,您正在请求和处理适配器的每个单元格中的数据 .

    try {
            holder.video_thum.setImageBitmap(retriveVideoFrameFromVideo(getItem(position).getURL()));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    

    那是罪魁祸首 . 您正在主线程(管理UI)中执行昂贵的非图形化任务 . 为了优化,您需要将这种操作移动到单独的线程,并通知在主线程内执行的类结果,以便它可以更新UI . 有几种方法可以做到这一点:Asynctasks,Observers,Event bus . 为了简单起见,让我们使用asynctask .

    以此为例:

    public class BitMapTask extends AsyncTask<Void, Void, Bitmap> {
    
    public interface OnBitmapLoaded{
        void loadBitmap(Bitmap bitmap);
    }
    
    private OnBitmapLoaded bitmapLoaded;
    private String url;
    
    public BitMapTask(String url){
        this.url = url;
    }
    
    public BitMapTask setBitMapLoaded(OnBitmapLoaded bitMapLoaded){
        this.bitmapLoaded = bitMapLoaded;
        return this;
    }
    
    @Override
    protected Bitmap doInBackground(Void... params) {
        return retriveVideoFrameFromVideo(url);
    }
    
    @Override
    protected void onPostExecute(Bitmap result){
        if(bitmapLoaded != null) bitmapLoaded.loadBitmap(result);
    }
    }
    

    然后,你应该做这样的事情,而不是try-catch .

    BitMapTask task = new BitMapTask(getItem(position).getURL())
                    .setBitMapLoaded(new OnBitmapLoaded() {
                @Override
                public void loadBitmap(Bitmap bitmap) {
                    if(bitmap != null){
                        setImageBitmap(bitmap);
                    }
                }
            }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    

    请记住,这种操作会忽略主线程状态;这意味着,即使应用程序处于休眠状态,单独的线程也会继续运行并执行,因此,如果应用程序转到前台,您需要使侦听器/取消订阅接收器无效 . 快乐的编码 .

相关问题