Progess Dialog with fragment inside activity call asynctask - android

I have a main activity, which has a fragment inside, that calls an Asynctask.
Main Activity - The main activity has a ViewPager that loads the fragment.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.viewPager = (ViewPager)findViewById(R.id.pager);
this.mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
/**
* on swiping the viewpager make respective tab selected
* */
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
//actionBar.setSelectedNavigationItem(position);
//Toast.makeText(getApplicationContext(), "this is my Toast message!!! =)",
//Toast.LENGTH_LONG).show();
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
Fragment - Makes Call to service
public class SomeFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.article_view, container, false);
ServiceHelper srv = new ServiceHelper(getActivity(), "GetHomeImage", postParameters, 2);
AsyncTask<String, Void, String> request = srv.execute();
return view;
}
}
Async Task Class - Show Progressdialog and make requests
public class ServiceHelper extends AsyncTask<String, Void, String> {
public ServiceHelper(Context c, String method, HashMap<String, Object> parameters, int requestType){
context = c;
this.method = method;
this.parameters = parameters;
this.requestType = requestType;
}
protected void onPreExecute(){
progressDialog = ProgressDialog.show(context, "Requisição", "Chamando Serviço", true, false);
}
protected String doInBackground(String... params) {
do stuff...
}
protected void onPostExecute(String result) {
progressDialog.dismiss();
}
}
The problem I'm facing is that the fragment is called, the request is made, but the Progessdialog only appears when the fragment is shown.
It's possible to show the Progressdialog when the call is made?
Thanks.

I think the simple way here is show progress dialog before call to execute AsyncTask. for closing progress dialog you should add a Listener to ServiceHelper and listen it to dismiss dialog on cancel or completion of task. here is code:
final Dialog progressDialog = ProgressDialog.show(context, "Requisição", "Chamando Serviço", true, false);
ServiceHelper srv = new ServiceHelper(getActivity(), "GetHomeImage", postParameters, 2);
srv.setListener(new ServiceHelperListener() {
public void onCancel() {
progressDialog.dismiss();
}
public void onCompelte() {
progressDialog.dismiss();
}
});
AsyncTask<String, Void, String> request = srv.execute();
and ServiceHelper class and Listener:
public class ServiceHelper extends AsyncTask<String, Void, String> {
private ServiceHelperListener mListener;
public ServiceHelper(Context c, String method, HashMap<String, Object> parameters, int requestType){
context = c;
this.method = method;
this.parameters = parameters;
this.requestType = requestType;
}
public void setListener(ServiceHelperListener listener) {
this.mListener = listener;
}
protected void onPreExecute(){
}
protected String doInBackground(String... params) {
do stuff...
}
protected void onPostExecute(String result) {
if (mListener != null) {
mListener.onCompelte();
}
}
#Override
protected void onCancelled(String s) {
super.onCancelled(s);
if (mListener != null) {
mListener.onCancel();
}
}
}
public interface ServiceHelperListener {
public void onCancel();
public void onCompelte();
}

Related

Toast in OnPostExecute() once the fragment changes

