RxJava 2 - Fatal Exception thrown on Scheduler - java

I trying to convert my Asynch task to JavaRx 2. I use google sheets api to download data from spreadsheets. (here is a link how this happens)
Here is a part of my code:
OnCreate:
/**
* JavaRx
*/
//Observable
Observable<String> observable
= Observable.create(
new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
//Use onNext to emit each item in the stream//
e.onNext("https://docs.google.com/spreadsheets/d/1W5S5W2QH6WHjUcL1VMwqIqOdFYVleTopJNryQJGw568/gviz/tq?tqx=out:QUERY&tq=select+B,X,Y,Z");
//Once the Observable has emitted all items in the sequence, call onComplete//
e.onComplete();
}
}
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
//Create our subscription
Observer<String> observer = new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
Log.e(TAG, "onSubscribe " + Thread.currentThread().getName());
}
#Override
public void onNext(String value) {
try {
String data = getLeagueData(value);
mLeagues.add(autoProcessJsonLeague("Argentina Primera Division", returnJSON(data)));
} catch (IOException e) {
e.printStackTrace();
}
Log.e(TAG, "onNext: " + value + Thread.currentThread().getName());
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError: ");
}
#Override
public void onComplete() {
Log.e(TAG, "onComplete: All Done! " + Thread.currentThread().getName());
}
};
observable.subscribe(observer);
Other Methods:
private String getLeagueData(String urlString) throws IOException {
//Download JSON file
InputStream is = null;
try {
URL url = new URL(urlString);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setInstanceFollowRedirects(true); //you still need to handle redirect manually.
HttpsURLConnection.setFollowRedirects(true);
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setInstanceFollowRedirects(true);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect(); //ERROR HAPPENS HERE!
int responseCode = conn.getResponseCode();
is = conn.getInputStream();
String contentAsString = convertStreamToString(is);
//Log.d("contentAsString", contentAsString);
return contentAsString;
} catch (ProtocolException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
is.close();
}
}
return null;
}
private String convertStreamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
private League autoProcessJsonLeague(String leagueName, JSONObject object) {
//Get the data from the JSON string
ArrayList<Team> teams = new ArrayList<>();
try {
JSONArray rows = object.getJSONArray("rows");
for (int r = 0; r < rows.length(); ++r) {
JSONObject row = rows.getJSONObject(r);
JSONArray columns = row.getJSONArray("c");
String name = columns.getJSONObject(0).getString("v");
int points = columns.getJSONObject(1).getInt("v");
double hGoalAv = columns.getJSONObject(2).getDouble("v");
double aGoalAv = columns.getJSONObject(3).getDouble("v");
hGoalAv = Utilities.round(hGoalAv, 2);
aGoalAv = Utilities.round(aGoalAv, 2);
teams.add(new Team(name, points, hGoalAv, aGoalAv));
//Log.d("Team", name + " " + hGoalAv + " " + aGoalAv);
}
} catch (JSONException e) {
e.printStackTrace();
e.printStackTrace();
}
return new League(leagueName, teams);
}
So I create an observable, I subscribe on IO thread and observeOn the main thread. With onNext I send the url link to the observer and then I try to connect to the server to download the json string file.
Error happens on method getLeagueData() on the line conn.connect();
It says java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.
FULL STACK TRACE ERROR:
08-16 08:53:09.934 29841-29841/com.aresproductions.bettingtools E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.aresproductions.bettingtools, PID: 29841
java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:111)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6195)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:874)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:764)
Caused by: android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:86)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:74)
at java.net.InetAddress.getAllByName(InetAddress.java:752)
at com.android.okhttp.internal.Network$1.resolveInetAddresses(Network.java:29)
at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:187)
at com.android.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:156)
at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:98)
at com.android.okhttp.internal.http.HttpEngine.createNextConnection(HttpEngine.java:346)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:329)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:247)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:457)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:126)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:89)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java)
at com.aresproductions.bettingtools.MainActivity.getLeagueData(MainActivity.java:307)
at com.aresproductions.bettingtools.MainActivity.access$000(MainActivity.java:80)
at com.aresproductions.bettingtools.MainActivity$2.onNext(MainActivity.java:180)
at com.aresproductions.bettingtools.MainActivity$2.onNext(MainActivity.java:171)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:198)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:250)
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
at android.os.Handler.handleCallback(Handler.java:751) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:154) 
at android.app.ActivityThread.main(ActivityThread.java:6195) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:874) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:764) 
08-16 08:53:09.936 29841-29841/com.aresproductions.bettingtools E/MQSEventManagerDelegate: failed to get MQSService.
Thanks in advance!

