Android : Thread not passing data to Handler - java

I am working on an Android project in which I have to connect to a server, retrieve a list of restaurants and display it in a ListView. The situation is when the app is started, for testing, I am starting another Intent and running a thread inside it. Inside the thread, I am retrieving data from a server which I would like to display it on the ListView. But the handler method which should get the data and display it, is not working.
Unfortunately I am getting no errors to isolate and deal with the problem, and as passing data between threads is a core function, I am getting lost in the so many methods it calls in between. Kindly have a look what I am doing wrong.
MainActivity class :
public class MyActivity extends Activity {
RestaurantServiceImpl restaurantService = new RestaurantServiceImpl();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button) findViewById(R.id.standortermitteln);
//Below starts the new view
Intent listScreen = new Intent(getApplicationContext(),RestaurantList.class);
}
RestaurantList.java :
public class RestaurantList extends Activity {
String url1 = "http://192.168.178.40:8080/restaurant/listing";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.restos);
ListView listView = (ListView) findViewById(R.id.restaurantList);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Log.d("Are we in listRestaurants","Checking");
try {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Collections.singletonList(new MediaType("application", "json")));
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
restTemplate.getMessageConverters().add(new GsonHttpMessageConverter());
ResponseEntity<Restaurant[]> responseEntity = restTemplate.exchange(url1, HttpMethod.GET, requestEntity, Restaurant[].class);
Restaurant[] restaurantList = responseEntity.getBody();
for(Restaurant restaurant : restaurantList){
// I am able to print these messages,
Log.d(restaurant.getRestaurantName(), "Restaurant name");
}
} catch (Exception e) {
Log.d("We are in stacktrace",e.toString());
e.printStackTrace();
}
}
});
thread.setPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// I am not reaching here to set the ListView
Handler messageHandler = new Handler(){
public void handleMessage(Message message){
try {
Restaurant[] restaurants = (Restaurant[]) message.obj;
for(Restaurant restaurant : restaurants){
Log.d(restaurant.getRestaurantName(), "Restaurant name");
}
if(restaurants.length<1){
Log.d("Restaurants from thread are empty"," check");
}
for(Restaurant restaurant : restaurants){
Log.d(restaurant.getRestaurantName(), "Restaurant name in messageHandler");
}
ArrayAdapter<Restaurant> adapter = new ArrayAdapter<>(getApplicationContext(),android.R.layout.simple_list_item_1,restaurants);
listView.setAdapter(adapter);
}catch (Exception e){
e.printStackTrace();
}
}
};
}
}

Add queueNewRestaurants(restaurantList); to the end of the first thread.
private static final int NEW_RESTAURANTS = 1;
public void queueNewRestaurants(Restaurant[] restaurants) {
android.os.Message message = messageHandler.obtainMessage(
NEW_RESTAURANTS,
restaurants
);
message.sendToTarget();
}

As you are sending the message to the handler from the thread, it will not call the handler. Add following line to your thread.
handleMessage.sendMessage(new Message());
Edit:
Add object
Message message =new Message();
message.obj=arrayobject;

