2011-12-14 33 views
6

Tôi đang gặp phải sự cố nghiêm trọng với Trình phát phương tiện (MP) bị kẹt ở phương thức prepare(). Ứng dụng của tôi chạy prepare() trong một số AsyncTask để tránh chặn giao diện người dùng vì các nguồn này đến từ web. Có một số nút 'phát' mà người dùng có thể nhấp vào bất kỳ lúc nào, vì vậy tôi đã thêm prepare() bên trong một phương thức được đồng bộ hóa để kiểm soát tốt hơn trạng thái của MP. Ứng dụng của tôi cũng gọi số release() bật để giải phóng các tài nguyên đã sử dụng.Android MediaPlayer bị kẹt trong chuẩn bị()

Thing là, tôi nhận thấy rằng nếu release() được gọi trong khi chuẩn bị, prepare() không bao giờ trả về và vì vậy tôi bị kẹt bên trong phương thức được đồng bộ hóa. Điều tồi tệ nhất là thread AsyncTask bị bế tắc và mỗi khi người dùng nhấp vào chơi ở trạng thái đó, một luồng khác bị lãng phí vì nó vẫn chờ đợi để có được màn hình đang sở hữu prepare() không bao giờ quay trở lại. Chẳng bao lâu tất cả các chủ đề AsyncTasks của tôi bị lãng phí và vì tôi sử dụng chúng rộng rãi, ứng dụng của tôi ngừng hoạt động.

Vì vậy, câu hỏi của tôi là: có ai có ý tưởng về cách khắc phục vấn đề này không? Tôi đang suy nghĩ nghiêm túc về việc làm lại tất cả công việc của mình với MediaPlayer, nhưng tôi cần phải biết cách tốt nhất để xử lý các tình huống như thế này trước đây.

Trả lời

3

Thay vào đó, bạn nên sử dụng prepareAsync(). Và bạn sẽ không cần AsyncTask chỉ vì mục đích chuẩn bị MediaPlayer.

+0

Tôi đã nghĩ về điều đó, nhưng tôi không bỏ chắc chắn rằng nó sẽ giải quyết được sự cố trong tầm tay. Tài liệu MediaPlayer nói rằng "Điều quan trọng cần lưu ý là trạng thái Chuẩn bị là trạng thái tạm thời và hành vi gọi bất kỳ phương thức nào có hiệu ứng phụ trong khi đối tượng MediaPlayer đang ở trạng thái Đang chuẩn bị chưa được xác định." Vì vậy, vấn đề sẽ vẫn còn, tôi sẽ gọi release() trong trạng thái 'đang chuẩn bị', và kết quả là không xác định. Tôi nghi ngờ rằng bên trong chuẩn bị đồng bộ của tôi(), MediaPlayer chuyển sang trạng thái "đang chuẩn bị" này và kết quả là trong các erros mà tôi nhận được. – hgm

+0

Theo sơ đồ và bảng trạng thái, bạn có thể gọi 'release()' từ bất kỳ trạng thái nào. Thôi buông đi. Tôi nghĩ rằng bạn đang phải đối mặt với lỗi đa luồng liên quan. Và loại trừ nhiều chủ đề nên "sửa" nó. – inazaruk

+0

Nó nói rằng tôi có thể gọi phát hành() bất cứ lúc nào, nhưng ngay cả như vậy tôi đang phải đối mặt với vấn đề bị mắc kẹt nếu tôi gọi đây bên trong một chuẩn bị(). Điều này không nên xảy ra và là một lỗi trong Android IMO. – hgm

0

Sự cố khi sử dụng Asyntasks hoặc prepareAsync() cho vấn đề đó là nó không tự động chuyển trạng thái MediaPlayer's sang chuẩn bị. Bạn sẽ nghĩ rằng nó có, nhưng vì lý do nào đó nó không. Tôi gặp vấn đề tương tự trong vài ngày cho đến khi có người đề nghị tôi triển khai OnPreparedListener và sử dụng điều đó để sử dụng số MediaPlayer của mình.

Thêm nó khá đơn giản.

Thứ nhất, thực hiện người nghe trong lớp học của bạn: (. Tôi đang sử dụng này cho một dịch vụ như vậy bạn sẽ trông hơi khác nhau)

public class MusicService extends Service implements OnPreparedListener 

Tiếp theo, trong vở kịch của bạn/chuẩn bị tuyên bố, sử dụng prepareAsync, và đặt người nghe.

mp.prepareAsync(); 
mp.setOnPreparedListener(this); 

Tiếp theo, thêm các phương pháp onPrepared và thêm mã bắt đầu của bạn:

public void onPrepared(MediaPlayer mediaplayer) { 
     // We now have buffered enough to be able to play 
     mp.start(); 
    } 

Điều đó sẽ làm các trick.

0

Cảm ơn tất cả các câu trả lời, nhưng tôi đã sửa lỗi này không sử dụng hàm chuẩn PrepareAsync() như được đề cập trong các câu trả lời trước. Tôi nghĩ rằng nếu một phương pháp chuẩn bị đồng bộ đã có sẵn, thì phải có cách làm cho nó hoạt động chính xác.

Bản sửa lỗi, mặc dù IMO không thanh lịch, rất đơn giản: tránh việc phát hành cuộc gọi() bên trong phần chuẩn bị(). Mặc dù sơ đồ trạng thái trong các tài liệu Media Player nói rằng release() có thể được gọi trong bất kỳ trạng thái nào, thử nghiệm đã chứng minh rằng gọi nó bên trong một chuẩn bị() (có lẽ trong trạng thái 'Preparing') sẽ treo ứng dụng của bạn. Vì vậy, tôi đã thêm một kiểm tra để xem liệu MP có ở trạng thái này hay không và bây giờ tôi đang phát hành lệnh gọi điện thoại() nếu có. Tôi cần phải thêm một boolean trong mã của tôi để kiểm tra điều này, vì không có phương thức isPreparing() trong MP.