The problem is that you are doing the network call on the main thread. Though you have subscribed on Schedulers.io(), the onNext() method where you are doing the network call will be called on main thread since you are observing on main thread observeOn(AndroidSchedulers.mainThread()).
The solution will be to call the getLeagueData(String urlString) inside subscribe() method of observable and call e.onNext(result) with the result of the network call.

I assume you get a network on main thread exception because the network call is at the wrong place. You should have moved it into the create thus it gets executed on the IO scheduler instead as part of observing the trivial string:
Observable<String> observable = Observable.create(
new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
String value = "https://docs.google.com/spreadsheets/d/1W5S5W2QH6WHjUcL1VM" +
"wqIqOdFYVleTopJNryQJGw568/gviz/tq?tqx=out:QUERY&tq=select+B,X,Y,Z";
String data = getLeagueData(value);
//Use onNext to emit the item in the stream//
e.onNext(data);
/* Once the Observable has emitted all items
in the sequence, call onComplete */
e.onComplete();
}
}
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
//Create our subscription
Observer<String> observer = new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
Log.e(TAG, "onSubscribe " + Thread.currentThread().getName());
}
#Override
public void onNext(String data) {
try {
mLeagues.add(autoProcessJsonLeague(
"Argentina Primera Division", returnJSON(data)));
} catch (IOException e) {
e.printStackTrace();
}
Log.e(TAG, "onNext: " + value + Thread.currentThread().getName());
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError: ");
}
#Override
public void onComplete() {
Log.e(TAG, "onComplete: All Done! " + Thread.currentThread().getName());
}
};
observable.subscribe(observer);
You may also want to move
autoProcessJsonLeague(
"Argentina Primera Division", returnJSON(data))
into the ObservableOnSubscribe in case the processing is expensive.

Related

StreamCorruptedException after ~1000 transferred objects

