My application uses the camera, it displays the camera preview, and on every preview frame it encodes the frame data to base64 form to call my remote web service.
Since the speed of producing frame doesn't match the speed of consuming frame(calling web service), I consider to use a buffer(message queue) in my service, so I use Messenger to bind the service.
Therefore, I consider to use the Service, and bind it to my CameraActivity. By default the service runs in the same thread as the UI thread, however, I want to run the service in another thread. I've read the Service and the Bound service, they just mention that I can run the service in other thread but don't give an example.
CameraActivity
public class CameraActivity extends AppCompatActivity implements Camera.PreviewCallback {
private Camera camera;
private CameraPreview preview;
int cnt = 0, RATE = 0;
private Messenger messenger;//use to communicate with CloudService
private boolean bound = false;
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
bound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
messenger = null;
bound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
camera = getCamera();
RATE = camera.getParameters().getPreviewFrameRate() / 5;
preview = new CameraPreview(this, camera);
FrameLayout layout = findViewById(R.id.camera_preview);
layout.addView(preview);
bindService(new Intent(this, CloudService.class), connection,
Context.BIND_AUTO_CREATE);
}
public static Camera getCamera() {
Camera camera = null;
try {
camera = Camera.open();
} catch (Exception e) {
e.printStackTrace();
}
return camera;
}
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (cnt == RATE) {
if (bound) {
Message message = Message.obtain(null, 0, data);
try {
messenger.send(message);
Log.d("activity", "send msg");
} catch (RemoteException e) {
e.printStackTrace();
}
}
cnt = 0;
}
cnt++;
}
protected void onStop() {
super.onStop();
if (bound){
unbindService(connection);
bound = false;
}
}
}
CloudService
private Thread thread;
private Messenger messenger;
public CloudService() {
Log.d("cloud service", "create");
thread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("cloud service", "thread running");
}
}
});
}
#Override
public IBinder onBind(Intent intent) {
messenger = new Messenger(new PreviewHandler(this));
return messenger.getBinder();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("cloud service", "start");
thread.start();
return START_STICKY;
}
#Override
public void onDestroy() {
thread.interrupt();
Log.d("cloud service", "destroy");
}
static class PreviewHandler extends Handler {
private CloudService service;
public PreviewHandler(CloudService service) {
this.service = service;
}
#Override
public void handleMessage(Message message) {
byte[] data = (byte[]) message.obj;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("message", "msg!!!");
}
}
}
How can I use thread in my CloudService properly?
Solution: Using HandlerThread and Handler together to achieve your task.
public class CloudService extends Service {
private static final String TAG = "CloudService";
private HandlerThread handlerThread;
private PreviewHandler handler;
private Messenger messenger;
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
handlerThread = new HandlerThread("CloudService_HandlerThread");
handlerThread.start();
handler = new PreviewHandler(handlerThread.getLooper());
}
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
messenger = new Messenger(handler);
return messenger.getBinder();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return START_STICKY;
}
#Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
handlerThread.quit();
}
static class PreviewHandler extends Handler {
PreviewHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message message) {
byte[] data = (byte[]) message.obj;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "handleMessage from thread " + Thread.currentThread().getName());
}
}
}
Related
Hi I am trying to make a service that keeps the microphone muted in the background on a galaxys6 the service is running as the toasts show and all state the mic is muted the problem is when I launch google voice search it pauses my service and turns the mic on. How can I prevent this from happening and only allow my app to stop or pause the service.
public class blockservice extends Service {
AudioManager audioManager;
// #Nullable
#Override
public void onCreate() {
super.onCreate();
audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
//audioManager.setMode(AudioManager.MODE_IN_CALL);
// audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
audioManager.setMicrophoneMute(true);
new Thread(new Runnable() {
public void run(){
for(int i=0;i<10;i++){
postToastMessage("boom "+i+" "+audioManager.isMicrophoneMute());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}).start();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
audioManager.setMicrophoneMute(false);
super.onDestroy();
}
public void postToastMessage(final String message) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
});
}
}
I am trying to play an audio using service and update seekbar item in adapter. But I am unable to get duration of audio from service. Also when scrolling recyclerview, more than one item view is updated instead of updating currently playing seekbar item. I have tried below code.
public class LocalService extends Service {
boolean mBound = false;
private final IBinder mBinder = new LocalBinder();
private String path = "";
private int timer;
private static MediaPlayer mediaPlayer = new MediaPlayer();
// Random number generator
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/**
* method for clients
*/
public int getTotalTime() {
return mediaPlayer.getDuration();
}
public int getDuration() {
// Log.v("player duration",timer+"");
try {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
Log.v("playing", (mediaPlayer.getCurrentPosition() / 1000) + "");
return mediaPlayer.getCurrentPosition() / 1000;
} else {
//Log.v("not playing", (mediaPlayer.getCurrentPosition() / 1000) + "");
return 0;
}
}catch (IllegalStateException i)
{
i.printStackTrace();
return 0;
}
catch (Exception e)
{
e.printStackTrace();
return 0;
}
}
#Override
public void onCreate() {
super.onCreate();
try {
mediaPlayer = new MediaPlayer();
} catch (Exception e) {
e.printStackTrace();
}
}
public void setAudio(String path)
{
try {
mediaPlayer.setDataSource(path);
} catch (IOException e) {
e.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void playAudio() {
Log.v("audio path", path);
try {
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.prepareAsync();
mediaPlayer.start();
timer=mediaPlayer.getDuration();
Log.v("total timer1",timer+"");
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
timer=0;
mediaPlayer.stop();
mediaPlayer.reset();
//mediaPlayer.release();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void unbindService(ServiceConnection conn) {
super.unbindService(conn);
mediaPlayer.reset();
mediaPlayer.release();
}
#Override
public void onDestroy() {
super.onDestroy();
mediaPlayer.reset();
mediaPlayer.release();
}
Service is started from adapter constructor.
Intent intent = new Intent(mContext, LocalService.class);
mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
player item click in recycler adpater
vhItemHolder.playPauseImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentlyPlayingPosition = position;
if (mBound) {
Log.v("service","bound");
//Make sure you update Seekbar on UI thread
mService.setAudio(audioDirectory+messageArrayList.get(position).getmMediaValue());
mPlayer=new MediaPlayer();
try {
mPlayer.setDataSource(audioDirectory+messageArrayList.get(position).getmMediaValue());
mPlayer.prepareAsync();
vhItemHolder.seekBar.setMax(mPlayer.getDuration());
} catch (IOException e) {
e.printStackTrace();
}
mService.playAudio();
Log.v("duration",(mService.getTotalTime())+"");
vhItemHolder.seekBar.setMax(mService.getTotalTime());
mHandler.removeCallbacks(null);
mHandler=new Handler();
((DoctorActivity)mContext).runOnUiThread(new Runnable() {
#Override
public void run() {
num = mService.getDuration();
Log.v("progress",num+"");
vhItemHolder.seekBar.setProgress((num));
vhItemHolder.runningTimerTextView.setText(convertMilliToMinutes((long)mService.getDuration()));
mHandler.postDelayed(this, 50);
}
});
}
else
{
Log.v("service","not bound");
}
Inside your adapter class:
You are making a new object of MediaPlayer (mPlayer) in viewHolder's playPauseImageView's onClickListener and calling setDataSource() method on it which sets data source for this local mPlayer object not to the mediaPlayer object of your LocalService class.
Calling mService.playAudio() and mService.getDuration() methods access your LocalService class' mediaPlayer object for which setDataSource() method is not called yet, hence must be returning wrong duration and unable to play.
You should make a change in your adapter class like below:
vhItemHolder.playPauseImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentlyPlayingPosition = position;
if (mBound) {
Log.v("service","bound");
//Make sure you update Seekbar on UI thread
mService.setAudio(audioDirectory+messageArrayList.get(position).getmMediaValue());
mPlayer = mService.mediaPlayer;// or mPlayer = LocalService.mediaPlayer; as being static object.
//rather new MediaPlayer();
try {
mPlayer.setDataSource(audioDirectory+messageArrayList.get(position).getmMediaValue());
mPlayer.prepareAsync();
vhItemHolder.seekBar.setMax(mPlayer.getDuration());
} catch (IOException e) {
e.printStackTrace();
}
mService.playAudio();
Log.v("duration",(mService.getTotalTime())+"");
vhItemHolder.seekBar.setMax(mService.getTotalTime());
mHandler.removeCallbacks(null);
mHandler=new Handler();
((DoctorActivity)mContext).runOnUiThread(new Runnable() {
#Override
public void run() {
num = mService.getDuration();
Log.v("progress",num+"");
vhItemHolder.seekBar.setProgress((num));
vhItemHolder.runningTimerTextView.setText(convertMilliToMinutes((long)mService.getDuration()));
mHandler.postDelayed(this, 50);
}
});
}
else
{
Log.v("service","not bound");
}
I use media player in service and activity with a button(play/stop stream).
I'm trying to do such a one thing:
start serice only once when activity starts, and than use UI part to operate with media player in service (start, stop streaming). I'm using for it Service Binder.
But when i click on button to start media player, it causes errors:
E/MediaPlayer: start called in state 1
E/MediaPlayer: error (-38, 0)
Than i added the flag isPrepared to onPrepared() and on play stream button click check the flag. Flag is always false.
It seems that onPrepare() is not called or smth like that. Why? What's wrong?
UPDATED
Activity:
public class MainActivity extends AppCompatActivity {
String url = "http://62.80.190.246:8000/ProstoRadiO128";
Button mPlayPauseButton;
boolean musicPlaying = false;
Intent serviceIntent;
MyService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initListeners();
}
#Override
protected void onStart() {
super.onStart();
serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("url", url);
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
startService(serviceIntent);
musicPlaying = true;
mPlayPauseButton.setBackgroundResource(R.drawable.pause);
Log.d("", "mConnection: " + mConnection);
}
#Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService.MyServiceBinder binder = (MyService.MyServiceBinder) service;
mService = binder.getService();
mBound = true;
Log.d("", "in onServiceConnected: mBound = " + mBound);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
private void initViews() {
mPlayPauseButton = (Button) findViewById(R.id.btn_play_pause);
mPlayPauseButton.setBackgroundResource(R.drawable.play);
}
private void initListeners() {
mPlayPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playPauseClick();
}
});
}
private void playPauseClick() {
if (musicPlaying == false) { //mBound &&
Log.d("", "mBound: " + mBound);
mService.startStream();
mPlayPauseButton.setBackgroundResource(R.drawable.pause);
musicPlaying = true;
}
else if (musicPlaying == true) { //!mBound
mService.stopStream();
mPlayPauseButton.setBackgroundResource(R.drawable.play);
musicPlaying = false;
}
}
}
UPDATED
Service:
public class MyService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener{
String TAG = "PlayerService__Log";
String url;
MediaPlayer mediaPlayer;
private final IBinder mBinder = new MyServiceBinder();
boolean isPrepared;
public class MyServiceBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onCreate() {
Log.v(TAG, "Creating Service");
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnCompletionListener(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
url = intent.getStringExtra("url");
Log.d(TAG, "url: " + url);
if (!mediaPlayer.isPlaying()) {
try {
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, e.getClass().getName() + " " + e.getMessage());
}
}
return START_STICKY;
}
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
Log.d(TAG, "media player prepared");
isPrepared = true;
}
#Override
public void onCompletion(MediaPlayer mp) {
Log.d(TAG, "onCompletion");
}
#Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.release();
mediaPlayer = null;
}
}
public void startStream() {
if (!mediaPlayer.isPlaying() ) { //&& isPrepared == true
mediaPlayer.start();
Log.d(TAG, "media player started");
}
else {
Log.d(TAG, "media player is not prepared");
//isPrepared = false;
}
}
public void stopStream() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
Log.d(TAG, "media player stoped");
}
}
}
You forgot to start the service. Call:
startService(serviceIntent);
Please read this as you will need to stop the service too.
and for pausing, rather than stop, call:
mediaPlayer.pause();
Familiarise yourself with the state diagram and invalid states in the docs.
onPrepared() is called when the player is successfully prepared/initialised.
But as the Error is indicating, something goes wrong during this phase and (probably) therfore onPrepared never gets called.
You could try to override onError, to get notified like this:
#Override
public boolean onError(MediaPlayer mediaPlayer, int what, int why) {
Log.e(TAG, "onError");
setPlayerState(PlayerState.ERROR);
if (MediaPlayer.MEDIA_ERROR_UNKNOWN == what) {
Log.d(TAG, "MEDIA_ERROR_UNKNOWN");
if (MediaPlayer.MEDIA_ERROR_IO == why) {
Log.e(TAG, "MEDIA_ERROR_IO");
if (this.playbackPosition > 0) { //we could play this video in the past, but cannot resume. start all over again.
Log.e(TAG, "Probably we requested a content range, but server didn't support that. (responded with 200), restarting!");
this.playbackPosition = 0;
start(this.playbackPosition);
} else {
callbacks.onUnrecoverableError(what, why);
}
}
if (MediaPlayer.MEDIA_ERROR_MALFORMED == why) {
Log.e(TAG, "MEDIA_ERROR_MALFORMED");
callbacks.onUnrecoverableError(what, why);
}
if (MediaPlayer.MEDIA_ERROR_UNSUPPORTED == why) {
Log.e(TAG, "MEDIA_ERROR_UNSUPPORTED");
callbacks.onUnrecoverableError(what, why);
}
if (MediaPlayer.MEDIA_ERROR_TIMED_OUT == why) {
Log.e(TAG, "MEDIA_ERROR_TIMED_OUT");
callbacks.onUnrecoverableError(what, why);
}
} else if (MediaPlayer.MEDIA_ERROR_SERVER_DIED == what) {
Log.e(TAG, "MEDIA_ERROR_SERVER_DIED");
callbacks.onUnrecoverableError(what, why);
}
return true;
}
I building a Radio Streaming app and i am having difficulties sending my MediaPlayer object from my service to to a fragment. Below is my Service and my Fragment code. When i try to run the code it's always crashing
public class StreamService extends Service implements
MediaPlayer.OnCompletionListener,
MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener,
MediaPlayer.OnBufferingUpdateListener {
private static final int NOTIFICATION_ID = 1;
private PhoneStateListener phoneStateListener;
private TelephonyManager telephonyManager;
private boolean isPausedInCall = false;
private NotificationCompat.Builder builder;
//intent
private Intent bufferIntent;
public static final MediaPlayer mediaPlayer = new MediaPlayer();
ScheduledExecutorService scheduleTaskExecutor, reminderTaskExecutor;
private ScheduleModel scheduleModel;
private int currentHour;
private String currentDay, notificationTitle;
private final IBinder mBinder = new LocalBinder();
#Override
public void onCreate() {
super.onCreate();
bufferIntent = new Intent(BROADCAST_BUFFER);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.reset();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
reminderTaskExecutor = Executors.newScheduledThreadPool(5);
reminderTaskExecutor.scheduleAtFixedRate(new Runnable() {
public void run() {
//initNotification();
}
}, 0, 1, TimeUnit.MINUTES);
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
phoneStateListener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_OFFHOOK:
case TelephonyManager.CALL_STATE_RINGING:
if (mediaPlayer != null) {
pauseMedia();
isPausedInCall = true;
}
break;
case TelephonyManager.CALL_STATE_IDLE:
if (mediaPlayer != null) {
if (isPausedInCall) {
isPausedInCall = false;
playMedia();
}
}
break;
}
}
};
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
initNotification();
mediaPlayer.reset();
/**
* play media
*/
if (!mediaPlayer.isPlaying()) {
try {
mediaPlayer.setDataSource(URL_STREAM);
// sent to UI radio is buffer
sendBufferingBroadcast();
mediaPlayer.prepareAsync();
} catch (IllegalArgumentException e) {
Log.d("error", e.getMessage());
} catch (IllegalStateException e) {
Log.d("error", e.getMessage());
} catch (IOException e) {
Log.d("error", e.getMessage());
}
}
return START_STICKY;
}
public class LocalBinder extends Binder {
public StreamService getService() {
// Return this instance of LocalService so clients can call public methods
return StreamService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {
}
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
stopMedia();
stopSelf();
}
#Override
public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
switch (what) {
case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
Toast.makeText(this, "Error not valid playback", Toast.LENGTH_SHORT).show();
break;
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
Toast.makeText(this, "Error server died", Toast.LENGTH_SHORT).show();
break;
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
Toast.makeText(this, "Error occurred, please try again", Toast.LENGTH_SHORT).show();
break;
}
return false;
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
// sent to UI, audio has buffered
sendBufferCompleteBroadcast();
playMedia();
}
private void pauseMedia() {
if (mediaPlayer.isPlaying())
mediaPlayer.pause();
}
private void playMedia() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
private void stopMedia() {
if (mediaPlayer.isPlaying())
mediaPlayer.stop();
}
/**
* sent buffering
*/
private void sendBufferingBroadcast() {
bufferIntent.putExtra("buffering", "1");
sendBroadcast(bufferIntent);
}
/**
* sent buffering complete
*/
private void sendBufferCompleteBroadcast() {
bufferIntent.putExtra("buffering", "0");
sendBroadcast(bufferIntent);
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("tag", "remove notification");
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.release();
}
if (phoneStateListener != null) {
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
cancelNotification();
}
}
My Fragment, code crashed on line mVisualizerView.link(StreamService.mediaPlayer);
private void startVisualizerView(){
mVisualizerView = (VisualizerView) getView().findViewById(R.id.visualizerView);
mVisualizerView.link(StreamService.mediaPlayer);
// Start with just line renderer
addLineRenderer();
}
private void startVisualizerView(){
mVisualizerView = (VisualizerView) getView().findViewById(R.id.visualizerView);
mVisualizerView.link(StreamService.mediaPlayer);
// Start with just line renderer
addLineRenderer();
}
private void startStreaming(Context context) {
stopStreaming();
try {
getActivity().startService(serviceIntent);
Toast.makeText(context, "Enjoy #Hashtag Radio...", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
}
}
private void stopStreaming() {
try {
getActivity().stopService(serviceIntent);
// reset streaming tag
isStreaming = false;
Utils.setDataBooleanToSP(getActivity(), Utils.IS_STREAM, false);
} catch (Exception e) {
}
}
private void startStreaming(Context context) {
stopStreaming();
try {
getActivity().startService(serviceIntent);
startVisualizerView();
Toast.makeText(context, "Enjoy #Hashtag Radio...", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
StreamService.LocalBinder binder = (StreamService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
I'm trying to create a service to run continuous speech recognition in Android 4.2. Using the answer from this link ( Android Speech Recognition as a service on Android 4.1 & 4.2 ), I created a service that is run from an Activity. My problem is that I get null exceptions when accessing mTarget.mAudioManager or mTarget.mSpeechRecognizerIntent in the handleMessage method. The target (and mTarget object created from it) is not null, but all the objects inside it are.
What am I doing wrong here?
Relevant Activity Code (static methods called from activity, activityContext is the activity this method is called from):
public static void init(Context context)
{
voiceCommandService = new VoiceCommandService();
activityContext = context;
}
public static void startContinuousListening()
{
Intent service = new Intent(activityContext, VoiceCommandService.class);
activityContext.startService(service);
Message msg = new Message();
msg.what = VoiceCommandService.MSG_RECOGNIZER_START_LISTENING;
try
{
voiceCommandService.mServerMessenger.send(msg);
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
Service Code:
public class VoiceCommandService extends Service
{
protected AudioManager mAudioManager;
protected SpeechRecognizer mSpeechRecognizer;
protected Intent mSpeechRecognizerIntent;
protected final Messenger mServerMessenger = new Messenger(new IncomingHandler(this));
protected boolean mIsListening;
protected volatile boolean mIsCountDownOn;
static final int MSG_RECOGNIZER_START_LISTENING = 1;
static final int MSG_RECOGNIZER_CANCEL = 2;
#Override
public void onCreate()
{
super.onCreate();
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
mSpeechRecognizer.setRecognitionListener(new SpeechRecognitionListener());
mSpeechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,
this.getPackageName());
}
protected static class IncomingHandler extends Handler
{
private WeakReference<VoiceCommandService> mtarget;
IncomingHandler(VoiceCommandService target)
{
mtarget = new WeakReference<VoiceCommandService>(target);
}
#Override
public void handleMessage(Message msg)
{
final VoiceCommandService target = mtarget.get();
switch (msg.what)
{
case MSG_RECOGNIZER_START_LISTENING:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
// turn off beep sound
target.mAudioManager.setStreamMute(AudioManager.STREAM_SYSTEM, true);
}
if (!target.mIsListening)
{
target.mSpeechRecognizer.startListening(target.mSpeechRecognizerIntent);
target.mIsListening = true;
//Log.d(TAG, "message start listening"); //$NON-NLS-1$
}
break;
case MSG_RECOGNIZER_CANCEL:
target.mSpeechRecognizer.cancel();
target.mIsListening = false;
//Log.d(TAG, "message canceled recognizer"); //$NON-NLS-1$
break;
}
}
}
// Count down timer for Jelly Bean work around
protected CountDownTimer mNoSpeechCountDown = new CountDownTimer(5000, 5000)
{
#Override
public void onTick(long millisUntilFinished)
{
// TODO Auto-generated method stub
}
#Override
public void onFinish()
{
mIsCountDownOn = false;
Message message = Message.obtain(null, MSG_RECOGNIZER_CANCEL);
try
{
mServerMessenger.send(message);
message = Message.obtain(null, MSG_RECOGNIZER_START_LISTENING);
mServerMessenger.send(message);
}
catch (RemoteException e)
{
}
}
};
#Override
public void onDestroy()
{
super.onDestroy();
if (mIsCountDownOn)
{
mNoSpeechCountDown.cancel();
}
if (mSpeechRecognizer != null)
{
mSpeechRecognizer.destroy();
}
}
protected class SpeechRecognitionListener implements RecognitionListener
{
private static final String TAG = "SpeechRecognitionListener";
#Override
public void onBeginningOfSpeech()
{
// speech input will be processed, so there is no need for count down anymore
if (mIsCountDownOn)
{
mIsCountDownOn = false;
mNoSpeechCountDown.cancel();
}
//Log.d(TAG, "onBeginingOfSpeech"); //$NON-NLS-1$
}
#Override
public void onBufferReceived(byte[] buffer)
{
}
#Override
public void onEndOfSpeech()
{
//Log.d(TAG, "onEndOfSpeech"); //$NON-NLS-1$
}
#Override
public void onError(int error)
{
if (mIsCountDownOn)
{
mIsCountDownOn = false;
mNoSpeechCountDown.cancel();
}
mIsListening = false;
Message message = Message.obtain(null, MSG_RECOGNIZER_START_LISTENING);
try
{
mServerMessenger.send(message);
}
catch (RemoteException e)
{
}
//Log.d(TAG, "error = " + error); //$NON-NLS-1$
}
#Override
public void onEvent(int eventType, Bundle params)
{
}
#Override
public void onPartialResults(Bundle partialResults)
{
}
#Override
public void onReadyForSpeech(Bundle params)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
mIsCountDownOn = true;
mNoSpeechCountDown.start();
mAudioManager.setStreamMute(AudioManager.STREAM_SYSTEM, false);
}
Log.d(TAG, "onReadyForSpeech"); //$NON-NLS-1$
}
#Override
public void onResults(Bundle results)
{
//Log.d(TAG, "onResults"); //$NON-NLS-1$
}
#Override
public void onRmsChanged(float rmsdB)
{
}
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
Class members in MainActivity
private int mBindFlag;
private Messenger mServiceMessenger;
Start service in onCreate()
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Intent service = new Intent(activityContext, VoiceCommandService.class);
activityContext.startService(service);
mBindFlag = Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH ? 0 : Context.BIND_ABOVE_CLIENT;
}
Bind service in onStart()
#Override
protected void onStart()
{
super.onStart();
bindService(new Intent(this, VoiceCommandService.class), mServiceConnection, mBindFlag);
}
#Override
protected void onStop()
{
super.onStop();
if (mServiceMessenger != null)
{
unbindService(mServiceConnection);
mServiceMessenger = null;
}
}
mServiceConnection member
private final ServiceConnection mServiceConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
if (DEBUG) {Log.d(TAG, "onServiceConnected");} //$NON-NLS-1$
mServiceMessenger = new Messenger(service);
Message msg = new Message();
msg.what = VoiceCommandService.MSG_RECOGNIZER_START_LISTENING;
try
{
mServiceMessenger.send(msg);
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
#Override
public void onServiceDisconnected(ComponentName name)
{
if (DEBUG) {Log.d(TAG, "onServiceDisconnected");} //$NON-NLS-1$
mServiceMessenger = null;
}
}; // mServiceConnection
In the service
#Override
public IBinder onBind(Intent intent)
{
Log.d(TAG, "onBind"); //$NON-NLS-1$
return mServerMessenger.getBinder();
}
Working example is given below,
MyService.class
public class MyService extends Service implements SpeechDelegate, Speech.stopDueToDelay {
public static SpeechDelegate delegate;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//TODO do something useful
try {
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
((AudioManager) Objects.requireNonNull(
getSystemService(Context.AUDIO_SERVICE))).setStreamMute(AudioManager.STREAM_SYSTEM, true);
}
} catch (Exception e) {
e.printStackTrace();
}
Speech.init(this);
delegate = this;
Speech.getInstance().setListener(this);
if (Speech.getInstance().isListening()) {
Speech.getInstance().stopListening();
} else {
System.setProperty("rx.unsafe-disable", "True");
RxPermissions.getInstance(this).request(permission.RECORD_AUDIO).subscribe(granted -> {
if (granted) { // Always true pre-M
try {
Speech.getInstance().stopTextToSpeech();
Speech.getInstance().startListening(null, this);
} catch (SpeechRecognitionNotAvailable exc) {
//showSpeechNotSupportedDialog();
} catch (GoogleVoiceTypingDisabledException exc) {
//showEnableGoogleVoiceTyping();
}
} else {
Toast.makeText(this, R.string.permission_required, Toast.LENGTH_LONG).show();
}
});
}
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
//TODO for communication return IBinder implementation
return null;
}
#Override
public void onStartOfSpeech() {
}
#Override
public void onSpeechRmsChanged(float value) {
}
#Override
public void onSpeechPartialResults(List<String> results) {
for (String partial : results) {
Log.d("Result", partial+"");
}
}
#Override
public void onSpeechResult(String result) {
Log.d("Result", result+"");
if (!TextUtils.isEmpty(result)) {
Toast.makeText(this, result, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onSpecifiedCommandPronounced(String event) {
try {
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
((AudioManager) Objects.requireNonNull(
getSystemService(Context.AUDIO_SERVICE))).setStreamMute(AudioManager.STREAM_SYSTEM, true);
}
} catch (Exception e) {
e.printStackTrace();
}
if (Speech.getInstance().isListening()) {
Speech.getInstance().stopListening();
} else {
RxPermissions.getInstance(this).request(permission.RECORD_AUDIO).subscribe(granted -> {
if (granted) { // Always true pre-M
try {
Speech.getInstance().stopTextToSpeech();
Speech.getInstance().startListening(null, this);
} catch (SpeechRecognitionNotAvailable exc) {
//showSpeechNotSupportedDialog();
} catch (GoogleVoiceTypingDisabledException exc) {
//showEnableGoogleVoiceTyping();
}
} else {
Toast.makeText(this, R.string.permission_required, Toast.LENGTH_LONG).show();
}
});
}
}
#Override
public void onTaskRemoved(Intent rootIntent) {
//Restarting the service if it is removed.
PendingIntent service =
PendingIntent.getService(getApplicationContext(), new Random().nextInt(),
new Intent(getApplicationContext(), MyService.class), PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
assert alarmManager != null;
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service);
super.onTaskRemoved(rootIntent);
}
}
For more details,
https://github.com/sachinvarma/Speech-Recognizer
Hope this will help someone in future.