Activity doesnt show until Asynctask is done - android

I am starting an activity and calling an asynctask in onCreateView() to fetch data from a webservice and populate the data in onPostExecute(). Currently the activity doesnt load until the asynctask in the fragment finishes.
How can i display the empty form immediately and update it as the task finishes? The activity just hosts fragments and the asyntasks are in the fragments, if that makes a difference.
Fragment onCreateView():
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_friends,container, false);
return v;
}
Fragment onActivityCreated():
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
//getFriends() calls a method that is an asyntask
peopleList = getFriends();
if (peopleList!=null){
list = (ListView)getActivity().findViewById( R.id.friendsList);
list.setAdapter(new FriendsAdapter(getActivity(), peopleList,"friends") );
}
}

This is because you are populating the data onPostExecute() which gets hooked on to the UI thread only after doInBackground() is complete. If you want to show the progress as and when things happen you need to call the publishProgress() method.
Now the publishProgress() method hooks onto the onProgressUpdate() method and this hooks onto the UI thread which updates the UI thread while doInBackground() is running. I've given a very simple example of something I did for practice sometime back - take a look at how it works. It basically keeps updating a ListActivity, one element at a time and shows this progress on screen - it does not wait until all the strings are added to display the final page:
class AddStringTask extends AsyncTask<Void, String, Void> {
#Override
protected Void doInBackground(Void... params) {
for(String item : items) {
publishProgress(item);
SystemClock.sleep(400);
}
return null;
}
#Override
protected void onProgressUpdate(String... item) {
adapter.add(items[0]);
}
#Override
protected void onPostExecute(Void unused) {
Toast.makeText(getActivity(), "Done adding string item", Toast.LENGTH_SHORT).show();
}
}

You should do as little time consuming tasks as possible in your Activity's onCreate() method. Note that your form will only been drawn after the onCreate() was 'entirely' executed.
A good idea is to start your time consuming task from a timer with a little delay (50 - 100 ms) to be sure that your onCreate() method finished executing.
Here is an example (put this instead of getFriends()):
Handler getFriendsDelayed = new Handler();
getFriendsDelayed.postDelayed(new Runnable()
{
public void run()
{
getFriends();
}
}, DELAY_IN_MILLISEC);
Hope it helps.

Related

Waiting until user actually sees something on the screen

