Search code examples
javaandroidandroid-activityviewhandler

Null textview when it's call on a handler


I'm trying to do a setText() on a Textview (already instantiate in the onCreate()) called by a Handler and using the ruiOnUiTread() but I have a nullPointerException on the Textview.

Where can the problem come from?

I saw in the debug that the instance of the activity was not the same between the instantiation and the setText() while I do not change activity but impossible to instantiate it in the same place as the setText().

private TextView ambianceTextView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ambianceTextView = findViewById(R.id.valeur_ambiance);

    StarterKillerPidroid.getInstance().startApp();
}

private final Runnable retrieveData = new Runnable() {
    public void run() {
        try {
            setText();
        } catch (Exception e) {
            e.printStackTrace();
        }
        handlerRecup.postDelayed(retrieveData, 1000);
    }
};

public void setText(){
  runOnUiThread(new Runnable() {
     @Override
     public void run() {
        ambianceTextView.setText("test");
     }
   });
}

public void doAfterLogin() {
    handlerRecup.postDelayed(retrieveData, 10000);
}

the runnable is started by a function called by a callback of an http request with Volley

public class StarterKillerPidroid {

void startApp() {
    //Sending a request 
    PostmanPidroid.getInstance().login();
}

public void ackLogin(Boolean isValid) {
    if (isValid) {
        ActivityMain.getInstance().doAfterLogin();
    } else {
        PostmanPidroid.getInstance().login();
    }
}

}

The class Postman :

  public class Postman {
   public void login(){

        // Parameters
        String email = "test@tes";
        String password = "test";

        // Encoding the request with parameters
        JsonObjectRequest request = EncoderDecoderPidroid.getInstance()
                .encodeRequestLogin(email, password);

        // Sending the request
        sendRequest(request);
     }

     void sendRequest(StringRequest message){
         // Creating the queu if it's not create
         if (queue == null) {
               queue = Volley.newRequestQueue(context);
          }
          // Adding the request to the queue
              queue.add(message);
           }
   }

When a success response is received, this callback is called :

 private Response.Listener<JSONObject> callbackLogin =
            new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            ...
            StarterKillerPidroid.getInstance().ackLogin(true);
        }
    };

Solution

  • Basically, this kind of problem is due to the instance. It may be possible that your textview instance is not initialized. One more thing using handler directly for updating UI thread is not a good idea. Instead of directly updating Ui with handler you should use FunctionalInterface for doing this.

    FunctionalInterface is a good approach for such cases.

    A functional interface is an interface that contains only one abstract method. They can have only one functionality to exhibit. From Java 8 onwards, lambda expressions can be used to represent the instance of a functional interface. ... Runnable, ActionListener, Comparable are some of the examples of functional interfaces.

    Java has a predefined FunctionalInterface Callable. It goes something like this

    public static void doDid(final Callable<Void> callable) {
        final Handler handler = new Handler();
    
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                try {
                    callable.call();
                    handler.postDelayed(this, every * repeattime);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
        }, every * tempvalue);
    }
    

    and use this for updating UI in this way

     doDid(new Callable<Void>() {
                        @Override
                        public Void call() {
                             textView.setText("Your text");
                            return null;
                        }
                    });
    

    There is one open-source library available for Android which works like a charm is such cases called Predictor. You can download it from here and import in your project. You can also contribute in this project for saving many developers life.

    Do you wanna see how predictor can do this?

     Predictor.every(3).second().doDid(new Job<Void>() {
                        @Override
                        public Void run() {
                            textView.setText("Your text");
                            return null;
                        }
                    });
    

    What can you do with predictor?

    Predictor gives you several ways of handling multithreading some of them are as follows:

    Predictor.every(3).second().doDid(something());
    Predictor.every(5).minutes().doDid(something());
    Predictor.every().hour().doDid(something());
    Predictor.every().week().doDid(something());
    Predictor.every().month().doDid(something());
    

    and many more...