Using Handler across unnested thread class

I have some code written, where in a seperate thread a connection is made and some data is retrieved. This is all working fine.

My problem then arises when I try to pass back this information to the main thread via Handler.

Because the code for the thread that is off the main thread is not nested inside the main class, the reference I pass to the handler does not seem to hold. Suggestions as to how I can modify the code to allow the handler data to be passed back to the main thread.

Code for the main thread (stripped out only leaving important bits):

public class MyActivity extends Activity {

Handler mHandler = new Handler(){
    public void handleMessage(Message msg){
        Bundle b;
        if(msg.what==1){
            b = msg.getData();
            weCanMove = b.getBoolean("key");
            Log.d("what did we get", String.valueOf(b.getBoolean("key")));
        }
        super.handleMessage(msg);
    }
};

 public void start(View view) throws InterruptedException {
    Context context;
    Boolean weHaveFile = checkFile();
    System.out.println(userinfo[0] + "n" + userinfo[1]);
    if (weHaveFile) {
        Intent intent = new Intent(MyActivity.this, OtherActivity.class);
        startActivity(intent);
    } else {

        //DatabaseMiddleMan login = new DatabaseMiddleMan();
        /*login.run();
        login.login(userinfo[0],userinfo[1]);*/
        ArrayList <String> uinfo = new ArrayList<String>();
        uinfo.add(0,userinfo[0]);
        uinfo.add(1,userinfo[1]);
        DatabaseMiddleMan login = new DatabaseMiddleMan(0,uinfo,this.mHandler);
        Thread thread = new Thread(login);
        thread.start();
        Thread.sleep();
        if(weCanMove) {
            Intent intent = new Intent(MyActivity.this, OtherActivity.class);
            startActivity(intent);
        }else{

            context = this.getApplicationContext();

            new AlertDialog.Builder(MyActivity.this)
                    .setTitle("Errore")
                    .setMessage("Credenziali errate!")
                    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {

                        }
                    })
                    .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            // do nothing
                        }
                    })
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .show();
        }
    }
}

The code for the object that runs in a seperate thread:

public class DatabaseMiddleMan implements Runnable{
public String serverAddress;
public boolean loginsucess = false;
private ArrayList<String> params;
private int whichMethodToExecute;
private Handler hd;

public DatabaseMiddleMan(int selection, ArrayList<String> params, Handler msg){
    this.params = params;
    this.whichMethodToExecute = selection;
    hd = msg;
}

public void run(){
    if(this.whichMethodToExecute ==  0){
        this.login();
    }
}
   public void login(){
    String username = params.get(0);
    String password = params.get(1);

    String link = "xxxxxxxxxxxxxx";
    String query = "xxxxxxxxxxxxxxx";
    HttpClient httpClient = new DefaultHttpClient();

    HttpPost httpPost = new HttpPost(link);

    BasicNameValuePair queryPair = new BasicNameValuePair("query", query);
    List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
    nameValuePairList.add(queryPair);
    Bundle b = new Bundle(1);

    try {
        UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(nameValuePairList);

        httpPost.setEntity(urlEncodedFormEntity);
        try {
            HttpResponse httpResponse = httpClient.execute(httpPost);
            InputStream inputStream = httpResponse.getEntity().getContent();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String bufferedStrChunk = null;
            StringBuilder stringBuilder = new StringBuilder();

            while ((bufferedStrChunk = bufferedReader.readLine()) != null) {
                stringBuilder.append(bufferedStrChunk);
            }

            String queryResult = stringBuilder.toString();
            Log.d("SQL PASS",queryResult);
            if (queryResult.equals(password)){
                loginsucess = true;

                b.putBoolean("key",true);
                Log.d("SQL PASS","test passed");
            } else {

                b.putBoolean("key",false);
            }

        } catch (ClientProtocolException cpe) {
            System.out.println("First Exception caz of HttpResponese :" + cpe);
            cpe.printStackTrace();
        } catch (IOException ioe) {
            System.out.println("Second Exception caz of HttpResponse :" + ioe);
            ioe.printStackTrace();
        }
    } catch (UnsupportedEncodingException uee) {
        System.out.println("An Exception given because of UrlEncodedFormEntity argument :" + uee);
        uee.printStackTrace();
    }

    Message msgs = hd.obtainMessage();
    msgs.setData(b);
    hd.sendMessage(msgs);
}

Answer

What is the message looper? It is an object that receives the queue of messages, and sequentially runs a message handlers in the same thread, one by another. Next will be run just after previous is completed.

So, imagine, there is some start event was put in a queue. The looper starts a handler, which will run cascade of methods, and finally your start(View view). Looper will be halted just until all methods are completed and handler is done. So, next message in queue, which will run handleMessage of your’s handler, could be executed only after your start is returns.

Never use the Thread.sleep() in the main thread. Better to use

thread.join();

to wait until asyn thread to complete.

If you want to pass one single object between threads just after thread is done, you can use this approach:

allocate in your Runnable a some volatile variable:

public class DatabaseMiddleMan implements Runnable{

    public volatile Bundle mResult;
    ...

just after your thread is completed, you can access this variable:

    DatabaseMiddleMan login = new DatabaseMiddleMan(0,uinfo,this.mHandler);
    Thread thread = new Thread(login);
    thread.start();
    ...
    // blah blah blah, here doing some actions in parallel, while thread doing it's work
    ...
    thread.join();

    Bundle b = login.mResult;        
    weCanMove = b.getBoolean("key");

note, instead of Bundle, you can pass here a boolean value itself. Also, you can create several result variables, if needed.

Leave a Reply

Your email address will not be published. Required fields are marked *