Tôi tự tin rằng điều này sẽ không xảy ra và là một lỗi trong chính Android.Như có thể thấy trong các bình luận, có một mâu thuẫn trong các tài liệu: nó nói rằng release() có thể được gọi ở bất kỳ trạng thái nào, nhưng cũng nói rằng gọi bất kỳ lệnh nào thay đổi trạng thái trong khi ở trạng thái Preparing có kết quả không xác định. Hy vọng rằng điều này sẽ giúp người khác.

0

tôi giải quyết vấn đề này trong ứng dụng của tôi như thế này:

Tạo đối tượng cho AsyncTasks (do đó bạn có thể kiểm tra nếu họ đang trong tiến trình):

private AsyncTask<String, Void, String> releaseMP; 
private AsyncTask<String, Void, String> setSource; 

Tạo một AsyncTask cho chuẩn bị cuộc gọi :

private class setSource extends AsyncTask<String, Void, String> { 
    @Override 
    protected synchronized String doInBackground(final String... urls) { 
     try { 
      mMediaPlayer.prepare(); 
     } catch (final IllegalStateException e) { 
      e.printStackTrace(); 
      return e.getMessage(); 
     } catch (final IOException e) { 
      e.printStackTrace(); 
      return e.getMessage(); 
     } catch (final Exception e) { 
      e.printStackTrace(); 
      return e.getMessage(); 
     } 

     return null; 
    } 

    @Override 
    protected void onCancelled() { 
     if (setSource != null) 
      setSource = null; 

     // Send error to listener 
     mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 

     releaseMP = new releaseMP().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 
    } 

    @Override 
    protected void onPostExecute(final String result) { 
     if (setSource != null) 
      setSource = null; 

     // Check for error result 
     if (result != null) { 
      mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 
     } 
    } 

    @Override 
    protected void onPreExecute() { 

    } 

} 

Bây giờ chuẩn bị của bạn mã:

try { 
     mMediaPlayer = new MediaPlayer(); 
     mMediaPlayer.setOnPreparedListener(mPreparedListener); 
     mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); 
     mDuration = -1; 
     mMediaPlayer.setOnCompletionListener(mCompletionListener); 
     mMediaPlayer.setOnErrorListener(mErrorListener); 
     mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); 
     mCurrentBufferPercentage = 0; 
     mMediaPlayer.setDataSource(getContext(), mUri, mHeaders); 
     mMediaPlayer.setDisplay(mSurfaceHolder); 
     mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 
     mMediaPlayer.setScreenOnWhilePlaying(true); 

     // mMediaPlayer.prepareAsync(); 
     // we don't set the target state here either, but preserve the 
     // target state that was there before. 
     mCurrentState = STATE_PREPARING; 
    } catch (final IOException ex) { 
     Log.w(TAG, "Unable to open content: " + mUri, ex); 
     mCurrentState = STATE_ERROR; 
     mTargetState = STATE_ERROR; 
     mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 
     return; 
    } catch (final IllegalArgumentException ex) { 
     Log.w(TAG, "Unable to open content: " + mUri, ex); 
     mCurrentState = STATE_ERROR; 
     mTargetState = STATE_ERROR; 
     mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 
     return; 
    } catch (final Exception ex) { 
     Log.w(TAG, "Unable to open content: " + mUri, ex); 
     mCurrentState = STATE_ERROR; 
     mTargetState = STATE_ERROR; 
     mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 
     return; 
    } 

    setSource = new setSource().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 

Và cuối cùng, khi bạn cần tiêu diệt mediaPlayer, bạn sẽ kiểm tra đối tượng setSource để xem nó có đang chuẩn bị trước khi bạn phát hành nó hay không. Nếu nó được chuẩn bị, bạn sẽ hủy AsyncTask và trong AsyncTask onCancelled, bạn sẽ thiết lập lại và thả đối tượng:

public void release(final boolean cleartargetstate) { 
    if (mMediaPlayer != null) { 
     if (setSource != null) { 
      setSource.cancel(true); 
     } else { 
      releaseMP = new releaseMP().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 
     } 
    } 
} 

Và đây là releaseMP AsyncTask của tôi (mà chỉ reset và giải phóng đối tượng):

private class releaseMP extends AsyncTask<String, Void, String> { 

    @Override 
    protected synchronized String doInBackground(final String... urls) { 
     Log.i(MethodNameTest.className() + "." + MethodNameTest.methodName(), "called"); 
     if (mMediaPlayer != null) { 
      // Release listeners to avoid leaked window crash 
      mMediaPlayer.setOnPreparedListener(null); 
      mMediaPlayer.setOnVideoSizeChangedListener(null); 
      mMediaPlayer.setOnCompletionListener(null); 
      mMediaPlayer.setOnErrorListener(null); 
      mMediaPlayer.setOnBufferingUpdateListener(null); 
      mMediaPlayer.reset(); 
      mMediaPlayer.release(); 
      mMediaPlayer = null; 
     } 
     mCurrentState = STATE_IDLE; 
     mTargetState = STATE_IDLE; 
     return null; 
    } 

    @Override 
    protected void onPostExecute(final String result) { 
     Log.i(MethodNameTest.className() + "." + MethodNameTest.methodName(), "called"); 

     if (releaseMP != null) 
      releaseMP = null; 
    } 

} 
Các vấn đề liên quan