You are creating a Handler but never sending anything to it. In this case I would recommend that once you have your restaurantList in your worker thread, you either post a runnable to a Handler that you create as part of your activity being constructed, or you use runOnUiThread.
Option 1.
public class RestaurantList extends Activity {
String url1 = "http://192.168.178.40:8080/restaurant/listing";
Handler mHandler = new Handler();
...
...
...
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
...
...
...
final Restaurant[] restaurantList = responseEntity.getBody();
mHandler.post(new Runnable() {
public void run() {
// update UI here
}
});
Option 2.
call
runOnUiThread(new Runnable(.....));
instead of mHandler.post(.....)
Option 3.
Use an AsyncTask, do the HTTP work in doInBackground() and update your UI in onPostExecute(). Using an AsyncTask you don't have to care about creating any thread at all, it's done for you.
In this particular case I would probably go for option 3.

Except for Handler, you also can use java API inside your thread run():
runOnUiThread(new Runnable() {
public void run() {
//communicate with main thread here, send the result data to main thread
}
});

Related

Handler and thread/runnable

Can someone tell me why this doesn't work? I am trying to figure out how to use thread/runnable. Thread doesnt do much but just to loop and let the main thread know to update the text. I dont know what I missed, the centertext doesnt update. Thanks so much.
public class MainActivity extends AppCompatActivity {
TextView centerText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final SysTimeObj sysTimeObj = new SysTimeObj();
centerText = findViewById(R.id.centerText);
Handler stHandler = new Handler(getMainLooper()){
#Override
public void handleMessage(#NonNull Message msg) {
super.handleMessage(msg);
centerText.setText("thread updated");
}
};
startThread(sysTimeObj, stHandler);
}
public void startThread(SysTimeObj sysTimeObj, Handler handler){
clockThread rc = new clockThread(sysTimeObj, handler);
Thread t1 = new Thread(rc);
t1.start();
}
}
public class clockThread implements Runnable {
//private String sysTime;
private Handler handler;
SysTimeObj sysTimeObj;
public clockThread(SysTimeObj sysTimeObj, Handler mHandler){
//sysTime = GregorianCalendar.getInstance().getTime().toString();
this.sysTimeObj = sysTimeObj;
handler = mHandler;
}
#Override
public void run() {
sysTimeObj.setTime();
handler.postDelayed(this, 100);
}
}
You want to do something on the Main/UI Thread after a certain amount of time ? On Android, you don't need a new thread for that.
The Main Thread has a message queue that you can Post to. That message queue is emptied on a regular basis. Posted messages can be configured to be executed at a later time (which is what you seem to want).
To post messages, you need to create a Handler for the target thread. This Handler will let you send messages to that thread. Then, Post a Runnable to that thread using one of the posting methods availlable (here, postDelayed).
You'll end with something like this :
public class MainActivity extends AppCompatActivity {
private TextView yourTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
yourTextView = findViewById(R.id.yourTextView);
Handler handler = new Handler(getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
yourTextView.setText("Updated after 100 ms");
}
}, 100);
}
}
If threads is really what you want, I suggest you look at AsyncTasks. You might also want to look at the official documentation about Process and Threads on Android Developpers.

Running CountDownTimer inside AsyncTask throws java.lang.RuntimeException - Looper.prepare()

