1

I want to develop an Android application which asks a server for some data, and displays these data in a ListView.

Currently, I am using a single Activity (without fragments), and the layout is very simple: it consists of an ImageView, an EditText and a ListView. When the ImageView is clicked it gets the content of the EditText and sends it to the server as a new item and automatically updates the Listview (am calling the method of retreiving the objects after the add operation).

I created an AsyncTask class with a progress dialog inside the Activity which the job in background is getting the objects from the server and then assigning them to a List (member of the enclosing class).

With that practice, am facing a lot of problems: the list gets displayed correctly but very slowly! and when I press the ImageView the AsyncTask is then called to do its job after adding the new item but the problem is that its dialog never dismisses.

My question is what is the best practice with this situation in Android? what is the best design pattern? should I use fragments? How should I manage my Threads?

UDATE:

here is the AsyncTask:

  class RemoteDataTask extends AsyncTask<Void, Void, Void> {
    private UserDetailsActivity context;

    RemoteDataTask(UserDetailsActivity context) {
        this.context = context;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();;
        mProgressDialog = ProgressDialog.show(context, "Looking for posts", "Loading...", true, false);
    }

    @Override
    protected Void doInBackground(Void... params) {
        UserDetailsActivity.this.posts.clear();
        posts = new PostManager(context).userPosts(ParseUser.getCurrentUser());
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        postList = (ListView) findViewById(R.id.post_list);
        adapter = new PostsListAdapter(context, UserDetailsActivity.this.posts);
        postList.setAdapter(adapter);
        mProgressDialog.dismiss();
    }
}

And the method wich retreives the posts:

public void refreshPostList() {
    try {
        BusInfo.getInstance().register(UserDetailsActivity.this); // register the Bus to recieve results.
    } catch (Exception e) {
       Log.d("My application says : ;) ", "Erro registering " + e);
    }

    pd = ProgressDialog.show(this, "Please Wait", "Loading");
    new ExprienceEdit(this, "hello").execute();
    }

And the Button with its method

  public void newPost(View v) {
    ParseObject post = new ParseObject("Post");
    post.put("content", editText.getText().toString());
    post.saveInBackground();
    refreshPostList();
     }

     <ImageView
                android:id="@+id/new_post"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:onClick="newPost"
                android:padding="10dp"
                android:src="@drawable/ic_action_post" />
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
GB11
  • 175
  • 1
  • 18

3 Answers3

0

The best way to deal with asynctasks is by using otto :

Otto actually is a singltone bus : please refer to this website http://square.github.io/otto/

Any piece of code would be great to help you more with the problem you are facing.

Any questions I am ready to answer.

BusInfo.getInstance.register(ActivityName.this) // register the Bus to recieve results.

pd = ProgressDialog.show(ActivityName.this, "Please Wait", "Loading");
new ExperienceEdit(getApplicationContext(), "hello").execute(); //async task to be executed let us say on button click

Now the experience edit is:

public class ExperienceEdit extends AsyncTask<Void, Void, String> {

Context c;
String id;

public ExperienceEdit(Context c, String id\) {
    this.c = c;
    this.id = id;
}

@Override
protected String doInBackground(Void... voids) {
    //right the call to back here
}

@Override
public void onPostExecute(String result) {
    try {
        BusInfo.getInstance().post(new ExperienceEditResult(result));
    } catch (Exception e) {
        e.printStackTrace();
    }
}
 }

The result after posting is subscribed at the activity like this :

@Subscribe
public void onAsyncTaskResult(EditExperienceResult result) {
    if (pd != null)
        pd.dismiss();
    object = result.getResult();
    if (object != null) {
        if (object.equals("success")) {
            Toast.makeText(getApplicationContext(), "Success", Toast.LENGTH_SHORT).show();
            onBackPressed();
        } else Toast.makeText(getApplicationContext(), "Failure", Toast.LENGTH_SHORT).show();
    } else
        Toast.makeText(getApplicationContext(), "Please try again later", Toast.LENGTH_SHORT).show();
}

The ExperienceEditResult here happens to be a string (you can have it whatever you want) :

public class ExperienceEditResult {
private String result;

public ExperienceEditResult(String result) {
    this.result = result;
}

public String getResult() {
    return result;
}
}

