This issue I am having is that my program can only send my string over TCP once through a AsyncTask. I have read that the AsyncTask can only run once and that after it runs it is supposed to be disposed and that if I need to send another command I should create another instance of the thread and use the new instance to send the new command. However for whatever reason when I attempt to create a second instance of my AsyncTask nothing happens. Something of note. I added a log command in the onPostExecute routine but I never see the log message.
I have the following in my Android-Manifest file:
<uses-permission android:name="android.permission.INTERNET/>
This is the code in the main activity thread that calls the async task.
public void SendCommand(int DeviceID, String DeviceName, String CommandName)
{
//Get the IP address and the port in order to communicate with the device
DatabaseHelper dbHelper = new DatabaseHelper(MainActivity.this);
SQLiteDatabase db = dbHelper.getWritableDatabase();
String dbQuery = "SELECT * FROM " + dbHelper.System_Table + ", " + dbHelper.Devices_Table + ", " +
dbHelper.Device_Commands_Table + " WHERE " + dbHelper.System_Table + "." + dbHelper.Attribute_Device_ID +
" = " + String.valueOf(DeviceID) + " AND " + dbHelper.Devices_Table + "." + dbHelper.Attribute_Device_ID +
" = " + String.valueOf(DeviceID) + " AND " + dbHelper.Device_Commands_Table + "." + dbHelper.Attribute_Device_ID +
" = " + String.valueOf(DeviceID) + ";";
Cursor c = db.rawQuery(dbQuery, null);
String IPAddress = "";
int Port = 0;
String DeviceCommand = "";
if (c.getCount() > 0)
{
int Finished = 0;
while (c.moveToNext() && Finished == 0)
{
int iColumnDeviceName = c.getColumnIndex(dbHelper.Attribute_Device_Name);
int iColumnDeviceCommandName = c.getColumnIndex(dbHelper.Attribute_Command_Name);
final String CheckDeviceName = c.getString(iColumnDeviceName);
final String CheckCommandName = c.getString(iColumnDeviceCommandName);
if (DeviceName.equals(CheckDeviceName) && CheckCommandName.equals(CommandName))
{
Finished = 1;
int iColumnIPAddress = c.getColumnIndex(dbHelper.Attribute_Device_IP);
int iColumnPort = c.getColumnIndex(dbHelper.Attribute_Device_Port);
int iColumnDeviceCommandString = c.getColumnIndex(dbHelper.Attribute_Command_String);
IPAddress = c.getString(iColumnIPAddress);
Port = c.getInt(iColumnPort);
DeviceCommand = c.getString(iColumnDeviceCommandString);
DeviceCommand = DeviceCommand.replace("<CR>", "\r");
Log.d("Device Command To Send", DeviceCommand);
}
}
c.close();
dbHelper.close();
ArrayList<String> passing = new ArrayList<String>();
ArrayList<String> result = new ArrayList<String>();
passing.add(IPAddress);
passing.add(String.valueOf(Port));
passing.add(DeviceCommand);
SendCommand SC = new SendCommand();
SC.execute(passing, result);
}
}
This is the seperate class file that the above routine creates a instance of and executes.
public class SendCommand extends AsyncTask<ArrayList<String>, Void, ArrayList<String>>
{
#Override
protected void onPreExecute()
{
//progress bar
}
#Override
protected ArrayList<String> doInBackground(ArrayList<String>... passing)
{
Log.d("Async Task", "Started to send command.");
//Get the connection info and command to send from the caller
String Data = passing[0].toString();
int StopAt = Data.indexOf(",");
String IPAddress = Data.toString().substring(1, StopAt);
Data = Data.replace("[" + IPAddress + ", ", "");
StopAt = Data.indexOf(",");
int Port = Integer.parseInt(Data.toString().substring(0, StopAt));
Data = Data.replace(Port + ", ", "");
StopAt = Data.indexOf("]");
String DeviceCommand = Data.toString().substring(0, StopAt);
Send_Command(IPAddress, Port, DeviceCommand);
return null;
}
protected void onPostExecute(Long result)
{
Log.d("Async Task", "Command Sent");
}
private void Send_Command(String IPAddress, int Port, String DeviceCommand)
{
//Setup the connection parameters
SocketAddress SA = new InetSocketAddress(IPAddress, Port);
int Timeout = 2000;
Socket socket = new Socket();
try
{
//Attempt to connect to the device
socket.connect(SA, Timeout);
int Count = 0;
int Kill = 0;
while (socket.isConnected() == false && Kill == 1)
{
//Waiting for the connection
Count = Count + 1;
if (Count == Timeout)
{
Kill = 1;
Log.d("Connection Status", "Timed out");
}
}
if (socket.isConnected() == true)
{
//send the command
BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
wr.write(DeviceCommand);
wr.flush();
Log.d("Sent Device Command", DeviceCommand);
//listen for the response
BufferedReader rd = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String Response;
while ((Response = rd.readLine()) != null)
{
Log.d("Received Device Response", Response);
}
rd.close();
}
//close the socket once the response is received
socket.close();
}
catch (UnknownHostException e)
{
Log.d("UnknownHostException", "Something bad happened");
}
catch (IOException e)
{
Log.d("IOException", "Something bad happened");
}
finally
{
if (socket != null)
{
try
{
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
Finally here is my log after attempting to send two commands over tcp.
01-14 23:53:29.773: D/Device Command To Send(31643): PN
01-14 23:53:29.773: D/Async Task(31643): Started to send command.
01-14 23:53:30.108: D/Sent Device Command(31643): PN
01-14 23:53:30.273: D/Received Device Response(31643): R
01-14 23:53:31.918: D/Device Command To Send(31643): PF
I can see that the first time the AsyncTask is started and it sends the command as well as receives the response from the target device. However I don't see "Async Task", "Command Sent" in the log so the onPostExecute is not executing.
Any thoughts on what I am doing wrong?
Your onPostExecute signature is wrong.
protected void onPostExecute(Long result)
It should be
protected void onPostExecute(ArrayList<String> result)
It might be also good to use #Override annotation.
Put some extra debug messages in your Send_Command, AsyncTask is probably hanging there.
Currently you are not Overriding onPostExecute from AsyncTask so change your onPostExecute signature as :
#Override
protected void onPostExecute(ArrayList<String> result)//<Change Long
// to ArrayList<String>
{
Log.d("Async Task", "Command Sent");
}
Related
I'm trying to parse a Large JSON file that contains 24,000 lines(it contains regular Expressions to detect sms from sms provider) , the file is locally stored in assets folder here's the code of my class
public class SmsActivity extends AppCompatActivity {
ListView lv;
ArrayList<String> msg;
ArrayAdapter<String> adapter;
JSONObject obj;
JSONArray rules_array;
JSONObject rule_object = null;
String body, address, smsDate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sms);
lv = (ListView) findViewById(R.id.list);
permission();
}
void permission() {
// first check for permissions
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS},
10);
} else {
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Inbox());
lv.setAdapter(adapter);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String permissions[], #NonNull int[] grantResults) {
switch (requestCode) {
case 10: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Inbox());
lv.setAdapter(adapter);
Toast.makeText(this, "Permission is granted",
Toast.LENGTH_SHORT).show();
} else {//If permission is not granted,then it will ask for permission .
permission();
}
}
}
}
public String loadJSONFromAsset() {
String json = null;
try {
InputStream is = getAssets().open("sms_formats.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
json = new String(buffer, "UTF-8");
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
return json;
}
//Arraylist is used to create dynamic array.The size can be varried .
public ArrayList<String> Inbox() {
msg = new ArrayList<>();
try {
//A Uri object is usually used to tell a ContentProvider what we want to access by reference.In this ,we are accessing inbox.
Uri uri = Uri.parse("content://sms/inbox");
//ContentResolver is used to request the content.
//cursor object gets the data.
Cursor cursor = getContentResolver().query(uri, new String[]{"_id", "address", "date", "body"}, null, null, null);
obj = new JSONObject(loadJSONFromAsset());
if (!obj.isNull("rules")) {
rules_array = obj.getJSONArray("rules");
//It checks whether there is any messages in inbox.If there is no message then the following if statement will not be executed.
if (cursor != null) {
while (cursor.moveToNext()) {
address = cursor.getString(1);
body = cursor.getString(3);
String date = cursor.getString(cursor.getColumnIndex("date"));
Long timestamp = Long.parseLong(date);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timestamp);
Date finaldate = calendar.getTime();
smsDate = finaldate.toString();
for (int i = 0; i < rules_array.length(); i++) {
rule_object = rules_array.getJSONObject(i);
if (!rule_object.isNull("name")) {
// you have a name for the rule
Log.e("NO", "error");
}
if (!rule_object.isNull("patterns")) {
JSONArray pattern_array = rule_object.getJSONArray("patterns");
for (int j = 0; j < pattern_array.length(); j++) {
JSONObject pattern_obj = pattern_array.getJSONObject(j);
if (!pattern_obj.isNull("regex")) {
String type = pattern_obj.getString("sms_type");
if (type.equals("transaction")) {
String regex = pattern_obj.getString("regex");
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(body);
if (matcher.find()) {
msg.add("\nSender=>" + address + "\n" + "Message=>" + body + "\n" + "Date and Time=>" + smsDate + "\n");
}
}
}
}
}
}
}
cursor.close();
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return msg;
}
}
The problem here is when i use Android studio debugger i can see that all JSON objects and JSONArrays have the values they are supposed to have.But when i run the app on the phone it gives me black screen with no output.
I tried to implement AsyncTask cause I thought the black screen was due the large amount of data it was processing on the main thread from the JSON file, but it didn't help either. Can Someone please point me in the right direction.
Ps-I'm a beginner in Android Development
edit added AsyncTask.
public class LoadData extends AsyncTask<String, Void, JSONObject> {
String bodyData;
#Override
protected JSONObject doInBackground(String... body) {
bodyData = body.toString();
if (!obj.isNull("rules")) {
try {
rules_array = obj.getJSONArray("rules");
for (int i = 0; i < rules_array.length(); i++) {
rule_object = rules_array.getJSONObject(i);
if (!rule_object.isNull("name")) {
// you have a name for the rule
Log.e("NO", "error");
}
if (!rule_object.isNull("patterns")) {
JSONArray pattern_array = rule_object.getJSONArray("patterns");
for (int j = 0; j < pattern_array.length(); j++) {
JSONObject pattern_obj = pattern_array.getJSONObject(j);
if (!pattern_obj.isNull("regex")) {
return pattern_obj;
}
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPostExecute(JSONObject pattern_obj) {
super.onPostExecute(pattern_obj);
String type = null;
try {
type = pattern_obj.getString("sms_type");
if (type.equals("transaction")) {
String regex = pattern_obj.getString("regex");
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(bodyData);
if (matcher.find()) {
msg.add("\nSender=>" + address + "\n" + "Message=>" + body + "\n" + "Date and Time=>" + smsDate + "\n");
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Parsing a large JSON file that contains 24,000 lines is complex work. Using AsynTask to perform that stuff in worker thread, then publish result in UI thread
When your app performs intensive work in response to user interaction, this single thread model can yield poor performance unless you implement your application properly. Specifically, if everything is happening in the UI thread, performing long operations such as network access or database queries will block the whole UI. When the thread is blocked, no events can be dispatched, including drawing events. From the user's perspective, the application appears to hang. Even worse, if the UI thread is blocked for more than a few seconds (about 5 seconds currently) the user is presented with the infamous "application not responding" (ANR) dialog. The user might then decide to quit your application and uninstall it if they are unhappy.
This stuff below could be done in worker thread, so you don't have to put it into onPostExecute() which runs on UI thread (That's the reason your UI had been blocking)
String type = null;
try {
type = pattern_obj.getString("sms_type");
if (type.equals("transaction")) {
String regex = pattern_obj.getString("regex");
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(bodyData);
if (matcher.find()) {
msg.add("\nSender=>" + address + "\n" + "Message=>" + body + "\n" + "Date and Time=>" + smsDate + "\n");
}
}
} catch (JSONException e) {
e.printStackTrace();
}
So it could be something like this
public class LoadData extends AsyncTask<String, Void, JSONObject> {
#Override
protected JSONObject doInBackground(String... body) {
// do your complex work
// This run on Worker Thread
return aJsonObject;
}
#Override
protected void onPostExecute(JSONObject jsonObject) {
super.onPostExecute(jsonObject);
// Doing anything with your view
// This run on UI Thread
}
}
i am trying to cancel an async task after a period of time. i have searched and found a lot of questions about this and all having the same answer like what i am doing below.
problem: after calling cancel, the webservice does not seem to cancel because it never reaches the onPostExecute.
please any help will be appreciated.
what i have tried:
after a certain period of time i call
class TimeOut that takes an asyncTask as its argument and performs the below:
if (task.getStatus() == AsyncTask.Status.RUNNING )
{task.cancel(true);}
if(task.isCancelled()==true)
{
Log.e(TAG,"TASK IS CANCELLED");
}
and in my async class
#Override
protected void onCancelled() {
webServiceError=true; //when timeout change it to true.(default false)
Toast.makeText(context, R.string.webserviceDownloadUserDataError, Toast.LENGTH_LONG).show();
if (pd.isShowing()) {
pd.dismiss();
}
super.onCancelled();
}
#Override
protected String doInBackground(String... params) {
int paramsTracker = 0;
webServiceError = false;
while(webServiceError==false) {
HttpClient httpclient = new DefaultHttpClient();
String url = params[paramsTracker];
paramsTracker = paramsTracker + 1;
HttpPost httppost = new HttpPost(url);
int paramsCount = params.length;
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(paramsCount);
for (int i = 0; i < (paramsCount - 1) / 2; i++) {
Log.d(TAG, "parameters: " + params[paramsTracker] + " - " + params[paramsTracker + 1]);
nameValuePairs.add(new BasicNameValuePair(params[paramsTracker], params[paramsTracker + 1]));
paramsTracker = paramsTracker + 2;
}
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
// execute http post request, call web service
response = httpclient.execute(httppost);
}
catch (Exception e) {
webServiceError = true;
Log.e(TAG, e.toString());
try {
AppLogging(TAG, "Error in calling web service " + e.toString(), LogFileName.ErrorLogFilename);
} catch (Exception a) {
}
}
if (webServiceError == false) {
int CNT = 0;
try {
String query = "SELECT COUNT(*) AS CNT FROM TASKS";
Cursor cursor = MainActivity.myDataBase.rawQuery(query, null);
if (cursor.moveToFirst()) {
do {
CNT = Integer.parseInt(cursor.getString(cursor.getColumnIndex("CNT")));
} while (cursor.moveToNext());
cursor.close();
}
} catch (Exception e) {
Log.e(TAG, e.toString());
try {
AppLogging(TAG, "Error in Database error getting task count " + e.toString(), LogFileName.ErrorLogFilename);
} catch (Exception a) {
}
}
if (CNT == 0) {
String webServiceResponse = "";
try {
Log.d(TAG, "Getting web service response");
// get web service response
HttpEntity entity = response.getEntity();
String content;
content = EntityUtils.toString(entity);
// Log.d(TAG, "content: "+ content);
// parse XML response
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(content));
Document doc = db.parse(is);
NodeList nodes = doc.getElementsByTagName("string");
Element line = (Element) nodes.item(0);
webServiceResponse = getCharacterDataFromElement(line);
String patternForON = "SET IDENTITY_INSERT \\[.*?\\] O?((N)|(FF))\\s*?;";
Pattern rForON = Pattern.compile(patternForON);
Matcher mForON = rForON.matcher(webServiceResponse);
String webServiceResponseWithOutIdentityInsert = mForON.replaceAll("");
String patternForInsert = "\\bINSERT (?!INTO)";
Pattern rForInsert = Pattern.compile(patternForInsert);
Matcher mForInsert = rForInsert.matcher(webServiceResponseWithOutIdentityInsert);
String webServiceResponseWITHOUTINSERTALONE = mForInsert.replaceAll("INSERT INTO ");
String webServiceResponsePURIFIED = webServiceResponseWITHOUTINSERTALONE.replaceAll(";;", ";");
Log.d(TAG, "content: " + webServiceResponsePURIFIED);
///////////END OF for removing queries that are not applicable in SQLITE
//FOR SPLITTING THE QUERIES AND PLACING EACH AT AN INDEX IN contentArray ARRAY
//String contentArray [] = webServiceResponse.split(";");
String contentArray[] = webServiceResponsePURIFIED.split(";");
Log.d(TAG, "contentArray length" + contentArray.length + ""); //GETS THE NUMBER OF QUERIES
pd.setMax(contentArray.length); //SETTING THE PROGRESS DIALOG (MAX PROGRESS)
for (int i = 0; i < contentArray.length; i++) {
// add the downloaded data to the local database
String query = contentArray[i];
try {
AppLogging(TAG, "Queries Downloaded splitted and purified\n query " + i + " :" + contentArray[i], LogFileName.LogFilename);
} catch (Exception l) {
}
Log.d(TAG, "query: " + query);
// if query contains "getdate()" replace with DATETIME('now'), to render the query executable in sqlite
query = query.replace("GETDATE()", "DATETIME('now')");
try {
MainActivity.myDataBase.execSQL(query); //EXECUTE QUERY
} catch (Exception e) {
Log.e(TAG, e.toString());
try {
AppLogging(TAG, "Error in performing query: " + query + "\nError: " + e, LogFileName.ErrorLogFilename);
} catch (Exception a) {
}
}
//Log.d("Initialize database, HTTPRequestGetUserData ", query);
publishProgress();
}
} catch (Exception e) {
webServiceError = true;
Log.e(TAG, e.toString());
try {
AppLogging(TAG, "Error: " + e.toString(), LogFileName.ErrorLogFilename);
} catch (Exception a) {
}
}
}
}
if (isCancelled()) return "0";
}
return "1";
}
As per Android AsyncTask documents, after calling cancel() on AsyncTask it won't call onPostExecute() instead it will call onCancelled(). So the flow is desired.
Now as per documentation here in, your code
#Override
protected String doInBackground(String... params) {
while(webServiceError=false) {
.....//calling webservice
if (isCancelled()) return "0";
}
return "1";
}
And now your onCancelled() looks like, with String parameters
#Override
protected void onCancelled(String object) { // Here object should be 0
webServiceError=true; //when timeout change it to true.(default false)
Toast.makeText(context, R.string.webserviceDownloadUserDataError, Toast.LENGTH_LONG).show();
if (pd.isShowing()) {
pd.dismiss();
}
super.onCancelled();
}
Now in this method argument object will be 0. And don't worry both methods onPostExecute() and onCancelled() will run on Main UI thread.
Now what I suggest you is, create a private method which you want to execute from onPostExecute() and onCancelled() with String parameters.
When you cancel a AsyncTask it will never hit the OnPostExecute procedure. It is mentioned here in the API Doc:
http://developer.android.com/reference/android/os/AsyncTask.html
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
First and foremost, I am an android newbie.
I am creating a simple app which pulls some data from the server through a PHP script and showing them in a ListView in the next Activity. However, I find that if the script returns nothing, the app crashes. So I kept a check that only if the script returns some data, the app would switch to the next activity.This is my code.
public class HomeScreen extends ActionBarActivity {
String message3;
String message_short;
String[] items;
String[] short_items;
int check = 0;
private ProgressDialog dialog;
public String readJSONFeed(String URL)
{
StringBuilder sb = new StringBuilder();
HttpClient client = new DefaultHttpClient();
HttpGet hg = new HttpGet(URL);
try
{
HttpResponse response = client.execute(hg);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if(statusCode == 200)
{
HttpEntity en = response.getEntity();
InputStream content = en.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
String line;
while((line = reader.readLine()) != null)
{
sb.append(line);
}
}
else
{
Log.e("JSON", "Failed to download File");
}
}
catch(ClientProtocolException e)
{
e.printStackTrace();
}
catch(IOException e)
{
e.printStackTrace();
}
return sb.toString();
}
private class ReadJSONFeedTask extends AsyncTask<String, Void, String>
{
protected void onPreExecute()
{
super.onPreExecute();
dialog = new ProgressDialog(HomeScreen.this);
dialog.setMessage("Downloading Notifications. Please wait . . .");
dialog.setIndeterminate(false);
dialog.setCancelable(false);
dialog.show();
}
protected String doInBackground(String...urls)
{
return readJSONFeed(urls[0]);
}
protected void onPostExecute(String result)
{
dialog.dismiss();
try
{
JSONArray jsonArray = new JSONArray(result);
items = new String[jsonArray.length()];
short_items = new String[jsonArray.length()];
for(int i = 0; i < jsonArray.length(); i++)
{
JSONObject jobj = jsonArray.getJSONObject(i);
message3 = "SUBJECT : " + jobj.getString("subject") + "\n\n" +
"DATE : " + jobj.getString("date") + "\n" + jobj.getString("time") + "\n\n"
+ "NOTICE : " + jobj.getString("notice");
message_short = "SUBJECT : " + jobj.getString("subject") + "\n"
+ "NOTICE : " + jobj.getString("notice").substring(0, 20) + "..."
+ "\n" + jobj.getString("time");
items[i] = message3;
short_items[i] = message_short;
check += 1;
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_screen);
}
public void notificationPane(View view)
{
String localhost = "http://10.0.2.2/example/json/notification.php";
new ReadJSONFeedTask().execute(localhost);
if(check != 0)
{
Intent i = new Intent(HomeScreen.this, NotificationPanel.class);
i.putExtra("items", items);
i.putExtra("short_items", short_items);
startActivity(i);
}
else
{
Toast.makeText(getBaseContext(), "Either there is no notification to display or there might be a problem with your internet connection.", Toast.LENGTH_SHORT).show();
}
}
public void recentNotice(View view)
{
String localhost = "http://10.0.2.2/example/json/recent_notification.php";
new ReadJSONFeedTask().execute(localhost);
if(check != 0)
{
Intent i = new Intent(HomeScreen.this, NotificationPanel.class);
i.putExtra("items", items);
i.putExtra("short_items", short_items);
startActivity(i);
}
else
{
Toast.makeText(getBaseContext(), "Either there is no notification to display or there might be a problem with your internet connection.", Toast.LENGTH_SHORT).show();
}
}
}
The int check is where I check whether the server has returned any JSON data. If the check is not 0, it does not switch to the next activity.But problem with this code is that when I click the button, it shows me the TOAST Either there is no notification to display or there might be a problem with your internet connection. even before it has pulled data from the server, thus it never goes to the next Activity NotificationPanel.class. I understand this is due to the AsyncTask. What is more weird is if I keep clicking the button several times, the app suddenly switches to the next activity showing the data in ListView. But it doesn't always work. Is there a work around to this problem?Please help.Thank you in advance.
Move your code with if else to the last in onPostExecute. AsyncTask is still in execution when you check the result.
You should put this piece of code in the onPostExecute method:
if(check != 0)
{
Intent i = new Intent(HomeScreen.this, NotificationPanel.class);
i.putExtra("items", items);
i.putExtra("short_items", short_items);
startActivity(i);
}
else
{
Toast.makeText(getBaseContext(), "Either there is no notification to display or there might be a problem with your internet connection.", Toast.LENGTH_SHORT).show();
}
Put them after your current code and tell me if it worked.
"What is more weird is if I keep clicking the button several times, the app suddenly switches to the next activity showing the data in ListView. But it doesn't always work. Is there a work around to this problem?"
What you are saying here is prety clear. You cannot run your intent at first, because json is not yet fetched, when you click the button first few times. But if you continue clicking it, it will eventually fetch JSON and be able to go to next activity.
SO I'm prety sure what I said is what needs to be done.
EDIT
I'm prety sure your async task should looklike this:
private class ReadJSONFeedTask extends AsyncTask<Void, Void, Void>
{
#Override
protected void onPreExecute()
{
super.onPreExecute();
dialog = new ProgressDialog(HomeScreen.this);
dialog.setMessage("Downloading Notifications. Please wait . . .");
dialog.setIndeterminate(false);
dialog.setCancelable(false);
dialog.show();
}
//no need a return statment
#Override
protected Void doInBackground(Void... params)
{
try
{
JSONArray jsonArray = new JSONArray(readJSONFeed(urls[0]));
items = new String[jsonArray.length()];
short_items = new String[jsonArray.length()];
for(int i = 0; i < jsonArray.length(); i++)
{
JSONObject jobj = jsonArray.getJSONObject(i);
message3 = "SUBJECT : " + jobj.getString("subject") + "\n\n" +
"DATE : " + jobj.getString("date") + "\n" + jobj.getString("time") + "\n\n"
+ "NOTICE : " + jobj.getString("notice");
message_short = "SUBJECT : " + jobj.getString("subject") + "\n"
+ "NOTICE : " + jobj.getString("notice").substring(0, 20) + "..."
+ "\n" + jobj.getString("time");
items[i] = message3;
short_items[i] = message_short;
check += 1;
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
#Override
protected void onPostExecute(String result)
{
if(check != 0)
{
Intent i = new Intent(HomeScreen.this, NotificationPanel.class);
i.putExtra("items", items);
i.putExtra("short_items", short_items);
startActivity(i);
}
else
{
Toast.makeText(getBaseContext(), "Either there is no notification to display or there might be a problem with your internet connection.", Toast.LENGTH_SHORT).show();
}
dialog.dismiss();
}
}
I have 2 projects: App Project and own library.
My project have this code:
main.java (MyProject)
...
// call method Lib.download()
String nameFiles[] = {"do.mp3","re.mp3","mi.mp3","billiejean.mp3"};
String url = "http://myweb.net/getfiles/";
if( Lib.download(Main.this, url, nameFiles, "internal", "folderExtra" ) == 1){
System.out.println("finish");
} else {
System.out.println("error download");
}
The problem is that Lib.download return the default value without waiting for it to finish the rest of code.
I'm trying controller this with Semaphore / CountDownLatch / whiles but it doesn't work and also i'm trying implement a callback but without success because classes for "download" must be out of my project. Any help?
The downloads are done correctly but the download method returns the value before completing the downloads ...
My intention is to call a method "download" from any activity, this launch the dialog process and the rest of my activity code does not run until this method "download" returns a value.
Lib.java (MyLib)
/** DOWNLOADER WITH PROCESSDIALOG **/
ProgressDialog pDialog;
Context context;
String urlName;
String[] filesToDownload;
int downloadOK = -1;
int currentFile = -1;
int totalFiles = 0;
String typeStorage = "internal";
String folder = "";
CountDownLatch controlLatch;
public int download(Context ctx, String urlName, String[] filesToDownload, String typeStorage, String extraFolder ){
this.context = ctx;
this.urlName = urlName;
this.filesToDownload = filesToDownload;
this.totalFiles = filesToDownload.length;
this.typeStorage = typeStorage;
/** Almacenamiento de la descarga - Interno o externo **/
if (typeStorage.equalsIgnoreCase("internal")) {
System.out.println("internal");
this.folder = context.getFilesDir().toString() + "/";
} else if (typeStorage.equalsIgnoreCase("external")) {
}
/** EXTRA OPTIONALL **/
if (extraFolder != null && extraFolder != "") {
folder += extraFolder;
}
File directoryFile = new File(folder);
if (!directoryFile.isDirectory()) {
if (!directoryFile.mkdir()) {
Toast.makeText(context, "problems create directory", Toast.LENGTH_LONG).show();
}
}
pDialog = new ProgressDialog(context);
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.setMax(100);
pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pDialog.setTitle("Descargando recursos...");
// controlLatch = new CountDownLatch(1);
startDownload();
/*
try {
System.out.println("STOP");
controlLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
return downloadOK;
}
private void startDownload() {
currentFile++;
System.out.println("startDownload!");
if (currentFile < totalFiles) {
pDialog.setMessage("Descargando " + (currentFile + 1) + " de " + totalFiles + "\n\n(..." + filesToDownload[currentFile] + ")");
System.out.println("startDownload currentFile +[" + currentFile + "] totalFiles [" + totalFiles + "]");
System.out.println("file: " + filesToDownload[currentFile].toString());
new DownloaderFile().execute(filesToDownload[currentFile]);
}
}
private class DownloaderFile extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... params) {
try {
File checkFile = new File(folder + params[0]);
if(!checkFile.exists()){
URL urlFinal = new URL(urlName+params[0]);
URLConnection connection = urlFinal.openConnection();
connection.connect();
int fileLength = connection.getContentLength();
InputStream input = new BufferedInputStream(urlFinal.openStream());
OutputStream output = new FileOutputStream(folder + params[0]);
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} else {
System.out.println("The file " + filesToDownload[currentFile] + " is downloaded" );
}
} catch (Exception e) {
e.printStackTrace();
return "error";
}
return "ok";
}
#Override
protected void onPreExecute() {
super.onPreExecute();
System.out.println("-1");
File checkFile = new File(folder + filesToDownload[currentFile]);
if(!checkFile.exists()){
System.out.println("pDialogShow");
pDialog.show();
}
}
#Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
pDialog.setProgress(progress[0]);
}
#Override
protected void onPostExecute(String res) {
System.out.println("10");
if (currentFile == (totalFiles - 1)) {
System.out.println("FINISH!!!!!!!!!!!!!!");
currentFile = 0;
pDialog.dismiss();
downloadOK = 1;
//controlLatch.countDown();
} else {
if (res.equals("ok")) {
startDownload();
} else {
System.out.println("error download");
downloadOK = 0;
}
}
}
}
AsyncTasks would run at separate thread once your .execute() is called. Your calling line startDownload() will never wait for AsyncTask to return any value, what you can do is:
Implement some callback interface on your main activity, so that your onPostExecute method would be able to notify your main activity once the operation is finished.
As you're the one developing this library, consider moving the AsyncTask to your main activity, and leave very plain download functions (what in your doInBackground method) in the library.
Use LocalBroadcastManager to send a broadcast from your library, listen and react to it on your main.java
I create simple Activity with dynamic table from file CSV.
I'd like to simply show progress bar before file is read and UI build.
I stuck on AsyncTask..
If i execute mytask.get() then the UI building is blocked and my desired progressDialog won't shown.
But if i call mytask.get() then app crashes bacause all my UI depends on that CSV.
I found this:
ProgressDialog not shown when AsyncTask.get() called
with related:
Common class for AsyncTask in Android?
but i can't even imagine how to use that concept in my case..
public void onCreate(Bundle savedInstanceState) {
...
Log.i(TAG, "before csv:");
ReadFromCSV mytask = new ReadFromCSV(this);
mytask.execute(char_name);
// try {
// mytask.get();
// } catch (InterruptedException e1) {
// // TODO Auto-generated catch block
// Log.i(TAG, "1 exception");
// e1.printStackTrace();
// } catch (ExecutionException e1) {
// Log.i(TAG, "2 exception");
// e1.printStackTrace();
// }
Log.i(TAG, "after csv");
// create table from var headers
set_title_with_charname(flipper.get_moves_group(0)); //set activity title
Log.i(TAG, "headers values:" + headers);
heading.create_header(this, headers, 0);
lay.addView(heading);
lay.addView(flipper);
lay.setOrientation(1);
setContentView(lay);
}
here is mytask:
private class ReadFromCSV extends AsyncTask<String, Void, String> {
public Activity activity;
private ProgressDialog Dialog = new ProgressDialog(MovesList.this);
public ReadFromCSV(Activity a) {
activity = a;
}
protected void onPreExecute() {
Log.i(TAG, "start onPreExecute");
Dialog.setMessage("Please wait...");
Dialog.show();
Log.i(TAG, "END onPreExecute");
}
protected String doInBackground(String... urls) {
Log.i(TAG, "doInBackground");
// return loadImageFromNetwork(urls[0]);
String new_val = urls[0] + "doInBack";
Log.i(TAG, "new_val: " + new_val);
try {
readfromcsv(flipper, char_name);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.i(TAG, "END doInBackground");
return new_val;
}
protected void onPostExecute(String result) {
Log.i(TAG, "onpostexecute");
Log.i(TAG, "result: " + result);
try {
if(Dialog.isShowing()) {
Dialog.dismiss();
}
// do your Display and data setting operation here
}
catch(Exception e) {
Log.i(TAG, "exception: ");
}
Log.i(TAG, "END onPostExecute");
}
}
here is a code of readfromcsv:
public void readfromcsv(MyFlipper flipper, String char_name)
throws IOException {
String filename = char_name + ".csv";
InputStream is = getAssets().open(filename);
BufferedReader in = new BufferedReader(new InputStreamReader(is,
"UTF-8"));
String reader = "";
int line_nb = 0;
MyTable table = null;
while ((reader = in.readLine()) != null) {
line_nb += 1;
Log.d(TAG, "file line: " + reader);
String[] row_data = reader.split("ą");
Log.d(TAG, "splitted: " + row_data[0] + ',' + row_data[1] + "..");
if (line_nb == 1) {
Log.i(TAG, "first row - memorized headers..");
headers = row_data;
Log.i(TAG, "headers memorized: " + row_data[0] + ","
+ row_data[1] + "..");
continue;
}
if (row_data[0].equals("")) {
// new table
// Log.i(TAG, "new moves_group..");
if (table != null) {
add_table(table);
}
Log.d(TAG, "creating new table");
table = new MyTable(this, true, row_data[1]);
Log.d(TAG, "new table created");
continue;
}
Log.d(TAG, "regular row..");
table.row_from_template(this, row_data, line_nb % 2);
if (line_nb == 60) {
break;
}
;
}
add_table(table);
in.close();
}
I am not sure in you case why you are using get as get
Waits if necessary for the computation to complete, and then retrieves its result.
you can simply do you UI work Asynchronously using onPostExecute.
And Avoid UI related wokr in doInBackground and function called from doInBackground ...
Most likely either your add_table your some code that you are calling from doInBackground is executing some UI functions. Since doInBackground does not happen on the UI thread you can't touch the UI in that function. Instead what you could do is call publishProgress with whatever data you need to display. You can alter the Type that is used by publishProgress through the generic type declaration. The middle one is what is used by publishProgress.