Hi I have a service in Android that handles the HTTP method POST as specified below. Now, I need to call an Intent in
replaceResourceSegment()
method. It has a handler that takes nearly 90 seconds to complete the task. Within that time, control exits the handler block. But I want my program to continue within handler for POST. In short, I want my service to pause for sometime inside the POST handler, till my Intent (with handler) completes its execution and I need to delay sending the response of HTTP Post. Can some one guide me how to do this implementation?
if(method.equals("POST"))
{
conn.receiveRequestEntity((HttpEntityEnclosingRequest)request);
HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
String content_type = ""+entity.getContentType();
JSONReceived = EntityUtils.toString(entity);
if(content_type.contains("json"))
{
Log.d(TAG,"Content received is: "+JSONReceived);
bufferedWriter = new BufferedWriter(new FileWriter(new File(getFilesDir()+File.separator+constants.UPDATED_SCRIPT_FILE)));
bufferedWriter.write(JSONReceived);
bufferedWriter.close();
try {
parseJSON(JSONReceived);
replaceResourceSegment(); //Call to an intent with startActivityForResult()
continueExecution(); //Continue the execution from here
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG,"IOException line 157");
}
Code for sending response back:
HttpResponse postResponse = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
postResponse.setEntity(new StringEntity("Got it"));
conn.sendResponseHeader(postResponse);
conn.sendResponseEntity(postResponse);
I managed to solve the problem by using a boolean variable with default value false. It will be checked periodically and keeps the control inside the POST method's handler.
android.os.SystemClock.sleep(30000); //Sleeps for 30 seconds and invoke busy waiting in a thread
Thread syncThread = new Thread(new LoopCheck());
syncThread.start();
synchronized(syncThread)
{
Log.d(TAG,"Inside synchronized blockk");
try
{
syncThread.wait();
}catch(InterruptedException ie){
ie.printStackTrace();
}
}
The thread class is defined as below:
class LoopCheck extends Thread{
public LoopCheck(){
}
public void run(){
while(true)
{
try {
Thread.sleep(10000);
if(write)
{
write = false;
synchronized(syncThread)
{
syncThread.notify();
}
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Related
when you make a network connection in Android you are blocking the main thread , so you have to move "some" of this task to a new thread
I have 2 questions on this part
1- which of the following operation is blocking the main thread (A or B)
//A:
HttpURLConnection c = (HttpURLConnection) (new URL(url)).openConnection();
//B:
InputStream stream=c.getInputStream();
2- if "both" of the above (A & B) must run in a new thread , dose it have a bad effect to run each one in a new separate thread? take a look to the following code:
//I temporary removed try & catch to simplify the code
public class connect{
HttpURLConnection c; String url;
public connect(String url){
this.url=url;
new Thread(new Runnable{
#override public void run(){
c = (HttpURLConnection) (new URL(url)).openConnection();
}
});
}
public InputStream get(){
return c.getInputStream();
//or make this one in a new thread
}
public InputStream post(Sring params){
c.setRequestMethod("POST");
//.. make some code for posting data , and then call get()
//thats why i cannot perform c.getInputStram() at the same time with openConnection()
return get()
}
}
which of the following operation is blocking the main thread (A or B)?
It is pretty evident that both operations A and B will block the main thread. Just calling the following on the main thread will throw an exception(NetworkOnMainThreadException) right away:
HttpURLConnection c = (HttpURLConnection) (new URL(url)).openConnection();
Also when you are calling the following line on main thread:
InputStream stream=c.getInputStream();
You are simply trying to read a stream of bytes over a network. Now there are various factors that will determine the time taken by this operation to complete. For instance, network speed, overall number of bytes that you want to read, etc. The application should not really need to wait and stay idle till the reading process has been completed. All the UI related process should be able to run and consume the resources as a user reacts with your application which will not be possible because of the ongoing byte reading process which is actually blocking the main thread.
if both A and B must run in a new thread , dose it have
a bad effect to run each one in a new separate thread?
Technically, yes it is bad to run both in separate threads. Besides why would you want to do so? Before initiating stream reading process you need to make sure that the connection has been opened. Calling A and B in separate threads will raise concurrency issues. You must call B after A so if you even resolve concurrency issues, it will be of no use to make two separate threads.
EDIT:
So as you said in comments that you want to avoid using AsyncTask. An alternative for that is Java Threads. Check out the following sample usage of threads:
static public class MyThread extends Thread {
#Override
public void run() {
try {
// add your url and open connecttion here
HttpURLConnection c = (HttpURLConnection) (new URL("your url here")).openConnection();
// read stream or whatever data you want
InputStream stream = c.getInputStream();
} catch (Exception e) {
e.printStackTrace();
} finally {
//close your connection & wipe input stream here.
}
}
}
Now here is how we can call this thread:
private Thread downloadThread = new MyThread();
downloadThread.start();
At any time, you can also check if your thread is running or not by using the following code:
if (downloadThread != null && downloadThread.isAlive()) {
// do something when thread is alive here
}
This solution uses handler to connect main thread with background thread(the thread that does the HTTP connection stuff)
public class MainActivity extends AppCompatActivity {
Thread mThread;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startThread();
}
public void startThread(){
String url = "www.google.com";
String result = "";
mThread = new Thread(new Runnable() {
public void run() {
InputStream is = null;
HttpURLConnection conn;
try {
ConnectivityManager connMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = null;
if (connMgr != null) {
networkInfo = connMgr.getActiveNetworkInfo();
}
if (networkInfo != null && networkInfo.isConnected() && !mThread.isInterrupted()) {
conn = (HttpURLConnection) url.openConnection();
is = conn.getInputStream();
//Here you get the result from inputStream
}
threadMsg(result);
}catch (IOException e){
e.printStackTrace();
}
finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void threadMsg(String msg) {
if (msg != null && !msg.equals("") && !mThread.isInterrupted()) {
Message msgObj = handler.obtainMessage();
Bundle b = new Bundle();
b.putString("message", msg);
msgObj.setData(b);
handler.sendMessage(msgObj);
}
}
private Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
String result = msg.getData().getString("message");
// What you want to do in UI thread
}
};
});
mThread.start();
}
I'm building an application for sending files between two Android phones, now i have a ListActivity that retrieves the sdcard and lists the files, when the ListActivity first starts on the two devices a ServerSocket is set up and listening with .accept() ...
this thread starts when the ListActivity starts :
ReceiveFileSendRequestThread ReceiveFileSendRequestThread = new ReceiveFileSendRequestThread();
ReceiveFileSendRequestThread.start();
and here is the full thread class:
static public class ReceiveFileSendRequestThread extends Thread {
public void run() {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(6789, 200);
connectionServ = serverSocket.accept();
requestFileInServer = new ObjectInputStream(
connectionServ.getInputStream());
requestFileString = (String) requestFileInServer.readObject();
handler.post(new AcceptFileSendAlertDialogRunnable());
while (okToSend == null) {
}
if (okToSend == true) {
requestFileOutServer = new ObjectOutputStream(
connectionServ.getOutputStream());
requestFileOutServer.writeObject("oktosend");
requestFileOutServer.flush();
serverSocket.close(); // // Receive File
} else if (okToSend == false) {
requestFileOutServer = new ObjectOutputStream(
connectionServ.getOutputStream());
requestFileOutServer.reset();
requestFileOutServer.writeObject("notosend");
requestFileOutServer.flush();
serverSocket.close();
ReceiveFileSendRequestThread ReceiveFileSendRequestThread = new ReceiveFileSendRequestThread();
ReceiveFileSendRequestThread.start();
}
} catch (IOException e) {
Log.d("Connection Error:", "Error binding port!");
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
And when onLongClick on an item (to send file) the following thread starts:
public boolean onItemLongClick(AdapterView<?> parentView, View childView,
int position, long id) {
// TODO Auto-generated method stub
fileClickedName = (((TextView) childView).getText()).toString();
fileClickedPath = file.toString() + "/" + fileClickedName;
fileClickedFile = new File(fileClickedPath);
SendFileThread SendFileThread = new SendFileThread();
SendFileThread.start();
return true;
}
SendFile Thread:
static public class SendFileThread extends Thread {
public void run() {
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress(sharedIp, 6789), 200);
requestFileOutClient = new ObjectOutputStream(
socket.getOutputStream());
requestFileOutClient.writeObject(fileClickedName);
requestFileOutClient.flush();
requestFileInClient = new ObjectInputStream(
socket.getInputStream());
toSendOrNot = (String) requestFileInClient.readObject();
if (toSendOrNot.equals("oktosend")) {
socket.close();
} else if (toSendOrNot.equals("notosend")) {
socket.close();
ReceiveFileSendRequestThread ReceiveFileSendRequestThread = new ReceiveFileSendRequestThread();
ReceiveFileSendRequestThread.start();
handler.post(new ClientFileSendAlertDialogRunnable());
}
// //
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Now, when I launch the ListActivity and longClick on an item, the the file name is sent to the second device, the second device pops up an alertDialog asking the user if to accept the file or not, (accepting file is still not ready for now) if the user presses on CANCEL (on the ReceiveFileSendRequestThread) a string is sent to the first device "notosend", the first user receives the string "notosend" and depending on that the thread closes the socket and re invoke the thread to listen to the second peer if he wants to send another file , AND pops up an alertDialog telling the first user that your file was refused to be received ... >>>> this is totally works >>>> but when the first device attempts to send another file (long press again on a file [item on the list] ) the second user receives the new file name selected by the first user successfully and alertDialog pops up if to accept or cancel BUT the first user receives that the file send was refused ... without having the second user pressing on the cancel button !!! ... i don't know why toSendOrNot = (String) requestFileInClient.readObject(); keeps on taking the previous value without even waiting for the second device to write the new object.
I would appreciate it if someone could help me with this , Many thanks.
AFTER HOURS OF DEBUGGING >>> I FINALLY FOUND THE BUG!
in thread ReceiveFileSendRequestThread() the Boolean variable okToSend is null when first receiving a request from the second device, when the second device cancels the request the oKtoSend will be set to notosend, so whenever the second device attempts to send another file the variable okToSend will always has the previously assigned value. So I simply added okToSend = null; before the while loop, and now is working perfectly.
i wrote a Server for our global Leadbord which actually works now.
I can send data to it if it's active. But if it's down my app does not stop. I dont get a solution for it i hope you can help me. Here is the sending:
public void saveToDatabase(LeadboardElement element2) {
final LeadboardElement element = element2;
send = false;
// Need to be a thread! else android blocks it because it could take to
// long to send!
this.thread = new Thread() {
public void run() {
try {
Socket soc = new Socket(Config.TCP_SERVERNAME_IP,
Config.TCP_PORT);
DataOutputStream out = new DataOutputStream(
soc.getOutputStream());
DataInputStream in = new DataInputStream(
new BufferedInputStream(soc.getInputStream()));
// to call the save statement!
out.writeInt(0);
// give the stuff
out.writeUTF(element.getName());
out.writeInt(element.getLevel());
out.writeInt(element.getKillPoints());
// close it
out.close();
in.close();
soc.close();
send = true;
//join at every error
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
};
// start it
thread.start();
// join thread
if (!send) {
boolean retry = true;
while(retry)
try {
this.thread.join();
retry = false;
Log.w(TAG, "sending to server stopped!");
} catch (InterruptedException e2) {
Log.w(TAG, "Thread could not be joined");
}
}
}
I noticed that i need to do it in a thread since API 5 so it works like this. It's called at the end of an Game if the player touches the screen. Everything get stopped and the data is sent to the Server. If hes down it does not work we stuck in the fade to black.
I guess i need something like a timeout. I tried it with a CountDownTimer but this acutally does not solve the problem.
Thanks alot!
Changing the way you initialize the socket, you can set a timeout.
Socket s1 = new Socket();
s1.setSoTimeout(200);
s1.connect(new InetSocketAddress("192.168.1." + i, 1254), 200);
Add a timeout when creating a new Socket
After reading a few posts and reading on the developers page about ASYNCTASK, I came up with the following code, and assigned it to a button:
private class TalkToServerTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
String response = "";
try {
InetAddress serverAddr = InetAddress.getByName(params[0]);
Socket s = new Socket(serverAddr, Integer.valueOf(params[1]));
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(s.getOutputStream())), true);
// WHERE YOU ISSUE THE COMMANDS
out.println(params[2]);
// BufferedReader input = new BufferedReader(
// new InputStreamReader(s.getInputStream()));
DataInputStream dataInputStream = null;
dataInputStream = new DataInputStream(s.getInputStream());
st = dataInputStream.readLine().toString();
// String st = s.readLine();
// st = input.readLine();
// read line(s)
s.close();
return st;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
#Override
protected void onPostExecute(String result) {
serverresponse = result;
}
}
The idea being, that when a button is clicked, the ASyncTask sends the word "getDomains" to a console app running on my server, the server acts on this and sends back a string with the list of domains created on an email server.
I have verified that the server is receiving the command "getdomains", and it in turn replies with a pipe-delimited string of domains. The problem being however, that I've set a Toast to pop up with the results of the socket transaction, and the toast shows nothing. If I hit the button again, the Toast shows the list of domains. To me, it seems as if the socket is first returning an empty
Here is the button code:
case R.id.btnDomains:
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(this);
IpAddress = sp.getString("ipaddress", "0.0.0.0");
Serverport = sp.getString("tcpport", "12345");
buttonpressed = "domains";
// TalkToServerTask task = new TalkToServerTask();
new TalkToServerTask().execute(IpAddress, Serverport, "getDomains");
Intent buttonActivity = new Intent(MainActivity.this, Rules.class);
buttonActivity.putExtra(MainActivity.DOMAINLIST, serverresponse);
Toast.makeText(getApplicationContext(), serverresponse,
Toast.LENGTH_SHORT).show();
the variable "serverresponse" is what at first is showing empty, but then shows the list of servers.
As Thepoosh mentioned, AsyncTask is working on a separate thread.
Therefore the thread is being executed and didn't get result yet when you first time press the button.
What you should do is to show the data in onPostExecute method. Also you should pass the context to your AsyncTask.
public TalkToServerTask(Context context) {
this.context = context;
}
#Override
protected void onPostExecute(String result) {
Intent buttonActivity = new Intent(context, Rules.class);
buttonActivity.putExtra(MainActivity.DOMAINLIST, result);
Toast.makeText(context.getApplicationContext(), result, Toast.LENGTH_SHORT).show();
}
You just need to change your code a little,
You need to move this 3 lines,
Intent buttonActivity = new Intent(MainActivity.this, Rules.class);
buttonActivity.putExtra(MainActivity.DOMAINLIST, serverresponse);
// and also the line to startActivity() as well.
Toast.makeText(getApplicationContext(), serverresponse,
Toast.LENGTH_SHORT).show();
from your Switch..case to onPostExecute of the AsyncTask where you have written serverresponse = result
That's because the AsyncTask run in a different thread, while you have executed in Swtich...case it's not like that the next line of code will not get executed untill the AsyncTask finishes, So all dependent code should be written in onPostExecute of the Task.
AsynTask is run on a separate thread from the UI thread. So there are 2 threads running parallel. While you are trying to display the list in a toast on main UI thread, the AsynTask is still preparing to fetch the list, or probably creating sockets in its own thread. Hence the list data is still empty.
The postexecute method of Asynctask is run on the main UI thread, so its safe and correct to update the UI in it.
In my application I download and parse a html page. However, I want to be able to stop the download in its tracks (i.e. when the user hits cancel).
This is the code I use now, which is being called from doInBackground from ASyncTask.
How do I cancel this request from outside of the ASyncTask?
I currently use htmlcleaner
HtmlCleaner cleaner = new HtmlCleaner();
CleanerProperties props = cleaner.getProperties();
props.setAllowHtmlInsideAttributes(true);
props.setAllowMultiWordAttributes(true);
props.setRecognizeUnicodeChars(true);
props.setOmitComments(true);
try {
URL url = new URL(urlstring);
URLConnection conn = url.openConnection();
TagNode node = cleaner.clean(new InputStreamReader(conn.getInputStream()));
return node;
} catch (Exception e) {
failed = true;
return;
}
Can't you use AsyncTask.cancel()? You should be able to then use the onCancelled callback to return to the main activity..
Ok, I believe I've solved this.
In my Activity class I have a variable (boolean) failed. Also, I have a private Downloader class within the activity which extends ASyncTask. This way, the Downloader class has access to the failed boolean. When the Activity launches, it starts the Downloader task and a progress dialog pops up. When the task finishes, it closes the dialog and then goes on processing the downloaded content.
However, when the user cancels the progress dialog, failed is set to true, and the user is sent back to the previous activity by a call to finished. In the meantime, Downloader is still busy downloading. Because the results are now unneccessary, we want it to stop using resources asap. In order to accomplish this, I have broken up the doInBackground method in as much steps as possible. After each step I check if failed is still false, when it is set to true, it simply doesn't go to the next step. See it in action below. Furthemore, the BufferedReader reader is public, and in the onCancelled method I execute reader.close(). This will throw all sorts of exceptions, but these are properly caught.
public void DoInBackground(.........) {
try {
URL url = new URL(uri);
URLConnection conn = url.openConnection();
if (!failed) {
isr = new InputStreamReader(conn.getInputStream());
if (!failed) {
reader = new BufferedReader(isr);
publishProgress(1);
if (!failed) {
TagNode node = cleaner.clean(reader);
publishProgress(2);
return node;
}
}
}
} catch (Exception e) {
failed = true;
Log.v("error",""+e);
}
}
#Override
protected void onCancelled() {
failed = true;
if (reader != null)
try {
reader.close();
} catch (IOException e) {
failed = true;
}
if (isr != null)
try {
isr.close();
} catch (IOException e) {
}
}
I know that I could have broken up the downloading process in even tinier bits, but I am downloading very small files, so it's not that important.