Android singleTask + AsyncTask and unexpected variable value changes - java

I have a main activity with launch mode set as "singleTask". When I bring it to the foreground and the onNewIntent method is called, it runs an AsyncTask containing a while loop which reads lines from a text file.
Part way into this loop, the value of one of my integer variables changes to 0. This doesn't always happen in the same stage in the loop cycle and the loop has nothing to do with this variable at all so I don't understand why this is happening.
Here's an image that might better explain the problem:
http://i.stack.imgur.com/ogLQh.jpg
EDIT: Code as requested:
private class ReadFile extends AsyncTask<String, String, String> implements DialogInterface.OnCancelListener {
protected void onPreExecute() {
//Launch dialog
}
protected String doInBackground(String... path) {
try {
File f = new File(path[0]);
FileInputStream in = new FileInputStream(f);
InputStreamReader ir;
ir = new InputStreamReader(in);
BufferedReader br = new BufferedReader(ir);
StringBuffer sb = new StringBuffer();
String str = new String();
while ((str = br.readLine()) != null) {
Log.i("My Variable",Integer.toString(myVariable));
sb.append(str);
sb.append("\n\n");
}
br.close();
myTextFile = sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
protected void onProgressUpdate(String... progress) {
//Nothing here
}
protected void onPostExecute(final String unusedString) {
//Dismiss dialog
}
protected void onCancelled() {
finish();
}
public void onCancel(DialogInterface dialog) {
cancel(true);
}
}

myVariable seems to be changed from UI thread (activity thread). Try to add logging in each place where you modify myVariable.

Related

How to return package name from txt file on server

So I have an activity that goes to my server a fetches a text file. This text file holds one line of text containing a package name. My goal is to fetch the package name then use the package name to get the versionCode of the package specified in the txt file on the server.
Here is the class that fetches the txt file from the server:
public class getter extends Activity {
Activity context;
TextView txtview;
ProgressDialog pd;
protected void onCreate(Bundle savedInstanceState) {
//TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get);
context=this;
}
public void onStart(){
super.onStart();
BackTask bt=new BackTask();
bt.execute("http://1.2.3.4/test.txt");
}
//background process to download the file from internet
public static class BackTask extends AsyncTask<String,Integer,Void>{
String text="";
protected void onPreExecute(){
super.onPreExecute();
//display progress dialog
}
protected Void doInBackground(String...params){
URL url;
try {
//create url object to point to the file location on internet
url = new URL(params[0]);
//make a request to server
HttpURLConnection con=(HttpURLConnection)url.openConnection();
//get InputStream instance
InputStream is=con.getInputStream();
//create BufferedReader object
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String line;
//read content of the file line by line
while((line=br.readLine())!=null){
text+=line;
}
br.close();
}catch (Exception e) {
e.printStackTrace();
//close dialog if error occurs
}
return null;
}
protected void onPostExecute(Void result){
String packageName = text;
}
}
public String getPackageName(Context mContext) {
if (mContext != null) {
BackTask bt=new BackTask();
bt.execute("http://1.2.3.4/test.txt");
}
return "";
}
}
And this is supposed to get the versionCode from the package specified on the server:
public static int getinstVersionCode(Context mContext) {
if (mContext != null) {
try {
getter.BackTask bt=new getter.BackTask();
bt.execute("http://1.2.3.4/test.txt");
return mContext.getPackageManager().getPackageInfo(String.valueOf(new getter.BackTask().execute("http://1.2.3.4/test.txt")), 0).versionCode;
} catch (PackageManager.NameNotFoundException ignored) {
}
}
return 0;
}
Why doesn't this return the versionCode of the package name on the server?
I think the error lies in the function below but I am not sure.
return mContext.getPackageManager().getPackageInfo(String.valueOf(new getter.BackTask().execute("http://1.2.3.4/test.txt")), 0).versionCode;
One issue is that your variable packageName in onPostExecute is local to that method. So even if that method gets called correctly, no other method would see that value.
You could try to declare packageName near the top of BackTask, near where you declare the variable text.
Then change this method to:
protected void onPostExecute(Void result){
packageName = text;
}
Disclaimer: I have not attempted to load and run your code or this fix!
if the network didn't have any issue then think the issue is caused because you communicate between the doInBackground and onPostExecute using a variable inside the the AsyncTask => text
you should using the return value on the doInBackground to pass it to the onPostExecute
change the Asynctask to
//background process to download the file from internet
public static class BackTask extends AsyncTask<String,Integer,String>{
protected void onPreExecute(){
super.onPreExecute();
//display progress dialog
}
protected String doInBackground(String...params){
URL url;
String text;
try {
//create url object to point to the file location on internet
url = new URL(params[0]);
//make a request to server
HttpURLConnection con=(HttpURLConnection)url.openConnection();
//get InputStream instance
InputStream is=con.getInputStream();
//create BufferedReader object
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String line;
//read content of the file line by line
while((line=br.readLine())!=null){
text+=line;
}
br.close();
}catch (Exception e) {
e.printStackTrace();
//close dialog if error occurs
}
return text;
}
protected void onPostExecute(String resultText){
String packageName = resultText;
}
}
You have twice
new getter.BackTask().execute("http://1.2.3.4/test.txt")).
Why? Looks no good.
Further you cannot get results from an AsyncTask with
String.valueOf(new getter.BackTask().execute("http://1.2.3.4/test.txt"))
You should handle the result of doInBackground in onPostExecute. Only there!

On retrieving images from json get "The application may be doing too much work on its main thread" error

I want to retrieve photos in background using AsyncTask. I get photo as string in base64 encoded form. However, I have "The application may be doing too much work on its main thread" error message.
My activity:
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, ItemClickHandler{
private RecyclerView recyclerView;
private RecyclerViewAdapter adapter;
private LayoutManager layoutManager;
private ArrayList<Device> devices;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_action_bar);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
adapter = new RecyclerViewAdapter(devices, this);
recyclerView.setAdapter(adapter);
initImages();
}
private void initImages() {
Thread thread = new Thread() {
#Override
public void run() {
for(int i = 0; i < devices.size(); i++){
final int pos = i;
GetImageJSON getImage = new GetImageJSON(MainActivity.this){
#Override
protected void onPostExecute(final String result) {
Log.d(TAG, result);
if(pos <= recyclerView.getLayoutManager().getChildCount()){
adapter.updateItem(ImageManager.convertToBitmap(result), pos);
}
}
};
getImage.execute(ConnectionConfig.getUserItemImage(devices.get(i).getId()));
}
}
};
thread.start();
}
}
GetImageJSON class:
public class GetDataJSON extends AsyncTask<String, Void, String> {
private static String charset = "UTF-8";
#Override
protected String doInBackground(String... args) {
String result = parseJSONString(args[0]);
if(!result.isEmpty()){
try{
JSONObject json = new JSONObject(result);
JSONObject jsonObject = json.getJSONObject(ConnectionConfig.TAG_RESULT);
String base64String = jsonObject.getString("image");
Log.d(TAG, base64String);
Bitmap bitmap = ImageManager.convertToBitmap(base64String);
bitmap = ImageManager.scaleDownBitmap(bitmap, context);
Log.d(TAG, "got result: " + result);
return ImageManager.convertBitMapToString(bitmap);
}catch (JSONException e){
e.printStackTrace();
}
}
return result;
}
public static String parseJSONString(String... args){
String result = "";
InputStream inputStream = null;
Log.d(TAG, args[0]);
try {
URL url = new URL(args[0]);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(false);
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept-Charset", charset);
conn.setConnectTimeout(15000);
conn.connect();
try {
InputStream in = new BufferedInputStream(conn.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
result += line;
}
} catch (IOException e) {
e.printStackTrace();
}
conn.disconnect();
} catch (Exception e) {
Log.d(TAG, "Exception", e);
} finally {
try{
if(inputStream != null)
inputStream.close();
}catch(Exception e){
Log.d(TAG, e.getMessage());
}
}
return result;
}
}
I could not find any solution. Please, suggest any one. How I can optimize the process of retrieving data.
I can't tell for sure without a runnable version of your code, but I would guess that having the line ImageManager.convertToBitmap(result)
in onPostExecute() is causing the "too much work on main thread" problem. Anything that happens in onPostExecute() happens on the main thread, so you want to keep that method as light as possible. As SRB suggested, you could avoid this by having doInBackground return the bitmap, instead of a String that needs to be converted back into a bitmap. Note that to change the return type, you'll need to change String to Bitmap in two places:
public class GetDataJSON extends AsyncTask<String, Void, Bitmap> {
#Override
protected Bitmap doInBackground(String... args) {
// TODO return the bitmap
}
//...the rest of your code
}
On a separate note, it looks like there's some room for improvement in your code. These are things that aren't directly related to your question but that it'd be good to understand.
When you call getImage.execute(), doInBackground method of the GetDataJSON class will be executed. The doInBackground method always runs on background thread (see "The 4 steps" section here), so there's no reason to create a new thread in the initImages() method.
One of benefits of using a recyclerview is that you don't need to load everything when recyclerview appears on the screen. If there are views that are off screen, those views can be created when the user scrolls towards them. By retrieving all the images when the activity is created, you're losing that benefit.
There are image loading libraries like Picasso and Glide that will do background image fetches for you. I don't know what your web API looks like, but if you're able to, using a preexisting library can make it simple to quickly deal with issues like placeholders, caching, resizing, etc.

