tags:

views:

524

answers:

2

Hi All,

Sorry my last question was not well formated. So I am deleting the content and re framing my question.

I liked @ChrisBunney answer to my question with an example to process image in another thread.

Here is proper code below

public class TestActivity extends ListActivity {

    int count =0;

    List<String> listString = null;

    String[] mString = null; 

      private RefreshHandler mRedrawHandler = new RefreshHandler();

      class RefreshHandler extends Handler {

            @Override

            public void handleMessage(Message msg) {

                TestActivity.this.updateUI();

            }



            public void sleep(long delayMillis) {

              this.removeMessages(0);

              sendMessageDelayed(obtainMessage(0), delayMillis);

            }

          };



          private void updateUI(){



              mRedrawHandler.sleep(1000);

              listString.add("Test "+count++);

              String[] mString = new String[listString.size()];

              int i = 0;

              for(String string : listString){
                  mString[i++]= string;
              }

              if(null != mString && mString.length>0)
              setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mString ));


          }



          @Override 

          public void onCreate(Bundle icicle) { 

            super.onCreate(icicle); 

            setContentView(R.layout.main);

            listString = new LinkedList<String>();


            updateUI();


          } 



}

What I intend to do is call UI update from another thread.

This will be the modification to my above code file.

private class HelperClass implements Runnable{

            public void run() {

                performSomeCalculation();

            }

            /*
             * This code will populate the list as it is executed.
             * As and when there is a new element in the list the UI is updated.
             */

            public void performSomeCalculation(){

                //update list String
                updateUI();
            }

/*




/*
           * 
           * The new update onCreate method calling another thread that populates the UI thread
           * 
           */

          @Override 

          public void onCreate(Bundle icicle) { 

            super.onCreate(icicle); 

            setContentView(R.layout.main);

            listString = new LinkedList<String>();


            //updateUI();

            Thread thread = new Thread(new HelperClass());
            thread.start();


          }

You can see in the above code I have created a HelperClass to perform some calculation , as and when there is ouput from the calculation thread it should update the UI thread.

I am working on it to make it work, if any one figures out plz update my post.

Thanks in advance.

A: 

If you want to thread blocking operations that update the UI (such as downloading an image from the internet), you should use the Handler class to create callback.

http://developer.android.com/reference/android/os/Handler.html

The Handler class will bind to the UI thread and will allow you to post objects implementing the Runnable interface to a queue for execution in the correct thread.

However, I'm not sure if it's possible to achieve what you seem to be aiming for, from the given code. Some further information about the circumstances and desired behaviour would be helpful.

A typical implementation using a Handler to update the UI, based on code from my own app that downloads a static map and updates the UI, may look like this:

public class HandlerExample extends Activity {


/**
 * Handler to allow other threads to send executable messages (in the form
 * of Runnable objects) to the main UI thread. This is used to download the
 * static image in a separate thread to stop the UI from blocking
 */
private final Handler handler = new Handler();
/**
 * Runnable implementation that, in conjunction with handler, displays a
 * given static map in the ImageView
 */
private final UpdateStaticMap updateStaticMap = new UpdateStaticMap();


@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

        //... other initialisation

    // This object implements Runnable and is used to post our update to the Handler
    updateStaticMap.setImageView((ImageView)findViewById(R.id.venuedetail_mapimage));
    // Then we pass the Runnable object, the Handler, and all the data we
    // need to get the
    // map into a Thread subclass to avoid blocking the UI thread
            // Note, the thread is started in the constructor so we don't worry about it here
    new MapGetter(handler, updateStaticMap);


}

/**
 * This private class extends Thread to handle the downloading of the static
 * map separately from the main UI thread to avoid
 * blocking it. It uses the Handler to trigger the UI thread to update the
 * ImageView when it's finished by passing updateStaticMap as a message
 * (it's a Runnable, thus will be executed by the handler in the UI thread).
 * This is necessary as only the thread that created the View components can
 * edit them.
 * 
 * @author Chris Bunney
 * 
 */
private class MapGetter extends Thread {

    /**
     * The Handler associated with the UI thread. Used to pass the map back
     * to the UI thread
     */
    private final Handler handler;
    /**
     * The Runnable implementation used to wrap the bitmap when passing it
     * back to the UI thread via handler
     */
    private final UpdateStaticMap updateStaticMap;

    /**
     * 
     * 
     * @param handler
     *            Handler for the UI thread so the MapGetter can update the
     *            ImageView with the map
     * @param updateStaticMap
     *            Runnable object that is used to send the map back to and
     *            update the UI
     **/
    public MapGetter(Handler handler, UpdateStaticMap updateStaticMap) {
        this.handler = handler;
        this.updateStaticMap = updateStaticMap;
        this.start();
    }