I have two fragments in my Activity : Fragment_A and Fragment_B.
In Fragment A, I created an AsyncTask (when the user "swipeRefreshes" the screen). In the onPostExecute() of this task, I want to display a Toast :
private class MakeRequestTask extends AsyncTask<Void, Void, List<String>> {
private Exception mLastError = null;
MakeRequestTask() {
//Some stuff
}
#Override
protected List<String> doInBackground(Void... params) {
//Some stuff
}
#Override
protected void onPreExecute() {
//Some stuff
}
#Override
protected void onPostExecute(List<String> output) {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), "TO_DISPLAY", Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onCancelled() {
swipeRefreshLayout.setRefreshing(false);
//Some stuff
}
}
If the user changes from Fragment_A to Fragment_B before the AsyncTask finishes, I get a crash:
java.lang.IllegalStateException: Fragment Fragment_A not attached to a context.
I know how to avoid the crash (by adding the condition isAdded()), but I want my Toast to be displayed no matter which Fragment is displayed/alive on top of my Activity.
1stly I would like to suggest you, please make your MakeRequestTask inner class as static as this can be a memory leak.
For your question, You need to pass the context to the class like below:
private static class MakeRequestTask extends AsyncTask<Void, Void, List<String>> {
private Exception mLastError = null;
private WeakReference<Context> weakReference;
MakeRequestTask(Context context) {
//Some stuff
weakReference = new WeakReference<>(context);
}
#Override
protected List<String> doInBackground(Void... params) {
//Some stuff
}
#Override
protected void onPreExecute() {
//Some stuff
}
#Override
protected void onPostExecute(List<String> output) {
// swipe layout will not be shown if fragment is not visible or destroyed
if (isFragmentVisible) {
swipeRefreshLayout.setRefreshing(false);
}
// toast will be shown no matter what fragment is visible
if (weakReference != null) {
Context context = weakReference.get();
if (context != null) {
Toast.makeText(context, "TO_DISPLAY", Toast.LENGTH_SHORT).show();
}
}
}
#Override
protected void onCancelled() {
if (isFragmentVisible) {
swipeRefreshLayout.setRefreshing(false);
}
//Some stuff
}
}
Try this way
Declare a boolean in Fragment_A
private boolean isFragmentVisible=false;
In Fragment_A class
Make this boolean true in onCreateView() of this Fragment_A
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.lyourlayout, container, false);
isFragmentVisible = true;
return view;
}
And make this boolean false in onDestroyView() of this fragment A
#Override
public void onDestroyView() {
super.onDestroyView();
isFragmentVisible = false;
}
Finally use it in Asyntask of Fragment_A like this
private class MakeRequestTask extends AsyncTask<Void, Void, List<String>> {
private Exception mLastError = null;
MakeRequestTask() {
//Some stuff
}
#Override
protected List<String> doInBackground(Void... params) {
//Some stuff
}
#Override
protected void onPreExecute() {
//Some stuff
}
#Override
protected void onPostExecute(List<String> output) {
// swipe layout will not be shown if fragment is not visible or destroyed
if(isFragmentVisible){
swipeRefreshLayout.setRefreshing(false);
}
// toast will be shown no matter what fragment is visible
Toast.makeText(getActivity(), "TO_DISPLAY", Toast.LENGTH_SHORT).show();
}
#Override
protected void onCancelled() {
if(isFragmentVisible){
swipeRefreshLayout.setRefreshing(false);
}
//Some stuff
}
}
Or you can just use and interface or an EventBus in the onPostExecute method, and show the Toast inside the activity.
#Override
protected void onPostExecute(List<String> output) {
swipeRefreshLayout.setRefreshing(false);
activityContractInterface.showToast()
}
}
And in your Activity:
#Override
public void showToast(){
Toast.makeText(getActivity(), "TO_DISPLAY", Toast.LENGTH_SHORT).show();
}
Or the EventBus approach:
#Override
protected void onPostExecute(List<String> output) {
swipeRefreshLayout.setRefreshing(false);
EventBus.getDefault().post(new ShowToastEvent())
//just create an empty class, hope you know what EventBus is
}
And in your activity:
#Subscribe(threadMode = ThreadMode.Main){
Toast.makeText(getActivity(), "TO_DISPLAY", Toast.LENGTH_SHORT).show();
}
EventBusLibrary

Only the original thread that created a view hierarchy can touch its views. - Strange behavior