I have a .lrc file and I need to go over every line with a CountDownTimer. I have tried using AsyncTask to do so but I get the error:
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
On line new CountDownTimer... I tried doing it with runnable but I still get the same error. My goal is to get it to go over every line in .lrc file which looks like this:
[00:04.15]Help me, it's like the walls are caving in
[00:10.46]Sometimes I feel like giving up
[00:13.63]But I just can't
...
I am not sure how efficient it is to do it the way I am trying to do. I am thinking of going through every line in the doInBackground(). If there is a better way to do it then let me know. But to begin with, why am I getting the EXCEPTION ?
Just to note.. I have simplified the code as much as I could so it would be easier to understand what I am trying to do.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyView myView = new
myView.play();
}
}
public class MyView{
public void play() {
new CustomAsync().execute();
}
}
class CustomAsync extends AsyncTask<Lyric, Void, Void> {
protected Void doInBackground(Lyric... param) {
startLyricCountDownTimer(param);
return null;
}
protected void onPostExecute(Void param) {
//Print Toast or open dialog
}
private void startLyricCountDownTimer(Lyric lyric){
new CountDownTimer(30000, 10) { //This is where it throws the error
public void onTick(long millisUntilFinished) {
//Do the thing
}
public void onFinish() {
}
}.start();
}
}
EDIT
Is it better to go with the AsyncTask and do like Son Truong suggested or to use the following code for each and every lrc line?
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
new CountDownTimer(millisInFuture,countDownInterval) {
#Override
public void onTick(
CountDownTimer uses a Handler to post messages to a Message Queue of a Thread which has a Looper. onTick and onFinish will be called on which thread based on where you create CountDownTimer instance.
In your case because you create CountDownTimer instance in doInBackground method of AsyncTask so these two methods will be call on AsyncTask thread.
In constructor of CountDownTimer, it will create Handler instance as well. The Handler will check whether or not current thread has a Looper, if not it will throw a RuntimeException with message.
Can't create handler inside thread that has not called
Looper.prepare()
Because AsyncTask uses a thread which has no Looper, that why your app crashes.
My suggestion is in doInBackground method you open a connection to .lrc file and read each line, for each line read, use runOnUIThread to send the line to UI thread (then you can process the line read there by display a Toast on screen, etc).
Update: I will demo how to read line by line from a file then display it on a text view each 3 seconds.
First write a class which read from an inputstream line by line
static class ReadLyricTask extends AsyncTask<InputStream, String, Void> {
WeakReference<MainActivity> mMainActivity;
ReadLyricTask(MainActivity activity) {
mMainActivity = new WeakReference<>(activity);
}
#Override
protected Void doInBackground(InputStream... inputStreams) {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStreams[0]));
String line;
try {
while ((line = reader.readLine()) != null) {
publishProgress(line);
}
} catch (IOException e) {
// Do nothing.
} finally {
try {
inputStreams[0].close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
MainActivity activity = mMainActivity.get();
if (activity != null) {
activity.displayLyricLineOnTextView(values[0]);
}
}
}
Then just use it in MainActivity
public class MainActivity extends AppCompatActivity {
private static final int UPDATE_LYRIC_TEXT_INTERVAL = 3000; // Change lyric text each 3 seconds.
private int mCurrentInterval = 0;
private TextView mLyricTextView;
private Handler mHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLyricTextView = findViewById(R.id.lyricText);
// I put a file named lyric.lrc in raw folder, for your case just open an input stream from a file.
InputStream inputStream = getResources().openRawResource(R.raw.lyric);
new ReadLyricTask(this).execute(inputStream);
}
private void displayLyricLineOnTextView(final String lyricLine) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mLyricTextView.setText(lyricLine);
}
}, mCurrentInterval);
mCurrentInterval += UPDATE_LYRIC_TEXT_INTERVAL;
}
}
CountDownTimer runs in separate thread and no need of asynctask to run a timer.Best solution would be create a service and make service to trigger a timer.
As timer run on non ui thread, while updating ui make sure you update from UI thread.You could use UI handler or runOnUithread method to update view.

Only the original thread that created a view hierarchy can touch its views - FATAL EXCEPTION

I am trying to use setContentView method to bring on my views to the front but on doing this i am getting this error.
From searching here Similar questions I found this error usually results when trying to get/set a value of layout view object for e.g. without instantiating it but that's not the case here, i am not even able to set the view.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ticker);
Thread tmr = new Thread() {
public void run() {
try {
sleep(1000);
} catch (InterruptedException iEx) {
iEx.printStackTrace();
} finally {
User usrObj = new User();
if (usrObj.authenticateUser()) {
Intent mainActivity = new Intent(
"android.intent.action.Homeview");
startActivity(mainActivity);
} else {
setContentView(R.layout.login); // <- ERROR
activateLogin();
}
}
}
};
tmr.start();
}
You need to call runOnUiThread to change your a layout from a thread.
runOnUiThread(new Runnable() {
#Override
public void run() {
User usrObj = new User();
if (usrObj.authenticateUser()) {
Intent mainActivity = new Intent("android.intent.action.Homeview");
startActivity(mainActivity);
} else {
setContentView(R.layout.login); // <- ERROR
activateLogin();
}
}
});
As a side note, make sure you don't hardcode strings, in order to avoid typo issues.-
Intent mainActivity = new Intent(YourActivity.this, android.intent.action.Homeview.class);
You're calling setContentView(...) from non-UI thread. Either move it to the main (UI) thread or wrap that single call in:
runOnUIThread(new Runnable() {
#Override
public void run() {
setContentView(R.layout.login);
}
});