    /**
     * Obtains the Static Map and passes it back to the main
     * UI thread to be displayed
     */
    @Override
    public void run() {
                    //Note, the getImage() method is just a stand in for whatever code you need to get the image
        updateStaticMap.setBitmap(getImage()));
                    //Now our Runnable object has all the data it needs, we post it back to the Handler
        handler.post(updateStaticMap);

    }
}

/**
 * This class implements the Runnable interface. It acts a message to be
 * delivered to the UI thread. This class updates a given ImageView with a
 * given Bitmap. The ImageView and Bitmap form the payload of the message.
 * This class is necessary because only the thread that created a View can
 * alter it, so the object is passed to the correct thread and executed by
 * it.
 * 
 * @author Chris Bunney
 * 
 */
private class UpdateStaticMap implements Runnable {

    /**
     * The ImageView to target when setting the bitmap
     */
    private ImageView mapImage;
    /**
     * The Bitmap to update the ImageView with
     */
    private Bitmap bm;

    /**
     * Inserts the Bitmap, bm, into the ImageView, mapImage
     * 
     */
    @Override
    public void run() {
        mapImage.setImageBitmap(bm);
        Log.d(TAG, "run: Static Map Updated");
    }

    /**
     * Accessor method to insert the bitmap payload into the message
     * 
     * @param bm
     *            Static map to be displayed in the UI
     */
    public void setBitmap(Bitmap bm) {
        this.bm = bm;
        Log.d(TAG, "setBitmap: Bitmap updated");
    }

    /**
     * Accessor method to associate the object with a particular ImageView
     * 
     * @param mapImage
     *            ImageView that the map should be displayed in
     */
    public void setImageView(ImageView mapImage) {
        this.mapImage = mapImage;
        Log.d(TAG, "setImageView: ImageView updated");
    }
}
}
chrisbunney
Your thread implementation is redundant, since your runnable is an inner class and the handler is a member object.
Dylan McClung
Could you expand that a bit? I don't quite understand what you mean.If you mean the thread implementation is not required, then I disagree. The purpose of the thread implementation is to get the information required to update the UI without causing the UI to block on I/O type operations. The purpose of the runnable is to act as a message to be passed from one thread (where it is filled with data) into another thread (where that data is used). The handler is the receiver of that message
chrisbunney
@chrisbunney Thanks for the answere, I am using it to rebuild my code. I have updated my question it should be more clear now in explaining what I looking for.
Ameya
@Ameya, from looking at your updated question I can say that the approach outlined in my answer will solve your problem and allow you to achieve the functionality you desire
chrisbunney
@chrisbunney the only problem I am having is setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,strings )); must belong to UpdateStaticMap for the list to update itself. How to do that ?
Ameya
Finally got it nailed, was using ListActivity which was causing the problem , replaced it with Activity and passed List via id to a method similar to UpdateStaticMapwill put the link to the sample solution in few min
Ameya
A: 

Finally with the help of Chris's code got solution to my own question.

Plz refer to Chris's code for method description.

Plz correct me for any convention losses.

The layout file content is as follows.

<ListView 
    android:id="@+id/list"         
    android:layout_span="1" 
    android:layout_width="324px" 
    android:layout_height="348px"/>

The java file content is as follows.

import java.util.LinkedList;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.ArrayAdapter;
import android.widget.ListView;


public class TestApp extends Activity {


    private final Handler handler = new Handler();

    private final UILoadingHelperClass uiLoadingThread = new UILoadingHelperClass();


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        uiLoadingThread.setListView(R.id.list);

        new DataLoadingThread(handler, uiLoadingThread);        


    }


    private class DataLoadingThread extends Thread {

        int count = 0;

        LinkedList<String> linkedList = null;

        private final Handler handler;

        private final UILoadingHelperClass uiLoadingThread;


        public DataLoadingThread(Handler handler, UILoadingHelperClass uiLoadingThread) {
            this.handler = handler;
            this.uiLoadingThread = uiLoadingThread;
            this.start();
        }

        @Override
        public void run() {

            while(true){
                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

                if(null == linkedList)
                    linkedList = new LinkedList<String>();

                linkedList.add("Test "+count++);

                int i=0;

                String[] strings = new String[linkedList.size()];

                for(String string : linkedList)
                    strings[i++]=string;

                uiLoadingThread.setSttrings(strings);

                handler.post(uiLoadingThread);
            }

        }
    }


    private class UILoadingHelperClass implements Runnable {

        private ListView listView = null;

        private String[] strings = null;

        public void setSttrings(String[] strings){
            this.strings = strings;
        }

        public void setListView(int id){
            listView = (ListView)findViewById(id);
        }

        public void run() {
            if(null != strings && strings.length>0)
                listView.setAdapter(new ArrayAdapter<String>(TestApp.this,android.R.layout.simple_list_item_1,strings));        

        }      
    }
}
Ameya