This is a very strange behavior and I don't know how to fix it.
I have an Activity as a Presenter (In a MVP Architecture).
When the activity starts, I attach a Fragment as a View. The fragment itself is very simple.
public class CurrentSaleFragment extends BaseFragment {
private MainMVP.SalesPresenterOps salesPresenterOps;
private SaleAdapter adapter;
private ListView lv;
#BindView(R.id.btn_sell)
FloatingActionButton btnAdd;
public static CurrentSaleFragment newInstance(){
CurrentSaleFragment fragment = new CurrentSaleFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_quick_sale );
fragment.setArguments(arguments);
return fragment;
}
#Override
protected void init() {
super.init();
lv = (ListView)view.findViewById(R.id.lv_sale);
}
#OnClick(R.id.btn_sell)
public void addToSale(View view){
mPresenter.moveToFragment(SellProductFragment.newInstance());
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
salesPresenterOps = (MainMVP.SalesPresenterOps)context;
}
#Override
public void onDetach() {
salesPresenterOps = null;
super.onDetach();
}
}
The BaseFragment from which this fragmend extends :
public class BaseFragment extends Fragment implements MainMVP.RequiredViewOps, View.OnClickListener,
LoaderRequiredOps{
protected View view;
protected MainMVP.PresenterOps mPresenter;
protected final static String LAYOUT_RES_ID = "layout_res_id";
#Override
public void showOperationResult(String message, final long rowId) {
Snackbar.make(view, message, Snackbar.LENGTH_LONG).setAction(
R.string.see, new View.OnClickListener() {
#Override
public void onClick(View v) {
onOperationResultClick(rowId);
}
}
).show();
}
#Override
public void showSnackBar(String msg) {
Snackbar.make(view, msg, Snackbar.LENGTH_SHORT).show();
}
#Override
public void showAlert(String msg) {}
protected void onOperationResultClick(long rowId){}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mPresenter = (MainMVP.PresenterOps)context;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
this.view = inflater.inflate(getArguments().getInt(LAYOUT_RES_ID), null);
init();
return view;
}
protected void addToClickListener(View ... params){
for (View v : params){
v.setOnClickListener(this);
}
}
protected void init() {
if (view != null){
ButterKnife.bind(this, view);
}
}
#Override
public void onDetach() {
mPresenter = null;
Log.d(getClass().getSimpleName(), "Fragment was detached");
super.onDetach();
}
#Override
public void onClick(View v) {}
#Override
public void onPreLoad() {
Dialogs.buildLoadingDialog(getContext(), "Loading...").show();
}
#Override
public void onLoad() {}
#Override
public void onDoneLoading() {
Dialogs.dismiss();
}
}
When I enter the method 'moveToFragment()' I just replace CurrentSaleFragment for a new Fragment:
protected void addFragment(BaseFragment fragment){
mView = fragment;
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_holder,
fragment, null).addToBackStack(null).commit();
}
Then the new fragment is attached:
public class SellProductFragment extends BaseFragment{
private ListView listView;
private ProductListAdapter adapter;
private MainMVP.SalesPresenterOps mSalesPresenter;
public static SellProductFragment newInstance(){
SellProductFragment fragment = new SellProductFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_inventory);
fragment.setArguments(arguments);
return fragment;
}
private void reload(){
final Loader loader = new Loader(this);
loader.execute();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mSalesPresenter = (MainMVP.SalesPresenterOps)context;
}
#Override
protected void init() {
super.init();
listView = (ListView)view.findViewById(R.id.lv_inventory);
reload();
FloatingActionButton button = (FloatingActionButton)view.findViewById(R.id.btn_add);
addToClickListener(button);
}
#Override
public void onLoad() {
adapter = new ProductListAdapter(getActivity().getApplicationContext(), R.layout.row_product_item,
mSalesPresenter.getProducts());
try{
updateListView();
}catch (Exception e){
Log.w(getClass().getSimpleName(), e.getMessage());
}
}
private void updateListView(){
if (adapter != null && listView != null){
listView.setAdapter(adapter);
}else{
throw new RuntimeException();
}
}
}
See that This fragment also extends from BaseFragment and implements LoaderRequiredOps. The interface is used to 'load' any data. It adds a dialog and updated the adapter when the loading is done:
public class Loader extends AsyncTask<Void, Void, Void> {
private LoaderRequiredOps presenter;
public Loader(LoaderRequiredOps presenter){
this.presenter = presenter;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
presenter.onPreLoad();
}
#Override
protected Void doInBackground(Void... params) {
presenter.onLoad();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
presenter.onDoneLoading();
presenter = null;
}
}
Now, when I try to execute the method reload() from the SellProductFragment i get the 'Only the original thread that created a view hierarchy can touch its views.'
This does not happen if the SellProductFragment is attached first instead of CurrentSaleFragment.
What is happening here?
Your Async Loader class calls the presenters method onLoad() from a background thread during doInBackground().
My guess is that in the onLoad() method of the presenter, a view is referenced.
In order to change the view at this point, post the view logic as a Runnable to the UI thread (you said your presenter is the activity, so this should be possible from the onLoad method).
#Override
public void onLoad() {
runOnUiThread(new Runnable() {
#Override
public void run() {
// Your ui code here...
}
});
// Rest of your code here...
}
For an unknown reason, an unidentified configuration allows to execute the setting of an adapter for a ListView on the doInBackground() method.
Moved it to onPostExecute() and now it's working

Progress Dialog in Twitter Async Task - Android