How to pass data to UI thread

Good evening.
I have an activity like this. In handleMessage I have access to largeText field and can change it, but I can't do smth whith stringLinks field, or each other field which is not a UI element(like TextView, Button, EditText, etc).
How can I add to stringLinks ?
public class AboutUsActivity extends Activity {
Handler h;
TextView largeText;
List<String> stringLinks;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
stringLinks = new ArrayList<String>();
largeText = (TextView) findViewById(R.id.textView1);
h = new Handler() {
public void handleMessage(android.os.Message msg) {
HtmlParser parser;
StringBuilder result = new StringBuilder();
try {
parser = new HtmlParser(String.valueOf(msg.getData()));
List<TagNode> links = parser.getContentByClassName("ab");
for (Iterator<TagNode> iterator = stringLinks.iterator(); iterator
.hasNext();) {
TagNode divElement = (TagNode) iterator.next();
result.append(divElement.getText().toString());
}
} catch (Exception e) {
e.printStackTrace();
}
largeText.setText(newhtml); //Work
stringLinks.add(newhtml); //doesn't work
}
};
MyHttpClientUsage connect = new MyHttpClientUsage(h);
try {
connect.getInfoAbout();
} catch (HttpException e) {
e.printStackTrace();
}
}
}
I dont know if I understood you correctly.
If i did, you can use AsyncTask class
Check out their main too methods, doInBackground that executes something in a separate thread and onPostExecute that runs in UI Thread. They can comunicate with each other passing objects, and you can also publish the progress of your task.
It really looks like SwingWorker (maybe you used it if you programmed Swing applications).

Call Main thread from secondary thread in Android

How to call Main thread from secondary thread in Android?
The simplest way is to call runOnUiThread(...) from your thread
activity.runOnUiThread(new Runnable() {
public void run() {
... do your GUI stuff
}
});
My recommendation to communicate threads in the same process is sending messages between those threads. It is very easy to manage this situation using Handlers:
http://developer.android.com/reference/android/os/Handler.html
Example of use, from Android documentation, to handling expensive work out of the ui thread:
public class MyActivity extends Activity {
[ . . . ]
// Need handler for callbacks to the UI thread
final Handler mHandler = new Handler();
// Create runnable for posting
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateResultsInUi();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[ . . . ]
}
protected void startLongRunningOperation() {
// Fire off a thread to do some work that we shouldn't do directly in the UI thread
Thread t = new Thread() {
public void run() {
mResults = doSomethingExpensive();
mHandler.post(mUpdateResults);
}
};
t.start();
}
private void updateResultsInUi() {
// Back in the UI thread -- update our UI elements based on the data in mResults
[ . . . ]
}
}
You'll need a Handler that passes the information back to the main thread.
Also, it's good to remember that if you get your secondary thread through an AsyncTask, you have the option to call onProgressUpdate(), onPostExecute(), etc., to do work on the main thread.
Sample code using HandlerThread
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Handler responseHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
//txtView.setText((String) msg.obj);
Toast.makeText(MainActivity.this,
"Result from UIHandlerThread:"+(int)msg.obj,
Toast.LENGTH_LONG)
.show();
}
};
HandlerThread handlerThread = new HandlerThread("UIHandlerThread"){
public void run(){
/* Add your business logic to pupulate attributes in Message
in place of sending Integer 5 as in example code */
Integer a = 5;
Message msg = new Message();
msg.obj = a;
responseHandler.sendMessage(msg);
System.out.println(a);
}
};
handlerThread.start();
}
}
Explanation:
In above example, HandlerThread post a Message on Handler of UI Thread, which has been initialized with Looper of UI Thread.
final Handler responseHandler = new Handler(Looper.getMainLooper())
responseHandler.sendMessage(msg); sends Message from HandlerThread to UI Thread Handler.
handleMessage processes Message received on MessageQueue and shows a Toast on UI Thread.

Categories

Resources