2013-09-30 6 views
1

Недавно я начал пытаться внедрить AsyncTasks в фрагменты моего Android-проекта и сразу же столкнулся с общей ошибкой изменения конфигурации, вызвавшей серьезную проблему. Методы, которые я видел в Интернете, нелегко включали в настройку ViewPager, которую я использовал, и я использовал свои знания для самостоятельной обработки изменений конфигурации.Разработка Android - Неверная реализация AsyncTask в фрагменте?

Мой вопрос: есть ли что-нибудь опасное для моего подхода? Наибольшее беспокойство вызывает утечка памяти, но я позаботился об отказе каждого созданного_View в методе onDetach().

Резюме реализации:

  • setRetainInstance истина для фрагмента, чтобы он не должен быть воссоздан и важные данные не теряются.

  • В onCreateView() раздел кода, всегда вызываемый при восстановлении вида фрагмента, приложение проверяет, работает ли его AsyncTask. Если это так, отобразите IndeterminateProgressBar, и когда он завершит onPostExecute, измените видимость его на GONE.

  • В onDetach(), убедитесь, что created_view View установлен в нуль, так что нет утечки памяти, связанной с деятельностью, которая первоначально используются

  • в onAttach до изменения конфигурации.

Код

public class RosterFragment extends Fragment 
{ 

List<RosterMember> dataforroster = new ArrayList<RosterMember>(); //List that will hold the Roster objects retrieved from Parse database, 
           //and later passed in to constructor for the RosterCustomArrayAdapter. 
List<ParseUser> retrieved_list = new ArrayList<ParseUser>(); //List that will hold values retrieved from ParseUser Query. 
View createdView; //View that will be passed back with built RosterFragment 
private ProgressBar roster_progress; //The indeterminate ProgressBar that will be displayed until the AsyncTask is finished downloading the roster. 
boolean running_task; 
private RosterAsyncTask get_roster; 

@Override 
public void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 
    // Retain this fragment across configuration changes. 
    setRetainInstance(true); 
    get_roster = new RosterAsyncTask(); //Create new RosterAsyncTask instance. 
    get_roster.execute(); 
    running_task = true; 

} 

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) 
{ 
    createdView = inflater.inflate(R.layout.rosterfragment, container, false); //Inflate the fragment using the specific layout 
    roster_progress = (ProgressBar) createdView.findViewById(R.id.loadingroster); //Find the ProgressBar in layout and set it to roster_progress. 
    if(running_task == true) 
    { 
     roster_progress.setVisibility(View.VISIBLE); 
    } 
    else 
    { 
     fill_roster(); 
    } 
    return createdView; 
} 

@Override 
public void onDetach() 
{ 
    super.onDetach(); 
    createdView = null; 
} 


public void fill_roster() 
{ 
    if(!dataforroster.isEmpty()) 
    { 
    //Get reference ListView in the inflated layout. 
    ListView the_Roster = (ListView) createdView.findViewById(R.id.rostercoachofficers); 
    //Create an instance of the RosterCustomArrayAdapter using the dataforroster List. 
    RosterCustomArrayAdapter roster_Adapter = new RosterCustomArrayAdapter(getActivity(), dataforroster); 
    //Sort the roster_Adapter so elements in ListView will be sorted alphabetically by first name. 
    roster_Adapter.sort(new RosterComparator());  
    //Attach adapter to the ListView to populate its data. 
    the_Roster.setAdapter(roster_Adapter); 
    } 

} 


//AsyncTask responsible for downloading roster in background thread. 
private class RosterAsyncTask extends AsyncTask<Void, Void , List<RosterMember>> 
{ 


    //The operations to perform in the AsyncTask background thread. The results(the roster data downloaded) will be passed to onPostExecute. 
    @Override 
    protected List<RosterMember> doInBackground(Void... params) 
    { 
     SystemClock.sleep(10000); 
     ParseQuery<ParseUser> query = ParseUser.getQuery(); //Get specific ParseQuery for ParseUsers. 

     try 
     { 
     retrieved_list = query.find(); //Initiate query. 
     for(ParseUser current_user: retrieved_list) //For every ParseUser returned from query, create a new RosterMember using the ParseUser 
           //data and then add it to the dataforroster List. 
      { 
       RosterMember current_member = new RosterMember(); 
       current_member.username = current_user.getUsername(); 
       ParseFile parse_ByteArray = (ParseFile)current_user.get("profile_picture"); 
       Bitmap profile_Picture = BitmapFactory.decodeByteArray(parse_ByteArray.getData(), 0, parse_ByteArray.getData().length); 
       current_member.profile_Picture = profile_Picture; 
       current_member.title = current_user.getString("title"); 
       dataforroster.add(current_member); 
      } 




     } 

     //If problem occurred in query execution, use Toast to display error message. 
     catch (ParseException e) 
     { 
      Toast.makeText(getActivity(), "Error, " + e.getMessage(), Toast.LENGTH_LONG).show(); 
     } 
     return dataforroster; 
    } 



    //Code to run in main UI thread once the doinBackground method is finished. 
    @Override 
    protected void onPostExecute(List<RosterMember> dataforroster) 
    {  
     running_task = false; 
     fill_roster(); 
     roster_progress.setVisibility(View.GONE); 

    } 



} 

}  
+0

В чем проблема, с которой вы сталкиваетесь? – Raghunandan

+0

Спасибо Рагхунандану за быстрый ответ. На самом деле это не проблема, приложение работает с изменением конфигурации (ориентация экрана), которую я пробовал во время работы AsyncTask. Но я хочу, чтобы контур, который я разработал, эффективен и не вызывал утечки памяти. Эта утечка памяти вызывает беспокойство, потому что я хочу проверить, что старые действия не исключаются из сбора мусора, потому что все еще есть ссылка на них. – blkhatpersian

