Calling methods from a running Thread - java

So I am trying to make a music player that runs on a separate thread. I want to have methods inside of the Player class (that implements Runnable) to pause, play and go to next/prev song. If I was to call inner methods of the Thread, would it still run on a separate thread? Everything works as it should, but I am still new to Threads so I'm not sure if this is good practice or not. Could someone shed some light on this?
class Player implements Runnable, OnCompletionListener {
public static final String TAG = "Player";
private MediaPlayer mp;
private boolean isPlaying = false;
private SongsManager sm = null;
private ArrayList<HashMap<String, String>> songsList = new ArrayList<HashMap<String, String>>();
private int currentSongIndex = 0;
public Player(int currentSongIndex){
mp = new MediaPlayer();
mp.setOnCompletionListener(this);
sm = new SongsManager();
songsList = sm.getSongList();
this.currentSongIndex = currentSongIndex;
}
#Override
public void run() {
try{
mp.reset();
mp.setDataSource(songsList.get(currentSongIndex).get(SongsManager.KEY_PATH));
mp.prepare();
mp.start();
isPlaying = true;
} catch (IllegalArgumentException e){
e.printStackTrace();
} catch (IllegalStateException e){
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
}
public synchronized void pause() {
if (isPlaying){
if (mp != null){
mp.pause();
isPlaying = false;
}
}
}
public synchronized void play() {
if (!isPlaying){
if (mp != null){
mp.start();
isPlaying = true;
}
}
}
...
}
So say, if I created a new Thread like so,
Player player = new Player(0);
playerThread = new Thread(player, Player.TAG);
playerThread.start();
and called player.pause(), then player.play() from a different thread, would the player still be running on the separate thread?

If I was to call inner methods of the Thread, would it still run on a separate thread?
No. Threads aren't magic. When you call a method you are calling that method with the current thread. To fork a thread you actually have to create a thread object (or executor service) and start it. Then it calls run(), etc..
So if you called player.pause() you would be calling pause from the current thread which is possibly the "main" thread. Your playerThread would be doing something else in the background. The whole reason why the pause() method is synchronized is so you can all it from the outside thread and the playerThread without them overlapping. It also means that multiple threads can test and set the isPlaying boolean and other threads will see the updates.
You should most likely start with the Java thread tutorial.

The content of the run() method is what executes on the separate thread. Anything that calls a method of of the class that contains run() will execute that method on its own thread. You must therefore insure that any data access is properly thread-safe.

Related

Android pause/resume AsynTask

I have a UI thread(MainActivity) calling an AsynTask object(DownloadManager), which, in turn calls 3 threads (DownloaderThread).
How do I pause/resume the 3 DownloaderThreads without pausing the AsynTask or the UI thread?
Following is the code I have currently implemented for the pause functionality, but when the Pause button is clicked, the app crashes with the dialog: "app has stopped". Code:
DownloadManager (AsynTask):
public void onPause() {
try{
t0.wait();
t1.wait();
t2.wait();
this.wait();
}catch (InterruptedException ex){
}
}
DownloaderThread (implements Runnable and is passed to a Thread instance in DownloadManager:
public class DownloaderThread implements Runnable {
private Object mPauseLock;
private Boolean mPaused;
private Boolean mFinished;
public void run(){
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
try{
while(!mFinished){
//do something
synchronized (mPauseLock){
while (mPaused){
try{
mPauseLock.wait();
}catch (InterruptedException e){
}
}
}
}
}
}
public void onPause(){
synchronized (mPauseLock){
mPaused = true;
}
}
You can't really pause an AsyncTask once it started. You can, however, store the progress when activity is paused/destroyed and start from the progress once you are back to the original Activity. But I recommend the best way to complete task for your scenario is to use Service.

wait() method on thread not working Android

I have a few downloads that are submitted as tasks to a ThreadPoolExecutor. Now, I am creating this ThreadPoolExecutor in a global class that extends Application. I am storing all the submitted tasks in a HashMap with ids.
I have a ListView in a Fragment. This ListView item contains pause and resume buttons. When I click on the list item itself, the download FutureTask is submitted to the global pool executor. Now, when I click on the pause button of the ListView item, I want that particular thread to pause/wait.
I have the onClick method of the pause button in my list view's custom adapter. So, when I click the button, in my adapter class, I get all the threads that are currently running, put them in an array and then get the thread with the name I want from the array. I can see in my log that the thread I want to get is running with the name I set. So once I get that thread, I do wait() on it.
In the onclick of my resume button, I notify that particular thread and set the flag to pause as false so that the thread resumes. But, the download inside that thread actually keeps running even after clicking pause. I do not know where I am going wrong.
My GlobalState Class:
public class GlobalState extends Application{
HashMap<String, Future<?>> futureMapMain = new HashMap<String, Future<?>>();
ThreadPoolExecutor mainExec = new ThreadPoolExecutor(2, 2, 2000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new YourThreadFactory());
public void submitTaskMain(String name, Thread task){
Future<?> longRunningTaskFuture = mainExec.submit(task);
futureMapMain.put(name, longRunningTaskFuture);
}
public HashMap<String, Future<?>> getFutureMap(){
return futureMapMain;
}
public ThreadPoolExecutor getMainExeutor(){
return mainExec;
}
public class YourThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
return new Thread(r, gettName());
}
}
}
My Download Method that is written inside my fragment class. gets executed on list item click:
public void abDownloadTask(){
Thread dThread = new Thread(new Runnable() {
final Handler pdfHandler = new Handler();
#Override
public void run() {
for(something){
/* DOES SOME DOWNLOAD USING url.getcontent() with different urls in a loop and stores files to sd card. */
}
}
}
dThread.setName(threadname);
Log.d("Tel Frag", "Thread name set as: "+dThread.getName());
mainGs.settName(threadname);
mainGs.submitTaskMain(threadname, dThread);
}
onclick of my pause button inside custom list adapter:
pause.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
needToPause = true;
Runnable runnable = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
Log.d("CLA", "Threads running size: "+threadArray.length);
Thread neededThread = null;
for ( Thread thread : threadArray ){
Log.d("CustomListAdapter", "Thread name in array: "+thread.getName());
if (thread.getName( ).equals(threadname)){
neededThread = thread;
}
}
if(neededThread!=null){
while(needToPause){
synchronized (neededThread) {
try {
neededThread.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
};
new Thread(runnable).start();
notifyDataSetChanged();
}
});
onclick of my resume button written inside custom list adapter class:
resume.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
needToPause = false;
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for ( Thread thread : threadArray ){
if ( thread.getName( ).equals(threadname) ){
thread.notify();
}
}
notifyDataSetChanged();
}
});
I have been trying to figure it out for 2 days now but no luck. I went through a lot of examples and stackoverflow questions. I am just trying to get a thread by its name and then pause and resume it. Any help would be appreciated. Thank you!
You can't reliably use wait() and notify() on Thread objects. This is clearly stated in the Javadoc. You already have Future objects, you shouldn't be using other mechanisms anyway.

