This question already has answers here:
WakeLock not working
(2 answers)
Closed 8 years ago.
I have posted this question before and no one could answer it so I am trying again as this issue makes my app worthless. I need the sound to keep playing when the screen times out or the user taps the power button. I have read almost every online post about wake locks that I can find and i can not get it to work. below is one of the .Java files that plays a sound based on the user selected input. Everything works great except that when the screen goes dark the sound stops playing. Just a note, I am very new to this so if this code is sloppy or redundant please let me know.
package com.androidsleepmachine.gamble;
import android.app.Activity;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
public class Ship extends Activity implements View.OnClickListener {
public static final Integer[] TIME_IN_MINUTES = { 30, 45, 60, 180, 360 };
public MediaPlayer mediaPlayer;
public Handler handler = new Handler();
public Button button2;
public Spinner spinner2;
public PowerManager.WakeLock wl;
// Initialize the activity
#Override
public void onCreate(Bundle savedInstanceState) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"Playwhenoff");
super.onCreate(savedInstanceState);
wl.acquire();
setContentView(R.layout.ship);
button2 = (Button) findViewById(R.id.btn2);
button2.setOnClickListener(this);
spinner2 = (Spinner) findViewById(R.id.spinner2);
ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this,
android.R.layout.simple_spinner_item, TIME_IN_MINUTES);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner2.setAdapter(adapter);
}
// Play the sound and start the timer
private void playSound(int resourceId) {
// Cleanup any previous sound files
cleanup();
// Create a new media player instance and start it
mediaPlayer = MediaPlayer.create(this, resourceId);
mediaPlayer.start();
// Create the timer to stop the sound after x number of milliseconds
int selectedTime = TIME_IN_MINUTES[spinner2.getSelectedItemPosition()];
handler.postDelayed(runnable, selectedTime * 60 * 1000);
}
// Handle button callback
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn2:
playSound(R.raw.ocean_ship);
break;
}
}
protected void onStop() {
cleanup();
super.onStop();
}
// Stop the sound and cleanup the media player
public void cleanup() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
wl.release();
}
// Cancel any previously running tasks
handler.removeCallbacks(runnable);
}
// Runnable task used by the handler to stop the sound
public Runnable runnable = new Runnable() {
public void run() {
cleanup();
}
};
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
wl.release();
}
}
This probably has nothing to do with the WakeLock. Your activity is probably being called with onStop() when the screen turns off.
Audio players usually use a service for the audio playback, so the playback can run independently of UI concerns like this.
You are releasing your wakeLock onPause ->
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
wl.release(); // -> This is the line where you release the wakelock
}
When your device "goes to sleep", it goes like onPause -> onStop.
If you release your wakelock onPause, you will not be able to play your music with your screen off, because the system will not keep your CPU awaked.
Release it somewhere else (perhaps onDestroy?) and it should work.
Related
I'm making music player app for Android and i have problem with keeping music playing while i change orientation of phone.
package nori.beta.musicplayer;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.graphics.Color;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import nori.beta.musicplayer.Class.BlurBuilder;
import nori.beta.musicplayer.Fragment.Playlist;
import nori.beta.musicplayer.Fragment.Utilities;
public class MainActivity extends Activity {
private ImageView bg; // blured backgroud of size of screen
private ImageView cover; // small image in center of activity that plays song
private BlurBuilder blured; // class to blur image for background
private SeekBar
progressBar; // Creating seekbar that show progress of song and allow us scroll and rewind song
private ImageButton
play_pause_stopButton; //on click do 1.play/2.paues/3.stop for all change icon
private MediaPlayer player; // Player that play music
private Handler mHandler = new Handler(); //Handler that help with refreshing progressBar
private Utilities utils; //Change seconds into min + sec
ArrayList<File> mySongs; // list of music file
ArrayList<Song> songsInfo; //list of music file with extract information about them
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// init all GUI staff
initGUI();
setButtons();
}
#Override
protected void onResume() {
super.onResume();
// init Database and rest of gui staff that need database
// Here have u data of all file and chosen song and can u make to play song
}
private void initGUI() {
// Image Part
bg = (ImageView) findViewById(R.id.main_background);
cover = (ImageView) findViewById(R.id.cover_image);
blured = new BlurBuilder();
//Buttons
play_pause_stopButton = (ImageButton) findViewById(R.id.play_pause_stop_button);
progressBar = (SeekBar) findViewById(R.id.progressBar);
player = new MediaPlayer();
utils = new Utilities();
mySongs = findSongs(Environment.getExternalStorageDirectory());
songsInfo = new ArrayList<Song>();
for (File f : mySongs) {
songsInfo.add(new Song(f));
}
//progressBar.setOnSeekBarChangeListener(this);
}
private ArrayList<File> findSongs(File root) {
ArrayList<File> al = new ArrayList<File>();
File[] files = root.listFiles();
/**
* findSongs Search for music file in memory
*
* for each file in memory
* 1.if is that file a folder , then take all file then give it in method findSongs
* and with requrency
* 2.Else if that file end with .mp3 or .wav ,then add to list
*/
for (File singleFile : files) {
if (singleFile.isDirectory() && !singleFile.isHidden()) {
al.addAll(findSongs(singleFile));
//Log.e("findsongs","Folder");
} else {
if (singleFile.getName().endsWith(".mp3") || singleFile.getName().endsWith(".wav")) {
al.add(singleFile);
Log.e("FileInfo.GetSong", singleFile.getName().toString());
}
}
}
return al;
}
private void setButtons() {
play_pause_stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //Play/Pause song button clicked
playSong(0);
}
});
progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { //Using progress bar to scrolling song
#Override
public void onStopTrackingTouch(SeekBar progressBar) {
}
#Override
public void onStartTrackingTouch(SeekBar progressBar) {
}
#Override
public void onProgressChanged(SeekBar progressBar, int progress, boolean fromUser) { //When user move progress bar song go to moment that user choosed
if (player != null && fromUser) {
player.seekTo(progress * 1000);
}
}
});
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer player) { //When song ended playing
playSong(0);
}
});
}
public void updateProgressBar() {
mHandler.postDelayed(mUpdateTimeTask, 100); //Updating progressBar every 100ms
}
private Runnable mUpdateTimeTask = new Runnable() {
public void run() { //Updating time of song and progressbar
long totalDuration = player.getDuration();
long currentDuration = player.getCurrentPosition();
// Updating progress bar
int mCurrentPosition = player.getCurrentPosition() / 1000;
progressBar.setProgress(mCurrentPosition);
// Running this thread after 100 milliseconds
mHandler.postDelayed(this, 100);
}
};
// set it in all changes of the privius songs <#-- Krzysiek -->
private void setBackground(int i) {
//setting the back image and cover image to the chosen song
if (songsInfo.get(i).getBackground() != null) {
bg.setImageBitmap(blured.blur(this, songsInfo.get(i).getBackground()));
cover.setImageBitmap(songsInfo.get(i).getBackground());
Log.i("FileInfo.SetCover", "Set cover of " + songsInfo.get(i).getName());
}
}
public void playSong(int index) {
try {
player.reset();
player.setDataSource(songsInfo.get(index).getPath()); //Getting song with proper index from list
player.prepare();
player.start(); //Playing prepared song
// Displaying Song title
String songTitle = songsInfo.get(index).getTitle();
String songArtist = songsInfo.get(index).getArtist();
setBackground(index);
// Changing Button Image to pause image
play_pause_stopButton.setImageResource(R.drawable.pause);
// set Progress bar values
progressBar.setProgress(0);
progressBar.setMax(player.getDuration() / 1000);
// Updating progress bar
updateProgressBar();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I tried with
android:configChanges="orientation|screenSize"
in manifest and it work but only for 1st change from vertical to horizontal orientation. When i change again from horizontal to vertical, same song start from beggining while old is still playing so i have 2 songs played at same moment. Can anyone help me with it?
keep the config changes and add an override to the onconfig...
#Override
public void onConfigurationChanged(Configuration newConfig) {
}
when the orientation changes the oncreate is called by default , you need to override it like this example or maybe use a singleton as a mediaplayer so it won't create two of them
It was just stupid but i just make player static. It helped and now it dont create additional instances
When you orientate your device, it redraw your layouts and that's why activity restarts (calls onConCreate()). You can save your instance with overriding this method:
#Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
}
and restore instance with overriding this method:
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
I created source in github about this topic, saving instances and restoring. Maybe it'll help you in some circumstances. Orientation Mode
I have a sleep machine app that I want to still play the sound if the phone goes into stand by. I just edited to have the wake lock added and it is added in the manifest as well. It isnt working correctly but I am not sure why.
package com.androidsleepmachine.gamble;
import android.app.Activity;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
public class Ship extends Activity implements View.OnClickListener {
public static final Integer[] TIME_IN_MINUTES = { 30, 45, 60, 180, 360 };
public MediaPlayer mediaPlayer;
public Handler handler = new Handler();
public Button button2;
public Spinner spinner2;
private PowerManager.WakeLock wl;
// Initialize the activity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
setContentView(R.layout.ship);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DoNjfdhotDimScreen");
button2 = (Button) findViewById(R.id.btn2);
button2.setOnClickListener(this);
spinner2 = (Spinner) findViewById(R.id.spinner2);
ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this,
android.R.layout.simple_spinner_item, TIME_IN_MINUTES);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner2.setAdapter(adapter);
}
// Play the sound and start the timer
private void playSound(int resourceId) {
// Cleanup any previous sound files
cleanup();
// Create a new media player instance and start it
mediaPlayer = MediaPlayer.create(this, resourceId);
mediaPlayer.start();
// Create the timer to stop the sound after x number of milliseconds
int selectedTime = TIME_IN_MINUTES[spinner2.getSelectedItemPosition()];
handler.postDelayed(runnable, selectedTime * 60 * 1000);
}
// Handle button callback
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn2:
playSound(R.raw.ocean_ship);
break;
}
}
protected void onStop()
{
cleanup();
super.onStop();
}
// Stop the sound and cleanup the media player
public void cleanup() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
wl.release();
}
// Cancel any previously running tasks
handler.removeCallbacks(runnable);
}
// Runnable task used by the handler to stop the sound
public Runnable runnable = new Runnable() {
public void run() {
cleanup();
}
};
}
You can make it happen with wake locks. Its part of the Powermanager api.
A detailed explanation can be found here.
The idea is to click the spinner and have it give you a list of times to play the sound. once you pick a sound and click the button it should play. it doesnt work and I cant figure out why. I have a home screen and a home activity that when you click a button take you to a new activity and a new layout. I can get the new layout and new activity to load, which is the one featured below, but the sound and spinner do not start.
package com.androidsleepmachine.gamble;
package com.androidsleepmachine.gamble;
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
public class Ship extends Activity implements View.OnClickListener {
public static final int[] TIME_IN_MINUTES = { 30, 45, 60 };
public MediaPlayer mediaPlayer;
public Handler handler = new Handler();
public Button button1;
public Spinner spinner1;
// Initialize the activity
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.ship);
button1 = (Button) findViewById(R.id.btnSubmit);
button1.setOnClickListener(this);
spinner1 = (Spinner) findViewById(R.id.spinner1);
ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this,
android.R.layout.simple_spinner_item);
}
// Play the sound and start the timer
private void playSound(int resourceId) {
// Cleanup any previous sound files
cleanup();
// Create a new media player instance and start it
mediaPlayer = MediaPlayer.create(this, resourceId);
mediaPlayer.start();
// Create the timer to stop the sound after x number of milliseconds
int selectedTime = TIME_IN_MINUTES[spinner1.getSelectedItemPosition()];
handler.postDelayed(runnable, selectedTime * 60 * 1000);
}
// Handle button callbacks
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSubmit:
playSound(R.raw.ocean_ship);
break;
}
}
// Stop the sound and cleanup the media player
public void cleanup() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
// Cancel any previously running tasks
handler.removeCallbacks(runnable);
}
// Runnable task used by the handler to stop the sound
public Runnable runnable = new Runnable() {
public void run() {
cleanup();
}
};
}
It looks like the problem is in your switch statemenet. You are checking for the id of button1 but the only button that you have assigned the listener to is button1 which has an id of btnSubmit. So your playSound() function is never getting called.
Change your onClick() to
// Handle button callbacks
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSubmit:
playSound(R.raw.ocean_ship);
break;
}
}
Edit
For your ArrayAdapter try something like
ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item, TIME_IN_MINUTES);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner1.setAdapter(adapter);
Are you doing this on emulator? Mediaplayer doesnt work on emulator. You will have to test this in real device.
I need to increase voice, recorder from microphone on Android device.
I try to read buffer from AudioRecord and then write it to AudioTrack... It works, but with delay, because min buffer size, returned bu method AudioRecord.getMinBufferSize with frequency like 44100 is 4480 bytes.
Any ideas?
Thanks.
I have this code
AudioRecord and AudioTrack latency
But it happens to that there is a 20ms delay, and I need to solve it,
The code above seems that plays something but there is no mic input, does it work?
Thanks!
I noticed there is no threading code. I would recommend trying to thread the recording and playback aspects and see if that better avoids the latency. Fill the buffer in from the mic one thread, and read it out to the speaker in the other. Avoid buffer overflows and underruns by handling those situations with some action (e.g. clearing the buffer for overflows). In theory, one should keep up with the other.
package org.example.audio;
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class AudioDemo extends Activity implements OnClickListener {
private static final String TAG = "AudioDemo";
private static final String isPlaying = "Media is Playing";
private static final String notPlaying = "Media has stopped Playing";
MediaPlayer player;
Button playerButton;
public void onClick(View v) {
Log.d(TAG, "onClick: " + v);
if (v.getId() == R.id.play) {
playPause();
}
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//player = MediaPlayer.create(this, R.raw.robotrock);
player.setLooping(false); // Set looping
// Get the button from the view
playerButton = (Button) this.findViewById(R.id.play);
playerButton.setText(R.string.stop_label);
playerButton.setOnClickListener(this);
// Begin playing selected media
demoPlay();
// Release media instance to system
player.release();
}
#Override
public void onPause() {
super.onPause();
player.pause();
}
// Initiate media player pause
private void demoPause(){
player.pause();
playerButton.setText(R.string.play_label);
Toast.makeText(this, notPlaying, Toast.LENGTH_LONG).show();
Log.d(TAG, notPlaying);
}
// Initiate playing the media player
private void demoPlay(){
player.start();
playerButton.setText(R.string.stop_label);
Toast.makeText(this, isPlaying, Toast.LENGTH_LONG).show();
Log.d(TAG, isPlaying);
}
// Toggle between the play and pause
private void playPause() {
if(player.isPlaying()) {
demoPause();
} else {
demoPlay();
}
}
}
I have a memory leak. Here's the code
package fourguys.testing.IntentTest;
import android.app.Activity; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.media.MediaPlayer; import android.media.AudioManager; import android.content.Context;
public class CanvasDrawingActivity extends Activity {
private static final int FIRE = 0;
private int initVolume = 0;
private Handler handler;
private MyCanvas v;
private MediaPlayer mp;
private AudioManager am;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
am = (AudioManager)this.getSystemService(Context.AUDIO_SERVICE);
// this method gets the current volume setting for music
initVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
am.setStreamVolume(AudioManager.STREAM_MUSIC,100,AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
mp = MediaPlayer.create(this, R.raw.test);
makeHandler();
v =new MyCanvas(this);
new Thread(new Runnable(){
#Override
public void run() {
while(true)
handler.sendEmptyMessage(FIRE);
}}).start();
setContentView(v);
mp.setLooping(true);
mp.start();
}
private void makeHandler()
{
handler = new Handler(){
#Override
public void handleMessage(Message msg) {
switch(msg.what)
{
case FIRE:
{
v.invalidate();
break;
}
}
}
};
}
protected void onPause() {
super.onPause();
mp.stop();
}
protected void onFinish() {
mp.stop();
}
}
and this:
package fourguys.testing.IntentTest;
import android.app.Activity; import android.content.Intent; import android.media.MediaPlayer; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.view.WindowManager;
public class IntentTest extends Activity { /** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
//reciever intentReceiver = new reciever();
// IntentFilter intentFilter = new IntentFilter("com.app.REC");
//registerReceiver(intentReceiver, intentFilter);
Button b = (Button)this.findViewById(R.id.endButton);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(IntentTest.this,CanvasDrawingActivity.class);
startActivity(i);
}
});
}
// the onPause method get called when the app is either being hidden or being closed so this the place where we would want to clean anything up like stoping the media player.
#Override
protected void onPause()
{
super.onPause();
}
}
I run the app and it gets wonky on exit. It locks the handset and causes the battery to run hot. I need to pull the battery physically to reboot. Any thoughts as to why that might be? It runs fantastically on the emulator. Should I be using onFinish instead, or am I not cleaning something up and I'm missing it?
It is this part of your code:
new Thread(new Runnable(){
#Override
public void run() {
while(true)
handler.sendEmptyMessage(FIRE);
}}).start();
You're doing three obvious things wrong here. 1) You're not killing it and/or pausing it in Activity#onPause. 2) You're not calling setDaemon(true); this will cause the process to continue and not die while this thread is running. 3) you're using a hot loop, i.e., you're not calling Thread#sleep() or some other type of equivalent method there to pause and stop fully using the cpu.