Android: AsycTask won't call onProgressUpdate - android

So I made an AsyncTask class that runs doInBackground() successfully. I have debugged it, and the entire doInBackground() method runs correctly:
public class MyClass extends AsyncTask<String , String , Boolean> {
protected Boolean doInBackground(String... strings)
{
ArrayList<String[]> stories = new ArrayList<String[]>();
...*irrelevant code that works*...
String[][] storiesA = new String[stories.size()][2];
...*irrelevant code that works*...
ArrayList<Integer> displayed = new ArrayList<Integer>();
//calls publishProgress() every 10 seconds with a random String[2] from storiesA
for (int x = 0 ; x < storiesA.length ; x++)
{
if (displayed.size() >= storiesA.length) displayed.clear();
Random gen = new Random();
int rand;
rand = gen.nextInt(storiesA.length);
while (displayed.contains(rand))
{
rand = gen.nextInt(storiesA.length);
}
displayed.add(rand);
publishProgress(storiesA[rand][0] , storiesA[rand][1]);
long t0 , t1;
t0=System.currentTimeMillis();
do
{
t1=System.currentTimeMillis();
} while (t1-t0<10000);
}
return true;
}
}
As you can see, I made it so the onProgressUpdate() parameter should be String..., as in the method in my UI:
protected void onProgressUpdate(String... stories) {
TextView title = (TextView)findViewById(R.id.bulletinBoardTitle);
TextView body = (TextView)findViewById(R.id.bulletinBoardText);
String titleText = stories[0];
String bodyText = stories[1];
title.setText(titleText);
body.setText(bodyText);
}
In case it's relevant, I also defined:
protected void onPostExecute(Boolean result) {
//will never call this method; it is deprecated in this context
TextView title = (TextView)findViewById(R.id.bulletinBoardTitle);
TextView body = (TextView)findViewById(R.id.bulletinBoardText);
title.setText("Refreshing...");
body.setText("Murica!");
}
So the problem is apparent with all the debuggers I've run. While the entire doInBackground() code (executed here:)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.media);
new MyClass().execute();
}
works well and runs fully, when it calls publishProcess() the onProgressUpdate() method never gets run.
I'm a very novice Android programmer, so please keep that in mind if you have any idea what might be the problem here.
Thanks for the help!

Related

No text appears on TextView

This is my code, it should print a random value (between 0 and 1) but it doesn't do that!
I don't know how to fix it! I tried multiple things, none of them are working out!
Here's the code:
package com.example.lode.coder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.TextView;
import java.util.Random;
public class Coder extends AppCompatActivity {
TextView display;
String one;
boolean bl= true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coder);
display = (TextView) findViewById(R.id.text);
}
void all() {
while (bl) {
Random rand = new Random();
int n = rand.nextInt(2);
one = n + (String) display.getText();
display.setText(one);
}
}
}
You don't show where the all() method is called. If it's not called anywhere, that would explain why display never gets any text set.
But assuming that it is called somewhere, there's still a big problem: the all() method is an infinite loop. Until you return control to the framework, any changes you make to display won't show up on the screen. (In fact, your app will likely then be killed off by the framework when it notices that the app has become unresponsive.)
If you want to change the text continuously, look into using a Handler. You can create a Runnable that does the actual change and then reschedules itself to run again after a short time. Don't use a loop like you currently have in all().
Something like this would work:
public class Coder extends AppCompatActivity {
private static long UPDATE_INTERVAL = 500; // every half second -- adjust as needed
TextView display;
String one;
Handler handler;
Runnable updater;
Random rand = new Random();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coder);
display = (TextView) findViewById(R.id.text);
Handler handler = new Handler();
updater = new Runnable() {
#Override public void run() {
int n = rand.nextInt(2);
one = n + display.getText().toString();
display.setText(one);
handler.postDelayed(updater, UPDATE_INTERVAL);
}
}
}
#Override protected void onStart() { // or override onResume() instead
super.onStart();
startUpdates();
}
#Override protected void onStop() { // or override onPause() instead
super.onStop();
stopUpdates();
}
void startUpdates() {
handler.post(updater);
}
void stopUpdates() {
handler.removeCallbacks(updater);
}
}
you haven't called all() function anywhere and even if you do, the logic would still be wrong because bl always stays true and so the while loop is an infinite loop which never stops. Try this code:
public class Coder extends AppCompatActivity {
TextView display;
String one;
boolean bl= true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coder);
display = (TextView) findViewById(R.id.text);
all();
}
void all() {
Random rand = new Random();
int n = rand.nextInt(2);
one = n + display.getText().toString();
display.setText(one);
}
}
if you want the loop you need to set your bl variable to false at some point to stop the loop from going on infinitely.