I've got a main Activity, an extra class for my fragment, and inside this fragment is an AsyncTask, which gathers data from various android library (Wifi SSID, BSSID, etc). When I start my app the app shows a blank screen, without any UI. Then after about 2 seconds, the whole data is being shown. I actually want to display my TextViews as "Not connected to a wifi network" in the background, while showing a ProgressDialog until the data is being displayed. I've got the ProgressDialog in my MainActivity, and calling it in my AsyncTask onProgressUpdate
MainActivity.progressDialog = ProgressDialog.show(MainActivity.c,
"ProgressDialog Title",
"ProgressDialog Body");
I'm updating my TextViews in the doInBackground methode (via another methode outside the Fragment)
((Activity) getActivity()).runOnUiThread(new Runnable() {
Would be too big a comment so i'll just put it here.
Sounds like you are using both fragment and AsyncTask in an incorrect way. You should never do anything UI relevant in doInBackground.
Here is an example of what you could do.
I assume the following scenario:
You have a main activity
You have a fragment containing TextViews
You wish to populate the TextViews after loading some data using AsyncTask with a progressDialog
The approach would be to:
Add the fragment in onCreate of your activity (if the fragment is not defined in the layout, then it will automatically be added).
Create the AsyncTask in your fragment like this:
private class LoadData extends AsyncTask<Void, Void, List<String>> {
ProgressDialog progressDialog;
//declare other objects as per your need
#Override
protected void onPreExecute()
{
// getActivity() is available in fragments and returns the activity to which it is attached
progressDialog= new ProgressDialog(getActivity());
progressDialog.setTitle("ProgressDialog Title");
progressDialog.setMessage("ProgressDialog Body");
progressDialog.setIndeterminate(true)
progressDialog.setCancelable(false)
progressDialog.show();
//do initialization of required objects objects here
};
#Override
protected Void doInBackground(Void... params)
{
List<String> results = new ArrayList<String>();
//do loading operation here
//add each of the texts you want to show in results
return results;
}
// onPostExecute runs on UI thread
#Override
protected void onPostExecute(List<String> results )
{
progressDialog.dismiss();
// iterate results and add the text to your TextViews
super.onPostExecute(result);
};
}
Start the AsyncTask in onCreate of your fragment:
#Override
public void onCreate(Bundle savedInstanceState) {
new LoadData().execute();
super.onCreate(savedInstanceState);
}
This way you avoid calling directly back to your activity, which really should not be necessary in your scenario (unless I have misunderstood).
Otherwise please post all the relevant code and layouts.
This line:
I'm updating my TextViews in the doInBackground methode
points to your problem. You need to use the AsyncTask method onProgressUpdate() to publish to the UI thread. You do not call onProgressUpdate() directly, instead you call publishProgress().
Interestingly, I answered a similar question yesterday here: android AsyncTask in foreach
and it includes an example.
Here's what you need to do.
(1) From the place you run the code that gathers data, you should first display the progress dialog. Something like this:
busy = new ProgressDialog (this);
busy.setMessage (getString (R.string.busy));
busy.setIndeterminate (true);
busy.setCancelable (false);
busy.show();
(2) Then you start your data gathering. This must be done in a separate thread (or Runnable). Do something like this:
Thread thread = new Thread ()
{
#Override
public void run()
{
try
{
... gather data ...
Message msg = handler.obtainMessage();
msg.what = LOADING_COMPLETE;
msg.obj = null;
handler.sendMessage (msg);
}
catch (Exception e)
{
Message msg = handler.obtainMessage();
msg.what = LOADING_FAILED;
msg.obj = e.getMessage(); // maybe pass this along to show to the user
handler.sendMessage (msg);
}
// get rid of the progress dialog
busy.dismiss();
busy = null;
}
}
(3) Add a handler to the activity to receive notification when data gathering is complete:
Handler handler = new Handler()
{
#Override
public void handleMessage (Message msg)
{
if (msg.what == LOADING_COMPLETE)
loadingComplete ();
else if (msg.what == LOADING_FAILED)
loadingFailed ((String)msg.obj);
}
};
(4) Implement the handlers:
private void loadingComplete ()
{
...
}
private void loadingFailed (String errorMessage)
{
...
}
That's the essentials.

Android AsyncTask onPostExecute off of main ui thread

I'm having an issue with AsyncTask and onPostExecute. I am finding that onPostExecute is executing on a different thread than the main ui thread, which is causing a CalledFromWrongThreadException to happen when I modify any views.
I put in some logging to see what threads onPreExecute, doInBackground, and onPostExecute are running on. I would see a result like this...
onPreExecute ThreadId: 1
doInBackground ThreadId: 25
onPostExecute ThreadId: 18
I believe the main ui thread id is 1 and I would expect both onPre and onPost to both execute on thread 1. I am making sure to create and also call the execute method from the ui thread (for example in onCreate of an Activity).
Another thing to note that I have noticed is that later async tasks will run their onPostExecute method on the same thread as previous async task onPostExecute methods (in this case thread 18).
Right now in order to get around this I am wrapping the code in my onPostExecute methods in a call to runOnUiThread, but I think this is hacky and would like to get to the real issue.
I am out of ideas! Any one have any insight? I'm happy to answer any questions that could helper with further investigation!
EDIT:
There are two ways that async tasks are being run in the code. I am wondering if the latter in these examples is causing something weird to happen?
public class SomeActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
new SomeAsyncTask().execute();
}
private class SomeAsyncTask extends AsyncTask<String, Void, Integer> {
#Override
public void onPreExecute() {
Thread.currentThread().getId() // 1
//Show a dialog
}
#Override
public Integer doInBackground(String... params) {
Thread.currentThread().getId() // 25
return 0;
}
#Override
public void onPostExecute(Integer result) {
Thread.currentThread().getId() // 18
//hide dialog
//update text view -> CalledFromWrongThreadException!!!
}
}
}
The above seems like a vanilla use of AsyncTask, but I still see this issue occurring even in simple cases like this. The next example uses an async task to run other async tasks. Maybe there is something I don't know about what happens when an async task gets constructed that is causing some weird behavior?
public class SomeActivity extends Activity implements TaskRunner.OnFinishListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
TaskRunner taskRunner = new TaskRunner();
taskRunner.setOnFinishListener(this);
taskRunner.addTask(new SingleTask());
taskRunner.addTask(new SingleTask());
taskRunner.execute();
}
#Override
public void onTaskFinish(List<Integer> results) {
//Thread id is 18 when it should be 1
//do something to a view - CalledFromWrongThreadException!!
}
}
//In a different file
public class SingleTask extends AsyncTask<String, Void, Integer> {
//This is a an async task so we can run it separately as an asynctask
//Or run it on whatever thread runnerExecute is called on
#Override
public Integer doInBackground(String... params) {
return runnerExecute(params);
}
//Can be called outside of doInBackground
public Integer runnerExecute(String... params) {
//some long running task
return 0;
}
}
//In a different file
public class TaskRunner {
private List<SingleTask> tasks;
private OnFinishListener onFinishListener;
public interface OnFinishListener {
public void onTaskFinish(List<Integer> results);
}
public TaskRunner() {
this.tasks = new ArrayList<SingleTask>();
}
public void setOnFinishListener(OnFinishListener listener) {
this.onFinishListener = listener;
}
public void addTask(SingleTask task) {
tasks.add(task);
}
public void executeTasks() {
new RunnerTask().execute((SingleTask[]) tasks.toArray());
}
//Calls the runnerExecute method on each SingleTask
private class RunnerTask extends AsyncTask<SingleTask, Integer, List<Integer>> {
#Override
public void onPreExecute() {
//Runs on thread 1
}
#Override
public List<Integer> doInBackground(SingleTask... params) {
//Runs on arbitrary thread
List<Integer> results = new ArrayList<Integer>();
for(SingleTask task : params) {
int result =task.runnerExecute(task.getParams());
results.add(result);
}
return results;
}
#Override
public void onPostExecute(List<Integer> results) {
//Runs on thread 18
onFinishListener.onTaskFinish(results);
}
}
}
Maybe what is going on here is just super weird, and not at all how async tasks are meant to be used, either way it would be nice to get to the bottom of the issue.
Let me know if you need any more context.
I have been experiencing the same problem and it turned out the the issue was using Flurry 3.2.1. However, the issue is not limited to the Flurry library.
The issue behind the scenes is having the first ever (when the app is loaded for the first time) AsyncTask call from a looper thread which is not the Main UI thread. This call initializes a sHandler static variable in AsyncTask to the wrong thread id, and this id is then used in all subsequent AsyncTask$onPostExecute() calls.
To solve the problem, I call an empty (do-nothing) AsyncTask on first app load, just to initialize AsyncTask correctly.
try using:
getBaseContext().runOnUiThread(new Runnable()
{
#override
public void run()
{
}
});
and write your code inside the run function
The AsyncTask is designed to be used from the main thread. Your problem is the second case, and is that you call execute on the SingleTask from a background thread. You call it in the doInBackground method of RunnerTask. The onPostExecute is then run from the backgroundthread of RunnerTask
Two options for you.
1: Trash RunnerTask, and execute the SingleTasks from you main thread, they'll all run in parallell and you won't know which finishes first, but onPreExecute and onPostExecute is called on the main thread
2: Trash the SingleTask and define them as Runnables instead, then you can run them in sequence in the RunnerTask's doInBackground. They'll all run in the background thread of RunnerTask, in the order you call Run. When it is finished, the onPostExecute of RunnerTask is run on the main thread.
i just tried your code and onPreExecute and onPostExecute does run on the same thread, how do you output the thread id ? try:
Log.d("THREADTEST","PRE"+Long.toString(Thread.currentThread().getId()));
Log.d("THREADTEST","BACKGROUND"+Long.toString(Thread.currentThread().getId()));
Log.d("THREADTEST","POST"+Long.toString(Thread.currentThread().getId()));
P.S. it should be:
new SomeAsyncTask().execute();
and
private class SomeAsyncTask extends AsyncTask<String, Void, Integer> { ... }
you are actually executing the SingleTask from RunnerTask's doinbackground method which is incorrect as asynctask should be executed from a main thread only. You need to relook into the logic which runs the set of SingleTasks from RunnerTask.

