31

I am developing an Android app and I am doing some heavy work (bringing data from an online web page and parsing it to store in database) in a service. Currently, it is taking about 20+ mins and for this time my UI is stuck. I was thinking of using a thread in service so my UI doesn't get stuck but it is giving error. I am using the following code:

Thread thread = new Thread()
{
      @Override
      public void run() {
          try {
              while(true) {
                  sleep(1000);
                  Toast.makeText(getBaseContext(), "Running Thread...", Toast.LENGTH_LONG).show();
              }
          } catch (InterruptedException e) {
           Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG).show();
          }
      }
  };

thread.start();

This simple code is giving run time error. Even If I take out the while loop, it is still not working. Please, can any one tell me what mistake I am doing. Apparently, I copied this code directly from an e-book. It is suppose to work but its not.

Cristian
  • 198,401
  • 62
  • 356
  • 264
U.P
  • 7,357
  • 7
  • 39
  • 61

4 Answers4

43

Android commandment: thou shall not interact with UI objects from your own threads

Wrap your Toast Display into runOnUIThread(new Runnable() { });

Sam R.
  • 16,027
  • 12
  • 69
  • 122
Konstantin Pribluda
  • 12,329
  • 1
  • 30
  • 35
34

Example of new thread creation taken from Android samples (android-8\SampleSyncAdapter\src\com\example\android\samplesync\client\NetworkUtilities.java):

public static Thread performOnBackgroundThread(final Runnable runnable) {
    final Thread t = new Thread() {
        @Override
        public void run() {
            try {
                runnable.run();
            } finally {

            }
        }
    };
    t.start();
    return t;
}

runnable is the Runnable that contains your Network operations.

Zelimir
  • 11,008
  • 6
  • 50
  • 45
  • This appears to be a function which means it needs to be called. should I call is like: performOnBackgroundThread( new Runnable (){ ....? in onCreate() of service? – U.P Nov 14 '10 at 18:15
  • You do not need to use it as a function. You can use it like you originally did. Important thing is that you create new Runnable and put code that communicates with server into it. So, you cannot just put new Runnable() as an argument of the function performOnBackgroundThread(), you need to define new Runnable as separate entity (function). – Zelimir Nov 14 '10 at 18:49
  • The original code starts a thread too. This answer isn't doing anything different. But at least we know it's officially allowed to start a thread :) – qris Dec 06 '12 at 00:56
  • 2
    Good to know that google has example at usage of service and thread. Thanks. – Houcheng Jan 06 '13 at 06:31
  • 5
    I don't know why this answer is upvoted so much. It is not any different to create a thread in android vs creating it in Java. The problem with OP's code (as Konstantin pointed out) lay in using the Toast from a background thread - this answer did not recognize this nor show a solution on how to do it. – likejudo Sep 08 '14 at 17:27
  • Fully agreed with you! – Zelimir Sep 09 '14 at 06:35
  • You can pass the `Runnable` directly to the `Thread` constructor. There is no need for the wrapper class. Maybe Android's `Thread` class is lacking in that regard. – MauganRa Feb 22 '17 at 11:25
  • likejudo is right. did you try commenting out the maketoast method and use just the android logging to see if it works. – Miten Dec 04 '18 at 12:51
14

You can use HandlerThread and post to it, here is an example to service that has one.

public class NetworkService extends Service {

    private HandlerThread mHandlerThread;
    private Handler mHandler;
    private final IBinder mBinder = new MyLocalBinder();

    @Override
    public void onCreate() {
        super.onCreate();

        mHandlerThread = new HandlerThread("LocalServiceThread");
        mHandlerThread.start();

        mHandler = new Handler(mHandlerThread.getLooper());
    }

    public void postRunnable(Runnable runnable) {
        mHandler.post(runnable);
    }

    public class MyLocalBinder extends Binder {
        public NetworkService getService() {
            return NetworkService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
Joe McBride
  • 3,789
  • 2
  • 34
  • 38
oznus
  • 2,446
  • 2
  • 25
  • 18
3

You may define your jobs in a runnable object, use a thread object for running it and start this thread in your service's onStartCommand() function. Here is my notes:

In your service class:

  1. define your main loop in an Runnable object
  2. create Thread object with the runnable object as parameter

In your service class's onStartCommand method():

  1. call thread object's start function()

my code :

private Runnable busyLoop = new Runnable() {
    public void run() {
        int count = 1;
        while(true) {
            count ++;
            try {
                Thread.sleep(100);
            } catch (Exception ex) {
                ;
            }
            ConvertService.running.sendNotification("busyLoop" + count);                      
        }
    }
};

public int onStartCommand(Intent intent, int flags, int startId) {
    sendNotification("onStartCommand");
    if (! t.isAlive()) {
        t.start();
    }
    return START_STICKY;
}
Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
Houcheng
  • 2,674
  • 25
  • 32