I'm programming an app with my brother, and today unfortunately, I encountered with a problem.
When the app load a php page via my asynctask class it works fine. but I would like to program this situation: if the remote server is down, or crash, and doesnt display the right page, the application will show error message. but instead, the app crashes =[
I tried to load this page, for example:
http://alonadoni.com/sql3.php
(I want to simulate that there is a problem with the server. the regular page is sql2.php and it works fine when the server works)
When the app try to load this page (sql3.php) , the app crashes.
I did another experiment : I created a file sql3.php, and wrote "aaaaaaaa" in the page, the app doesn't crash in this situation. it downloaded the data "aaaaa". in this case, the app show jsonexecption error.
Unfortunately, I can't get logcat because my old computer can't run emulators, and my phone also can't connect to my computer on developer mode =[ When I try application I create an apk then transfer the file to my phone and install.
my code is:
in OnCreate:
String serverURL = sss() + "sql3.php?imei=" + imei;
new LongOperation().execute(serverURL);
outside OnCreate:
private class LongOperation extends AsyncTask<String, Void, Void> {
private final HttpClient Client = new DefaultHttpClient();
private String Error = null;
protected void onPreExecute() {
}
protected Void doInBackground(String... urls) {
try {
HttpGet httpget = new HttpGet(urls[0]);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
data[x] = Client.execute(httpget, responseHandler);
} catch (ClientProtocolException e) {
Error = e.getMessage();
Toast.makeText(getApplicationContext(),"error2" , Toast.LENGTH_LONG).show();
cancel(true);
} catch (IOException e) {
Error = e.getMessage();
Toast.makeText(getApplicationContext(),"error34" , Toast.LENGTH_LONG).show();
cancel(true);
}
return null;
}
public void onPostExecute(Void unused) {
if (Error != null) {
} else {
try {
JSONObject json = new JSONObject(data[x]);
name = json.getString("name");
} catch (JSONException e) {
Toast.makeText(getApplicationContext(),"e" + e, Toast.LENGTH_LONG).show();
}
}
x++;
}
}
DoInBackground of asynctask needs to contain only NON UI work , hence referring to context and performing UI operations in UI thread may cause crash.
You can perform UI operations in postexecute of asynctask.
Hence Removing toast from above code which refers to UI operation will solve your issue
Related
Having some issues with a custom class that extends AsyncTask. My app is Targeting Android 4.0.3 and the below code works fine for 30+ people testing it. However there are two users that are seeing the app crash when I call new AsyncRequest like below.
I've got a working logger that is recording to a text file on the users storage and doesn't record the entry that is in the AsyncRequest constructor. So I have to assume that the crash is happening before the constructor is called.
One of the two devices that are experiencing this crash is running Android 4.0.4 apparently. Not sure what the other device is running. Unfortunately I dont' have access to the two devices so can't see a logcat output.
Any input as to why the object creation is causing a crash would be greatly appreciated.
String url = "www.google.com";
new AsyncRequest(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
And here is the full AsyncRequest class
public class AsyncRequest extends AsyncTask<String, String, String>{
HttpURLConnection connection;
InputStream inStream;
IApiCallback callback;
Context context_;
public AsyncRequest(IApiCallback callback, Context context) {
// Log entry added for testing. Never gets called.
FileLogger.getFileLogger(context).ReportInfo("Enter AsyncRequest Constructor");
this.callback = callback;
context_ = context;
}
#Override
protected String doInBackground(String... uri) {
try {
URL url = new URL(uri[0] + "?format=json");
FileLogger.getFileLogger(context_).ReportInfo("Async Request: Sending HTTP GET to " + url);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.addRequestProperty("Accept-Encoding", "gzip");
connection.addRequestProperty("Cache-Control", "no-cache");
connection.connect();
String encoding = connection.getContentEncoding();
// Determine if the stream is compressed and uncompress it if needed.
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
inStream = new GZIPInputStream(connection.getInputStream());
} else {
inStream = connection.getInputStream();
}
if (inStream != null) {
// process response
BufferedReader br = new BufferedReader(new InputStreamReader(inStream));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
} catch (SocketTimeoutException e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: SocketTimeoutException", e);
Log.i("AsyncRequest", "Socket Timeout occured");
} catch (MalformedURLException e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: MalformedUrlException", e);
} catch (IOException e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: IOException", e);
Log.i("doInBackground:","IOException");
if (e != null && e.getMessage() != null) {
Log.i("doInBackground:",e.getMessage());
}
} catch (Exception e) {
FileLogger.getFileLogger(context_).ReportException("Async Request: Exception", e);
} finally {
if (connection != null)
connection.disconnect();
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result != null)
FileLogger.getFileLogger(context_).ReportInfo("Async Request: Response is valid");
else
FileLogger.getFileLogger(context_).ReportInfo("Async Request: Invalid response");
callback.Execute(result);
}
}
EDIT: As per comments below.
Here is the full method that I call my custom AsyncTask from. All the logging messages I have up to creating the AsyncTask are showing in the log. None of the exceptions are.
The logging displays the url value just before creating my AsyncRequest and the URL is not malformed at all. It's what I'm expecting.
public void GetServerInfoAsync(IApiCallback callback, Context context) throws IllegalArgumentException, Exception {
if (callback == null)
throw new IllegalArgumentException("callback");
if (context == null)
throw new IllegalArgumentException("context");
try {
FileLogger.getFileLogger(context).ReportInfo("Build URL");
String url = GetApiUrl("System/Info");
FileLogger.getFileLogger(context).ReportInfo("Finished building URL");
if (url != null) {
FileLogger.getFileLogger(context).ReportInfo("GetServerInfoAsync: url is " + url);
new AsyncRequest(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
} else {
FileLogger.getFileLogger(context).ReportError("GetServerInfoAsync: url is null");
}
} catch (IllegalArgumentException iae) {
FileLogger.getFileLogger(context).ReportException("GetServerInfoAsync: IllegalArgumentException", iae);
throw iae;
} catch (Exception e) {
FileLogger.getFileLogger(context).ReportException("GetServerInfoAsync: Exception", e);
throw e;
}
}
First of all, just keep in mind that executeOnExecutor() is not available prior to Api 11. You have already said the issue is with a 4.0.4 device, but just keep this in mind.
Here are the steps I would take in order to troubleshoot what the problem is. It seems as if you have already done a few of these with all those ReportInfo() statements.
First, I assume your call to GetServerInfoAsync is within a try...catch, correct? I am checking because of your use of Throw. Also, you have already added logging to check for errors with the url. Since the errors occur before you actually use it, the error cannot be with the url, or any internet permissions.
You call the AsyncTask generation with references to callback and context. You have added logging via ReportInfo() which references context, and those work, yes? Therefore, context is not your issue. However, you never check what callback is. You throw an error if it is null, but you never do anything with it before you call AsyncRequest. Try a ReportInfo(callback.toString()) to see what it is.
If all else fails, it would seem to be an error with threading. Why not try using just AsyncTask, instead of executeOnExecutor(). Do you really need more than 1 background thread?
Sorry for not getting back to this sooner. There were numerous issues here.
First off... Thanks to Wolfram's suggestion I was able to catch the exception and diagnose that the issue was that my FileLogger (and another class) was a static reference and these two tablets couldn't find the reference at runtime. So I ended up removing Logging from my async methods.
Secondly, after making the above changes there was another issue which was that a looper had to be called from the main thread. It turned out that these two tablets weren't calling my async task from the main thread. So I had to enclose the async call in a new Handler using the MainLooper.
AsyncTask works fine in Android 4.x, but not for Android 2.3.6. I've step-by-step debugged Android 2.3.6 with a physical mobile device.
It hangs on here:
myTask = new GetDataFromServer();
GetDataFromServer is the class of AsyncTask.
What's going on?
Here under is my code, I only used 1 AsyncTask in my code and received messages from server.
that's all.
class GetDataFromServer extends AsyncTask<String, String, String>
{
protected void onPreExecute ()
{
progressDialog1=ProgressDialog.show(MainActivity.this, "Loading data", "Please wait...",true);
}
protected String doInBackground(String... params)
{
String resulttxt="";
try {
serverIp = InetAddress.getByName("192.168.1.123");
int serverPort=31000;
Socket clientSocket=new Socket(serverIp,serverPort);
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
bw.write(params[0]);
bw.flush();
BufferedReader br=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
resulttxt=br.readLine();
if(resulttxt.contains("OK"))
{
publishProgress(resulttxt);
}
else
{
publishProgress(resulttxt);
clientSocket.close();
bw.close();
br.close();
return null;
}
resulttxt="";
resulttxt=br.readLine();
resulttxt=resulttxt.trim();
clientSocket.close();
} catch (IOException e) {
if(Status_txt!=null)
Status_txt.append( "Server is done.");
}
catch (NetworkOnMainThreadException e){
if(Status_txt!=null)
Status_txt.append( "NetworkOnMainThreadException");
}
return resulttxt;
}
protected void onProgressUpdate(String...inStr){
String[] strData=inStr[0].split("_");
String szTemp="Last Purchase Date: ";
szTemp+=strData[1];
szTemp+=" ,Valid days: ";
szTemp+=strData[2];
//Status_txt.setText(szTemp);
if(Status_txt!=null)
Status_txt.setText("You Are The Super User");
}
protected void onPostExecute(String data) {
tl_prediction2.removeAllViews();
if (data == null)
{
}
else {
if((data.contains("#")==true) || (data.contains("*")==true)
||data.contains("&")==true)
{
String[] arrayTmp=data.split("#");
for(Integer i=0;i<arrayTmp.length;i++)
{
String[] SubArrayTmp=arrayTmp[i].split("_");
tl_prediction2.addView(generateRow(4,SubArrayTmp));
}
}
}
progressDialog1.dismiss();
}
};
Since you haven't posted any code, I could only give you some random probable solutions:
May be your AsyncTask is taking a lot of time to download. Trying increasing its priority using android.os.Process.setThreadPriority(9) inside doInBackground()
Check if you have other previous running long AsyncTask in your code. AsyncTask by default operates on a single background thread. That means your AsyncTask task wouldn't be executed unless your previous AsyncTask are done. To allow parallel execution use executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params). You can read more here
Check for Internet and other permissions in Manifest. This is mostly where people make mistake.
AsyncTask works with ThreadPool. If there too many synctasks are executing, the later AsyncTask will be blocked by others. I think you can use the thread tool in DDMS to check the How many ayncTasks are executing.
I created an Android project on 2.3.3 and tried it on mobile 2.3.3, everything works OK. It didn't work on mobile 4, so I re-built for Android 4, but I have the same problem.
This is the code:
public void FTP_Download(){
String server = "192.168.1.135";
int port = 21;
String user = "pc1";
String pass = "1551";
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(server, port);
ftpClient.login(user, pass);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
Toast.makeText(getBaseContext(), "download starting.",Toast.LENGTH_LONG).show();
// APPROACH #1: using retrieveFile(String, OutputStream)
String remoteFile1 = "i.xml";
File downloadFile1 = new File("sdcard/i.xml");
OutputStream outputStream1 = new BufferedOutputStream(new FileOutputStream(downloadFile1));
boolean success = ftpClient.retrieveFile(remoteFile1, outputStream1);
outputStream1.close();
if (success) {
Toast.makeText(getBaseContext(), "File #1 has been downloaded successfully.",Toast.LENGTH_LONG).show();
}
} catch (IOException ex) {
System.out.println("Error: " + ex.getMessage());
ex.printStackTrace();
} finally {
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Note:
I use commons-net-3.1 to connect.
In android version 2.3 above you can not start internet connection from main UI thread. Instead you should use AsyncTask. I assumed you are not using AsyncTask. If you are, then post the code and log cat also. Some examples of other operations that ICS and HoneyComb won't allow you to perform on the UI thread are:( from link posted in comment below ) -
Opening a Socket connection (i.e. new Socket()).
HTTP requests (i.e. HTTPClient and HTTPUrlConnection).
Attempting to connect to a remote MySQL database.
Downloading a file (i.e. Downloader.downloadFile()).
You should not use the main UI Thread to start a network connection or read/write data from it as #phazorRise explained it. But I strongly disagree with using an AsyncTask to perform your download. AsyncTask have been designed for short living operations and downloading a file doesn't belong to that category.
The most relevant way to achieve your goal, if your files are big (and I assume it depends on users, so we can say they are big) is to use a service to download the files.
I invite you to have a look at RoboSpice, it will give your app robustness for networking and it's really the most interesting library for network requests on Android.
Here is an inforgraphics to get familiarized with alternatives and understand why using a service is better than any other technology.
When I use "internet conections" programming for andoid 4, I do an Async Task as follows:
You can put this Class code into the same file as principal file to intercatue with global variables or functions.
public class MyAsyncTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String url = urls[0]
try {
//Connection request code, in your case ftp
} catch (Exception e) {
//Catch code
}
}
#Override
protected void onPostExecute(String result) {
//Code to de After the connection is done
}
}
Then, in the Activity I call the Asyn Task
String url = "http://...";
new MyAsyncTask().execute(url);
Edit
Here it's explained how to use Async Task, with an example
http://developer.android.com/reference/android/os/AsyncTask.html
I added this code, and all thing OK
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Note: I taked the code from #user1169115 comment in another post.
This isn't the best soluation, but I don't know why asynctask isn't work, so I don't have another choice.
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.
I am working with Android Facebook SDK and wanted to get a friends list. I have created an "AsyncTask" for doing such a thing. I am pasting my doInBackgroundMethod here.
#Override
protected String doInBackground(String... url) {
String jsonResponse;
try {
jsonResponse = Factory.getFacebook().request(Utils.LOGGEDIN_USER_FRIENDS);
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
return jsonResponse;
}
Utils Code
public static final String LOGGEDIN_USER_FRIENDS = "me/friends";
The problem I am running in to is that it is returning an empty jsonResponse for the first time the application runs. When I open my app the second time I am getting the JsonResponse. But for the first time however I am getting empty jsonResponse.
Can any one help me out in this regard.
Well it was easy... It was messed up by creating a String variable in a Factory. Later on, the same day I saw that, and called the AsyncTask built in method get which provides the result in the same thread.