How can initialized array became null?

Here is the code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.fadein, R.anim.fadeout);
setContentView(R.layout.startpage);
rowItems = new ArrayList<RowItem>();
//... Filling this array.
}
Later, from another activity StartPage.rowItems.size() throw NullPointerException
It can be 0 (failed to retrieve data or I did .clear()), but how, the hell, it became null? I definitely never set it to null.
One more point - this array variable is public static and I use it from another activity. Can it be possible android unloads parent activity (what contains all global variables for the whole app)?
P.S. I cannot check it more thoroughly, because this error is not appears in my emulator/devices, but I got reported it on Google Play. So I can't check what was before and when the array became null...
Thank you
More exact code:
public class StartPage extends Activity implements View.OnTouchListener {
public static List<RowItem> rowItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(R.anim.fadein, R.anim.fadeout);
setContentView(R.layout.startpage);
rowItems = new ArrayList<RowItem>();
pDialog = new ProgressDialog(this);
pDialog.setMessage("Loading data...");
pDialog.setCancelable(false);
pDialog.show();
gc=new GetData();gc.execute();
}
public class GetData extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
rowItems.clear();
inProgress=true;
}
#Override
protected Void doInBackground(Void... arg0) {
ServiceHandler sh = new ServiceHandler();
String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET);
if (jsonStr != null) {
try {
JSONObject jsonObj = new JSONObject(jsonStr);
items = jsonObj.getJSONArray(TAG_COINS);
for (int i = 0; i < itemss.length(); i++) {
JSONObject c = items.getJSONObject(i);
String id = c.getString(TAG_ID).toUpperCase();
String price = c.getString(TAG_PRICE);
String name = c.getString(TAG_NAME);
RowItem item = new RowItem(id, name, price);
rowItems.add(item);
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
Log.e("ServiceHandler", "Couldn't get any data from the url");
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
inProgress=false;
pDialog.dismiss();
}
}
Then call another activity:
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
int x = (int) event.getX();
int y = (int) event.getY();
int w=view.getWidth()-20;
int h=view.getHeight()-20;
if (x<w*0.05 || x>w*0.95 || y<h*0.13 ) return false; // Misclicked
if (x<w*0.5 && y<h*0.38) {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
}
return true;
}
On another activity (MainActivity), try to refresh the listview with the data from main activity:
public class MainActivity extends ListActivity implements View.OnClickListener {
void refresh_list() {
if (StartPage.rowItems.size()>0) { <-- Here is NPE
ListAdapter adapter = new CustomListAdapter(MainActivity.this,R.layout.list_item,StartPage.rowItems);
setListAdapter(adapter);
((BaseAdapter) getListAdapter()).notifyDataSetChanged();
}
}
Google play report:
Caused by: java.lang.NullPointerException
at halfprice.coinmanager.MainActivity.refresh_list(MainActivity.java:116)
at halfprice.coinmanager.MainActivity.onCreate(MainActivity.java:105)
at android.app.Activity.performCreate(Activity.java)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java)
Hope this helps...
you are loading your data in static ArrayList and Acessing it to different activity. its not good practice to do.
Let me first tell your answer as you have created this object in Oncreate(). Its better you make create it Globally than this problem will not occure.
Example :
public class StartPage extends Activity implements View.OnTouchListener {
public static List<RowItem> rowItems = new ArrayList<RowItem>();
OnCreate(){
//and perform the action you want to do.
}
//Hope this will help you definately.
Now Another Method which is the good Practice in Programming language
Passing data object from one Activity to another is simple, If you want to pass Array object than the object should be serialized. Eg;
ArrayList rowItems = new ArrayList();
for Passing array object you have to use intent PutExtra, Eg:
Intent intent = new Intent(SplashScreen.this, MainActivity.class);
intent.putExtra("key",array); startActivity(intent);
//intent.putExtra("key",array); will show error if your Model class is not implements Serializable eg: public class Model implements Serializable{
String id;
String price;
String name;
//generate your getter setter and set data in to this.
}
//For getting data in to another class just use
ArrayList<Model> data = (ArrayList<Model>)getIntent().getSerializable("key");
Now you can play arround with this data object. You should always try to play around with private or protected object.
Hope this will help you.
If i'm not mistaken:
When your activity is launched, the onCreate() method is called.
But when you come back to the same activity from another activity, then the onCreate method is skipped and onResume() method is called..so my suggestion is to initialize in the onResume() method
#Override
protected void onResume(Bundle savedInstanceState) {
overridePendingTransition(R.anim.fadein, R.anim.fadeout);
setContentView(R.layout.startpage);
rowItems = new ArrayList<RowItem>();
//... Filling this array.
}
This answer might not solve your current problem ( not enough code to give a suggestion) but will help you head in the right direction.
Do provide a central data store for your objects, you should consider using singleton design pattern. Also, since the data will be accessed from multiple threads, you should make the arraylist (in your case) thread safe.
Note: if you are using synchronized list, you should lock the object to prevent access when it is iterated.

