2016-11-28 35 views
0

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

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

我还在使用一个设备来测试。

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

很显然,对'mediaMetadataRetriever'的一次调用非常耗时,请考虑将此操作移至工作线程。 – Egor

+0

您可以在https://developer.android.com/reference/android/os/AsyncTask.html中包装整个retrieveVideFrameFromVideo查看如何实施AsyncTask – X3Btel

回答

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

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

相关问题