Устройство записи мультимедиа Android зависает (блокируется) поток пользовательского интерфейса

Я разрабатываю приложение GPS, в котором пользователь может нажать в любом месте экрана, чтобы записать фрагмент аудио, который затем прикрепляется к дорожке по мере ее регистрации. Я использую медиа-рекордер, чтобы сделать это прямо внутри своего приложения, а не выдавать неявное намерение. Таким образом, пользователь может работать «без глаз» следующим образом. Я закодировал два режима: (1) пользователь нажимает, и приложение записывает короткий фиксированный (10 секунд) фрагмент; (2) Пользователь нажимает, чтобы запустить медиа-рекордер, а затем снова нажимает, чтобы остановить его.

Я слежу за тапами в Overlay.onTap и запускаю медиа-рекордер в отдельном потоке. Далее следует код.

Вот моя проблема, в режиме (2): После первого нажатия медиа-рекордер запускается нормально, но последующие нажатия не срабатывают при нажатии. На самом деле поток пользовательского интерфейса кажется полностью замороженным на 60 секунд записи.

Кстати, я изначально закодировал материал для записи мультимедиа прямо в Activity; затем я переместил его в фоновый поток, чтобы попытаться решить проблему зависания, но безрезультатно.

Код наложения следующий:

/**
* DKR: When User has tapped on a media icon,
* The MapView calls us here, giving us the GeoPoint
* (lat and lon) where User tapped. 
*/
@Override
public boolean onTap(GeoPoint tappedGeoPoint, MapView mapview)
{
  return commonOnTap(tappedGeoPoint);
}
//boolean isRecording = false;
 boolean oneTapAudio = false;
 boolean twoTapAudio = true;
 boolean firstTap = false;

 /**
 * Method AsyncOverlay.onTap calls commonOnTap and sends us the GeoPoint of the tap.
  * Send the tapped GeoPoint to each segment object, to see which one has
* the corresponding media waypoint, if any.
 */
 public boolean commonOnTap(GeoPoint tappedGeoPoint)
{
   boolean isLogging = GPSLoggerService.isLogging();
   if ( isLogging )
   {
      if ( oneTapAudio )  // Override so that tap takes User to audio recorder @ InsertNote.addQuickAudio
      {                   // Possibly run addQuickAudio in background thread for greater reliability.
         Intent intent = new Intent(mLoggerMap.getActivity(), InsertNote.class);
         String s = GPStracking.AUTHORITY+"/Request";
         intent.putExtra(s, InsertNote.ONETAPAUDIO);
         mLoggerMap.getActivity().startActivity(intent);
         return true;
         //mLoggerMap.invalidate();
         //InsertNote insert = new InsertNote();
         //insert.addQuickAudio();
      }
      if ( twoTapAudio )
      {
         if ( !firstTap )
         {
            firstTap = true;
            Intent intent = new Intent(mLoggerMap.getActivity(), InsertNote.class);
            String s = GPStracking.AUTHORITY+"/Request";
            intent.putExtra(s, InsertNote.TWOTAPONAUDIO);
            mLoggerMap.getActivity().startActivity(intent);
         }
         else
         {
            firstTap = false;
            Intent intent = new Intent(mLoggerMap.getActivity(), InsertNote.class);
            String s = GPStracking.AUTHORITY+"/Request";
            intent.putExtra(s, InsertNote.TWOTAPOFFAUDIO);
            mLoggerMap.getActivity().startActivity(intent);
         }
         return true;
      }
   }

  boolean handled = false;
  for (SegmentRendering segment : mSegmentRenderersList)
  {
     if (!handled)
     {
        handled = segment.commonOnTap(tappedGeoPoint);
     }
  }
  return handled;
}

Далее следует код активности:

@Override
protected void onCreate(Bundle savedInstanceState)
{
  super.onCreate(savedInstanceState);
  this.setVisible(false);
  paused = false;
  isRecording = false;
  mLoggerServiceManager = new GPSLoggerServiceManager(this);

  final Semaphore audioSemaphore = new Semaphore(0);
  mRecordAudioThread = new Thread("audioRecorder")
  {

     @Override
     public void run()
     {
        Looper.prepare();             // Initialize the current thread as a looper.
        mAudioThreadHandler = new Handler();     // Create an Handler to communicate with the Looper.
        audioSemaphore.release(); // the only release in the whole program
        Looper.loop();                // Run the message queue in this thread.
     }
  };
  mRecordAudioThread.start();
  try
  {
     audioSemaphore.acquire();
  }
  catch (InterruptedException e)
  {
     Log.e(TAG, "Failed waiting for a semaphore", e);
  }
 }

@Override
protected void onResume()
{
  int request = 0;
  super.onResume();
  String s = GPStracking.AUTHORITY+"/Request";
  Bundle extras = getIntent().getExtras();
  if (extras != null) request = extras.getInt(s);      
  if ( request  == ONETAPAUDIO || request  == TWOTAPONAUDIO || request  == TWOTAPOFFAUDIO )
  {
     QuickAudioLogic( request );
     return;
  }

  if (mServiceBindAction == null)
  {
     mServiceBindAction = new Runnable()
     {
        @Override
        public void run()
        {
           showDialog(DIALOG_INSERTNOTE);
        }
     };
  }
  ;
  mLoggerServiceManager.startup(this, mServiceBindAction);
}

