Xch3l
Xch3l

Reputation: 21

How to *not* use runOnUiThread

As you can see in the title, I'm trying to not use runOnUiThread each time I need it. I'll explain what I mean later, but first, here is my current code (part of):

private void filePaste()
{
  final File src = new File(FileClip); //Source file (as a string)
  final File dest = new File(myPath.getText()+"/"+src.getName()); //Destination file
  final ProgressDialog dlg = new ProgressDialog(AppContext);

  final Thread copythread = new Thread(new Runnable() {
    public void run() {
      if(FileClip==null)
        Main.this.runOnUiThread(new Runnable() {
          public void run(){
            toast(getString(R.string.msg_EmptyClip));
          }
        });
      else
      {
        if(src.canRead()){
          if(!src.isDirectory())
          {
            try{
              BufferedInputStream in = new BufferedInputStream(new FileInputStream(src), 8192);
              BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));

              long pos = 0;
              long read = 8192;
              byte[] data = new byte[(int)read];

              while(pos<src.length()){
                int bytes = in.read(data, 0, (int)read);
                if(bytes>-1) {
                  out.write(data,0,bytes);
                  in.skip(read);
                  in.mark((int)pos+bytes);
                  in.reset();
                  dlg.incrementProgressBy(bytes);
                  pos+=bytes;
                }
              }

              Main.this.runOnUiThread(new Runnable() {
                public void run(){
                  dlg.dismiss();
                }
              });
              in.close();
              out.close();
            }catch(final Exception e)
            {
              Main.this.runOnUiThread(new Runnable() {
                public void run(){
                  alert("filePaste():\n"+e);
                }
              });
            }

            if(Moving==true) {
              boolean q = src.delete();

              if(q==true)
                Main.this.runOnUiThread(new Runnable() {
                  public void run(){
                    toast(getString(R.string.msg_MoveOK,src.getName()));
                  }
                });
              else
                Main.this.runOnUiThread(new Runnable() {
                  public void run(){
                    toast(getString(R.string.msg_MoveNO,src.getName()),1);
                  }
                });

              Moving = false;
            }
            else {
              Main.this.runOnUiThread(new Runnable() {
                public void run(){
                  toast(getString(R.string.msg_CopyOK,src.getName()));
                }
              });
            }
          }
        }
        else
          Main.this.runOnUiThread(new Runnable() {
            public void run(){
              toast(getString(R.string.msg_CopyNO,src.getName()));
            }
          });
        FileClip = null;
        Main.this.runOnUiThread(new Runnable() {
          public void run(){
            getDir(myPath.getText().toString());
          }
        });
      }
    }
  });

  dlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  dlg.setTitle(Moving?getString(R.string.moving):getString(R.string.copying));
  dlg.setMessage(src.getName());
  dlg.setCancelable(true);
  dlg.setButton(DialogInterface.BUTTON_NEGATIVE, getString(android.R.string.cancel), new DialogInterface.OnClickListener()
  {
    @Override
    public void onClick(DialogInterface dialog, int which) {
      dlg.cancel();
    }
  });
  dlg.setOnCancelListener(new DialogInterface.OnCancelListener()
  {
    @Override
    public void onCancel(DialogInterface dialog) {
      copythread.interrupt();
      if(dest.exists())
        dest.delete();
    }
  });
  dlg.setMax((int)src.length());
  dlg.show();

  copythread.start();
}

What this code does is try to copy a given file (stored as a string in FileClip).

As you can see, the code is quite long, because the excessive use of runOnUiThread. I want to know how to move all of the

Main.this.runOnUiThread(new Thread(new Runnable()
  public void run()
  {
    //Simple stuff for 6 lines
  }
));

to a class or something. I was thinking of

public class runAtUI implements Runnable

But I stop at that point, I don't know how to make constructors. I hope you know what I mean with this; something like:

new runAtUI()
{
  //This makes less lines IMO, and could be executed
}

*Note: I looked for topics about this but all saying the same, use the runOnUiThread. I know it's ok but for something short (like displaying a toast -using toast() with the same purpose- or an alert -again, but with alert()-) it's pointless.

PS: This is for a simple file manager that I am doing and will not be published on Google Play because of the lack of money, and I don't want to use ads either (breaking the "simplicity" in the name). Yes, I know that there are free and adsless (that word exists?) and better apps but I wanted to learn how to make one n_n;

Any information, idea or guide will be appreciated.


Edit #2: Thank you to all that answered quickly! The samples that you provided will work for now but I'd like it to be more flexible. Like eval("code as string") in javascript (without the code being a string). I'll consider this question answered but leave it open to let people give some more ideas ;D


Edit #3: Okay, first, sorry for the long time no response. Second, I'm adding links to the current code as it is now, from 123 lines (this one) to 77 lines of code at pastebin. I'm also adding the code for uithread. About the app: If you want it for testing, see how it is right now or anything, drop me a mail and I'll send it to you ;)

Upvotes: 0

Views: 2028

Answers (3)

Sparky
Sparky

Reputation: 8477

From http://developer.android.com/guide/components/processes-and-threads.html:

However, as the complexity of the operation grows, this kind of code can get complicated and difficult to maintain. To handle more complex interactions with a worker thread, you might consider using a Handler in your worker thread, to process messages delivered from the UI thread. Perhaps the best solution, though, is to extend the AsyncTask class, which simplifies the execution of worker thread tasks that need to interact with the UI.

... skipping ahead ...

You can call publishProgress() at anytime in doInBackground() to execute onProgressUpdate() on the UI thread

It looks to me like this is what you want to do.

With regard to your other question (you should try to ask only one question per topic), copying files in Java is kind of a solved problem. Among threads on SO, I think this one has some pretty good answers: Standard concise way to copy a file in Java?

Upvotes: 0

Alex Klimashevsky
Alex Klimashevsky

Reputation: 2495

You can create a Handler in activity:

Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
        case R.string.cancel:
            //show toast
            break;
        default:
            break;
        }
    }
};

And use it from your thread like:

handler.sendEmptyMessage(R.string.cancel);

Upvotes: 0

Nick
Nick

Reputation: 8317

You could create a utility class to encapsulate the steps of creating a runnable to be run by the UI thread and invoke Toast. Here's a simple implementation:

public abstract class ToastUtils {
      /**
       * Displays a toast message, making sure that the toast is always invoked from the main (ui) thread.
       * @param act Calling Activity
       * @param text Toast text
       * @param duration Toast duration
       */
      public static void showToast(final Activity act, final String text, final int duration) {
         act.runOnUiThread(new Runnable() {
            public void run() {
               Toast.makeText(act, text, duration).show();
            }
         });
      }
   }

Using this class you would do this to toast wherever necessary:

ToastUtils.showToast(this, "some text", Toast.LENGTH_SHORT);

Upvotes: 1

Related Questions