ユーザーが画面上の任意の場所をタップしてオーディオスニペットを記録し、ログに記録するときにトラックに添付できる GPS アプリを開発しています。暗黙のインテントを発行するのではなく、アプリ内でこれを行うためにメディア レコーダーを使用しています。これにより、ユーザーは次のように「アイズフリー」で操作できます。私は 2 つのモードをコーディングしました。(1) ユーザーのタップとアプリは短い固定 (10 秒) のスニペットを記録します。(2) ユーザーがタップしてメディア レコーダーを開始し、もう一度タップして停止します。
Overlay.onTap でタップを監視し、独自のスレッドでメディア レコーダーを実行します。コードは次のとおりです。
モード(2)での私の問題は次のとおりです。最初のタップの後、メディアレコーダーは正常に起動しますが、後続のタップは onTap を起動しません。実際、UI スレッドは 60 秒間の記録で完全にフリーズしているように見えます。
ところで、私はもともとメディア レコーダーをアクティビティでコーディングしていました。次に、それをバックグラウンド スレッドに移動して、フリーズの問題を解決しようとしましたが、役に立ちませんでした。
オーバーレイ コードは次のとおりです。
/**
* 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;
}
};