Populate ArrayList in ListView

I'm trying to populate a simple listview using an array of strings in ArrayList. Every time I try though it force closes. I know I'm getting the correct strings in the array as I've seen with Logcat. I can't seem to figure out why it is force closing. Maybe I'm forgetting something in ArrayAdapter (it looks correct to me) or maybe I'm putting my populate method in the wrong place... Can someone help me with this?
public class SchedLayout extends Activity {
public ArrayList<String> titleArray;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sched_layout_layout);
new doParse().execute();
}
private class doParse extends AsyncTask<Void, Void, Void> {
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + "/directory/");
File file = new File(dir, "file.html");
#Override
protected Void doInBackground(Void... params) {
try {
FileInputStream input = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(
input, "UTF-8"));
String line;
titleArray = new ArrayList<String>();
while ((line = br.readLine()) != null) {
String html = line;
Document doc = Jsoup.parse(html);
Elements rels = doc.select("a[rel]");
for (Element title : rels) {
String exclude = "Follow";
if (title.attr("title").contains(exclude)) {
continue;
}
titleArray.add(title.attr("title"));
// Log.v("", title.attr("title")); <--works
}
}
br.close();
input.close();
populate(titleArray); <--does not work
} catch (FileNotFoundException e) {
//Never happens
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private void populate(ArrayList<String> array) {
ListView showList = (ListView) findViewById(R.id.listView1);
ArrayAdapter<String> shows = new ArrayAdapter<String>(
getApplicationContext(),
android.R.layout.simple_list_item_1, array);
showList.setAdapter(shows);
}
}
}
Move your populate call to onPostExecute. You cannot modify the ListView in doInBackground or anything UI related.
#Override
protected void onPostExecute(Void v) {
populate(titleArray);
}

