I don't understand where is my error. I have a Fragment and on onCreateView I creat and execute an AsyncTask. but it shows no results.
My fragment
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.thread_layout, container, false);
TextView tv = (TextView) view.findViewById(R.id.textView1);
ThreadAsyncTask threads = new ThreadAsyncTask(addressOfThread);
threads.execute();
tv.setText(threads.getResulf());
return view;
}
}
and my AsyncTask
public class ThreadAsyncTask extends AsyncTask<String, Void, String> {
private String addressOfThread = "";
public String resulf = "aaaa";
public ThreadAsyncTask(String url) {
this.addressOfThread = url;
}
public String getResulf(){
return new String(this.result);
}
}
You must override doInBackground method to get/handling your data.
This data is available in onPostExecute method and from here, call back it to your UI (Activity or Fragment).
Please see my example gits to understand this process:
AsyncTask: https://gist.github.com/hongthaiis/77a847d2011f627c2c60#file-getweatherdatatask-java
Activity with call back method: https://gist.github.com/hongthaiis/43477678bb2764cfc0f6#file-weatheractivity-java
Hope this help! :D
you need to implement doInBackground method, in this method you need to specify the operations you want the task to execute in a separate thread.
After this if you need to modify the UI with the result you obtained from doInBaackground, you have to implement the metod onPostExecute.
Related
I have a ProgressBar in the layout of a Fragment that I want to set to Visible at the beggining of an OnClick method of a Button, and then back to Gone at the end of the method:
public class ProfileTab extends Fragment {
private Button update;
private ProgressBar wait;
which I initialize in OnCreateView:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.profile_tab_layout, container, false);
update= v.findViewById(R.id.Update);
wait = v.findViewById(R.id.WaitUpdateInfo);
//wait.setVisibility(View.VISIBLE);
//wait.setVisibility(View.GONE);
update.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
wait.setVisibility(View.VISIBLE);
/*
Code to execute during OnCLick
*/
wait.setVisibility(View.GONE);
}
});
I have checked and I am sure the visibility of wait never changes during the OnClick method. However, it DOES change during the OnCreateView if I uncomment the pertinent code.
Also, trying this same scenario in a regular Activity (a button and a ProgressBar, initialized and assigned the OnClick method in the Activity's OnCreate), the visibility is modified both in the Oncreate method AND on the execution of OnClick, so I can only guess there is some behaviour relative to Fragments that I'm not managing to catch...
OnClick runs on the Main UI thread. Your view will not be updated until this logic has completed. You update the wait view in OnClick, but that change will not be visible until the OnClick method has completed, and the UI is updated. At the end of your OnClick method, nothing has changed with the wait view (remains GONE, as it was at the start of the method). Try running your logic off of the main thread. AsyncTask is designed for this paradigm https://developer.android.com/reference/android/os/AsyncTask:
private class DoWork extends AsyncTask<Void, Void, Void>{
#Override
protected void onPreExecute() {
super.onPreExecute();
wait.setVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground(Void... voids) {
/*
Code to execute while wait is visible
*/
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
wait.setVisibility(View.GONE);
}
}
...
update.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DoWork doWork = new DoWork();
doWork.execute();
}});
I have 9 custom views (extending the View class each of them) in one of my game window that are causing a freeze in my UI thread, when I press the "Play" button, the app freezes (when inflating the layout in "onCreateView", i'm using Fragments) until the game window is generated, something very very ugly.
I'm trying to do this in a separate thread but all are problems, Android doesn't allow me to create new views out of the main (UI) thread.
I tried so many things but I can't get it, could anyone tell me how to achieve this?
Thank you very much
For very cpu intensive drawing you can use.
http://developer.android.com/reference/android/view/SurfaceView.html
One of the purposes of this class is to provide a surface in which a
secondary thread can render into the screen.
I solved it by manually inflating the layout in an AsyncTask. I call the AsyncTask from the "Play Window" where I show a "loading" view and in "onPostExecute" I create the "Game Window" (Fragment) and replace it.
Some dummy code (PlayFragment is the previous screen of GameFragment):
"Play button click" (PlayFragment):
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_play:
Bundle bundle = new Bundle();
bundle.putSerializable(ARG_SELECTED_GAME, selectedGame);
rlLoadingGame.setVisibility(View.VISIBLE);
GameFragment gameFragment = GameFragment.newInstance(selectedGame);
gameFragment.loadGame(activity, bundle);
break;
}
}
loadGame method (GameFragment):
public void loadGame(Activity activity, Bundle bundle) {
this.activity = activity;
if (bundle != null) {
currentGame = (Game) bundle.getSerializable(ARG_SELECTED_GAME);
}
new GenerateGame(bundle).execute();
}
GenerateGame AsyncTask (GameFragment):
class GenerateGame extends AsyncTask<Void, Void>, View> {
private Bundle bundle;
public GenerateGame(Bundle bundle) {
this.bundle = bundle;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
// do your stuff
}
#Override
protected View doInBackground(Void... params) {
View child = activity.getLayoutInflater().inflate(R.layout.game_fragment_layout, null);
// do all your heavy load stuff
return child;
}
#Override
protected void onPostExecute(View layout) {
super.onPostExecute(layout);
// initialize and set all UI elements
replaceFragment(layout, bundle);
}
}
replaceFragment method (GameFragment):
private void replaceFragment(View newView, Bundle bundle) {
fragmentLayout = newView;
// call to fragment manager replace/add or required method passing this as Fragment to replace and the bundle if needed
}
onCreateView (GameFragment):
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (fragmentLayout != null) {
return fragmentLayout;
} else {
return null;
}
}
This is the first approach so it can be refactored and so many things can be done in a better way but it is up to you.
I've an AppCompatActivity that uses the NavigationDrawer pattern, managing some fragments. In one of these, that has no setRetainInstance(true), I show a DialogFragment with a ProgressDialog inside and an AsyncTask with this code:
SavingLoader savingLoader = SavingLoader.newInstance(savingLoaderMaxValue);
savingLoader.show(getChildFragmentManager(), SAVING_LOADER_TAG);
new MyAsyncTask().execute();
Where the SavingLoader class is this one:
public class SavingLoader extends DialogFragment {
private static final String MAX_VALUE_TAG = "MAX_VALUE_TAG";
private static final String PROGRESS_VALUE_TAG = "PROGRESS_VALUE_TAG";
public static SavingLoader newInstance(int max_value){
SavingLoader s = new SavingLoader();
Bundle args = new Bundle();
args.putInt(MAX_VALUE_TAG, max_value);
s.setArguments(args);
return s;
}
private ProgressDialog dialog;
public SavingLoader(){}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setCancelable(false);
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState){
dialog = new ProgressDialog(getActivity(), getTheme());
dialog.setTitle(getString(R.string.dialog_title_saving));
dialog.setMessage(getString(R.string.dialog_message_saving));
dialog.setIndeterminate(false);
int max = (savedInstanceState == null ?
getArguments().getInt(MAX_VALUE_TAG) : savedInstanceState.getInt(MAX_VALUE_TAG));
if (max >= 1){
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setProgress((savedInstanceState == null ?
0 : savedInstanceState.getInt(PROGRESS_VALUE_TAG)));
dialog.setMax(max);
} else dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
return dialog;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(MAX_VALUE_TAG, dialog.getMax());
outState.putInt(PROGRESS_VALUE_TAG, dialog.getProgress());
}
public int getProgress(){
return dialog.getProgress();
}
public int getMax(){
return dialog.getMax();
}
public void incrementProgressBy(int value){
if (dialog.getProgress() + value <= dialog.getMax())
dialog.incrementProgressBy(value);
}
}
In the onPostExecute() method I need to perform some UI update so here's my problem: if I start the dialog and the AsyncTask (like above) and I don't rotate my phone, all works as expected. Same thing if I rotate phone AFTER the onPostExecute() method. But if I rotate my phone WHILE the AsyncTask is still running, when it completes and reach the onPostExecute() method it gives me the IllegalStateException saying that the fragment hosting the AsyncTask and the Dialogfragment is no longer attached to the activity. So I tried to override both the onAttach() and the onDetach() methods (with a simple System.out.println) of my fragment, to see when the onPostExecute() gets called. The result is that when I rotate my phone, I always got this output:
onDetach
onAttach
... (if I rotate more my phone)
onPostExecute
So shouldn't the fragment be attached when the AsyncTask completes? Thank you all for your time and attention.
I've finally managed to solve this problem by stop using AsyncTask and using LoaderManager + AsyncTaskLoader following this article. In short, your fragment must implement the LoaderCallbacks interface and manage the AsyncTaskLoader. A skeleton fragment could be something like this:
public class MyFragment extends Fragment implements LoaderManager.LoaderCallbacks {
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
// Inflate here your view as you usually do and find your components
// For example imagine to have a button tha will fire the task
Button b = (Button) view.findViewById(R.id.my_button);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Use this to start task for the first time
getLoaderManager().initLoader(0, null, this);
// .. or this for restart the task, details in
// the provided article
// getLoaderManager().restartLoader(0, null, this);
}
});
// Get fragments load manager
LoaderManager lm = getLoaderManager();
if (lm.getLoader(0) != null) {
// Reconnect to an existing loader
lm.initLoader(0, null, this);
}
// Return your view here
return view;
}
// LoaderCallbacks methods to override
#Override
public Loader onCreateLoader(int id, Bundle args) {
// Create an instance of the loader passing the context
MyTaskLoader loader = new MyTaskLoader(getActivity());
loader.forceLoad();
return loader;
}
#Override
public void onLoadFinished(Loader loader, Object data) {
// Use this callback as you would use the AsyncTask "onPostExecute"
}
#Override
public void onLoaderReset(Loader loader) {}
// Now define the loader class
private static class MyTaskLoader extends AsyncTaskLoader {
public MyTaskLoader(Context context){
super(context);
}
#Override
public Object loadInBackground() {
// Do here your async work
}
}
}
I've checked other answers regarding the asynchronous updates of ListFragments and have figured out that the most common problem for the notifyDataSetChanged() method not working is, that developers tend to overwrite the initial adapter source, causing the adapter to lose reference and hence not updating the view. I have a ListFragment that I want to update asynchronously. What I've decided to do, is parse the list data in an AsyncTask and finally update the fragment in onPostExecute(). I want the UI to be as fluid as possible, so I want to avoid doing heavy processing on the main thread. I call the AsyncTask after the views are created to avoid null pointers.
public class CategoryFragment extends ListFragment {
ArrayList<Category> categoriesArray = new ArrayList<Category>();
CategoryAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_category, container, false);
adapter = new CategoryAdapter(getActivity(), R.layout.category_list_item, categoriesArray);
setListAdapter(adapter);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
new UpdateUITask().execute(categories);
}
...
// The async task to update the UI.
class UpdateUITask extends AsyncTask<String, String, ArrayList<Category>>{
#Override
protected ArrayList<Category> doInBackground(String... input) {
// Do some data processing, to fill the categoriesArray.
// categoriesArray.add(...); -- loop
return categoriesArray;
}
#Override
protected void onPostExecute(ArrayList<Category> result) {
super.onPostExecute(result);
adapter.notifyDataSetChanged();
}
}
}
The refresh method fires, but produces no result. What else am I missing here? Should I pick a completely different approach?
Its looking like your instance of categoriesArray is getting lost. adapter.notifyDataSetChanged(); doesn't work only in case when your refrence of the listArray which you just passed to the adapter has been lost or changed. So, I would reccomend you to please make sure about this.
Also if you are going to populate your custom array then, use onProgressUpdate() method of the AsyncTask. It will reduce the loading time too.
You can do this like this:
class UpdateUITask extends AsyncTask<String, Category, ArrayList<Category>>
{
#Override
protected ArrayList<Category> doInBackground(String... input)
{
// Do some data processing, to fill the categoriesArray.
// and get the category objects one by one and call
//publishprogress till data is there
publishProgress(Category);
// and finallly just return somthing to get in onpostexecute
}
#Override
protected void onProgressUpdate(Category... values)
{
// TODO Auto-generated method stub
super.onProgressUpdate(values);
categoriesArray.add(...);
adapter.notifyDataSetChanged();
}
#Override
protected void onPostExecute(ArrayList<Category> result)
{
super.onPostExecute(result);
adapter.notifyDataSetChanged();
}
}
use below code for your AsyncTask, but please make sure again you are changing in the original categoryList.
private class UpdateUITask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// Do some data processing, to fill the categoriesArray.
return null;
}
#Override
protected void onPostExecute(Void result) {
adapter.notifyDataSetChanged();
}
}
and just run this task with passing any argument.
new UpdateUITask().execute();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View myFragmentView = inflater.inflate(R.layout.fragment_home, container, false);
return myFragmentView;
}
I normally have this onCreateView executing and all my code was in it, due to a recommendation I took and inserted the remainder of my code into onViewCreated. I am not complaining but the exact same thing happened. The ASyncTask doesn't execute as I open the fragment activity. Here's my onViewCreated :
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
Requ connReq = new Requ();
connReq.execute(); //go go power rangers
}
It's not that complicated after all but for some reason it doesn't launch my asynctask. It 'shows' the ProgressDialog which begins in onPreExecute and dismisses in onPostExecute. So you could say that it just won't execute my doInBackground What am I doing wrong with this thing? I just want my ASyncTask to execute as I open the fragment and load my data.
I'd really appreciate the help, thanks in advance. I searched all over the place but I really couldn't find a proper solution for this, I thought there'd be one.
PS: the asynctask works just well when I add the execution to an onClickListener
my asynctask:
private class Requ extends AsyncTask<String, String[], String[]> {
final String pdMessage = getString(R.string.pd_wait);
ProgressDialog pd = new ProgressDialog(getActivity());
#Override
protected void onPreExecute () {
pd.setMessage(pdMessage);
pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();
}
#Override
protected String[] doInBackground(String... params) {
String[] connResult;
final String SipUrlStr = getString(R.string.sip_url);
String bsm = "";
String bst= "";
String hs= "";
String as= "";
try {
JSONTokener SipTokener = new JSONTokener(Sources.httpConnGet(SipUrlStr).toString());
JSONArray SipArray=new JSONArray(SipTokener);
for(int i=0; i<(SipArray.length()); i++)
{
JSONObject json_obj_sip = yeniSipArray.getJSONObject(i);
bsm = json_obj_sip.getString("mt");
bst = json_obj_sip.getString("tt");
hs = json_obj_sip.getString("pta");
as = json_obj_sip.getString("pta2");
}
} catch (Exception e) {
bsm = getString(R.string.def_sip);
bst = getString(R.string.def_sip);
hs = getString(R.string.has);
as = getString(R.string.ass);
}
connRes = new String[]{bsm, bst, hs, as};
return connRes;
}
#Override
protected void onPostExecute(String[] connRes) {
super.onPostExecute(connRes);
res[0] = connRes[0];
res[1] = connRes[1];
res[2] = connRes[2];
res[3] = connRes[3];
res[4] = connRes[4];
res[5] = connRes[5];
res[6] = connRes[6];
res[7] = connRes[7];
res[8] = connRes[8];
res[9] = connRes[9];
res[10] = connRes[10];
res[11] = connRes[11];
pd.dismiss();
}
}
Try starting your async task in
onActivityCreated
Function
public void onActivityCreated(Bundle b){
super.onActivityCreated(b);
// Execute here
//Get views
getView().findViewById(<ID>);
}
As it turns out it was about the cycle. Since I was trying to launch AsyncTask on MainThread the code flew by and AsyncTask didn't have enough time to finish its job. And one absolutely shouldn't make the thread wait for AsyncTask. It's against the first rule of AsyncTask. 'not doing that.' Okay, so I moved the execution of AsyncTask to onCreate() that way it will have more time and it won't execute everytime I open the fragment...
#Override
public void onCreate(Bundle savedInstanceState) {
Requ connReq = new Requ();
connReq.execute(); //go go power rangers
super.onCreate(savedInstanceState);
}
then, this is important, I moved all my code into onPostExecute() method of AsyncTask. If you want to change a variable or change a view you should just do it onPostExecute().