executing asynctask as a fragmentactivity starts (doesn't execute)

#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View myFragmentView = inflater.inflate(R.layout.fragment_home, container, false);
return myFragmentView;
}
I normally have this onCreateView executing and all my code was in it, due to a recommendation I took and inserted the remainder of my code into onViewCreated. I am not complaining but the exact same thing happened. The ASyncTask doesn't execute as I open the fragment activity. Here's my onViewCreated :
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
Requ connReq = new Requ();
connReq.execute(); //go go power rangers
}
It's not that complicated after all but for some reason it doesn't launch my asynctask. It 'shows' the ProgressDialog which begins in onPreExecute and dismisses in onPostExecute. So you could say that it just won't execute my doInBackground What am I doing wrong with this thing? I just want my ASyncTask to execute as I open the fragment and load my data.
I'd really appreciate the help, thanks in advance. I searched all over the place but I really couldn't find a proper solution for this, I thought there'd be one.
PS: the asynctask works just well when I add the execution to an onClickListener
my asynctask:
private class Requ extends AsyncTask<String, String[], String[]> {
final String pdMessage = getString(R.string.pd_wait);
ProgressDialog pd = new ProgressDialog(getActivity());
#Override
protected void onPreExecute () {
pd.setMessage(pdMessage);
pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();
}
#Override
protected String[] doInBackground(String... params) {
String[] connResult;
final String SipUrlStr = getString(R.string.sip_url);
String bsm = "";
String bst= "";
String hs= "";
String as= "";
try {
JSONTokener SipTokener = new JSONTokener(Sources.httpConnGet(SipUrlStr).toString());
JSONArray SipArray=new JSONArray(SipTokener);
for(int i=0; i<(SipArray.length()); i++)
{
JSONObject json_obj_sip = yeniSipArray.getJSONObject(i);
bsm = json_obj_sip.getString("mt");
bst = json_obj_sip.getString("tt");
hs = json_obj_sip.getString("pta");
as = json_obj_sip.getString("pta2");
}
} catch (Exception e) {
bsm = getString(R.string.def_sip);
bst = getString(R.string.def_sip);
hs = getString(R.string.has);
as = getString(R.string.ass);
}
connRes = new String[]{bsm, bst, hs, as};
return connRes;
}
#Override
protected void onPostExecute(String[] connRes) {
super.onPostExecute(connRes);
res[0] = connRes[0];
res[1] = connRes[1];
res[2] = connRes[2];
res[3] = connRes[3];
res[4] = connRes[4];
res[5] = connRes[5];
res[6] = connRes[6];
res[7] = connRes[7];
res[8] = connRes[8];
res[9] = connRes[9];
res[10] = connRes[10];
res[11] = connRes[11];
pd.dismiss();
}
}
Try starting your async task in
onActivityCreated
Function
public void onActivityCreated(Bundle b){
super.onActivityCreated(b);
// Execute here
//Get views
getView().findViewById(<ID>);
}
As it turns out it was about the cycle. Since I was trying to launch AsyncTask on MainThread the code flew by and AsyncTask didn't have enough time to finish its job. And one absolutely shouldn't make the thread wait for AsyncTask. It's against the first rule of AsyncTask. 'not doing that.' Okay, so I moved the execution of AsyncTask to onCreate() that way it will have more time and it won't execute everytime I open the fragment...
#Override
public void onCreate(Bundle savedInstanceState) {
Requ connReq = new Requ();
connReq.execute(); //go go power rangers
super.onCreate(savedInstanceState);
}
then, this is important, I moved all my code into onPostExecute() method of AsyncTask. If you want to change a variable or change a view you should just do it onPostExecute().

