0

For a musical application (a sequencer), I have a few buttons

static ArrayList<Button> Buttonlist = new  ArrayList<Button>(); 

    Buttonlist.add(0,(Button) findViewById(R.id.case_kick1));
    Buttonlist.add(1,(Button) findViewById(R.id.case_kick2));
    Buttonlist.add(2,(Button) findViewById(R.id.case_kick3));
    Buttonlist.add(3,(Button) findViewById(R.id.case_kick4));
    Buttonlist.add(4,(Button) findViewById(R.id.case_kick5));
    Buttonlist.add(5,(Button) findViewById(R.id.case_kick6));
    Buttonlist.add(6,(Button) findViewById(R.id.case_kick7));
    Buttonlist.add(7,(Button) findViewById(R.id.case_kick8)); 

that I activate through onClickListeners with for example

Buttonlist.get(0).setActivated(true);

I played the activated sound via a loop and ifs :

if (Buttonlist.get(k).isActivated()) {mSoundManager.playSound(1); Thread.sleep(250);}

The app played fine but I couldn't access the play/pause button when it was playing : I searched and found out about AsyncTasks.

I have a nested class PlayPause :

class PlayPause extends AsyncTask<ArrayList<Button>,Integer,Void>

in which I have this :

protected Void doInBackground(ArrayList<Button>... params) {

    for(int k=0;k<8;k++)
    {
    boolean isPlayed = false;

    if (Buttonlist.get(k).isActivated()) {
    mSoundManager.playSound(1); 
    isPlayed = true;
    try {Thread.sleep(250);
    } 
    catch (InterruptedException e) 
    {e.printStackTrace();}}

    if(!isPlayed){
    try {Thread.sleep(250);}
    catch (InterruptedException e) 
    {e.printStackTrace();}
    }

   }
 return null;

}

I launch it via

   Play.setOnClickListener(new PlayClickListener());

with PlayClickListener :

public class PlayClickListener implements OnClickListener {

private Tutorial acti;


public PlayClickListener(){
    super();
    }

    @SuppressWarnings("unchecked")
    public void onClick(View v) {
    if(Tutorial.play==0){
        Tutorial.play=1;
        Tutorial.Play.setBackgroundResource(R.drawable.action_down);
        acti = new Tutorial();
    Tutorial.PlayPause myPlayPause = acti.new PlayPause();
    myPlayPause.execute(Tutorial.Buttonlist);
    }
    else
    {
    Tutorial.play=0;
    Tutorial.myPlayPause.cancel(true);
    Tutorial.Play.setBackgroundResource(R.drawable.action);
    }
}

 }

But it doesn't work. when I click on buttons and I touch Play/Pause, I have this :

07-02 11:06:01.350: E/AndroidRuntime(7883): FATAL EXCEPTION: AsyncTask #1
07-02 11:06:01.350: E/AndroidRuntime(7883): Caused by: java.lang.NullPointerException
07-02 11:06:01.350: E/AndroidRuntime(7883):     at com.Tutorial.Tutorial$PlayPause.doInBackground(Tutorial.java:603)
07-02 11:06:01.350: E/AndroidRuntime(7883):     at com.Tutorial.Tutorial$PlayPause.doInBackground(Tutorial.java:1)

And I don't know how to get rid of this error. Obviously, the Asynctask doesn't find the Buttonlist activated status, even if the list is static. I don't know how to access these buttons' states, isPressed doesn't work either.