@Override
protected void onPause()
{
  super.onPause();
  mLoggerServiceManager.shutdown(this);
  paused = true;
  if (mRecorder != null) {
     mRecorder.release();
     mRecorder = null;
 }
}

public void QuickAudioLogic(int request)
{
  mRecorder = new MediaRecorder();
  if ( request == ONETAPAUDIO )  
  {
     if ( !isRecording )
     {
        isRecording = true;
        mDuration = ONETAPDURATION;
        mAudioThreadHandler.post(recordAudioAsync);
        //            QuickAudioAction ( ONETAPDURATION );
     }
     return;
  }
  if ( request == TWOTAPONAUDIO )
  {
     if ( !isRecording )
     {
        isRecording = true;
        mDuration = TWOTAPDURATION;
        mAudioThreadHandler.post(recordAudioAsync);
        //            QuickAudioAction ( TWOTAPDURATION );
     }
     return;
  }
  if ( request == TWOTAPOFFAUDIO )
  {
     if ( isRecording )
     {
        isRecording = false;
        Toast.makeText(this, "Stop recording audio",Toast.LENGTH_SHORT).show();
        mRecorder.reset();
        mRecorder.release();
        mRecorder = null;
        finish();
     }
  }
}

Предыстория (медиа-рекордер) следующая:

public Runnable recordAudioAsync = new Runnable()
{

  @Override
  public void run()
  {
     String newName;
     Calendar c = Calendar.getInstance();
     newName = String.format( "Audio" + getString( R.string.dialog_filename_default)+".3gpp", c, c, c, c, c, c );
     //newName = String.format("Audio_%tY-%tm-%td_%tH%tM%tS.3gpp", c, c, c, c, c, c);
     File newFile = new File(Constants.getSdCardDirectory(InsertNote.this) + newName);
     newName = newFile.toString();
     android.net.Uri.Builder builder = new Uri.Builder();;
     sUri = builder.scheme("file").appendEncodedPath("/"+newName).build();
     //sUri = Uri.parse(newName);  // store Uri static, so that   
     try {
        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        mRecorder.setOutputFile(newName);
        mRecorder.setMaxDuration( mDuration );//mDuration = 10000 for one tap or 60000 for two tap mode
        mRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener(){ 
           @Override 
           public void onInfo(MediaRecorder mr, int what, int extra) { 
                 if(what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED ) 
                 {
                    InsertNote.this.mLoggerServiceManager.storeMediaUri(sUri);
                    //setResult(MENU_VOICE, new Intent());
                    //mLoggerMap.invalidate();
                    isRecording = false;
                    Toast.makeText(InsertNote.this, "Timeout recording audio",Toast.LENGTH_SHORT).show();
                    mRecorder.reset();
                    mRecorder.release();
                    finish();
                    //startActivity(new Intent(this.getApplicationContext(), ANOTHERACTIVITY.class)); 
                 } 
              } 
           });
        mRecorder.prepare();
        isRecording = true;
        Toast.makeText(InsertNote.this, "Start recording audio",Toast.LENGTH_LONG).show();
        mRecorder.start();
        //((Button)(findViewById(R.id.record_button))).setText("stop");
     } 
     catch (Exception e) {
        Toast.makeText(InsertNote.this, "Error starting audio recorder.",Toast.LENGTH_SHORT).show();
     }
     return;
  }
};

person BinCodinLong    schedule 16.03.2013    source источник
comment
Извините, но ваш код беспорядок - слишком много и некоторые из них не имеют смысла. Это руководство по захвату звука... developer.android.com/ guide/topics/media/audio-capture.html Я предлагаю вам сделать шаг назад и переписать все на основе этого руководства. Кроме того, MediaRecorder использует собственную многопоточность — если вы получаете зависание пользовательского интерфейса, вы делаете что-то еще неправильно.   -  person Squonk    schedule 17.03.2013
comment
Публикация всего кода не имеет смысла, пожалуйста, предоставьте и извлеките проблему резко!   -  person E J Chathuranga    schedule 30.04.2019


Ответы (1)


Компоненты пользовательского интерфейса могут быть изменены только из потока пользовательского интерфейса. Либо удалите всплывающие сообщения, либо попробуйте запустить поток как поток пользовательского интерфейса.

public void run(){
activity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
                    // Things to do
                 }
              });
             }
person Hassi    schedule 25.03.2013
comment
Я решил эту проблему, переместив код MediaRecorder в класс Overlay. После этого MediaRecorder больше не блокирует метод onTap. Почему? Кто знает! Тем не мение... - person BinCodinLong; 01.04.2013
comment
@BinCodinLong, что вы имеете в виду под классом Overlay? - person Usman Rana; 23.09.2019