I want to bring up a progress dialog when the user loads up the twitter feed and when the twitter feed has loaded the progress dialog disappears.
This is the TwitterAsyncTask class:
public class TwitterAsyncTask extends AsyncTask<Object, Void, ArrayList<TwitterTweet>> {
ListActivity callerActivity;
private ProgressDialog pd;
final static String TWITTER_API_KEY = "ddd";
final static String TWITTER_API_SECRET ="fffff";
Context cnt;
#Override
protected void onPreExecute() {
super.onPreExecute();
pd = new ProgressDialog(cnt.getApplicationContext());
pd.setMessage("loading");
pd.show();
}
#Override
protected ArrayList<TwitterTweet> doInBackground(Object... params) {
ArrayList<TwitterTweet> twitterTweets = null;
callerActivity = (ListActivity) params[1];
if (params.length > 0) {
TwitterAPI twitterAPI = new TwitterAPI(TWITTER_API_KEY,TWITTER_API_SECRET);
twitterTweets = twitterAPI.getTwitterTweets(params[0].toString());
}
return twitterTweets;
}
#Override
protected void onPostExecute(ArrayList<TwitterTweet> twitterTweets) {
ArrayAdapter<TwitterTweet> adapter =
new ArrayAdapter<TwitterTweet>(callerActivity, R.layout.twitter_tweets_list,
R.id.listTextView, twitterTweets);
callerActivity.setListAdapter(adapter);
ListView lv = callerActivity.getListView();
lv.setDividerHeight(0);
lv.setBackgroundColor(callerActivity.getResources().getColor(R.color.white));
if (pd != null)
{
pd.dismiss();
}
}
}
And here is the class that calls the TwitterAsyncTask class:
public class MainActivity extends ListActivity {
final static String twitterScreenName = "CFABUK";
final static String TAG = "MainActivity";
private AsyncTask<Object, Void, ArrayList<TwitterTweet>> tat;
boolean done;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
done = false;
AndroidNetworkUtility androidNetworkUtility = new AndroidNetworkUtility();
if (androidNetworkUtility.isConnected(this)) {
new TwitterAsyncTask().execute(twitterScreenName, this);
} else {
Log.v(TAG, "Network not Available!");
}
}
public void timerDelayRemoveDialog(long time, final ProgressDialog d) {
new Handler().postDelayed(new Runnable() {
public void run() {
d.dismiss();
}
}, time);
}
}
the error seems to appear for the line pd = new ProgressDialog(cnt.getApplicationContext());...What should I put for this? thanks
Just add Constructor in your AsyncTask like
Context mContext;
public TwitterAsyncTask(Context mContext){
this.mContext=mContext;
}
And from your activity
Context mContext=this;
new TwitterAsyncTask(mContext).execute(twitterScreenName, this);
Instead of cnt.getApplicationContext() replace it with YourClassName.this
Embed your Async task in your activity, and replace your Context cnt by YourActivity.class... And please include some log.

create new async task with sending callback to ui