Thanks for reading and helping me !

  • Where is line 603 in the code? – Jimbali Jul 02 '12 at 09:43
  • line 603 is `if (Buttonlist.get(k).isActivated()) {mSoundManager.playSound(1); Thread.sleep(250);}` – Intern John Smith Jul 02 '12 at 09:45
  • I don't think this is the problem but you seem to be passing in the ArrayList as a parameter and then just using the original reference instead of the parameter. – Jimbali Jul 02 '12 at 09:52
  • The ArrayList – 10s Jul 02 '12 at 10:54
  • @10s : But even when I use it (see the answer below), I get a nullPointerException because the if condition with isActivated doesn't work. – Intern John Smith Jul 02 '12 at 13:10
  • ok, passing the reference of a static parameter is wrong by its core. You should make the changes to the same static variable, including all the appropriate locks and stuff. So you are sure that you change the setActivated property of the button before you call the AsyncTask, correct? – 10s Jul 02 '12 at 13:21
  • I have for each button : `Buttonlist.get(1).setOnClickListener(new OnClickListener() { int on =0; public void onClick(View v) { if(on==0){ Buttonlist.get(1).setBackgroundResource(R.drawable.bouton_down); Buttonlist.get(1).setActivated(true); on=1; } else{ Buttonlist.get(1).setBackgroundResource(R.drawable.bouton); Buttonlist.get(1).setActivated(false); on=0; }` The AsyncTask is called after the pattern of sound is done. – Intern John Smith Jul 02 '12 at 14:02
  • Man I am totally curious why this doesn't work. ok try something in the debugger and post the results: debug line in the if statement and "watch" the 2 arrays of the buttons, the one inside async task and the static variable on top. – 10s Jul 02 '12 at 15:14

1 Answers1

1

To access the ButtonList from the AsyncTask, you must refer to it by the name of the parameter passed into the AsyncTask, in this case params. With an AsyncTask, the params passed in is always an Array of the type specified in between the <....> and to access it (if you are only passing in one array list) would be like this...

ArrayList<Button> Buttonlist = params[0];

You can then access it from your AsyncTask. Although I would suggest giving it a different name just incase there is any conflicts.

To clarify, you can pass as many objects as you want into an AsyncTask, comma seperated, and the system takes care of packaging them into an Array which it hands to the AsyncTask. In your case you a passing in only one array list, so the async task is being handed an array with only one object in it. So you would look in the first space of the params array, hence params[0]

This is all a little confusing, but it's because the system is managing a thread pool for you and alot of the implementation is hidden.

Sam Clewlow
  • 4,293
  • 26
  • 36
  • I did what you said : `ArrayList – Intern John Smith Jul 02 '12 at 13:01
  • Hmmm, I'm not really sure what you are trying to achieve with all this. Why are you passing in the buttons? When you start the AsyncTask, you are handing it static data, so it's going to loop 8 times, possibly play each sound once (if it's activated) then return. If this is the behaviour you want, why not work out the state of each button on the main thread, then pass an array of corresponding booleans into asyncTask? – Sam Clewlow Jul 02 '12 at 15:11
  • Buttons are UI elements, and you shouldn't ever try and access any UI elements from a background thread (async task has special synchronized methods to communicating back to the UI onProgressUpdate()). Also remember that if you are planning on repeating this process, you will need to create a new instance of the AsyncTask class each time (reusing an asyncTask instance will crash your program too) – Sam Clewlow Jul 02 '12 at 15:12
  • I'm trying to do something similar to this : http://www.youtube.com/watch?v=3q8CMAOdRWQ I use asynctask because I give the ability to loop those 8 times with a while(repeat==true){play} and when I'm doing it on the main thread, the sounds are looping but I can't stop the while loop since the app is waiting for the loop to finish... But I don't know if this is the right way to do it. Doing this will create a new Asynctask every second and kill the precedent one... I will try to pass an array of corresponding booleans, it's a good idea. – Intern John Smith Jul 02 '12 at 15:56
  • could you paste up your whole class? In general I've found that using an AsyncTask for this kind of thing will turn out to be more hassle than it's worth. You might consider kicking off threads manually, using Runnables and spawning a new thread for each sound? If you can post up the whole class somewhere that would help me understand it a bit better – Sam Clewlow Jul 03 '12 at 08:59
  • I used Threads and a runnable while using arrays of booleans, and it works perfectly ! thanks ! – Intern John Smith Jul 03 '12 at 09:22
  • Well perfectly ... The timing isn't sharp, but I've read that android's sound libraries weren't very reliable... – Intern John Smith Jul 03 '12 at 09:25
  • great! This stuff can be a real headache sometimes – Sam Clewlow Jul 03 '12 at 09:43