I want to transfer objects (AssignmentListener) from one Java Server to 5 Java Clients.
Therefore I wrote a method to send out the message:
private void sendMessage(AssignmentListener listener, int[] subpartitionIndices){
boolean success = false;
int failCount = 0;
// retry for the case of failure
while(!success && failCount < 10) {
try {
// get the stored socket & stream if stored
if(listener.getSocket() == null) {
if (localMode) {
listener.setSocket(new Socket("localhost", listener.getPort()));
} else {
listener.setSocket(new Socket(listener.getIp(), listener.getPort()));
}
listener.setOutputStream(new ObjectOutputStream(listener.getSocket().getOutputStream()));
}
AssignmentListenerMessage assignmentListenerMessage = new AssignmentListenerMessage(subpartitionIndices);
System.out.println("Sending " + assignmentListenerMessage);
listener.getOutputStream().writeObject(assignmentListenerMessage);
listener.getOutputStream().flush();
success = true;
} catch (IOException se) {
se.printStackTrace();
System.err.println("Failed to forward " + Arrays.toString(subpartitionIndices) + " to " + listener);
failCount++;
}
}
}
On the client side, I have the following:
public void run() {
String mode = "remote";
if(localMode) mode = "local";
// we need to register this listener at at the OverpartitioningManager
if(register(isLocalRequest)) System.out.println("Registered AssignmentListenerServer for index "+subpartitionIndex+" at ForwardingServer - "+mode);
running = true;
while (running) {
try {
socket = serverSocket.accept();
// Pass the socket to the RequestHandler thread for processing
RequestHandler requestHandler = new RequestHandler( socket );
requestHandler.start();
} catch (SocketException se) {
se.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
class RequestHandler extends Thread {
private Socket socket;
RequestHandler(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
try {
System.out.println("Received a connection");
// Get input and output streams
inStream = new ObjectInputStream(socket.getInputStream());
//outStream = new DataOutputStream(socket.getOutputStream());
AssignmentListenerMessage incomingMessage = null;
while(socket.isBound()) {
try {
incomingMessage = (AssignmentListenerMessage) inStream.readObject();
}catch (StreamCorruptedException sce){
System.out.println("Failed to read AssignmentMessage from Stream, but will try again... (no ack)");
sce.printStackTrace();
continue;
}
// do stuff with the message
}
// Close our connection
inStream.close();
socket.close();
System.out.println("Connection closed");
} catch (Exception e) {
e.printStackTrace();
}
}
}
This works multiple times, but at one point I get the following exception:
java.io.StreamCorruptedException: invalid type code: 00
Does anyone have an idea or any other performance improvement for what I'm doing?
Thanks.

unhandled exception: org.xml.sax.SAXException

I am reading an RSS newsfeed and want to check if the calling App has been cancelled/stopped (eg device rotated). Most RSS feeds have a loop in them which allows a statement in the loop to check for isCancelled(). I am using what is described as a Simplified SAX read. There is no loop. I call the parse, and have handlers for different items. I have one of them, (end Element) to check for isCancelled().
I get "unhandled exception: org.xml.sax.SAXException", on the red underlined throw statement. It will not compile.
I have tried as many combinations as I can think of where the try/catch statements go, and putting in the org.xml.sax.
I presume one option I have is to use one of teh RSS feed read options that has a loop, but if possible, I would like to use this simplified SAX read as it is supposed to be the most efficient.
public ArrayList<FeedItem> GetWithSimplifiedSax(String theUrl)
throws SAXException {
try {
url= new URL(theUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
InputStream inputStream = connection.getInputStream();
final FeedItem currentItem = new FeedItem();
RootElement root = new RootElement("rss");
final ArrayList<FeedItem> feedItems = new ArrayList<>();
android.sax.Element channel = root.getChild("channel");
android.sax.Element item = channel.getChild("item");
item.setEndElementListener(new EndElementListener(){
public void end() {
feedItems.add(currentItem.myCopy());
if(isCancelled()){
throw new SAXException("cancel");
//<<DOES NOT LIKE ABOVE LINE
// *** Gives unhandled exception:org.xml.sax.SAXException
}
}
});
item.getChild("title").setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentItem.setTitle(body);
}
});
item.getChild("link").setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentItem.setLink(body);
}
});
item.getChild("description").setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentItem.setDescription(body);
}
});
item.getChild("pubdate").setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentItem.setPubDate(body);
}
});
try {
Xml.parse(inputStream, Xml.Encoding.UTF_8, root.getContentHandler());
} catch(SAXException e){
Log.e("SAX", e.getMessage());
return null;
}
return feedItems;
} catch ( IOException e) {
Log.e("MYERROR", e.getMessage());
//e.printStackTrace();
return null;
} //catch (org.xml.sax.SAXException e) {
// Log.e("SAX", e.getMessage());
// //e.printStackTrace();
// return null;
//} //try catch
} //method: public ... simplified SAX

Why ListView updates only if scrolled on device, but works fine on virtual device?