AsyncTask make the application crash

I'm trying to contact my database using .php
It works fine the first time the ASYNC runs, though the second time i run it (without exiting the application) the application crashes (cant catch the error message from LogCat)
And also for some reason it won't enter the while-loop at all. I've written code like this many times before and it worked out just fine, though not this time.
Code:
class LOAD_USERS extends AsyncTask<String, Void, Void>
{
//Internet Input
URL url;
InputStream iS;
InputStreamReader iSR;
BufferedReader r;
//Variables
List<String> Users = new ArrayList<String>();
public String s = "";
public String DOWNLOAD_SUCCESS = "fail";
String charset = "iso-8859-1";
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(MenuActivity.this);
pDialog.setMessage("Letar efter spelare");
pDialog.setIndeterminate(false);
pDialog.setCancelable(true);
pDialog.show();
}
#Override
protected Void doInBackground(String... sUrl) {
try{
url = new URL(sUrl[0]);
iS = url.openStream();
iSR = new InputStreamReader(iS, charset);
r = new BufferedReader(iSR);
Users.clear();
while((s = r.readLine()) != null)
{
Users.add(s);
DOWNLOAD_SUCCESS = "success";
}
}catch(Exception e)
{
Log.e(e.toString(),e.toString());
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
super.onPostExecute(result);
pDialog.dismiss();
if(DOWNLOAD_SUCCESS.equalsIgnoreCase("success"))
{
MenuActivity.this.CheckValidation();
}
else if(DOWNLOAD_SUCCESS.equalsIgnoreCase("fail"))
{
Toast.makeText(getBaseContext(), "Check Your Internet Connection", Toast.LENGTH_SHORT).show();
}
DownloadComplete = true;
}
}
If you could catch my error, that would be great. Thank you!
From the AsyncTask documentation (Threading rules section):
The task can be executed only once (an exception will be thrown if a second execution is attempted.)
My guess is that Async tasks created to run once, meaning you need to create a new instance of your class.

AsyncTask from Activity Class

I am using this class to download txt file
class RetreiveFeedTask extends AsyncTask<String, Void,String> {
#Override
protected ArrayList<Item> doInBackground(String... params) {
try{
URL url = new URL("myurl");
InputStream is = url.openStream();
BufferedReader in = new BufferedReader(new InputStreamReader(is));
String str;
while ((str = in.readLine()) != null){
}
in.close();
return str;
}
catch (MalformedURLException e){}
catch (IOException e){}
return null;
}
protected void onPostExecute(Strin str) {
}
this is how i call this class from my activity:
new RetreiveFeedTask().execute();
And i want to know if there is a way to pass out this result to the activity that make the call.
You can create a listener which you'll pass in the constructor of your AsyncTask. In your onPostExecute you just call this listener.
Something like that:
public interface OnTaskCompleteListener {
void onTaskComplete(String result);
}
protected void onPostExecute(String str) {
onTaskCompleteListener.onTaskComplete(str);
}

Categories