Refresh Game Score TextView using AsyncTask

I am writing a board game in Android where the UI consists of textViews for the scores (CPUScore and PlayerScore). The problem I have is that the UI does not update the score from its initial value when onCreate is called. I have looked at similar questions and the solution most suggested is to use AsyncTask to update the UI thread in the background. However I did not find a solution that dealt explicitly with how to use textViews in AsyncTask.
Here is my attempt:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//....
setContentView(R.layout.main_layout);
//.....
//------------ textViews declared here don't refresh -------------------
TextView playerScoreForm = (TextView) findViewById(R.id.PlayerTotalScore);
playerScoreForm.setText(Integer.toString(PlayerTotal));
playerScoreForm.invalidate();
TextView CPUScoreForm = (TextView) findViewById(R.id.CPUTotalScore);
CPUScoreForm.setText(Integer.toString(CPUTotal));
CPUScoreForm.invalidate();
//--------------------------------------------------------------------------
//AsyncTask method:
new updatePlayerScore().execute(PlayerTotal);
new updateCPUScore().execute(CPUScoreForm);
}
The AsyncTask subclasses:
private class updatePlayerScore extends AsyncTask<TextView, Void, Void> {
#Override
protected TextView doInBackground(TextView... params) {
// what to put here??
}
return playerScoreForm;
}
#Override
protected void onProgressUpdate(Integer... values) {
//??
}
protected void onPostExecute(Integer result) {
playerScoreForm.setText(Integer.toString(result));
}
}
private class UpdateCPUScore extends AsyncTask<TextView, Integer, Integer> {
// same syntax as updatePlayerScore
}
Question:
how do I transfer the textViews that I declared in the onCreate method to the AsyncTask method? I am stumped. I am fairly new to Android development.
a) I'm pretty sure you shouldn't need to invalidate the TextViews after you set them; Android should do that automagically.
b) In theory you'd set your TextView references to be member variables and then reference them in onPostExecute instead of passing them into doInBackground. doInBackground in turn will take whichever bits of data enable you to calculate the new score. What you would do on doInBackground is whatever action would cause a new score to be calculated. The return value from doInBackground gets passed into onPostExecute. You would then update the TextView (now a member variable) with this data in onPostExecute. Does that make sense? You haven't actually posted any code here that would update those score values.
See here for a quick example.
private TextView myScoreView; //initialized in onCreate as you do above.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//....
setContentView(R.layout.main_layout);
//.....
myScoreView = (TextView) findViewById(R.id.PlayerTotalScore);
myScoreView.setText(Integer.toString(PlayerTotal));
new updatePlayerScore().execute(1,2); //parameters for calculation
}
private class updatePlayerScore extends AsyncTask<Integer, Integer, Integer> {
#Override
protected TextView doInBackground(Integer... params) {
int score = params[0] + 2 * params[1];
return score;
}
#Override
protected void onProgressUpdate(Integer... values) {
//if you want to provide some indication in the UI that calculation
//is happening, like moving a progress bar, that's what you'd do here.
}
#Override
protected void onPostExecute(Integer scoreCalculationResult) {
myScoreView.setText(Integer.toString(scoreCalculationResult));
}
}
Edit: If you don't want to do the calculation logic in doInBackgroundThread, you probably just want to use:
runOnUiThread(new Runnable(){
#Override
public void run(){
myScoreView.setText(PlayerScoreValue);
}
});
Or:
myScoreView.post(new Runnable(){
#Override
public void run(){
myScoreView.setText(PlayerScoreValue);
}
});
You can pass the TextView in the constructor of the AsyncTask and update it from the onPostExecute method
private class updatePlayerScore extends AsyncTask<Void, Void, Integer> {
private TextView view;
public updatePlayerScore(TextView textView){
this.view = textView;
}
#Override
protected Integer doInBackground(Void... params) {
int score = 0;
//do you calculation the
return score;
}
protected void onPostExecute(Integer result) {
view.setText(Integer.toString(result));
}
}
note: if you Activity configuration change for any reason i.e the user rotate the device and the you AsyncTask hasn't finish it task the update of you TextView will not be updated so you should retain an instance of you AsyncTask and update the the TextView

