I want to use return a value from AsyncTask class by using interface. Problem is that my following code is work fine in Activity but not in fragment class.
I got ClassCastException like this:
java.lang.ClassCastException: com.demo.HomeActivity cannot be cast to com.demo.helper.OnTaskCompleteListener
at com.demo.util.JSONParseAsync.<init>(JSONParseAsync.java:33)
at com.demo.fragment.PersonalDetailFragment.loadProfileAction(PersonalDetailFragment.java:93)
at com.demo.fragment.PersonalDetailFragment.onCreate(PersonalDetailFragment.java:81)
at android.support.v4.app.Fragment.performCreate(Fragment.java:1942)
Interface class:
public interface OnTaskCompleteListener {
void onTaskComplete(JSONObject jsonObject);
}
PersonalDetailFragment class:
public class PersonalDetailFragment extends Fragment implements OnTaskCompleteListener {
private View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_personal_detail, container,
false);
loadProfileAction();
return view;
}
private void loadProfileAction() {
SessionPreference preference = new SessionPreference(getActivity());
try {
String encodedUrl = URLEncoder.encode(preference.getSessionId(), "UTF-8")
+ ","
+ URLEncoder.encode(Constants.URL_TOKEN, "UTF-8");
// URL base64Encode
String processUrl = Base64.encodeToString(encodedUrl.getBytes("UTF-8"), Base64.DEFAULT);
JSONParseAsync parseAsync = new JSONParseAsync(getContext()); // also try getActivity()
parseAsync.execute((URLConstants.GET_USER_DETAIL_URL+processUrl).trim());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onTaskComplete(JSONObject jsonObject) {
try {
boolean status = jsonObject.getBoolean(URLConstants.TAG_STATUS);
Log.e(Constants.DEBUG_TAG, "Status:- " + status);
if (status == true) {
JSONArray dataarray = jsonObject.getJSONArray(URLConstants.TAG_DATA);
JSONObject data = dataarray.getJSONObject(0);
fillProfileData(data);
} else if (status == false) {
Snackbar.make(view,
"Incorrect User Name OR Password",
Snackbar.LENGTH_LONG).show();
}
Log.i("GARG", "Excution Line Finish ");
} catch (Exception e) {
e.printStackTrace();
}
}
JSONParseAsync class:
public class JSONParseAsync extends AsyncTask<String, String, JSONObject>{
private Context mContext;
ProgressDialog mProgress;
private OnTaskCompleteListener mCallback;
public JSONParseAsync(Context context){
this.mContext = (AppCompatActivity)context;
this.mCallback = (OnTaskCompleteListener) mContext;
}
#Override
protected JSONObject doInBackground(String... URL) {
JSONObject jsonObj = null;
try{
Log.d(Constants.DEBUG_TAG, "line excucation 2 doInBackground");
ServiceHandler sh = new ServiceHandler();
String url = URL[0];
Log.d(Constants.ACTIVITY_TAG, "...." + url);
// Making a request to url and getting response.
String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET);
Log.d(Constants.JSON_TAG, "" + jsonStr);
if (jsonStr != null) {
jsonObj = new JSONObject(jsonStr);
Log.e(Constants.JSON_TAG, "" + jsonObj);
}
} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return jsonObj;
}
#Override
protected void onPreExecute() {
Log.d(Constants.DEBUG_TAG, "line excucation 1 onPreexcute");
mProgress = new ProgressDialog(mContext);
mProgress.setMessage("Downloading nPlease wait...");
mProgress.show();
}
#Override
protected void onProgressUpdate(String... values) {
Log.d(Constants.DEBUG_TAG, "line excucation 3 onProgressUpdate");
mProgress.setMessage(values[0]);
}
#Override
protected void onPostExecute(JSONObject result) {
Log.d(Constants.DEBUG_TAG, "line excucation 3 onPostExecute");
mProgress.dismiss();
//This is where you return data back to caller
Log.d(Constants.APP_TAG, " final result:- "+result);
mCallback.onTaskComplete(result);
}
}
Please help me:
By doing this
JSONParseAsync parseAsync = new JSONParseAsync(getContext());
you're sending the Activity to your AsyncTask, but it is your Fragment that implements OnTaskCompleteListener.
Either have your Activity implement your interface, or
do this :
JSONParseAsync parseAsync = new JSONParseAsync(this, getContext());
and change your AsyncTask constructor to
public JSONParseAsync(OnTaskCompleteListener listener, Context context){
this.mContext = context;
//this.mContext = (AppCompatActivity)context; -> you don't need that cast, AppCompatActivity is a subclass of Context
this.mCallback = listener;
}
Use 2 Parameters for your Tasks Constructor:
public JSONParseAsync(Context context, OnTaskCompleteListener listener){
this.mContext = context;
this.mCallback = listener;
}
and create a new instance by using
JSONParseAsync parseAsync = new JSONParseAsync(getContext(), this);
Because when you pass the Context, you don't pass the fragment but the underlying activity. So you'll probaly need 2 parameters if you want to use your task inside your fragment.
You problem is, because you have passed the Activity context to JSONParseAsync and inside its constructor you are casting it to activity AppCompatActivity following the activity casting to your OnTaskCompleteListener.. So, your activity should be implementing the listener and not your fragment.
just let HomeActivity implement your interface OnTaskCompleteListener.
Example:
public class HomeActivity extends AppCompatActivity implements OnTaskCompleteListener{
...
}
In my opinion, don't create your custom classes unless and until its required. In your case you should have achieved your task with anonymous class too.. there's no need to create separate class..
I hope you got my point.
Still, what you can do is, pass the context and OnTaskCompleteListener in the constructor
private void loadProfileAction() {
...
JSONParseAsync parseAsync = new JSONParseAsync(getContext(), this); // also try getActivity()
...
}
and change the constructor to:
public JSONParseAsync(Context context, OnTaskCompleteListener listener){
this.mContext = context;
this.mCallback = listener;
}
Related
This question already has answers here:
How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?
(17 answers)
Closed 5 years ago.
How to catch moment when AsyncTask is finished?
I have ProfileUpdate class which extends AsyncTask, and from another activity I calling this method and after I need update my data. How to know that asynctask finished? My asynctask method in another class and not in activity class!!!
this is my onRefresh method in the activity:
#Override
public void onRefresh() {
if (!AlertView.isInternetAvailable(getContext())) {
swipeLayout.setRefreshing(false);
Toast.makeText(getContext(), Messages.CONNECTION_ERROR + ": " + Messages.NO_INTERNET, Toast.LENGTH_SHORT).show();
} else {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
new UpdateProfile(getActivity(), swipeLayout, sharedPreferences.getString(Constants.TOKEN, ""), user.getIin()).execute(Urls.GET_USER);
}
});
profileDefaults();
}
}
and here my AsyncTask method:
public class UpdateProfile extends AsyncTask <String, Void, String> {
private Activity activity;
private SwipeRefreshLayout swipeRefreshLayout;
private String token;
private String userIin;
private SharedPreferences sharedPreferences;
public UpdateProfile(Activity activity, SwipeRefreshLayout swipeRefreshLayout, String token, String userIin) {
this.activity = activity;
this.swipeRefreshLayout = swipeRefreshLayout;
this.token = token;
this.userIin = userIin;
sharedPreferences = this.activity.getSharedPreferences(Constants.PROJECT, Context.MODE_PRIVATE);
}
#Override
protected String doInBackground(String... params) {
URL url = null;
try {
url = new URL(params[0]);
try {
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("iin", userIin)
.build();
Request request = new Request.Builder()
.url(url)
.addHeader(Constants.AUTH_TOKEN, token)
.post(body)
.build();
Response responses = null;
try {
responses = okHttpClient.newCall(request).execute();
} catch (Exception e) {
AlertView.showAlertView(activity, Messages.CONNECTION_ERROR, Messages.NO_INTERNET, Messages.OK);
}
assert responses != null;
return responses.body().string();
} catch (Exception e) {
AlertView.showAlertView(activity, Messages.CONNECTION_ERROR, Messages.NO_INTERNET, Messages.OK);
}
} catch (Exception e) {
AlertView.showAlertView(activity, Messages.CONNECTION_ERROR, Messages.NO_INTERNET, Messages.OK);
}
return null;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
try {
JSONObject jsonObject = new JSONObject(s);
int code = Integer.valueOf(jsonObject.getString(Constants.CODE));
if (code == Codes.OK) {
Toast.makeText(activity, "Данные обновлены", Toast.LENGTH_SHORT).show();
String userHeader = jsonObject.getString("user");
JSONObject userInfo = new JSONObject(userHeader);
String mobilePhone = userInfo.getString(SingletonConstants.MOBILE_PHONE);
Boolean isActive = userInfo.getBoolean(SingletonConstants.IS_ACTIVE);
Boolean signedAgreement = userInfo.getBoolean(SingletonConstants.SIGNED_AGREEMENT);
Boolean esfEntered = userInfo.getBoolean(SingletonConstants.ESF_ENTERED);
String address = userInfo.getString(SingletonConstants.ADDRESS);
String iin = userInfo.getString(SingletonConstants.IIN);
String certExpDate = userInfo.getString(SingletonConstants.CERT_EXP_DATE);
String firstName = userInfo.getString(SingletonConstants.FIRST_NAME);
String middleName = userInfo.getString(SingletonConstants.MIDDLE_NAME);
String workPhone = userInfo.getString(SingletonConstants.WORK_PHONE);
String secondName = userInfo.getString(SingletonConstants.SECOND_NAME);
String avatarUrl = userInfo.getString(SingletonConstants.AVATAR_URL);;
String secondEmail = userInfo.getString(SingletonConstants.SECOND_EMAIL);
String email = userInfo.getString(SingletonConstants.EMAIL);
User newUser = new User(mobilePhone, isActive, signedAgreement, esfEntered, address, iin, certExpDate, firstName, middleName, workPhone, secondName, avatarUrl, secondEmail, email);
Gson gson = new Gson ();
String userGson = gson.toJson (newUser);
sharedPreferences.edit().putString(SingletonConstants.USER, userGson).apply();
swipeRefreshLayout.setRefreshing(false);
} else {
AlertView.showAlertView(activity, Messages.ERROR, jsonObject.getString(Constants.MESSAGE), Messages.OK);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
How I can call profileDefaults()? It's into my activity. And I need to call it after onPostExecute!
A cleaner way to do it is to use interfaces as listeners.
Create this interface:
public interface OnAsyncFinished{
void onAsyncFinished(Object o);
}
Add the interface as a parameter in your AsyncTaskClass constructor:
private OnAsyncFinished onAsyncFinished;
public UpdateProfile(..., OnAsyncFinished onAsyncFinished) {
...
this.onAsyncFinished = onAsyncFinished;
}
...
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
onAsyncFinished.onAsyncFinished(s); //This will notifiy the method on main activity, and you can now resume the work there
...
}
Implement the interface in your main activity:
public MainActivity extends Activity implements OnAsyncFinished {
#Override
public void onAsyncFinished(Object o) {
//This will get called after onPostExecute, do what u want with the object you got from onPostExecute, json or string in ur example
profileDefaults(); //call ur function
}
}
EDIT:
When creating the async task in your main activity pass this in the constructor likeso:
#Override
public void run() {
new UpdateProfile(..., this).execute(Urls.GET_USER);
}
I don't know why this error happens. I have checked all the code that link to this one but still doesn't give any solution.
The IDE just gimme error like this :
java.lang.NullPointerException: Attempt to invoke virtual method 'int android.os.Bundle.getInt(java.lang.String)' on a null object reference
at com.gook.rebill.fragment.HomeFragment.onAttach(HomeFragment.java:74)
I know it's a null object reference, but still i don't get where the cause of error.
Please help me.
this is my code :
public class HomeFragment extends Fragment implements InternetConnectionListener, ApiHandler.ApiHandlerListener {
private static final String ARG_SECTION_NUMBER = "section_number";
private final int CATEGORY_ACTION = 1;
private CategorySelectionCallbacks mCallbacks;
private ArrayList<Category> categoryList;
private ListView categoryListView;
private String Error = null;
private InternetConnectionListener internetConnectionListener;
public HomeFragment() {
}
public static HomeFragment newInstance(int sectionNumber) {
HomeFragment fragment = new HomeFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((HomeActivity) activity).onSectionAttached(getArguments().getInt(ARG_SECTION_NUMBER));
try {
mCallbacks = (CategorySelectionCallbacks) activity;
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement CategorySelectionCallbacks.");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
categoryListView = (ListView) rootView.findViewById(R.id.categoryListView);
return rootView;
}
#Override
public void onResume() {
super.onResume();
if (UtilMethods.isConnectedToInternet(getActivity())) {
initCategoryList();
} else {
internetConnectionListener = (InternetConnectionListener) HomeFragment.this;
showNoInternetDialog(getActivity(), internetConnectionListener,
getResources().getString(R.string.no_internet),
getResources().getString(R.string.no_internet_text),
getResources().getString(R.string.retry_string),
getResources().getString(R.string.exit_string), CATEGORY_ACTION);
}
}
public class getCategList extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
/**
* json is populating from text file. To make api call use ApiHandler class
*
* <CODE>ApiHandler apiHandler = new ApiHandler(this, URL_GET_CATEGORY);</CODE> <BR>
* <CODE>apiHandler.doApiRequest(ApiHandler.REQUEST_GET);</CODE> <BR>
*
* You will get the response in onSuccessResponse(String tag, String jsonString) method
* if successful api call has done. Do the parsing as the following.
*/
URL hp = null;
try {
hp = new URL(
getString(R.string.liveurl) + "foodcategory.php");
Log.d("URL", "" + hp);
URLConnection hpCon = hp.openConnection();
hpCon.connect();
InputStream input = hpCon.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(input));
String x = "";
x = r.readLine();
String total = "";
while (x != null) {
total += x;
x = r.readLine();
}
Log.d("UR1L", "" + total);
JSONArray j = new JSONArray(total);
Log.d("URL1", "" + j.length());
categoryList = new ArrayList<Category>();
for (int i = 0; i < j.length(); i++) {
Category category = new Category();// buat variabel category
JSONObject Obj;
Obj = j.getJSONObject(i); //sama sperti yang lama, cman ini lebih mempersingkat karena getJSONObject cm d tulis sekali aja disini
category.setId(Obj.getString(JF_ID));
category.setTitle(Obj.getString(JF_TITLE));
category.setIconUrl(Obj.getString(JF_ICON));
if (!TextUtils.isEmpty(Obj.getString(JF_BACKGROUND_IMAGE))) {
category.setImageUrl(Obj.getString(JF_BACKGROUND_IMAGE));
}
Log.d("URL1",""+Obj.getString(JF_TITLE));
categoryList.add(category);
}
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
categoryListView.setAdapter(new CategoryAdapter(getActivity(), mCallbacks, categoryList));
}
});
}catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Error = e.getMessage();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Error = e.getMessage();
} catch (JSONException e) {
// TODO Auto-generated catch block
Error = e.getMessage();
e.printStackTrace();
} catch (NullPointerException e) {
// TODO: handle exception
Error = e.getMessage();
}
return null;
}
}
//! function for populate category list
private void initCategoryList() {
new getCategList().execute();
}
#Override
public void onConnectionEstablished(int code) {
if (code == CATEGORY_ACTION) {
initCategoryList();
}
}
#Override
public void onUserCanceled(int code) {
if (code == CATEGORY_ACTION) {
getActivity().finish();
}
}
//! catch json response from here
#Override
public void onSuccessResponse(String tag, String jsonString) {
//! do same parsing as done in initCategoryList()
}
//! detect response error here
#Override
public void onFailureResponse(String tag) {
}
//! callback interface listen by HomeActivity to detect user click on category
public static interface CategorySelectionCallbacks {
void onCategorySelected(String catID, String title);
}
}
Here where I use the fragment code, in HomeActivity:
public void onSectionAttached(int number) {
switch (number) {
case 1:
mTitle = getString(R.string.title_section1);
break;
case 2:
mTitle = getString(R.string.title_section2);
break;
case 3:
mTitle = getString(R.string.title_section3);
break;
}
}
I think getArguments() return null.
You can see these example:
Android Fragment getArguments() returns null
Fragment getArguments() returns null
From the code that you have attached, it appears to happen here.
hp = new URL(
getString(R.string.liveurl) + "foodcategory.php");
Question is - what is R. Can R be null while you are trying to access string.liveurl? This seems to be the likely problem. Can you use debugger to check what is R?
Try this -
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallbacks = (CategorySelectionCallbacks) activity;
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement CategorySelectionCallbacks.");
}
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
((HomeActivity) getActivity()).onSectionAttached(getArguments().getInt(ARG_SECTION_NUMBER));
}
You should do in this way:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallbacks = (CategorySelectionCallbacks) activity;
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement CategorySelectionCallbacks.");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
categoryListView = (ListView) rootView.findViewById(R.id.categoryListView);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((HomeActivity) getActivity()).onSectionAttached(getArguments().getInt(ARG_SECTION_NUMBER));
}
Edit 1:
You should replace HomeFragment by this way:
HomeFragment homeFragment = HomeFragment.newInstance(my_int);
_viewPagerAdapter.addFragments(homeFragment,"Home");
Reason: Simply you were creating instance of Fragment by default constructor instead of instance with Arguments.
Hope this would help you.
So, I want to display a spinning loading indicator while my ListView is being populated. I successfully have implemented the progress bar, BUT for some reason it disappears BEFORE all of the listings are displayed. What I want is the progressbar to be present during the TOTAL load time of the listings. Basically, what it seems like, each listing is being displayed one at a time, not all at once when they are all loaded.
What I'm doing is
1. Creating a new custom adapter class
2. Populating the ListView in an AsyncTask using this adapter class
3. Setting the ListView to this adapter
This works properly, the progress bar just disappears before all of the listings are displayed. Does anyone have any ideas?
Activity class:
public class MainActivity extends ActionBarActivity {
ArrayList<Location> arrayOfLocations;
LocationAdapter adapter;
// public static Bitmap bitmap;
Button refresh;
ProgressBar progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progress=(ProgressBar)findViewById(R.id.progressbar_loading);
// Construct the data source
arrayOfLocations = new ArrayList<Location>();
// Create the adapter to convert the array to views
adapter = new LocationAdapter(this, arrayOfLocations);
FillLocations myFill = new FillLocations();
myFill.execute();
refresh = (Button) findViewById(R.id.refresh);
refresh.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
finish();
startActivity(getIntent());
}
});
}
private class FillLocations extends AsyncTask<Integer, Void, String> {
String msg = "Done";
protected void onPreExecute() {
progress.setVisibility(View.VISIBLE);
}
// Decode image in background.
#Override
protected String doInBackground(Integer... params) {
String result = "";
InputStream isr = null;
try {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://afs.spotcontent.com/"); // YOUR
// PHP
// SCRIPT
// ADDRESS
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
isr = entity.getContent();
// resultView.setText("connected");
} catch (Exception e) {
Log.e("log_tag", "Error in http connection " + e.toString());
}
// convert response to string
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(isr, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
isr.close();
result = sb.toString();
} catch (Exception e) {
Log.e("log_tag", "Error converting result " + e.toString());
}
// parse json data
try {
JSONArray jArray = new JSONArray(result);
for (int i = 0; i < jArray.length(); i++) {
final JSONObject json = jArray.getJSONObject(i);
try {
BitmapWorkerTask myTask = new BitmapWorkerTask(
json.getInt("ID"), json);
myTask.execute();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (Exception e) {
// TODO: handle exception
Log.e("log_tag", "Error Parsing Data " + e.toString());
}
return msg;
}
protected void onPostExecute(String msg) {
// Attach the adapter to a ListView
ListView listView = (ListView) findViewById(R.id.listView1);
// View header = (View) getLayoutInflater().inflate(
// R.layout.listview_header, null);
// listView.addHeaderView(header);
listView.setAdapter(adapter);
progress.setVisibility(View.GONE);
}
}
}
Adapter class:
public class LocationAdapter extends ArrayAdapter<Location> {
public LocationAdapter(Context context, ArrayList<Location> locations) {
super(context, R.layout.item_location, locations);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Location location = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_location, parent, false);
}
// Lookup view for data population
TextView tvName = (TextView) convertView.findViewById(R.id.tvName);
TextView tvDetails = (TextView) convertView.findViewById(R.id.tvDetails);
TextView tvDistance = (TextView) convertView.findViewById(R.id.tvDistance);
TextView tvHours = (TextView) convertView.findViewById(R.id.tvHours);
ImageView ivIcon = (ImageView) convertView.findViewById(R.id.imgIcon);
// Populate the data into the template view using the data object
tvName.setText(location.name);
tvDetails.setText(location.details);
tvDistance.setText(location.distance);
tvHours.setText(location.hours);
ivIcon.setImageBitmap(location.icon);
// Return the completed view to render on screen
return convertView;
}
}
The reason for that behavior is that you are starting multiple threads.
FillLocations preExecute --> SHOW ProgressBar
BitmapWorkerTask_1 --> new thread
BitmapWorkerTask_2 --> new thread
...
BitmapWorkerTask_N --> new thread
FillLocations postExecute --> HIDE ProgressBar
BitmapWorkerTask_K --> continue execution
BitmapWorkerTask_K+1 --> continue execution
etc.
If you want the list to be displayed until it's all loaded, Simply make BitmapWorker's processing synchronous. If you still want to display the list right away but keep the spinner until it's all finished, then keep a counter in your activity and increase it in preexecute and decrease it in postExecute of BitmapWorker via a setter. Once the counter hits 0, remove hide the progressBar.
In activity:
private int asynchCounter = 0;
private void updateCounter(int delta){
asynchCounter+=delta;
if(asynchCounter<=0){
progress.setVisibility(View.GONE);
}else{
progress.setVisibility(View.VISIBLE);
}
}
And instead of BitmapWorkerTask use
class CountedBitmapWorkerTask extends BitmapWorkerTask {
protected void onPreExecute() {
super.onPreExecute();
updateCounter(1);
}
protected void onPostExecute(String msg) {
super.onPostExecute();
updateCounter(-1);
}
}
I had this exact problem, to solve it I had to write AsyncTask complete listener. Which sends a notification to UI thread, that data was loaded and it has to change something, in this case hide the ProgressBar.
This is the basic example of how this should look like. I am not sure this will work for you after you copy it to your project, but complete listener is what you need, so after studying this case you should be able to find a solution.
AsyncTaskCompleteListener.java - listener interface.
public interface AsyncTaskCompleteListener {
public void onTaskComplete();
}
LoadDataTask.java
class LoadDataTask extends AsyncTask<Object, Object, Object> {
/* Your object types according to your task. */
private AsyncTaskCompleteListener callback; // Callback field
public LoadDataTask(AsyncTaskCompleteListener cb){
this.callback = cb;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Object doInBackground(String... urls) {
/* Your task here */
return result;
}
#Override
protected void onPostExecute(Object o) {
callback.onTaskComplete(); // Set the Callback
}
}
MainActivity.java
public class MainActivity implements AsyncTaskCompleteListener{
/* ...Other methods and fields... */
/* onTaskComplete method which fires after your data is loaded. */
#Override
public void onTaskComplete(){
// Hide ProgressBar
}
}
Self Plug: https://github.com/horvste/EasyWebPageDownloadForAndroid
This would separate the threading from the implementation and solve your problem. This is very similar to what Tony suggested except it's already implemented for you.
Github Readme:
Good for connecting to REST API's, HTML parsing, and many other uses. Using this library is meant to be easy:
Create a class which implements OnProgressUpdate
public class SampleClass implements OnProgressUpdate {
#Override
public void onUpdate(Integer percentProgress) {
}
#Override
public void onUpdateFailure() {
}
#Override
public void onSuccess(StringBuilder result) {
}
#Override
public void onFailure() {
}
}
}
Instantiate DownloadWebPage object
DownloadWebPage webPage = new DownloadWebPage(new SampleClass(), myUrl);
Call .downloadHtml() from the DownloadWebPage
webPage.downloadHtml();
Also if the asynchtask is updating properly and the amount of items is to large look here:
listing a listview is taking too much time and memory in android
Another option would be to only list a certain amount of items then have a next page button or gesture to deal with the ListView loading too slow.
I have AsyncTask2 called from AsyncTask1... that's my 'scenario':
AsyncTask1 download an rss feed, parse the xml and for every item recognized create&execute AsyncTask2 inside the doInBackground method.
AsyncTask2 in the doInBackground method download the enclosure url attribute of the item passed from AsyncTask1 and in onPostExecute method add the item to the global items array and notify the change of items to the associated adapter.
It works fine and not crashing, why? AsyncTasks are supposed to run from the UI thread (threading rules) and now I'm a little confused about this supposition.
Sorry for bad english, I hope question is clear enough.
EDIT
Here some code...
DownloadRssAsyncTask = AsyncTask2,
RssAsyncTask = AsyncTask1
public class ParseActivity extends Activity {
public class FeedItemAdapter extends ArrayAdapter<FeedItem> {
int resource;
public FeedItemAdapter(Context context, int resource, List<FeedItem> items) {
super(context, resource, items);
this.resource = resource;
}
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout myView;
FeedItem item = getItem(position);
if (convertView == null) {
myView = new LinearLayout(getContext());
String inflaterService = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflaterService);
li.inflate(resource, myView, true);
} else {
myView = (LinearLayout) convertView;
}
TextView titleFeedItem = (TextView) myView.findViewById(R.id.itemTitle);
TextView dateFeedItem = (TextView) myView.findViewById(R.id.itemDate);
ImageView imageFeedItem = (ImageView) myView.findViewById(R.id.imageThumb);
titleFeedItem.setText(item.mTitle);
dateFeedItem.setText(item.mPubDate);
imageFeedItem.setImageBitmap(item.bitmapEnclosure);
return myView;
}
}
private class DownloadRssAsyncTask extends AsyncTask<FeedItem, Void, FeedItem> {
#Override
protected FeedItem doInBackground(FeedItem... params) {
FeedItem item = params[0];
if (item.mEnclosure == null) {
Log.i("info: ", "no enclosure tag");
item.bitmapEnclosure = null;
return item;
}
try {
URL imageUrl = new URL(item.mEnclosure);
item.bitmapEnclosure = BitmapFactory.decodeStream(imageUrl.openStream());
} catch (IOException e) {
Log.e("error", "download image resource error: "+item.mEnclosure);
item.bitmapEnclosure = null;
}
return item;
}
#Override
protected void onPostExecute(FeedItem result) {
items.add(result);
arrayAdapter.notifyDataSetChanged();
dbHelper.putItem(result.mGuid, result.mTitle, result.mDescription, result.mEnclosure, result.mPubDate);
}
}
private class RssAsyncTask extends AsyncTask<String, Integer, Void> {
#Override
protected Void doInBackground(String... params) {
int dimParams = params.length;
for (int i=0; i<dimParams; i++) {
Log.i("doInBackground", "rss feed num "+ (i+1) + " of "+ dimParams+ ": " + params[i]);
refreshFeed(params[i]);
}
return null;
}
#Override
protected void onPostExecute(Void result) {
Log.i("onPostExecute in RssAsyncTask", "notifyDataSetChanged");
}
}
public static class FeedItem {
public String mAuthor;
public String mCategory;
public String mComments;
public String mDescription; //r
public String mEnclosure;
public Bitmap bitmapEnclosure;
public String mGuid;
public String mLink; //r
public String mPubDate;
public String mSource;
public String mTitle; //r
public FeedItem() {
// TODO Auto-generated constructor stub
}
#Override
public String toString() {
return
"Data: "+mPubDate+
"\nLink:\n"+mLink+
"\nAutore:\n"+mAuthor+
"\nTitolo:\n"+mTitle+
"\nEnclosure:\n"+mEnclosure;
}
}
private FeedReaderDbHelper dbHelper;
private FeedItemAdapter arrayAdapter;
private ArrayList<FeedItem> items;
private ListView myListView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_parse);
items = new ArrayList<FeedItem>();
new ArrayList<FeedItem>();
myListView = (ListView) findViewById(R.id.myListView);
arrayAdapter = new FeedItemAdapter(this, R.layout.feed_item, items);
myListView.setAdapter(arrayAdapter);
dbHelper = new FeedReaderDbHelper(this);
//RssAsyncTask: download and parsing rss feed
new RssAsyncTask().execute(getString(R.string.my_feed));
}
public void refreshFeed(String feed) {
final String TAG = "refreshFeed";
Log.i(TAG, feed);
URL url = null;
try {
url = new URL(feed);
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
int httpCode = httpConnection.getResponseCode();
if (httpCode == HttpURLConnection.HTTP_OK) {
processFeed(httpConnection.getInputStream());
} else {
Log.i(TAG, httpCode + httpConnection.getResponseMessage());
}
} catch (MalformedURLException e1) {
Log.i(TAG, "MalformedUrlException in " + feed);
} catch (IOException e) {
Log.i(TAG, "IOException in " + url.toString());
}
}
private void processFeed(InputStream inputStream ) {
final String TAG = "processFeed";
final String ITEM = "item";
final String AUTHOR ="author";
final String TITLE ="title";
final String CATEGORY ="category";
final String COMMENTS ="comments";
final String DESCRIPTION ="description";
final String GUID ="guid";
final String LINK ="link";
final String PUBDATE="pubDate";
final String SOURCE ="source";
final String ENCLOSURE = "enclosure";
Log.i(TAG, inputStream.toString());
XmlPullParserFactory pullParserFact;
try {
pullParserFact = XmlPullParserFactory.newInstance();
pullParserFact.setNamespaceAware(true);
XmlPullParser pullParser = pullParserFact.newPullParser();
pullParser.setInput(inputStream, null);
int eventType = pullParser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && pullParser.getName().equals(ITEM)){
final FeedItem item = new FeedItem();
eventType = pullParser.next();
while ( !(eventType == XmlPullParser.END_TAG && pullParser.getName().equals(ITEM)) ) {
if ( eventType == XmlPullParser.START_TAG ) {
String name = pullParser.getName();
switch (name) {
case AUTHOR:
item.mAuthor = pullParser.nextText();
break;
case TITLE:
item.mTitle = pullParser.nextText();
break;
case CATEGORY:
item.mCategory = pullParser.nextText();
break;
case COMMENTS:
item.mComments = pullParser.nextText();
break;
case DESCRIPTION:
item.mDescription = pullParser.nextText();
break;
case GUID:
item.mGuid = pullParser.nextText();
break;
case LINK:
item.mLink = pullParser.nextText();
break;
case PUBDATE:
item.mPubDate = pullParser.nextText();
break;
case SOURCE:
item.mSource = pullParser.nextText();
break;
case ENCLOSURE:
item.mEnclosure = pullParser.getAttributeValue(null, "url");
default:
break;
}
}
eventType = pullParser.next();
}
//download the optional enclosure resource and update UI
new DownloadRssAsyncTask().execute(item);
}
eventType = pullParser.next();
}
} catch (XmlPullParserException e) {
Log.i(TAG, "XmlPullparserException");
} catch (IOException e) {
Log.i(TAG, "IOException");
}
}
}
Because of the inner workings of AsyncTask.
AsyncTask internally uses a static Handler instance, basically the Android way for thread communication. With a Handler you can send messages and run code on threads; in particular, AsyncTask uses it to run its callbacks such as onPostExecute().
Now, when Handler is initialiazed, it binds on the thread that initializes it. In AsyncTask this is done during class initialization/loading at the line:
private static final InternalHandler sHandler = new InternalHandler();
Since sHandler is also final, it cannot be modified after that, and the callbacks will be always triggered on that thread.
In your case, you create an instance of RssAsyncTask in onCreate(), which is run on the UI thread. This triggers the loading of the AsyncTask class and bind AsyncTask's Handler to the UI thread. Therefore, from that point your onPostExecute()s will always be run on the UI Thread. This is despite you create some AsyncTasks in another background thread.
The Threading Rules want to ensure the class is loaded/initialized on the UI thread (see this) and want to enforce good threading practices.
Also, I recommend IntentService for simple network operations, rather than AsyncTask.
I want to send Cus_id from postParamName to web server.
According to cus_id I want to fetch data from server and get it into listview.
I have no error in my code...but the code still not able to fetch data from server..
Plz look at my code...i have been working on this code since last two days. but I am not able to find the mistake
Point1.java
public class Points1 extends ListActivity implements FetchDataListener {
SessionManager session;
TextView tvCusPoints1, tvCusPoints2, tvcusName;
TextView bus_name;
TextView cus_points;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.points);
initView();
}
private void initView() {
session = new SessionManager(getApplicationContext());
// get user data from session
HashMap<String, String> user = session.getUserDetails();
// ID
String cus_id = user.get(SessionManager.KEY_ID);
ArrayList<NameValuePair> postParamName = new ArrayList<NameValuePair>();
postParamName.add(new BasicNameValuePair("cus_id", cus_id));
String url = "http://10.0.2.2/android_api_main/business_points.php";
FetchDataTask task = new FetchDataTask(this);
task.execute(url);
}
#Override
public void onFetchComplete(List<Application> data) {
// dismiss the progress dialog
// create new adapter
ApplicationAdapter adapter = new ApplicationAdapter(this, data);
// set the adapter to list
setListAdapter(adapter);
}
#Override
public void onFetchFailure(String msg) {
// dismiss the progress dialog
}
}
Application.java
public class Application
{
private String bus_name;
private String cus_points;
public String getbus_name() {
return bus_name;
}
public void setbus_name(String bus_name) {
this.bus_name = bus_name;
}
public String getcus_points() {
return cus_points;
}
public void setcus_points(String cus_points) {
this.cus_points = cus_points;
}
}
ApplicationAdapter.java
public class ApplicationAdapter extends ArrayAdapter<Application> {
private List<Application> items;
public ApplicationAdapter(Context context, List<Application> items) {
super(context, R.layout.point_list_item, items);
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater li = LayoutInflater.from(getContext());
v = li.inflate(R.layout.point_list_item, null);
}
Application app = items.get(position);
if (app != null) {
TextView titleText = (TextView) v.findViewById(R.id.item_bname1);
TextView dlText = (TextView) v.findViewById(R.id.item_bpoint1);
if (titleText != null)
titleText.setText(app.getbus_name());
if (dlText != null)
dlText.setText(app.getcus_points());
}
return v;
}
}
FetchDataTask.java
public class FetchDataTask extends AsyncTask<String, Void, String> {
private final FetchDataListener listener;
private String msg;
String cus_id, responseString, success, bus_name, cus_points;
SessionManager session;
public FetchDataTask(FetchDataListener listener) {
this.listener = listener;
}
#Override
protected String doInBackground(String... params) {
if (params == null)
return null;
// get url from params
String url = params[0];
try {
// create http connection
HttpClient client = new DefaultHttpClient();
HttpGet httpget = new HttpGet(url);
// connect
HttpResponse response = client.execute(httpget);
// get response
HttpEntity entity = response.getEntity();
responseString = EntityUtils.toString(entity);
// get response content and convert it to json string
} catch (IOException e) {
msg = "No Network Connection";
}
return responseString;
}
#Override
protected void onPostExecute(String sJson) {
try {
JSONObject json = new JSONObject(responseString);
JSONArray jArray = json.getJSONArray("customer");
List<Application> apps = new ArrayList<Application>();
for (int i = 0; i < jArray.length(); i++) {
JSONObject json_data = jArray.getJSONObject(i);
bus_name = json_data.getString("bus_name");
cus_points = json_data.getString("cus_points");
success = json_data.getString("success");
Application app = new Application();
app.setbus_name(json.getString("bus_name"));
app.setcus_points(json.getString("cus_points"));
// add the app to apps
apps.add(app);
}
if (listener != null)
listener.onFetchComplete(apps);
} catch (JSONException e) {
msg = "Invalid response";
if (listener != null)
listener.onFetchFailure(msg);
return;
}
}
}
FetchDataListener.java
public interface FetchDataListener {
public void onFetchComplete(List<Application> data);
public void onFetchFailure(String msg);
}
Your FetchDataTask constructor accepts FetchDataTaskListener as parameter
public FetchDataTask(FetchDataListener listener) {
this.listener = listener;
}
But you have initialized it using activity's context
FetchDataTask task = new FetchDataTask(this);
Could you please check this.
You should set listener correctly, something like this
this.mListener = (FetchDataListener) activity