ответ

1
public class RosterFragment extends Fragment 
{ 
    List<RosterMember> dataforroster = new ArrayList<RosterMember>(); //List that will hold the Roster objects retrieved from Parse database, 
            //and later passed in to constructor for the RosterCustomArrayAdapter. 
    List<ParseUser> retrieved_list = new ArrayList<ParseUser>(); //List that will hold values retrieved from ParseUser Query. 
    View createdView; //View that will be passed back with built RosterFragment 
    private ProgressBar roster_progress; //The indeterminate ProgressBar that will be displayed until the AsyncTask is finished downloading the roster. 
    boolean running_task; 
    private RosterAsyncTask get_roster; 
    private boolean successful_query; 
    private String error_message; 

    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     // Retain this fragment across configuration changes. 
     setRetainInstance(true); 
     get_roster = new RosterAsyncTask(); //Create new RosterAsyncTask instance. 
     get_roster.execute(); 
     running_task = true; 

    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) 
    { 
     createdView = inflater.inflate(R.layout.rosterfragment, container, false); //Inflate the fragment using the specific layout 
     roster_progress = (ProgressBar) createdView.findViewById(R.id.loadingroster); //Find the ProgressBar in layout and set it to roster_progress. 
     fill_roster(); 
     return createdView; 
    } 

    @Override 
    public void onDetach() 
    { 
     super.onDetach(); 
     createdView = null; 
    } 


    public void fill_roster() 
    { 

      if(running_task == true) 
      { 
       roster_progress.setVisibility(View.VISIBLE); 
      } 
      else 
      { 

       roster_progress.setVisibility(View.GONE); 

       if(!dataforroster.isEmpty())//Get reference ListView in the inflated layout. 
       { 
        ListView the_Roster = (ListView) createdView.findViewById(R.id.rostercoachofficers); 
        //Create an instance of the RosterCustomArrayAdapter using the dataforroster List. 
        RosterCustomArrayAdapter roster_Adapter = new RosterCustomArrayAdapter(getActivity(), dataforroster); 
        //Sort the roster_Adapter so elements in ListView will be sorted alphabetically by first name. 
        roster_Adapter.sort(new RosterComparator());  
        //Attach adapter to the ListView to populate its data. 
        the_Roster.setAdapter(roster_Adapter); 

       } 
       else 
       { 
        if(successful_query == false) 
        { 
         Toast.makeText(getActivity(), error_message, Toast.LENGTH_LONG).show(); 
        } 
       } 
      } 


    } 




    //AsyncTask responsible for downloading roster in background thread. 
    private class RosterAsyncTask extends AsyncTask<Void, Void , Void> 
    { 


     //The operations to perform in the AsyncTask background thread. The results(the roster data downloaded) will be passed to onPostExecute. 
     @Override 
     protected Void doInBackground(Void... params) 
     { 
      dataforroster.clear(); 
      ParseQuery<ParseUser> query = ParseUser.getQuery(); //Get specific ParseQuery for ParseUsers. 

      try 
      { 
      retrieved_list = query.find(); //Initiate query. 
      for(ParseUser current_user: retrieved_list) //For every ParseUser returned from query, create a new RosterMember using the ParseUser 
            //data and then add it to the dataforroster List. 
       { 
        RosterMember current_member = new RosterMember(); 
        current_member.username = current_user.getUsername(); 
        ParseFile parse_ByteArray = (ParseFile)current_user.get("profile_picture"); 
        Bitmap profile_Picture = BitmapFactory.decodeByteArray(parse_ByteArray.getData(), 0, parse_ByteArray.getData().length); 
        current_member.profile_Picture = profile_Picture; 
        current_member.title = current_user.getString("title"); 
        dataforroster.add(current_member); 
       } 


      successful_query = true;   

      } 

      //If problem occurred in query execution, use Toast to display error message. 
      catch (ParseException e) 
      { 
       successful_query = false; 
       error_message = "Error, " + e.getMessage(); 
      } 
      return null; 

     } 



     //Code to run in main UI thread once the doinBackground method is finished. 
     @Override 
     protected void onPostExecute(Void ignore) 
     {  

      running_task = false; 

      if(getActivity()!=null) 
      { 
       fill_roster(); 
      } 

     } 



    } 
} 
0

Две основные вещи, которые я могу видеть, как сделку с вашей деятельностью. Ваш тост в doInBackground() должен быть вызван из основного потока, поэтому я уверен, что это не удастся. Активность, которую вы передаете адаптеру, также отделена от фрагмента, поэтому всякий раз, когда у вас есть изменение конфигурации, которое будет уничтожено и больше не будет действительным.

Чтобы предотвратить закрытие какой-либо силы, вы можете захотеть заменить тост сообщением logcat и убедиться, что активность и roster_progress не являются нулевыми, прежде чем что-либо делать в onPostExecute.

+0

Спасибо, Джон за быстрый ответ. Я изменил Toast с doInBackground на onPostExecute. Что меня озадачивает, так это то, что, несмотря на то, как я передаю действие адаптеру, в настоящий момент нет силы или сбоя. Изменение конфигурации, которое я пробовал, было ориентацией экрана и без проблем проверено на таймер сна. – blkhatpersian

+0

Все дело в сроках. В большинстве случаев вы можете быть в порядке, но если он поймает его до того, как представление может быть воссоздано или добавлено новое действие, вы получите исключение NullPointerException. – Jon

+0

Я вижу, что вы сейчас говорите, Джон. Если бы я определил, что действие или roster_progress было нулевым в onPostExecute, что бы вы предположили в этом сегменте кода? Цикл while для продолжения работы до тех пор, пока оба эти значения не равны нулю? – blkhatpersian

Смежные вопросы