Multiple AsyncTasks load the same WebView

I'm trying to run many AsyncTasks to do loadData() on the same WebView. For example, I have 3 threads with 3 contents: "One", "Two", "Three" and a WebView with content "abc". (like the code below)
After 3 tasks finished, I want the WebView has content: "abcOneTwoThree". The idea of this code is that three threads will append its content to WebView at anytime, so the result could be "abcTwoOneThree" or "abcTwoThreeOne", etc ...
I read many concurrency articles but still don't understand how to implement this. This is my code. It just prints "abcThree".
public class UsingSameWebViewActivity extends Activity implements OnClickListener {
private Button button1;
private WebView webView;
private String result;
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button1 = (Button)findViewById(R.id.button1);
button1.setOnClickListener(this);
webView = (WebView)findViewById(R.id.webView1);
result = "abc";
}
#Override
public void onClick(final View v) {
final String[] contents = {"One", "Two", "Three"};
for(int i = 0; i < 3; ++i) {
final FooTask task = new FooTask(webView, result);
task.execute(contents[i]);
}
}
private class FooTask extends AsyncTask<String, Void, String> {
private final WebView resultView;
private String result;
// This is what I try to make it work right.
private synchronized String getResult() {
return result;
}
public FooTask(final WebView resultView, final String result) {
this.resultView = resultView;
this.result = result;
}
#Override
protected String doInBackground(final String... params) {
// Do a long time work randomly then returns a string.
return params[0];
}
#Override
protected void onPostExecute(final String content) {
result = getResult() + content;
resultView.loadData(result, "text/html", "utf-8");
}
}
}
Remove the private String result; line from the Async Task class.
There is nothing much to do here.. just add this line task.execute(contents[i]);
in postExecute() of AsynTask and make contents[i] as a class variable.. task.execute(contents[i]); call this twice since you want it to do "one" and "two"
In onPostExecute() you want to "return" your result somewhere. This is to say FooTask needs a place to put its result. A likely candidate would be some sort of method FooTask can call on its caller to place the result. If you do this note that the method must be synchronized, otherwise some of the returns may be lost.
Alternately you could give FooTask a Handler that it can send a Message to with the result. In this case, you will not need to synchronize anything since all the Messages will be sent to the main/UI thread which will process them serially.

Categories

Resources