Only one Looper may be created per thread

I have an app which request data information on internet (client-server app), but this communication is very slow, thus i have decided to create an AsyncTask to manage the delay.
inside of doInBackground i call Looper.prepare() then a my "view generator (which retrives data)".
in detail (the problem):
I have an activity that dinamically create the rows of a list view. but every time i try to inflate rows, android throws a Looper exception "Only one Looper may be created per thread"
i followed the steps:
call Looper.preapare()
use a first inflaction to create a container of my list
use a second inflaction to create a list row
I suppose I cannot inflate two times but i don't know how i can resolve that
AsyncTask
private class DrawerView extends AsyncTask<ActivityGroup, String, View>{
Exception exc=null;
#Override protected void onPreExecute() {
super.onPreExecute();
}
#Override protected View doInBackground(ActivityGroup... params) {
try {
Looper.prepare();
return processAct();
}catch (ConnectionException e) {
exc =e;
return null;
}
catch (Exception e) {
exc = e;
return null;
}
}
#Override protected void onPostExecute(View result) {
super.onPostExecute(result);
if(exc!= null){
Utils.usrMessage(getApplicationContext(), "Oh Noo!:\n"+exc.getMessage());
Utils.logErr(getApplicationContext(), exc);
finish();
}
if(result!= null){
setContentView(result);
}
}
}
processAct() is an abstract method implemented in this way
#Override protected View processAct() throws Exception {
Bundle bundle = getIntent().getExtras();
User user = (User)bundle.getSerializable("user");
Team team = Team.getTeamInformation(this,user.getTeamId());
ArrayList<Player> players =Player.getPlayerList(this,user.getTeamId());
PlayersListAdapter view = new PlayersListAdapter(this,players,team);
return view;
}
PlayerListAdapter is the class which builds/sets first view (list container)..here the first inflation
public PlayersListAdapter(Context context, ArrayList<Player> players,Team team) throws Exception{
super(context);
View view = inflate(getContext(), R.layout.team_players, this);
TextView tv_teamName = (TextView)view.findViewById(R.id.tbplrs_tmnm);
TextView tv_playersNum = (TextView)view.findViewById(R.id.tbplrs_nplrs);
tv_teamName.setText(team.getName());
String msg = players.size()+" ";
msg += (players.size()!=1)?context.getString(R.string.playerPlural):context.getString(R.string.playerSingle);
tv_playersNum.setText(msg);
ListView lView = (ListView)view.findViewById(R.id.tbplrs_plrslst);
PlayersRowListAdapter plAdapter = new PlayersRowListAdapter(context, players);
lView.setAdapter(plAdapter);
}
at last PlayerRowListAdapter which extends BaseAdapter,...here the second inflation
#Override public View getView(int position, View view, ViewGroup parent) {
if (view == null){
LayoutInflater lInflator = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = lInflator.inflate(R.layout.team_player_singlplayer,null);
}
....
....
}
N.B. if i drop the second adapter PlayerRowListAdapter...all works fine...(obviously without list)
Regards
p.s. sorry for my english
Instead of just calling Looper.prepare();, first check if Looper does not already exist for your Thread, if not, call that function. Like this:
if (Looper.myLooper()==null)
Looper.prepare();
The only reason you need to call Looper.prepare() and Looper.loop() is when you want to have a message Handler in a thread that is not the UI thread. Basically, it keeps the thread alive forever so that the Handler that was created inside the thread can still send and receive messages. The same goes for callback methods like LocationListener or something similar. You are responsible for killing the thread when it is done by calling Looper.getMyLooper().quit() inside the thread that it is in.
If you are inflating views in the UI thread, then you do not need to call Looper.prepare() or Looper.loop() as this is already done in the background. You should never inflate Views outside the UI thread.
AsyncTask already has its own Looper. If you want to update your UI from your doInBackground() method use publishProgress(..) which then invokes onProgressUpdate(..) in the main thread. In your onProgressUpdate you can inflate your Views and add them to your UI.
Edit: example code: http://developer.android.com/reference/android/os/AsyncTask.html
Here's how I worked around this.
As the Developer Reference for AsyncTask says,
doInBackground creates a new thread to manage it (this has forced me to call Looper.prepare()), while onPostExecute() uses the main thread.
So I sliced processAct() in two methods: prepareData() which retrieves data and createView() which calls adapter.
I have put the first method into doInBackground(), and the second one (createView()) I have put into onPostExecute().