android activity create multiple instance of my thread

i need to check a variable for change, if the change happens i will update a textview.
so i created a new thread with a while loop for this purpose. i check the variable every 1 second, via Thread.sleep()
this thread created and started in onCreate(). so it's one time.
the problem is every time i flip my phone (from vertical to horizontal or ...) a new thread will be created.
here is my code:
public class HomeActivity extends Activity
{
private final static int LOAD_SUCCESSFULL = 1;
private final long LOCATION_THREAD_SLEEP = 1000;
private boolean flag = false;
static TextView lat;
static TextView lon;
static Location currentLocation = null;
Thread locationThread;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.new_home2);
this.getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.new_home_titlebar);
lat = (TextView)findViewById(R.id.t2rt3);
lon = (TextView)findViewById(R.id.t2rt4);
/* FindLocation class is a helper class that find location via simcard or gps in separate thread,
same problem happen with this thread also, it create multiple thread, for ease of work i
commented this part.
*/
//FindLocation fn = new FindLocation(this);
locationThread = new Thread(null, loadLocation, "loadLocationHomePage");
locationUpdater();
}
private static Handler locationUpdateHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch(msg.what)
{
case LOAD_SUCCESSFULL:
lat.setText(Double.toString(currentLocation.getLatitude()));
lon.setText(Double.toString(currentLocation.getLongitude()));
//stopThread();
break;
}
}
};
private Runnable loadLocation = new Runnable()
{
public void run()
{
//boolean flag = false;
while(!flag)
{
if(Data.currLocation != null)
{
currentLocation = new Location(Data.currLocation);
Message msg = locationUpdateHandler.obtainMessage(LOAD_SUCCESSFULL);
locationUpdateHandler.sendMessage(msg);
//return;
flag = true;
//return;
}
else
{
try
{
Thread.sleep(LOCATION_THREAD_SLEEP);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
};
public void locationUpdater()
{
//Thread locationThread = new Thread(null, loadLocation, "loadLocationHomePage");
locationThread.start();
}
so how i can solve this?
Actually the problem is that EveryTime you flip the phone a new instance of Activity is created and because of this you on every rotation you get a call on onCreate() where you are blindly creating a new Thread and Starting the new Thread.
This is the default behavior of every Activity but we can change this re-creation of Activity by stating an attribute in AndroidManifest file for the Activity
<activity
android:name="yourPackage.ActivityName"
android:configChanges="keyboardHidden|orientation|screenSize"
</activity>
This will prevent from creation of Activity on orientation change.
You will also get these orientation event if you override
#Override
public void onConfigurationChanged(Configuration newConfig) {}
Hope this will solve this problem without implementing such a complex logic which may broke in some other uses case.
I think you aren't perhaps going about this in the most efficient way possible.
But if your question is simply, how do i prevent multiple worker threads from being spawned, you should look into a UIless fragment.
http://www.vogella.com/articles/AndroidFragments/article.html#headlessfragments1
i don't know why android doing this. if i putted my code in onResume(), then this behavior make sence but in my case, i don't know.
anyway i found a dirty solution. the idea is finding list of all thread and search them for mine, if it existed prevent to create another.
public boolean checkThreadExist()
{
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for(int i = 0; i < threadArray.length ; i++)
{
if(threadArray[i].getName().equalsIgnoreCase("loadLocationHomePage"))
return true;
}
return false;
}
updated onCreate() :
if(checkThreadExist())
{
}
else
{
locationThread = new Thread(null, loadLocation, "loadLocationHomePage");
locationUpdater();
}

what is a good way to stop a thread in android

I am trying to passthrough the input obtained from the microphone to the speaker (the goal is to be able to perform audio processing in real time in the future). This is the code:
public class MainActivity extends Activity {
AudioManager am = null;
AudioRecord record =null;
AudioTrack track =null;
final int SAMPLE_FREQUENCY = 44100;
final int SIZE_OF_RECORD_ARRAY = 1024; // 1024 ORIGINAL
final int WAV_SAMPLE_MULTIPLICATION_FACTOR = 1;
int i= 0;
boolean isPlaying = true;
class MyThread extends Thread{
#Override
public void run(){
recordAndPlay();
}
}
MyThread newThread;
private void init() {
int min = AudioRecord.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
record = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, min);
int maxJitter = AudioTrack.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
track = new AudioTrack(AudioManager.MODE_IN_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, maxJitter, AudioTrack.MODE_STREAM);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setVolumeControlStream(AudioManager.MODE_IN_COMMUNICATION);
init();
newThread = new MyThread();
newThread.start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void recordAndPlay() {
short[] lin = new short[SIZE_OF_RECORD_ARRAY];
int num = 0;
am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
record.startRecording();
track.play();
while (true) {
num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY);
for(i=0;i<lin.length;i++)
lin[i] *= WAV_SAMPLE_MULTIPLICATION_FACTOR;
track.write(lin, 0, num);
}
}
public void passStop(View view){
Button playBtn = (Button) findViewById(R.id.playBtn);
// /*
if(!isPlaying){
record.startRecording();
track.play();
isPlaying = true;
playBtn.setText("Pause");
}
else{
record.stop();
track.pause();
isPlaying=false;
playBtn.setText("Pass through");
}
// */
}
/*
#SuppressWarnings("deprecation")
#Override
public void onDestroy(){
newThread.stop();
}
*/
#Override
protected void onDestroy() {
android.os.Process.killProcess(android.os.Process.myPid());
// killProcess(android.os.Process.myPid());
}
}
Brief overview:
The while(true) {} infinite loop in recordAndPlay() function continuously reads raw audio samples from the microphone and outputs the raw samples to the speaker. recordAndPlay() is called from a Thread started in the onCreate() function. So it starts sending the input on the microphone to the speaker as soon as the program starts (well actually after a few seconds lag but I think this latency in unavoidable). I also have a button that can pause and resume this pass through. Now if the Thread is not stopped, the pass through continues even when I exit the application or the application looses focus (so even when the phone is on the desktop it keeps doing the passthrough).
#SuppressWarnings("deprecation")
#Override
public void onDestroy(){
newThread.stop();
}
This code causes the app to crash on exit (Why?) so I used
#Override
protected void onDestroy() {
android.os.Process.killProcess(android.os.Process.myPid());
// killProcess(android.os.Process.myPid());
}
that I found somewhere in stackoverflow (I forgot where). It seems to do what I want for now, but I want know if this is the proper way to stop the Thread or not. It does what I need, that is, it stops the passthrough when I exit the application, but I am not sure what exactly the killProcess() function does to my application overall, and if it is the best way to stop a Thread that I started myself.
Also, I can get the same effect if I exit my application (or loose focus to it) while the passthrough is being paused. But I assume this means the Thread is still running which means the infinite loop is also continuously running as well. Is it a good idea to do this, that is, just leave the Thread running, as long as my overall program is behaving as I want it to? What if I have lots of Threads or other background processes running? Can this practice cause memory problems in the future if the app grows too big?
Threads should periodically check for some shouldTerminate flag in their loop, then just set this flag from UI thread and (optionally) wait until thread terminate gracefully. Don't forget volatile or proper field synchronization.
Please remember to call super.onDestroy after releasing your memory or finishing the thread. Otherwise it will throw Exception:
#Override
protected void onDestroy() {
// You code here to finish the thread
super.onDestroy(); // Please call THIS too
}
Hope this helps.
Change your Thread class to something like this:
class MyThread extends Thread {
private volatile boolean finished = false;
#Override
public void run() {
while (!finished) {
// do stuff on thread
}
}
public void stopThread() {
finished = true;
}
}
In your onDestroy method call stopThread().
#Override
protected void onDestroy() {
newThread.stopThread();
super.onDestroy();
}
If you wish, you can also wait for thread to stop, by using this method:
private void joinThread(Thread thread) {
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// to be handled as you wish
}
}
}
Put this method in your activity and call it after newThread.stopThread().
There is already a provided flag for interuption.
Correct your while loop to the following.
And just call interupt(); in onDestroy or wherever.
private class thrd extends Thread {
#Override
public void run() {
super.run();
while (!isInterrupted()) {
//TODO
}
}
}
#Override
protected void onDestroy() {
super.onDestroy();
thrd.interupt();
}

Android Media Player Seek bar not updating on start after stop

I'm having a hard time working with this.
I have a Media player, and I can play, pause, stop, play again after pause, stop... whatever.
Now I wanted to have a SeekBar to give a visual component. My problem is:
When I start the player for the first time everything works well. Music plays, and seek bar updates. also works when I pause.
Now, if I stop the player and start it again, the player starts, the run() method executes, but the seekbar doesn't update, and soon the app gives a not responging error.
What am I missing here?
The run method is an implementation from the Runnable interface, and with a simple log, I can see it's being executed, even after the stop/play case. The only thing that seems not to be working is the seek.setProgress(...).
some help, please? :)
Here's my code:
public class MediaPlayerTestingActivity extends Activity
implements OnClickListener, OnPreparedListener, Runnable {
private MediaPlayer mp;
private boolean paused = false;
private SeekBar seek;
private boolean threadStarted = false;;
private Thread thread = new Thread(this);
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
seek = (SeekBar)findViewById(R.id.seek);
mp = new MediaPlayer();
Button start = (Button)findViewById(R.id.start);
start.setOnClickListener(this);
Button pause = (Button)findViewById(R.id.pause);
pause.setOnClickListener(this);
Button stop = (Button)findViewById(R.id.stop);
stop.setOnClickListener(this);
}
//click handlers
public void onClick(View v)
{
int buttonId = v.getId();
switch (buttonId)
{
case R.id.start:
if(mp != null)
{
if(!mp.isPlaying() && !paused)
prepareTostartPlayer();
else
mp.start(); //if it was just paused
}
else
{
mp = new MediaPlayer();
prepareTostartPlayer();
}
break;
case R.id.pause:
if(mp.!= null && mp.isPlaying())
{
mp.pause();
paused = true;
}
break;
case R.id.stop:
if(mp != null && mp.isPlaying())
{
mp.stop();
mp.release();
mp = null;
}
break;
default:
break;
}
}
//When the player is ready to play
public void onPrepared(MediaPlayer arg0)
{
seek.setMax(mp.getDuration());
mp.start();
if(!threadStarted)
thread.start();
else
thread.run();
}
//Method to prepare to start the player
private void prepareTostartPlayer()
{
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mp.setDataSource("http://feedproxy.google.com/~r/rc-cadernetadecromos/~3/JH1kfZCmP3M/cdc_190112_2.mp3");
mp.prepareAsync();
mp.setOnPreparedListener(this);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void run()
{
threadStarted = true;
int current = 0;
while(mp != null && current < mp.getDuration())
{
try {
current = mp.getCurrentPosition();
seek.setProgress(current);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I see three issues here:
First, this part:
public void onPrepared(MediaPlayer arg0)
{
seek.setMax(mp.getDuration());
mp.start();
if(!threadStarted)
thread.start();
else
thread.run();
}
The first time your run the thread you set threadStarted to true in the thread code, but you never set it back to false when the thread finishes. So after the first run it will be true forever and thus the else above will always execute, running the player code sequentially and thus blocking your user interface.
Second, you are updating the user interface (the SeekBar) from a different thread than the UI thread. See this related question for how to correctly update the UI in this case: Update UI from Thread
Third, this may be just a recommendation, but since your threadStarted variable is modified between threads, it's better to declare it volatile to prevent some optimizations from breaking your code:
private volatile boolean threadStarted = false;
According to my experience and what is written in docs, threads in Android work only in purely algorythmic parts of a prog. Any try to use them in activities, or anything connected to UI, goes to error. This way it simply won't work. Never can you repair it!
Use AsyncTask (or child Activity/Fragment for greater things) instead.

Categories

Resources