i have listview and the content of listview change depend on user interface
and i need to know how to create new instance of async task if i implement callback receiver
public interface CallbackReciever {
public void receiveData(String result);
}
and the async task class
public abstract class ConnectDB extends AsyncTask<String, String, String> implements CallbackReciever {
ProgressDialog pDialog;
Context context;
public String resString;
public ConnectDB(Context context)
{
// TODO Auto-generated constructor stub
this.context=context;
}
#Override
public abstract void receiveData(String object);
/**
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute()
{
super.onPreExecute();
pDialog = new ProgressDialog(context);
pDialog.setMessage("Loading. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
/**
* getting All products from url
* */
protected String doInBackground(String... args)
{
// getting JSON string from URL
resString =Connection.Get(path);
return resString;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String file_url)
{
// dismiss the dialog after getting all products
pDialog.dismiss();
if(resString!=null)
{
receiveData(resString);
}
}
}
and the MainActivity class
public class MainActivity extends Activity {
ListView list;
MovieAdapter adapter;
public ArrayList<Movie> data=new ArrayList<Movie>();
public ConnectDB conDB;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context t=this.getApplicationContext();
setContentView(R.layout.activity_main);
conDB= new ConnectDB(MainActivity.this) {
#Override
public void receiveData(String result) {
// TODO Auto-generated method stub
try {
Log.d("ds", result);
JSONObject json = null;
JSONArray jArray = new JSONArray(result);
int i=0;
while ( i< jArray.length())
{
final Movie tmp=new Movie();
json = jArray.getJSONObject(i);
tmp.setName(json.getString("name"));
tmp.setUrl(json.getString("image"));
tmp.setDescription(json.getString("desc"));
tmp.setTime(json.getString("time"));
tmp.setTime(Utils.ChangeTolocaclTime(tmp.getTime()));
tmp.setWatch(Utils.CanWatch(tmp.getTime()));
data.add(tmp);
i++;
}
}
catch (JSONException e) {
e.printStackTrace();
}
list=(ListView)findViewById(R.id.list);
if(data.size()==0)
{
Toast.makeText(t, "Server Error", Toast.LENGTH_SHORT).show();
list.setVisibility(View.INVISIBLE);
}
// Create custom adapter for listview
adapter=new MovieAdapter(MainActivity.this, data,t);
//Set adapter to listview
list.setAdapter(adapter);
}
};
conDB.execute("MBC2");
}
The activity should implement the receiver and the async task should call it, something like this:
public class MyActivity extends Activity implements CallbackReceiver{
#Override
public void onCreate(Bundle savedInstanceState) {
//Set everything up
MyAsyncTask task = new MyAsyncTask();
task.setOnDataReceivedListener(this);
task.execute();
}
#Override
public void onReceiveData(String data){
//Do something with the data
}
}
Then, in the async task, you can call the receiver method in onPostExecute
public abstract class MyAsyncTask extends AsyncTask<String, String, String>{
private CallbackReciever receiver = null;
public void setOnDataReceivedListener(CallbackReciever receiver){
this.receiver = receiver
}
protected void onPostExecute(String file_url)
{
if(receiver != null){
receiver.onReceiveData(file_url);
}
}
}
The second way to do this is to simply make an anonymous inner class of your async task and override onPostExecute:
public class MyActivity extends Activity implements CallbackReceiver{
#Override
public void onCreate(Bundle savedInstanceState) {
//Set everything up
MyAsyncTask task = new MyAsyncTask(){
#Override
protected void onPostExecute(String file_url){
//Do what you want with your data here
}
};
task.execute();
}
}

progressDialog stops to update when I rotate screen

I'm with a little problem that is driving me crazy. I'm using a AsyncTask in a retained Fragment to update a progressDialog in my activity. I'm using callbacks to send the progress from my fragment.
The problem is: When I rotate my screen it simply stops do update the progressDialog in the recreated activity. It seems like onProgressUpdate stops to being called in the rotated activity.
the relevant part of the code is shown below:
Worker Fragment
public class WorkerFragment extends Fragment {
Context mContext;
...
public static interface TaskCallbacks {
void onPreExecute();
void onProgressUpdate(int... progress);
void onPostExecute();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (TaskCallbacks) activity;
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
}
//inner class
class DownloadFileFromURL extends AsyncTask<String, Int, Void> {
#Override
public void onPreExecute() {
if (mCallbacks != null) {
mCallbacks.onPreExecute();
}
#Override
public String doInBackground(String... f_url) {
... //some verifications of files
if(!localFile.exists()){
//http connection, check, buffer, inputstream, etc...
publishProgress((int)((bytesDownloaded*100)/remoteFileSize));
...
}
}
#Override
protected void onProgressUpdate(int... percent) {
if (mCallbacks != null) {
mCallbacks.onProgressUpdate(percent);
}
}
...
}
}
and the Activity
public class Updater extends Activity implements WorkerFragment.TaskCallbacks {
private WorkerFragment myWorker;
private ProgressDialog pDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_updater);
pDialog=null;
showDialog(1);
FragmentManager fm = getFragmentManager();
myWorker = (WorkerFragment) fm.findFragmentByTag("task");
if(myWorker == null)
{
myWorker = new WorkerFragment();
fm.beginTransaction().add(myWorker, "task").commit();
}
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case 1:
pDialog = new ProgressDialog(this);
pDialog.setMessage("Baixando arquivos de mídia");
pDialog.setIndeterminate(false);
pDialog.setMax(100);
pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pDialog.setCancelable(false);
pDialog.show();
dialogType=progress_bar_type;
return pDialog;
}
}
#Override
public void onProgressUpdate(int... progress) {
//setting progress percentage
if(pDialog!=null)
{
pDialog.setProgress(progress[0]);
}
}
#Override
public void onPostExecute(){
if(pDialog!=null)
{
dismissDialog(1);
...
}
}
Try to put this code in manifest, where you've indicated your activity
android:configChanges="orientation|screenSize"

Categories

Resources