2016-11-28 112 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); 

請記住,這樣的操作會忽略主線程狀態;這意味着,即使應用程序處於休眠狀態,單獨的線程仍會繼續運行並執行,因此,如果應用程序轉到前臺,您需要使偵聽器/取消訂閱接收器無效。快樂的編碼。

相關問題