Android开发 - Fragment中AsyncTask的错误实现?

时间:2013-09-30 03:49:34

标签: android android-fragments android-asynctask fragment

我最近开始尝试在Android项目的片段中实现 AsyncTasks ,并立即遇到导致重大问题的配置更改的常见错误。我在网上看到的方法并没有轻易地融入到我的ViewPager设置中,而且我利用自己的知识自行处理配置更改。

我的问题是:我的做法有什么危险吗?最大的问题是内存泄漏,但我确保在onDetach()方法上清空每个created_View。

实施摘要:

  • 对于Fragment,setRetainInstance为true,因此不必重新创建,也不会丢失重要数据。

  • 在onCreateView()中,当必须重新创建Fragment的View时,始终调用代码部分,应用程序将检查其AsyncTask是否正在运行。如果是,则显示IndeterminateProgressBar,当它完成onPostExecute时,将其可见性更改为GONE。

  • onDetach()中,确保created_view视图设置为null,因此没有与最初使用的Activity相关的内存泄漏

  • 配置更改前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);

  }



}

}    

2 个答案:

答案 0 :(得分: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();
          }

      }



    }   
}

答案 1 :(得分:0)

我能看到的两件主要事情都涉及你的活动。 doInBackground()中的toast需要从主线程调用,所以我很确定会失败。您传递给适配器的活动也与片段是分开的,因此每当您有一个配置更改将被销毁并且将不再有效时。

为了帮助防止强制关闭,您可能希望用tocat的消息替换toast,并在onPostExecute中执行任何操作之前验证activity和roster_progress是否为null。