Android Won't Display A Layout

I recently discovered that since my app is pulling so much data from a few URLs, it takes about 3-7 seconds for it to load the main layout. So, I made a layout called 'loading' to display, which is just simply a TextView that states "Please wait while data is being collected...". However, when I run my app, it won't display the 'loading' layout. It simply goes black for a while, like it used to before, and then go to the main layout. I tried cleaning the project too, and it still does this. Here's a portion of my Main.java:
#Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.loading);
populateArray();
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Set up click listeners for all buttons
View v1 = findViewById(R.id.continueButton);
v1.setOnClickListener(this);
View v2 = findViewById(R.id.colorCheck);
v2.setOnClickListener(this);
View v3 = findViewById(R.id.terms);
v3.setOnClickListener(this);
}
populateArray(); is the method that is pulling all the information of the internet. So, I figured, "Why not tell it to set the content view immediately to 'loading', have it run populateArray();, then display the main layout?" Obviously, I must be missing something here. Any ideas?
==========================================================================
EDIT: I tried using AsyncTask, but I'm getting a force close. I'm also getting a warning saying the AsyncTask class is never used. Here's my code:
P.S. ProgressDialog dialog; is a global definition.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dialog = ProgressDialog.show(this, "",
"Please wait while data is collected...", true);
// Set up click listeners for all buttons
View v1 = findViewById(R.id.continueButton);
v1.setOnClickListener(this);
View v2 = findViewById(R.id.colorCheck);
v2.setOnClickListener(this);
View v3 = findViewById(R.id.terms);
v3.setOnClickListener(this);
}
And...
private class LoadData extends AsyncTask <String[][], String, String> {
#Override
protected String doInBackground(String[][]... voidThisArray) {
String voidThisString = null;
populateArray();
return voidThisString;
}
#Override
protected void onPostExecute(String voidThisString) {
setContentView(R.layout.main);
dialog.dismiss();
}
}
I would give you the LogCat for the force close but for some reason the LogCat isn't displaying anything...
You want to use an AsyncTask to first: create a ProgressDialog that will display the loading message. Then the AsyncTask will work in the background collecting all of the data.
Hope that helps.
private class Task extends AsyncTask<Void,Integer, Void> {
private ProgressDialog dia;
#Override protected void onPreExecute() {
dia = new ProgressDialog(MyContext.this);
dia.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dia.setMessage("Loading. Please wait...");
dia.setCancelable(false);
dia.show();
// Set up preserver download stuff
}
#Override protected Void doInBackground(Void... voids) {
// perform server download stuff
}
#Override public void onProgressUpdate(Integer... prog) {
if (prog == null)
return;
dia.setProgress(prog[0]);
}
#Override protected void onPostExecute(Void voids) {
// Do any post op stuff
dia.cancel();
}
}
You should see this page for information about threading. Basically if your application is going to do any long running operation, you want it in a thread. Your UI is locking up because your performing this operation on your UI thread.
As far as progress updates, I personally use a ProgressDialog to show when something is happening, although you could build a custom view to indicate this.
Look at AsyncTask and execute
populateArray();
in doInBackground method of AsyncTask.
And call
setContentView
only once you can show loading by using onPreExecute and onPostExecute method of AsyncTask