The BusInfo class is :

public class BusInfo {
private static final Bus BUS = new Bus();

public static Bus getInstance() {
    return BUS;
}
}

Do not forget to unregister the bus onDestroy of the activity: BusInfo.getInstance().unregister(ActivityName.this);

If you aslso want to prevent the progress dialogue from always showing because sometimes it is showing twice due to a double click on button add this : if(pd!=null&&pd.isShowing()){ Log.v("pd is showing","showing"); } else {pd= ProgressDialgue.show...}

Miriana Itani
  • 865
  • 9
  • 25
  • Thank you for the Otto project, please do you have a project example ? – GB11 Aug 02 '15 at 10:42
  • Let me tell you what you need to do : asynctasks should be in separate class not linked to the activity. You call the progress dialogue before executing the tasks. This is practically what otto does. Please refer to this link: http://simonvt.net/2014/04/17/asynctask-is-bad-and-you-should-feel-bad/ – Miriana Itani Aug 02 '15 at 11:02
  • @GB11 please check the edited answer and let me know – Miriana Itani Aug 02 '15 at 11:35
  • Thank you @Miriana, i followed your suggestion, and i got **object already registred** error, i unregistred the bus in onDestroy() but i the error happens even the Activity is not destroyed – GB11 Aug 02 '15 at 12:14
  • try { BusInfo.getInstance().unregister(ActivityName.this); } catch (Exception e) { e.printStackTrace(); } Surround the unregiser and register with try cach – Miriana Itani Aug 02 '15 at 12:17
  • Ok, i will try and tell you – GB11 Aug 02 '15 at 12:27
  • The registering failed with the same exception **Object already registered** at the line 'BusInfo.getInstance().register(this);' by the way, i think i am not understanding well the role of the method onAsyncTaskResult, it is marked as never used in Android Studio, does this mean that the annotation @Subscribe is not working ? – GB11 Aug 02 '15 at 12:40
  • No the annotation is working fine. Do not worry. However, android studio tends to do that while eclipse does not. What is your activity name. Can you please try editing your question for the whole code. Also BusInfo.getInstance().register(ActivityName.this); please – Miriana Itani Aug 02 '15 at 12:42
  • Ok, i have updated it, see the refreshPostList method – GB11 Aug 02 '15 at 12:53
  • Okay the problem is that you are trying to register the Activity every time the button is clicked. Simply register it once after setContentView(R.layout.main); and unregister it in the onDestroy(); – Miriana Itani Aug 02 '15 at 12:56
  • Ok, thnx, will try that. – GB11 Aug 02 '15 at 12:58
  • Please also add if(pd!=null&&pd.isShowing()){//Log the event} else {//show the progress dialogue}. Sometimes the button is clicked and the progress dialogue will show twice. Then it will not dismiss. – Miriana Itani Aug 02 '15 at 12:59
0

Regarding the progress dialog not being dismissed: Where is mProgressDialog dialog declared? I suggest you move it into the RemoteDataTask. (I'm guessing you are at some point overriding the current instance and therefore the dismiss isn't working)

Regarding the slow refresh of the list, post your Adapter code. You should do correct recycling of views and you shouldn't recreate the Adapter everytime but set the data and call notifyDataSetChanged so the listView will recycle the views with the new data. Look into this answer regarding correct recycling of views: https://stackoverflow.com/a/6923513/348378

Edit 1 I also suggest this to prevent having multiple refreshTasks:

public void refreshPostList() {
    if(dataTask == null) {
       dataTask = new RemoteDataTask(this).execute();
    }
}

@Override
protected void onPostExecute(Void result) {
    // you stuff
    dataTask = null;
}

You can also consider cancelling the current task and starting a new one depending on required behavior.

Community
  • 1
  • 1
Raanan
  • 4,777
  • 27
  • 47
0

you should pass ProgressDialog to your AsyncTask class constructor and in any class that want to use AsyncTask class(in your case RemoteDataTask) you should instantiate progress dialog and pass as second argument to your RemoteDataTask to control the visibility from specific custom class. maybe this help.

Setmax
  • 946
  • 6
  • 10