I recently started learning android development (am new to java as well) and I am currently working on a chat/messenger application
The problem I am facing, as the title says, is that the listview in which the messages are shown does not update on the device, unless scrolled, but it works fine on the virtual machine. I only tested on LG Optimus l5 II so far, but i need to fix this anyway.
I think it has something to do with multithreading, because this didn't happen until i added some new threads, so the adapter for listview, android manifest and rest I say are set up correctly. I can add them if it helps.
The 2 threads i added that might cause this:
Checks the connection status and if disconnected tries to reconnect.
The thread used for communicating with the server.
I tested running only with the second thread on, and the problem still occurs.
I want to specify this is the first time I try something like this (servers-client, multithreading, java, android (I'm still in college and they don`t teach us these kinds of stuff there) ), and had no documentation ahead regarding how I should set up the communication between the server and the client. This is the most efficient way I could think of.
this is at the end of onCreate:
StartConnectingRoutine(); // so you know where it all starts
and the code for it:
private void StartConnectingRoutine()
{
Thread t = new Thread()
{
#Override
public void run()
{
while(true)
{
if(!connected)
{
if( connect != null)
{
if(!connect.isAlive())
{
ConnectListener();
}
}
else
{
ConnectListener();
}
}
try {
sleep(CONNECTION_CHECK_TIME_MS); // this is set to 10000 (10 seconds)
} catch (InterruptedException e) {
Log.e("Intrerrupted", e.toString());
}
}
}
};
t.start();
}
and the connectListener():
private void ConnectListener()
{
Log.d("Connecting", "Connecting...");
connect = new Thread()
{
JSONObject info = new JSONObject();
String receivedMessage;
#Override
public void run()
{
try {
info.put("Name", user.GetName());
info.put("PORT", MY_PORT);
info.put("IPv4", getIpAddress());
} catch (JSONException e1) {
Log.e("JSON", "JSON error: " + e1.toString());
}
try
{
ServerSocket = new Socket(SERVER_IP, SERVER_PORT);
dis = new DataInputStream(ServerSocket.getInputStream());
dos = new DataOutputStream(ServerSocket.getOutputStream());
dos.writeUTF(info.toString());
dos.flush();
String response = dis.readUTF();
if(response.equals("connected"))
{
Log.d("Connect", "Connected!");
connected = true;
}
else
Log.d("Connect", "Failed to connect!");
while(connected)
{
receivedMessage = dis.readUTF();
DisplayNewMessage(new MMessage(receivedMessage, MMessage.MessageType.Received));
}
}catch(SocketException e)
{
try {
if(connected)
{
ServerSocket.close();
dis.close();
dos.close();
connected = false;
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
catch(Exception e)
{
Log.d("Connect", "Failed to connect");
Log.e("Connect", e.toString());
connected = false;
}
}
};
connect.start();
}
Fixed:
Reconnecting thread (i tried using asyncTask for this too, but it wouldn`t open the other asyncTask, even if I tried to open it from onProgressUpdate()-which it is supposed to be able to run ui thread components):
private void startConnectingRoutine()
{
Thread t = new Thread()
{
#Override
public void run()
{
Log.d("ConnectingRoutine", "Started connecting routine.");
while(true)
{
if(!connected)
{
startListener();
}
try {
sleep(CONNECTION_CHECK_TIME_MS);
} catch (InterruptedException e) {
Log.e("Intrerrupted", e.toString());
}
}
}
};
t.start();
}
Listener thread:
private void startListener()
{
new Listener().execute();
}
.
private class Listener extends AsyncTask<Long, String, Long>
{
#Override
protected Long doInBackground(Long... params) {
Log.d("Connecting...", "Connecting...");
JSONObject info = new JSONObject();
String receivedMessage;
try {
info.put("Name", user.GetName());
info.put("PORT", MY_PORT);
info.put("IPv4", getIpAddress());
} catch (JSONException e1) {
Log.e("JSON", "JSON error: " + e1.toString());
}
try
{
serverSocket = new Socket(SERVER_IP, SERVER_PORT);
dis = new DataInputStream(serverSocket.getInputStream());
dos = new DataOutputStream(serverSocket.getOutputStream());
dos.writeUTF(info.toString());
dos.flush();
String response = dis.readUTF();
if(response.equals("connected"))
{
Log.d("Connect", "Connected!");
connected = true;
}
else
Log.d("Connect", "Failed to connect!");
while(connected)
{
receivedMessage = dis.readUTF();
publishProgress(receivedMessage);
}
}
catch(Exception e)
{
Log.d("Connect", "Failed to connect");
Log.e("Connect", e.toString());
return null;
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
displayNewMessage(new MMessage(values[0], MMessage.MessageType.Received));
}
#Override
protected void onPostExecute(Long result) {
super.onPostExecute(result);
connected = false;
try{
if(serverSocket != null)
serverSocket.close();
if(dis != null)
dis.close();
if(dos != null)
dos.close();
}catch(Exception e)
{
Log.e("Listener", "There was a problem closing the connection: " + e.toString());
}
}
}
There are perhaps multiple things going wrong here, but two that jump out are:
You're calling DisplayNewMessage() from outside the UI thread.
You're not notifying the adapter that its dataset has changed.
I urge you to look in to better mechanisms for executing tasks in the background than simply creating a Thread. Using AsyncTasks would be a good start, but you'll need to take special care to handle tasks between configuration changes (such as rotating the device).
Furthermore, your code is very difficult to read as you capitalize your method names. This is against Java code conventions. You will make things easier for yourself by formatting your code neatly (a good IDE helps with that) and learning to follow conventions!

Android:NetworkOnMainThreadException error inside AsyncTask

okay so i created a inner class which extends AsycTask in order for my code to run outwith the UI thread. However i'm getting this error so i assume this means some part of my onPostExecute needs to be done in doInBackground however i cant figure out exactly what this is
public class asyncTask extends AsyncTask<String, Integer, String> {
ProgressDialog dialog = new ProgressDialog(PetrolPriceActivity.this);
#Override
protected void onPreExecute() {
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setProgress(0);
dialog.setMax(100);
dialog.setMessage("loading...");
dialog.show();
}
#Override
protected String doInBackground(String...parmans){
{
for(int i = 0; i < 100; i++){
publishProgress(1);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
String urlString = petrolPriceURL;
String result = "";
InputStream anInStream = null;
int response = -1;
URL url = null;
try {
url = new URL(urlString);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
return null;
}
URLConnection conn = null;
try {
conn = url.openConnection();
} catch (IOException e) {
// TODO Auto-generated catch block
return null;
}
// Check that the connection can be opened
if (!(conn instanceof HttpURLConnection))
try {
throw new IOException("Not an HTTP connection");
} catch (IOException e) {
// TODO Auto-generated catch block
return null;
}
try
{
// Open connection
HttpURLConnection httpConn = (HttpURLConnection) conn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.connect();
response = httpConn.getResponseCode();
// Check that connection is OK
if (response == HttpURLConnection.HTTP_OK)
{
// Connection is OK so open a reader
anInStream = httpConn.getInputStream();
InputStreamReader in= new InputStreamReader(anInStream);
BufferedReader bin= new BufferedReader(in);
// Read in the data from the RSS stream
String line = new String();
while (( (line = bin.readLine())) != null)
{
result = result + "\n" + line;
}
}
}
catch (IOException ex)
{
try {
throw new IOException("Error connecting");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return result;
}
}
#Override
protected void onProgressUpdate(Integer...progress){
dialog.incrementProgressBy(progress[0]);
}
#Override
protected void onPostExecute(String result) {
// Get the data from the RSS stream as a string
errorText = (TextView)findViewById(R.id.error);
response = (TextView)findViewById(R.id.title);
try
{
// Get the data from the RSS stream as a string
result = doInBackground(petrolPriceURL);
response.setText(result);
Log.v(TAG, "index=" + result);
}
catch(Exception ae)
{
// Handle error
errorText.setText("Error");
// Add error info to log for diagnostics
errorText.setText(ae.toString());
}
if(dialog.getProgress() == dialog.getMax())
dialog.dismiss();
}
}
if someone could point out my error as well as show an example of where the code is suppose to go in my doInBackground that would be great. Thanks
problem:
result = doInBackground(petrolPriceURL);
you are implicitly calling the doInbackground method in the onPostExecute which will actually run in your UI thread instead on a different thread thus resulting to Android:NetworkOnMainThreadException.
Also it is unnecessary to call doInBackground that it is already executed before onPostExecute when you execute your Asynctask. Just directly use the result parameter of the onPostExecute.
sample:
#Override
protected void onPostExecute(String result) {
// Get the data from the RSS stream as a string
errorText = (TextView)findViewById(R.id.error);
response = (TextView)findViewById(R.id.title);
response.setText(result);
if(dialog.getProgress() == dialog.getMax())
dialog.dismiss();
}
I suspect the error is related to this part of your code:
try
{
// Get the data from the RSS stream as a string
result = doInBackground(petrolPriceURL);
response.setText(result);
Log.v(TAG, "index=" + result);
}
doInBackgound is called automatically when you call asynctask.execute. To start your task correctly you should (1) create a new instance of your task; (2) pass the string params you need to use in doInBackground in the execute method; (3) use them; (4) return the result to onPostExecute.
For Example:
//in your activity or fragment
MyTask postTask = new MyTask();
postTask.execute(value1, value2, value3);
//in your async task
#Override
protected String doInBackground(String... params){
//extract values
String value1 = params[0];
String value2 = params[1];
String value3 = params[2];
// do some work and return result
return value1 + value2;
}
#Override
protected void onPostExecute(String result){
//use the result you returned from you doInBackground method
}
You should try to do all of your "work" in the doInBackground method. Reutrn the result you want to use on the main/UI thread. This will automaticlly be passed as an argument to the onPostExecute method (which runs on the main/UI thread).

How to implement java production grade RabbitMQ consumer

I having a problem with the RabbitMQ Work Queue implementation. im current running it in Tomcat, and i have the following class constantly listerning to new task in the queue. But after a day or two, sudden it behaving strangely, where by the object DeliveryOK return by channel.queueDeclare(taskQueueName, isDurable, false, false, null); is always zero. (i print out this in the log below mentioning "Current poolSize").
But in Rabbit admin (./rabbitmqadmin list queues or the RabbitMq Admin portal) it always return a number greater than zero (say 1267 messages in the queue). And it will not reduce to zero until i restart the tomcat, the class below only able to detect that there are actually some messages in the queue.
Initially i thought that this class was terminated somehow, but it is able to consume those messages that newly arrive. It will not consume those 1267 messages that is left hanging inside the queue. For example messages 1267 in the queue, will not be consume until i restart tomcat.
From the code below, is it because buggy implementation or is there a better way to implement a queue consumer specifically for RabbitMQ? i have read a related stack post(Producer/Consumer threads using a Queue), but im not sure if it helps.
Also, is it true that this consumer implementation below will not survive a RunTimeException?
MqConsumer Class:
#Service
public class MqConsumer implements Runnable{
private static final Logger logger = LoggerFactory.getLogger(MqConsumer.class);
private final int MAX_ALERT_THRESHOLD = 10000;
#Autowired
private AsynchSystemConnections asynchSystemConnections;
public MqConsumer(){
}
#PostConstruct
private void init() {
(new Thread(new MqConsumer(asynchSystemConnections))).start();
}
public MqConsumer(AsynchSystemConnections asynchSystemConnections){
this.asynchSystemConnections = asynchSystemConnections;
}
#Override
public void run() {
logger.info("Execute Consumer instance...");
while (true) { // infinite loop until it die due server restart
boolean toSleep = consume(asynchSystemConnections);
if (toSleep){
logger.error("Sleeping for 1 second...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.error("", e);
}
}
}
}
private boolean consume(AsynchSystemConnections asynchSystemConnections) {
com.rabbitmq.client.Connection mqConnection = null;
Channel mqChannel = null;
DatasiftMq dMq = null;
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(asynchSystemConnections.getMqServerHost());
mqConnection = factory.newConnection();
mqChannel = mqConnection.createChannel();
//consumePushInteractionJob method will forward to AsynchTwService.consume(connection, channel, AsynchTwService.PUSH_INTERACTION_QUEUE )
dMq = asynchSystemConnections.getAsynchService().consumePushInteractionJob(mqConnection, mqChannel);
int poolSize = asynchSystemConnections.getAsynchService().getPushInteractionQueueSize();
logger.info("Current poolSize: " + poolSize);
} catch(NullPointerException e) {
logger.error("", e);
if (dMq != null) {
try {
logger.error("Removing JSON with" + dMq.getLogHeader(dMq));
asynchSystemConnections.getAsynchService().ack(mqChannel, dMq.getDelivery());
logger.error("Removed JSON with" + dMq.getLogHeader(dMq));
} catch (IOException e1) {
logger.error("Remove JSON Failed: ", e);
}
}
return true;
} catch (IOException e) {
logger.error("Unable to create new MQ Connection from factory.", e);
return true;
} catch (InterruptedException e) {
logger.error("", e);
return true;
} catch (ClassNotFoundException e) {
logger.error("", e);
return true;
} catch (Exception e) {
logger.error("Big problem, better solve this fast!!", e);
asynchSystemConnections.getNotificationService().notifySystemException(null, e);
return true;
} finally {
try {
asynchSystemConnections.getAsynchService().ack(mqChannel, dMq.getDelivery());
asynchSystemConnections.getAsynchService().disconnect(mqConnection, mqChannel);
} catch (IOException e) {
logger.error("", e);
}
}
return false;
}
AsynchTwService Class:
#Service("asynchTwService")
public class AsynchTwService implements AsynchService {
static final String FAVOURITE_COUNT_QUEUE = "favourite_count_queue";
static final String FRIENDS_FOLLOWERS_QUEUE = "friends_followers_queue";
static final String DIRECT_MESSAGE_RECEIVE_QUEUE = "direct_message_receive_queue";
static final String PUSH_INTERACTION_QUEUE = "push_interaction_queue";
private static String mqServerHost;
private static final Logger logger = LoggerFactory.getLogger(AsynchTwService.class);
private static final boolean isDurable = true;
private boolean autoAck = false;
private ConcurrentHashMap<String, Integer> currentQueueSize = new ConcurrentHashMap<String, Integer>();
#Override
public Connection getConnection() throws IOException{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(mqServerHost);
return factory.newConnection();
}
#Override
public void produce(Connection connection, Channel channel, Object object, String taskQueueName) throws IOException {
sendToQueue(connection, channel, object, taskQueueName);
}
#Override
public QueueItem consume(Connection connection, Channel channel, String taskQueueName) throws IOException, InterruptedException, ClassNotFoundException{
Serializer serializer = new Serializer();
try {
Delivery delivery = listenFromQueue(connection, channel, taskQueueName);
Object messageObj = serializer.toObject(delivery.getBody());
QueueItem queueItem = (QueueItem)messageObj;
queueItem.setDelivery(delivery);
return queueItem;
} catch (InterruptedException e) {
throw e;
} catch (ClassNotFoundException e) {
logger.error("Unable to serialize the message to QueueItem object", e);
throw e;
}
}
#Override
public int getQueueSize(String taskQueueName){
return this.currentQueueSize.get(taskQueueName);
}
private Delivery listenFromQueue(Connection connection, Channel channel, String taskQueueName) throws IOException, InterruptedException, ClassNotFoundException{
try {
DeclareOk ok = channel.queueDeclare(taskQueueName, isDurable, false, false, null);
currentQueueSize.put(taskQueueName, ok.getMessageCount());
logger.info("Queue ("+ taskQueueName + ") has items: " +ok.getMessageCount());
} catch (IOException e) {
// TODO Auto-generated catch block
}
logger.info(" [*] Consuming "+taskQueueName+" message...");
QueueingConsumer consumer = new QueueingConsumer(channel);
try {
channel.basicConsume(taskQueueName, autoAck, consumer);
} catch (IOException e) {
logger.error("", e);
}
try {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
return delivery;
} catch (ShutdownSignalException e) {
logger.error("Unable to retrieve message from Queue", e);
throw e;
} catch (ConsumerCancelledException e) {
logger.error("Unable to retrieve message from Queue", e);
throw e;
} catch (InterruptedException e) {
logger.error("Unable to retrieve message from Queue", e);
throw e;
}
}
private void sendToQueue(Connection connection, Channel channel, Object object, String taskQueueName) throws IOException{
//Initialization, create Message Queue broker connection
try{
channel.queueDeclare(taskQueueName, isDurable, false, false, null);
}catch(IOException e) {
logger.error(e.getMessage());
logger.error("Error create Message Queue connection for queue name:" + taskQueueName, e);
throw e;
}
//send message to broker
try {
long start = System.currentTimeMillis();
Serializer serializer = new Serializer();
logger.info("Sending Twitter QueueItem to Message Queue...");
channel.basicPublish("", taskQueueName, MessageProperties.PERSISTENT_TEXT_PLAIN,
serializer.toBytes(object));
logger.info("Queue successfully sent, process took: " + (System.currentTimeMillis()-start)+ "ms");
} catch (IOException e) {
logger.error("Error while sending object to queue : " + taskQueueName, e);
throw e;
}
}
public static String getMqServerHost() {
return mqServerHost;
}
public static void setMqServerHost(String mqServerHost) {
AsynchTwService.mqServerHost = mqServerHost;
}
#Override
public void disconnect(Connection connection, Channel channel) throws IOException{
try {
if (channel != null){
if (channel.isOpen()){
channel.close();
}
}
if (connection != null){
if (connection.isOpen()){
connection.close();
}
}
logger.debug("MQ Channel Disconnected");
} catch (IOException e) {
throw e;
}
}
#Override
public void ack(Channel channel, QueueingConsumer.Delivery delivery) throws IOException {
// this is made as another method call is to avoid Ack too fast un intentionally
try {
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
logger.info("[x] acked" );
} catch (IOException e) {
logger.error("Unable Acknowledge Queue Message", e);
throw e;
}
}
#Override
public DatasiftMq consumeDatasiftInteraction(Connection connection, Channel channel,
String taskQueueName) throws IOException, InterruptedException, ClassNotFoundException {
Serializer serializer = new Serializer();
try {
Delivery delivery = listenFromQueue(connection, channel, taskQueueName);
Object messageObj = serializer.toObject(delivery.getBody());
DatasiftMq dto = (DatasiftMq)messageObj;
dto.setDelivery(delivery);
return dto;
} catch (InterruptedException e) {
throw e;
} catch (ClassNotFoundException e) {
logger.error("Unable to serialize the message to DatasiftDTO object", e);
throw e;
}
}
#Override
public void reQueue(Channel channel, Delivery delivery) throws IOException {
try {
channel.basicNack(delivery.getEnvelope().getDeliveryTag(), false, true);
logger.info("[x] Nacked" );
} catch (IOException e) {
logger.error("Unable Acknowledge Queue Message", e);
throw e;
}
}
}
Seems like you are missing some basics here.
Taken from here and some code of mine.
Setting up the connection outside of the consumer thread:
//executed once
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("someHost");
factory.setUsername("user");
factory.setPassword("pass");
Connection connection = factory.newConnection();
What you have to do inside your thread:
//Consumer - executed in a Thread
QueueingConsumer consumer = new QueueingConsumer(connection.createChannel());
boolean autoAck = false;
channel.basicConsume("hello", autoAck, consumer);
while (!Thread.current().isInterrupted())) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
//...
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
In general I do still recommand you check out the spring-amqp library it integrates perfectly.

Categories