Android: display image during async background streaming task

I am trying to retrieve JPGs from an MJPG Stream using an async task. I first thought, i could let the task continuously be running and just letting the buffered JPGs pop up in the onProgressUpdate() method. But this doesn't seem to work, because the method only displays integers, no drawables...what I tried:
private class StreamTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
image = readMJPGStream();
return null;
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
imView.setImageDrawable(image); // << doesn't work
}
}
Do you have any ideas? What approach should I try?
Thank you very much in advance!
Huck
When you start a AsyncTask, the doInBackground is called, but it is only run once. I'm not sure what readMJPGStream does, but assuming it blocks until an image is retrieved, its certainly possible that onProgressUpdate gets called several times while your image is still null, then once image get set to something valid and doInbackground quickly exits, there are no further calls to OnProgressUpdate.
You should probably put the call to imView.setImageDrawable in onPostExecute to make sure it gets called when the image has been downloaded. You may also need to call invalidate() on your image view to ensure it gets redrawn. Additionally, if you intend to loop and continue downloading images, you'll need to devise a mechanism of triggering updates. In reality, the issue you're having is because you want 'on demand' get on the UI Thread, but you're dependent on calls to onProgressUpdate. You'd have much better luck using a Handler and your own Runnable/Thread because then when an image download completes, you can post to the UI thread (using the Handler) without having to wait for the onProgressUpdate.
I now got it running this way:
public class StreamActivity extends Activity implements Runnable{
// ...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
imView = (ImageView) findViewById(R.id.imView);
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
while (connected){
image = readMJPGStream();
handler.sendEmptyMessage(0);
}
}
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
imView.setImageDrawable(image);
}
};
public Drawable readMJPGStream() {
//...
return image;
}
}
The only problem is that my while loop is not fast enough. Any ideas are very much appreciated!
Huck

Categories

Resources