0

I'm trying to use a spinning ProgressDialog to tell the user when the program is working. I have gotten the ProgressDialog to show and dismiss correctly, however only when there is simple code in between the calls. I am currently initializing the ProgressDialog within my onCreate method, then upon a button press, the dialog will show, then a lot of code is traversed through, followed by a dismiss call. The code all executes properly, but the dialog is never shown.

public class Waves extends Activity {
    private ProgressDialog pd;
    public void onCreate( Bundle savedInstanceState ) {
        super.onCreate( savedInstanceState );
        pd = new ProgressDialog( this );  
        pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        pd.setMessage("Reading...");
        pd.setCancelable(false);
        setContentView( R.layout.waves );

        readButton = (Button) findViewById( R.id.readButton );
        readButton.setOnClickListener( new OnClickListener() {

        public void onClick(View arg0) {
            pd.show( );
            if( myService.getState() != 3 ) {
                myService.setHandler( mHandler );
                myService.connect( MainMenu.previousDevice, true );
            } else {
                readWaves();
            }
        }
    });

}

readWaves is shown below. At the end of readWaves is where pd.dismiss() is called. There are many function calls from within readWaves which I will try to show without it getting too lengthy.

private void readWaves() {
    if( spinnerChoice == 0 ) {
        Log.i( "IN IF", "IN IF" );
        // Diff Voltage
        waveResults = RelayAPIModel.NativeCalls.GetWavesJava( RelayAPIModel.WAVES_V );
    } else {
        // Current 
        waveResults = RelayAPIModel.NativeCalls.GetWavesJava( RelayAPIModel.WAVES_I );
    }

    updateGraph();
    netV1Check.setChecked( true );
    netV2Check.setChecked( true );
    netV3Check.setChecked( true );
    vdi1Check.setChecked( true );
    vdi2Check.setChecked( true );
    vdi3Check.setChecked( true );
    hasRead = true;
    pd.dismiss();
}

The updateGraph() call does simple cosmetics to the graphs, so I don't think the problem is there. The RelayAPIModel.NativeCalls.GetWavesJava() call is where I think the problems arise.

GetWavesJava() is a function written in C located in my native library. From within GetWavesJava(), there are a large number of function calls to other native C functions, as well as a large number of function calls back to the Java end of my application.

This sounds stupid, but the reason for this is that the native library in C is an existing library which needs to be used. However, it does not support Android's bluetooth capabilities, so all methods which send/receive data via Bluetooth need to be described on the Java end.

There are six total Java functions used for the I/O stream, and they are used many many times throughout the application, not just for this one call. I considered putting pd.dismiss() in one of those functions, but it is only needed like 1% of the time those functions are called.

Does anyone know a solution to ensure that my dialog is shown before native function calls, and stops after?

This is a similar SO question but not exactly the same

EDIT :

Using the answer below, this is my code:

readButton = (Button) findViewById( R.id.readButton );
readButton.setOnClickListener( new OnClickListener() {

        public void onClick(View arg0) {

            pd.show( );
            Thread export = new Thread() {  
                 public void run() {
                        if( myService.getState() != 3 ) {
                            Log.i( "myService", "12222" );
                            myService.setHandler( mHandler );
                            myService.connect( MainMenu.previousDevice, true );
                        } else {
                            Log.i("myService",  "32222" );
                            readWaves();
                            Log.i( "myService", "42222" );
                        }

                 mHandler.obtainMessage(MainMenu.READ_FINISHED );
                 }
            };
            export.start();
                        }
    });

The Log( 42222 ) is never reached. The spinner does appear, but the program crashes and restarts in a few seconds. I have added .sendToTarget() in the mHandler.obtainMessage() line, but this results in no spinner appearing, and the program crashing.

EDIT 2 :

I would also like to add that the readWaves function uses existing running threads to execute. I'm not sure if that makes any difference

EDIT 3 :

Here I updated the second code block from the initial submission. Using this code, the progress dialog never shows itself. The code executes as all code involving the ProgressDialog was not there at all.

private void readWaves() {
    if( spinnerChoice == 0 ) {
        Log.i( "IN IF", "IN IF" );
        // Diff Voltage
        waveResults = RelayAPIModel.NativeCalls.GetWavesJava( RelayAPIModel.WAVES_V );
    } else {
        // Current 
        waveResults = RelayAPIModel.NativeCalls.GetWavesJava( RelayAPIModel.WAVES_I );
    }

    pd.dismiss();
    updateGraph();
    netV1Check.setChecked( true );
    netV2Check.setChecked( true );
    netV3Check.setChecked( true );
    vdi1Check.setChecked( true );
    vdi2Check.setChecked( true );
    vdi3Check.setChecked( true );
    hasRead = true;

}

EDIT 4: This is my most recent code. The ProgressDialog shows up where I want it to but the spinner does not spin. I have tried replacing pd.dismiss() with pd.hide() and putting pd.dismiss() later in the code but this results in the dialog always staying on the screen. And since .setCancelable(false) is used I can never back out of it. I have also taken the declaration of pd outside of the onClickListener because I cannot pass the argument this to the ProgressDialog within the OnClickListener. I don't remember what I can pass to it to make it work, I tried getApplicationContext(), ClassName.java and ClassName.class but none of them worked.

