I have some working code, which takes a few buttons and when they are pressed, plays some audio files. However, this code seems uncessearily large and I feel it could be reduced down into smaller methods that could be called. I have, however, taken enough time and effort just to get this working so I can't see how to reduce it. Can you provide a cleaner / smaller and more resuable way of calling audio tracks as this code will only get larger as I intend to have around 30 tracks.
audioBtn1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mediaPlayer != null && mediaPlayer.isPlaying())
{
mProgressStatus = 0;
mediaPlayer.stop();
try {
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
progressBar1.setProgress(mProgressStatus);
mHandler.removeCallbacks(runnable1);
t.interrupt();
audioBtn2.setEnabled(true);
audioBtn3.setEnabled(true);
} else {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.song1);
mediaPlayer.start();
t = new Thread(runnable1);
t.start();
mProgressStatus = 0;
progressBar1.setProgress(mProgressStatus);
audioBtn2.setEnabled(false);
audioBtn3.setEnabled(false);
}
}
});
audioBtn2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mediaPlayer != null && mediaPlayer.isPlaying())
{
mProgressStatus = 0;
mediaPlayer.stop();
try {
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
progressBar2.setProgress(mProgressStatus);
mHandler.removeCallbacks(runnable2);
t.interrupt();
audioBtn1.setEnabled(true);
audioBtn3.setEnabled(true);
} else {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.song2);
mediaPlayer.start();
t = new Thread(runnable2);
t.start();
mProgressStatus = 0;
progressBar2.setProgress(mProgressStatus);
audioBtn1.setEnabled(false);
audioBtn3.setEnabled(false);
}
}
});
audioBtn3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mediaPlayer != null && mediaPlayer.isPlaying())
{
mProgressStatus = 0;
mediaPlayer.stop();
try {
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
progressBar3.setProgress(mProgressStatus);
mHandler.removeCallbacks(runnable3);
t.interrupt();
audioBtn1.setEnabled(true);
audioBtn2.setEnabled(true);
} else {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.song3);
mediaPlayer.start();
t = new Thread(runnable3);
t.start();
mProgressStatus = 0;
progressBar3.setProgress(mProgressStatus);
audioBtn1.setEnabled(false);
audioBtn2.setEnabled(false);
}
}
});
}
private Runnable runnable1 = new Runnable() {
public void run() {
try {
while (mProgressStatus < 30) {
mProgressStatus++;
mHandler.post(new Runnable() {
public void run() {
progressBar1.setProgress(mProgressStatus);
}
});
Thread.sleep(1000);
}
} catch (InterruptedException e) {
mProgressStatus = 0;
mHandler.removeCallbacks(runnable1);
}
}
};
private Runnable runnable2 = new Runnable() {
public void run() {
try {
while (mProgressStatus < 30) {
mProgressStatus++;
mHandler.post(new Runnable() {
public void run() {
progressBar2.setProgress(mProgressStatus);
}
});
Thread.sleep(1000);
}
} catch (InterruptedException e) {
mProgressStatus = 0;
mHandler.removeCallbacks(runnable2);
}
}
};
private Runnable runnable3 = new Runnable() {
public void run() {
try {
while (mProgressStatus < 30) {
mProgressStatus++;
mHandler.post(new Runnable() {
public void run() {
progressBar3.setProgress(mProgressStatus);
}
});
Thread.sleep(1000);
}
} catch (InterruptedException e) {
mProgressStatus = 0;
mHandler.removeCallbacks(runnable3);
}
}
};
One thing I've already looked at is moving the setting of buttons to enabled/disabled to a separate class, but that involves creating more large un-resuable scripts to handle calling the main thread (As I need to reset the buttons in the runnable threads after songs have finished)
I'm also not sure if it's possible to pass element ids as arguments to functions, in which case that would help reduce the code.
You can reduce duplicate codes by defining an abstract OnClickListener class and delegate the responsibility of the "different" parts to whoever extends it. You can of course define this as an Inner Class to access the parameters of the current class. this is called Template method pattern
first define MyClickListener:
public abstract class MyClickListener extends View.OnClickListener {
#Override
public void onClick(View v) {
if(mediaPlayer != null && mediaPlayer.isPlaying())
{
mProgressStatus = 0;
mediaPlayer.stop();
try {
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
setProgress(mProgressStatus);
mHandler.removeCallbacks(runnable1);
t.interrupt();
enableButtons(true);
} else {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.song1);
mediaPlayer.start();
t = getThread();
t.start();
mProgressStatus = 0;
setProgress(mProgressStatus);
enableButtons(false);
}
}
public abstract void setProgress(int progress);
public abstract void enableButtons(boolean enable);
public abstract Thread getThread();
}
then use it like this:
audioBtn1.setOnClickListener(new MyClickListener(){
public abstract void setProgress(int progress){
progressBar1.setProgress(progress);
}
public abstract void enableButtons(boolean enable){
audioBtn2.setEnabled(enable);
audioBtn3.setEnabled(enable);
}
public abstract Thread getThread(){
return new Thread(runnable1);;
}
})
And so on for other buttons.
You can apply this pattern for the runnables too!
Related
In my application i have timer for some works.
When my application running after some time my application freeze and not work any View !
In this timer every 500ms i emit socket.io
My Codes:
AsyncTask.execute(new Runnable() {
#Override
public void run() {
socketPingTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if (isSendSocketPing) {
checkSocketPingTimer += startSocketPingTimer;
if (checkSocketPingTimer == sendSocketPingTimer) {
currentTimerForSocket = System.currentTimeMillis();
try {
detailSocketUtils.getSendRTTforPing(currentTimerForSocket + "");
} catch (Exception e) {
}
}
//Show ping (from search)
Constants.currentActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
if (isShownPing) {
detailToolbar_ping.setVisibility(View.VISIBLE);
if (checkSocketPingTimer > 500) {
detailToolbar_ping.setText(checkSocketPingTimer + "");
detailToolbar_ping.setTextColor(Color.RED);
} else {
detailToolbar_ping.setText(checkSocketPingTimer + "");
detailToolbar_ping.setTextColor(Color.GREEN);
}
} else {
detailToolbar_ping.setVisibility(View.GONE);
}
}
});
socketPing = checkSocketPingTimer;
}
}
}, 500, startSocketPingTimer);
}
});
How can i run this timers in another thread and not freeze my app ?
It should be something similar to this code:
class MyActivity extends Activity
{
private void executeLoop()
{
Handler myHandler = new Handler()
{
public void handleMessage(Message msg)
{
if (isShownPing)
{
detailToolbar_ping.setVisibility(View.VISIBLE);
if (checkSocketPingTimer > 500) {
detailToolbar_ping.setText(checkSocketPingTimer + "");
detailToolbar_ping.setTextColor(Color.RED);
} else {
detailToolbar_ping.setText(checkSocketPingTimer + "");
detailToolbar_ping.setTextColor(Color.GREEN);
}
} else
{
detailToolbar_ping.setVisibility(View.GONE);
}
}
}
socketPingTimer.scheduleAtFixedRate(new TimerTask()
{
#Override
public void run()
{
if (isSendSocketPing)
{
checkSocketPingTimer += startSocketPingTimer;
if (checkSocketPingTimer == sendSocketPingTimer) {
currentTimerForSocket = System.currentTimeMillis();
try {
detailSocketUtils.getSendRTTforPing(currentTimerForSocket + "");
} catch (Exception e) {
}
}
myHandler.sendEmptyMessage();
socketPing = checkSocketPingTimer;
}
}
}, 500, startSocketPingTimer);
}
}
private void startTimerAtFixRate() {
android.os.Handler handler = new android.os.Handler();
Runnable updateTimerThread = new Runnable() {
public void run() {
//write here whatever you want to repeat
// Like I called Log statement
// After every 1 second this below statement will be executed
Log.e("CALLED-->", "TRUE");
handler.postDelayed(this, 1000);
}
};
handler.postDelayed(updateTimerThread, 100);
}
I've set MediaPlayer to play different songs. However, when I try to close the Application, the Activity gets closed, but the song still run in the Background. Here is how I've coded it!
try {
mp.setDataSource(text);
mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
mp.prepare();
mp.setVolume(0.4f, 0.4f);
songPlayer.setDataSource(song);
songPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer songPlayer) {
songPlayer.start();
}
});
songPlayer.prepare();
songPlayer.setLooping(true);
voicePlayer.setDataSource(voice);
voicePlayer.setLooping(true);
voicePlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer voicePlayer) {
voicePlayer.start();
}
});
voicePlayer.prepare();
voicePlayer.setVolume(0.5f,0.5f);
}
catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
catch (IllegalMonitorStateException e)
{
e.printStackTrace();
}
//THIS IS WHERE I THINK THE ERROR IS :
//WHERE `mp` IS LOOPED WITH DELAY OF 5000ms
try {
final Handler hlr = new Handler();
final Runnable looper = new Runnable() {
#Override
public void run() {
if (mp != null) {
if (mp.isPlaying()) {
mp.stop();
}
}
mp.start();
}
};
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
hlr.postDelayed(looper, 5000);
}
});
}
catch(IllegalStateException e)
{
e.printStackTrace();
}
}
}
And there is also One method which restarts the activity after a specified time. And the code for that Activity is :
public void snoozeup(View view)
{
SharedPreferences sa = getSharedPreferences("SnoozeList", Context.MODE_PRIVATE);
int snox = sa.getInt("SnoozeX",0);
if(snox==2) {
Handler handler = new Handler();
Runnable x = new Runnable() {
#Override
public void run()
{
Intent intent = new Intent(Time_Date.this, Time_Date.class);
intent.putExtra("finisher", state);
startActivity(intent);
}
};
handler.postDelayed(x, 120000);
}
else
{
Handler handler = new Handler();
Runnable x = new Runnable() {
#Override
public void run() {
startActivity(new Intent(Time_Date.this, Time_Date.class));
}
};
handler.postDelayed(x, 180000);
}
try
{
if (mp != null && mp.isPlaying())
{
Log.d("TAG------->", "mp is running");
mp.stop();
Log.d("TAG------->", "mp is stopped");
mp.reset();
mp.release();
Log.d("TAG------->", "mp is released");
mp=null;
}
if (voicePlayer != null && voicePlayer.isPlaying()) {
Log.d("TAG------->", "voiceplayer is running");
voicePlayer.stop();
Log.d("TAG------->", "voiceplayer is stopped");
voicePlayer.release();
Log.d("TAG------->", "voiceplayer is released");
}
if (songPlayer != null && songPlayer.isPlaying()) {
Log.d("TAG------->", "songplayer is running");
songPlayer.stop();
Log.d("TAG------->", "songplayer is stopped");
songPlayer.release();
Log.d("TAG------->", "songplayer is released");
}
}
catch(Exception e)
{
e.printStackTrace();
}
finish();
}
The problem is with mp . Other instances of MediaPlayer gets stopped. But not mp. And sadly, Can't see any error in logcat as well. So where am I doing wrong? Any help will be appreciated.
In order to stop the music you should put this code in the onPause() method.
This method can be overrided from your class that inherite from Activity
#Override
protected void onPause() {
super.onPause();
mp.stop
}
You can also take a look at Managing the Activity Lifecycle to better understand how an activity is managed by Android.
You are destroying your activity, but you need to stop/destroy your media player too. Please write down the following code in your activity.
#Override
public void onDestroy() {
super.onDestroy();
if (mp!= null) {
//destory ur object here.
}
}
Put in either onPause() or onDestroy() Example:
#Override
protected void onPause() {
super.onPause();
mp.stop();
}
And how should i solve it ?
This is my button click method that i call it from inside onCreate:
public void addListenerOnButton()
{
btnClick = (Button) findViewById(R.id.checkipbutton);
btnClick.setOnClickListener(new OnClickListener()
{
byte[] response = null;
#Override
public void onClick(View arg0)
{
text = (TextView) findViewById(R.id.textView2);
Thread t = new Thread(new Runnable()
{
#Override
public void run()
{
for (int i = 0; i < ipaddresses.length; i++)
{
try
{
response = Get(ipaddresses[i]);
if (response == null)
{
text.setText("Connection Failed: " + generateRunnablePrinter(i));
}
}
catch (Exception e)
{
String err = e.toString();
}
if (response != null)
{
try
{
final String a = new String(response, "UTF-8");
text.post(new Runnable()
{
#Override
public void run()
{
text.setText(a);
}
});
iptouse = ipaddresses[i].substring(0, 26);
connectedtoipsuccess = true;
Logger.getLogger("MainActivity(inside thread)").info(a);
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
Logger.getLogger("MainActivity(inside thread)").info("encoding exception");
}
Logger.getLogger("MainActivity(inside thread)").info("test1");
break;
}
else
{
}
}
}
});
t.start();
}
});
}
At this place in the method inside the FOR loop the variable 'i' should be final:
text.setText("Connection Failed: " + generateRunnablePrinter(i));
But since 'i' is also the variable of the FOR loop i can't make it final.
So i added the method : generateRunnablePrinter
private Runnable generateRunnablePrinter(final int value)
{
return new Runnable() {
public void run()
{
text.setText("Connection Failed: " + ipaddresses[value]);
}
};
}
But now using this method I'm getting the exception:
ViewRootImpl$CalledFromWrongThreadException
You can't change the UI "text of the TextView" from another thread so you can try AsyncTask to do the work in the background in doInBackground() method then change the UI in the method onPostExecute().
Check out the AsyncTask:
http://developer.android.com/reference/android/os/AsyncTask.html
Since you're adjusting your UI, you'll need to run it from the UI thread. I'd recommend this function:
runOnUiThread(new Runnable() {
#Override
public void run() {
// Your code here...
}
});
If you're calling it from anywhere else but an Activity, you'll need to either pass down the activity or get the activity using getActivity() (i.e. from a fragment), and then call the function from the activity, i.e. getActivity().runOnUiThread() { ... }
Handler hnd = new Handler() {
#Override
public void handleMessage(Message msg) {
int id = sequence.get(msg.arg1);
if(msg.arg1 % 2 == 0) {
sq.get(id-1).setBackgroundResource(R.drawable.square_show);
} else {
sq.get(id-1).setBackgroundResource(R.drawable.square);
}
}
};
#Override
public void onResume() {
super.onResume();
Thread background = new Thread(new Runnable() {
public void run() {
try {
for(int i = 0; i < sequence.size()-1; i++) {
record_tv.setText(""+i);
Thread.sleep(200);
Message msg = hnd.obtainMessage();
msg.arg1 = i;
msg.sendToTarget();
}
} catch(Throwable t) {
}
}
});
background.start();
}
[CODE UPDATED] now it goes through the first loop and stops
do you have any idea why the code in the first runOnUiThread gets executed but it doesn't do what i want?
what i want is: change the image to "square", wait 2 seconds, change the image to "square_show", wait 2 secs and repeat the loop
i've been struggling for an hour now...
You can easily set image using following code.
sq.get(id-1).setImageResource(R.drawable.square_show);
sq.get(id-1).setImageResource(R.drawable.square);
public void show(int size) {
// CICLE THROUGH EACH SQUARE
for(int i = 0; i <= size-1; i++) {
Thread thrd = new Thread() {
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
sq.get(id-1).setImageResource(R.drawable.square_show);
// System.out.println("1st..........");
try {
sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sq.get(id-1).setImageResource(R.drawable.square);
// System.out.println("2nd..........");
try {
sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
};
thrd.start();
}
}
This is a wrong way to achieve it. This may help you.
http://developer.android.com/guide/topics/graphics/2d-graphics.html#tween-animation
I would suggest you to use a handler
int drawablebkg[] ={R.drawable.ic_launcher,R.drawable.icon};
Handler m_handler;
Runnable m_handlerTask ;
ImageView iv;
iv = (ImageView)findViewById(R.id.imageView1);
m_handler = new Handler();
m_handlerTask = new Runnable()
{
#Override
public void run() {
iv.setImageResource(android.R.color.transparent);
if(i<2)
{
ivsetBackgroundResource(drawablebkg[i]);
i++;
}
else
{
i=0;
}
m_handler.postDelayed(m_handlerTask, 2000);
}
};
m_handlerTask.run();
In onPause() of your activity
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
//_t.cancel();
m_handler.removeCallbacks(m_handlerTask);
}
Another way
iv= (ImageView)findViewById(R.id.imageView1);
AnimationDrawable animation = new AnimationDrawable();
animation.addFrame(getResources().getDrawable(R.drawable.ic_launcher), 2000);
iv.setImageResource(android.R.color.transparent);
animation.addFrame(getResources().getDrawable(R.drawable.icon), 2000);
animation.setOneShot(false);
iv.setBackgroundDrawable(animation);
//set setBackgroundDrawable(animation) is decprecreated i guess. not sure in which api
// start the animation!
animation.start();
Another way
Define background.xml in drawable folder
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="#drawable/ic_launcher" android:duration="2000" />
<item android:drawable="#drawable/icon" android:duration="2000" />
</animation-list>
I your activity onCreate();
ImageView iv = (ImageView)findViewById(R.id.imageView1);
iv.setBackgroundResource(R.drawable.background);
AnimationDrawable animation= (AnimationDrawable)loadingRaven.getBackground();
loadingRaven.setImageResource(android.R.color.transparent);
animation.start();
Note to stop the animation you need to call animation.stop()
Because the resource changes in the UI thread and you are sleeping your background thread. The UI thread is running normally.
Use handlers:
public class MainActivity extends Activity {
Button b;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
b = (Button) findViewById(R.id.button1);
}
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.arg1 % 2 == 0) {
b.setBackgroundResource(R.drawable.analytic_icon);
} else {
b.setBackgroundResource(R.drawable.ic_launcher);
}
}
};
#Override
public void onResume() {
super.onResume();
Thread background = new Thread(new Runnable() {
public void run() {
try {
for (int i = 0; i < 20; i++) {
Thread.sleep(2000);
Message msg = handler.obtainMessage();
msg.arg1 = i;
msg.sendToTarget();
}
} catch (Throwable t) {
// just end the background thread
}
}
});
background.start();
}
}
Try this,It will work:
public void show(final int size) {
Thread thrd = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i <= size - 1; i++) {
id = (Integer) sequence.get(i);
runOnUiThread(new Runnable() {
#Override
public void run() {
sq.get(id - 1).setBackgroundResource(
R.drawable.square_show);
}
});
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
#Override
public void run() {
sq.get(id - 1).setBackgroundResource(
R.drawable.square);
}
});
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thrd.start();
}
Handler hnd = new Handler() {
#Override
public void handleMessage(Message msg) {
int id = sequence.get(msg.arg1);
if(msg.arg1 % 2 == 0) {
sq.get(id-1).setBackgroundResource(R.drawable.square_show);
} else {
sq.get(id-1).setBackgroundResource(R.drawable.square);
}
}
};
#Override
public void onResume() {
super.onResume();
Thread background = new Thread(new Runnable() {
public void run() {
try {
for(int i = 0; i <= sequence.size()-1; i++) {
Thread.sleep(200);
Message msg = hnd.obtainMessage();
msg.arg1 = i;
msg.setTarget(hnd); // EDITED
msg.sendToTarget();
record_tv.setText(""+i);
}
} catch(Throwable t) {
}
}
});
background.start();
}
the code arrives to msg.sendToTarget(), does its things and then never came back
sendToTarget(); throws a null pointer exception it you have no set a receiver with setTarget(Handler).
Also, in your code I see
record_tv.setText(""+i);
this line, inside your thread will throw a
android.view.ViewRoot$CalledFromWrongThreadException