    readButton = (Button) findViewById( R.id.readButton );
    final ProgressDialog pd = new ProgressDialog( this );  
    readButton.setOnClickListener( new OnClickListener() {
        public void onClick(View arg0) {
            pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            pd.setMessage("Reading...");
            pd.setCancelable(false);            
            pd.show( );

            Thread export = new Thread() {  
                 public void run() {
                        if( myService.getState() != 3 ) {
                            Log.i( "myService", "12222" ); 
                            myService.setHandler( mHandler );
                            myService.connect( MainMenu.previousDevice, true );
                        } else {
                            runOnUiThread(new Runnable() {
                                public void run() {
                                    readWaves();
                                }
                            });
                        }
                 mHandler.obtainMessage(MainMenu.READ_FINISHED ).sendToTarget();
                 pd.dismiss();
                 }
            };
            export.start(); 

EDIT 5: Trying to get the spinner to appear for the if statement:

    readButton = (Button) findViewById( R.id.readButton );
    readButton.setOnClickListener( new OnClickListener() {
        public void onClick(View arg0) {
            final ProgressDialog pd = new ProgressDialog( Waves.this );  
            pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            pd.setMessage("Reading...");
            pd.setCancelable(false);            
            pd.show( );

            Thread export = new Thread() {  
                 public void run() {
                        if( myService.getState() != 3 ) {
                            Log.i( "myService", "12222" ); 
                            myService.setHandler( mHandler );
                            runOnUiThread( new Runnable() {
                                public void run() {
                                    myService.connect( MainMenu.previousDevice, true );
                                }
                            });
                        } else {
                            runOnUiThread(new Runnable() {
                                public void run() {
                                    readWaves();
                                }
                            });
                        }
                 mHandler.obtainMessage(MainMenu.READ_FINISHED ).sendToTarget();
                 pd.dismiss();
                 }
            };
            export.start();
Community
  • 1
  • 1
JuiCe
  • 4,132
  • 16
  • 68
  • 119
  • Using your second version, I have a few questions: 1. Does readWaves() update the UI in any way, or touch ANY of the UI tree objects? 2. Did you declare your progressDialog as final? 3. Why is pd.dismiss() in readWaves() and at the end rather than in the line AFTER readWaves() in your Thread's run()? – varevarao Jan 03 '13 at 19:06
  • Actually, it all boils down to what updateGraph(); does. What DOES it do? – varevarao Jan 03 '13 at 19:09
  • @varevarao Take a look at my third edit. All UI is updated within `updateGraph()`, and not within `readWaves()`. – JuiCe Jan 03 '13 at 19:16
  • Bingo, hang on. I might've nailed your problem. – varevarao Jan 03 '13 at 19:17

2 Answers2

2

Given you've mentioned All UI is updated within updateGraph(), your code (from the second edit, which is mostly correct) is throwing the CalledFromWrongThreadException. Your updateGraph() method is called from the readWaves() method, which in turn is invoked from the new Thread() you created. This means you're trying to update certain views from a Thread which is not the actual parent of those views.

Try:

final ProgressDialog pd = new ProgressDialog( this );  
pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pd.setMessage("Reading...");
pd.setCancelable(false);            
pd.show( );
Thread export = new Thread() {  
    public void run() {
        if( myService.getState() != 3 ) {
            Log.i( "myService", "12222" );
                myService.setHandler( mHandler );
                myService.connect( MainMenu.previousDevice, true );
        } else {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    readWaves();
                }
            });
            pd.dismiss();
        }
        mHandler.obtainMessage(MainMenu.READ_FINISHED );
    }
}.start();

The runOnUiThread() call ensures that the UI updates being done by updateGraph() from within readWaves() is done ON the UI Thread. Oh and you can remove the pd.dismiss() at the end of readWaves().

EDIT: For the spinner not spinning, there's a question here. Solution says:

If you dismiss a Dialog, you must not show the same instance again. Either create a new one, or use hide() instead of dismiss(). When using hide() you still have to dismiss() it when no longer needed.

EDIT:

Also notice in the above code I've moved the declaration of the ProgressDialog (in your case it was global) to the local context of the onClick() event handler. This means a new ProgressDialog is created for a new click event. Let me know if you've divulged from this pattern in any way.

I don't remember what I can pass to it to make it work, I tried getApplicationContext(), ClassName.java and ClassName.class but none of them worked.

Try ClassName.this.

Community
  • 1
  • 1
varevarao
  • 2,186
  • 13
  • 26
  • Thank you, this is like 95% of what I want. The progress dialog shows up and disappears once its done reading. However, the spinner in the dialog doesn't spin. It sits at the initial state. Any ideas on that? – JuiCe Jan 03 '13 at 19:42
  • Ah, actually this isn't working completely. The first time I tried it the spinner appeared for the full period it should have. But now, every time I expect it to show up it only flashes on the screen at the very end. – JuiCe Jan 03 '13 at 19:46
  • OK wait, let me fool around a bit here sorry. – JuiCe Jan 03 '13 at 19:47
  • @JuiCe, try putting pd.dismiss() inside the runOnUiThread() method, after readWaves() and let me know if that works. – varevarao Jan 03 '13 at 19:54
  • I don't know whats going on. I'm trying a bunch of different variations using `hide()`, `dismiss()`, or both, but the best I get is the dialog showing without the spinner spinning. I need to continue messing around to narrow down what my problems are. I will get back to you in a little bit. Thanks for the help though. – JuiCe Jan 03 '13 at 20:31
  • Okay, sure. But just give the last suggestion a try: _try putting pd.dismiss() inside the runOnUiThread() method, after readWaves()_. Probably because the progress dialog was made in the UI Thread, dismissing it there would ensure it is closed. – varevarao Jan 03 '13 at 20:34
  • I have it there. But still the spinner doesn't spin. Not sure what to do. I have tried using `hide()` instead of `dismiss()` but that doesn't work. Any other ideas? – JuiCe Jan 03 '13 at 21:43
  • @JuiCe, added another edit. Could you post your latest code trial in the question now? – varevarao Jan 04 '13 at 05:38
  • Thanks, just added another edit. Seems to working other than the spinner actually spinning. I realized that the problem I was having was Toast Dialogs from previous activities which halted the ProgressDialog – JuiCe Jan 04 '13 at 14:57
  • I have followed everything you said and it works as of now without the Spinner spinning. However, I also want the spinner to work in the `if` part of the `if/else` statement. I added the `runOnUiThread` part to it but it still does not work. It flashes quickly across the screen then disappears. It is in my last edit. – JuiCe Jan 04 '13 at 21:08
0

This may be asynchrony issue, try to show dialogue then use async Handler to fire long task (your native calls). I had to deal with this as well because sometimes things like "dismiss"/"show" post into message queue and then you block it with long task. Try to use thread for long tasks and don't block UI

Look at my blog post here:

Progress Dialogue in Android

itadapter DKh
  • 596
  • 3
  • 7
  • I gave it a shot but it didn't work out too smoothly. I edited my code if you'd like to take a look. Thanks anyway – JuiCe Dec 28 '12 at 19:28
  • If 4222 is not shown then you have definitely a bug in native/C guts of your code (inside readWaves()). My advice to you is to instrument all of your native C code with something like: #include ......in code: __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:MyApp: [%s]", myInteger); – itadapter DKh Dec 28 '12 at 19:50
  • I do not think it is a bug in my C code. The code works 100% fine without a `ProgressDialog`. – JuiCe Dec 28 '12 at 19:51
  • You dismiss dialogue by: handler.sendEmptyMessage(0); and then handling like: public void handleMessage(Message msg) { dialog.dismiss(); } }; – itadapter DKh Dec 28 '12 at 19:52
  • If you see 3222 but not 4222 per your code then readWaves() is to blame? – itadapter DKh Dec 28 '12 at 19:53
  • Also I see now some service reference, keep in mind that usually people make wrong assumptions in the way the services get bound, in other words, CONNECT guarantees nothing if your service has multiple threads that take time to start - so maybe you assume that service is there but it is NOT there yet logically. I always use my own STATE flag on all services that get set to READY only after complete logical initialization. So once again, this looks like asynch issue to me – itadapter DKh Dec 28 '12 at 19:56
  • Yes, readWaves() is where it fails. But it works correctly. It doesn't work when this thread is implemented with it. `connect()` also works correctly, but I have been testing the code in a way to avoid the `connect()` call all together. It never touches `connect()`. I'm not saying your implementation is wrong but I do not think it will work alongside my project as is. – JuiCe Dec 28 '12 at 19:59