【问题标题】:Android how to make a thread wait until a certain event happens?Android如何让线程等到某个事件发生?
【发布时间】:2014-08-28 07:20:33
【问题描述】:

[我的情况]
现在在 MainActivity 中,我创建了一些片段,其中有一些 TextView。在 MainActivity 中有一个异步线程 AsyncTask 用于从 web 获取信息。 AsyncTask获取完成后会通过回调方法更新上述TextViews中的文本(Fragment实现了一个OnInfoGotInterface并在接口中实例化一个onInfoGot()方法。onInfoGot()会调用Fragment中定义的方法setTextView()更新信息)。

[我的问题]
在执行程序时,我发现AsyncTask 完成从web 获取信息的时间点早于Fragment 调用onCreateView() 的时间点。换句话说,当 AsyncTask 调用 (OnInfoGotInterface)fragment.onInfoGot() 设置 TextViews 时,TextViews 还没有被实例化(TextViews 在 CreateView() 上通过 rootView.findViewById() 方法实例化)。结果,出现了 NullPointerException。

[我需要一个解决方案]
现在我想做这样的事情:当 AsyncTask 完成从 web 获取信息并要调用 onInfoGot() 时,我们停止它,并让它等到 Fragment 完成 onCreateView()。之后我们唤醒 AsyncTask 并允许它更新片段。

附言。有人建议我应该在 Fragment 的定义中的 onCreateView() 之后调用 new AsyncTask.excute()。但是这里 AsyncTask 和 Fragment 都是在 MainActivity 中创建的。它们是 MainActivity 中的两个不同的任务线程,一个用于显示数据,另一个用于从 Web 获取数据。

谁能给点建议?我真的很感激! 代码如下:

MainActivity.java:

public class MainActivity extends Activity{
private List<City> cityList;
private ViewPager mPager;  /*ViewPager for show the page of city info*/
private ScreenSlidePagerAdapter mPagerAdapter;  /* Adapter for the ViewPager, 
        * ScreenSlidePagerAdapter is a subclass of FragmentStatePagerAdapter*/
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_show_city);

    cityList = new ArrayList<City>();
    mPager = (ViewPager) findViewById(R.id.pager);
    mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);
    init();
}

private void init(){
    loadCities();  //Load cities from database into cityList
    initFragments();
    getCityInfo();
}

private void initFragments(){
    mPagerAdapter.removeAllFragments();
    for(City city: cityList){
        PageFragment fragment = new PageFragment(city.getName());
        mPagerAdapter.addFragment(fragment);
        mPagerAdapter.notifyDataSetChanged();
    }
}

private void getCityInfo(){
    for(City city: cityList){
        String cityName = city.getName(); 
        obtainCityInfo(cityName);  
    }
}

private obtainCityInfo(String cityName){
    String request = "http://example.abc.com/"
            + cityName + "&output=json";
    new AccessWebServiceTask().execute(request, cityName); 
}

private class AccessWebServiceTask extends AsyncTask<String, Void, CityInfo>{
    @Override
    protected CityInfo doInBackground(String... urls) {
        String result = getWebContent(urls[0]);  /*Access web through HTTP*/
        String cityName = urls[1];
        /*Transform the String result to a CityInfo object containing information of a city*/
        CityInfo cityInfo = encodeJason(result, cityName);  

        return cityInfo;
    }

    protected void onPostExecute(CityInfo cityInfo){
            OnCityGot(cityInfo); 
    }
}

public void onCityGot(CityInfo cityInfo){
    if(cityInfo != null){  
        String cityName = cityInfo.getCityName();
        /*Set the info field of a city object*/
        cityList.getByName(cityName).setCityInfo(cityInfo);
        mPagerAdapter.updateFragment(cityInfo);
    }
}

}

ScreenSlidePagerAdapter.java

public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter{
    private List<PageFragment> fragmentsList;
    public ScreenSlidePagerAdapter(FragmentManager fm) {
        super(fm);
        fragmentsList = new ArrayList<PageFragment>();
    }

    public ScreenSlidePagerAdapter(FragmentManager fm, List<PageFragment> list){
        super(fm);
        fragmentsList = list;
    }

    @Override
    public PageFragment getItem(int position) {
        return fragmentsList.get(position);
    }

    @Override
    public int getCount() {
        return fragmentsList.size();
    }

    public void setFragmentsList(List<PageFragment> fragmentsList){
        this.fragmentsList = fragmentsList;
        notifyDataSetChanged();
    }

    public void addFragment(PageFragment f){
        fragmentsList.add(f);
        notifyDataSetChanged();
    }

    public void removeFragment(int position){
        fragmentsList.remove(position);
        notifyDataSetChanged();
    }

    public void removeAllFragments(){
        fragmentsList.clear();
        notifyDataSetChanged();
    }

    private PageFragment findFragmentByName(String cityName){
        for(PageFragment fragment: fragmentsList){
            if(fragment.getCityName().equals(cityName))
                return fragment;
        }
        return null;
    }
    public void updateFragment(CityInfo cityInfo){
        String cityName = cityInfo.getCityName();
        OnCityInfoChanged fragment = (OnCityInfoChanged)findFragmentByName(cityName);
        String population = cityInfo.getPopulation();
        fragment.onCityInfoChanged(population);
        notifyDataSetChanged();
    }
}

PageFragment.java:

public class PageFragment extends Fragment implements OnCityInfoChanged{
    private TextView cityNameText;
    private TextView populationText;
    String cityName;
    String population;

    public PageFragment(){}

    public PageFragment(String cityName, String population){
        this.cityName = cityName;
        this.population = population
    }

    public PageFragment(CityInfo cityInfo){
        this.cityName = cityInfo.getCityName();
        this.population = cityInfo.getPopulation();
    }

    public PageFragment(String cityName){
        this.cityName = cityName;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState){
         ViewGroup rootView = (ViewGroup) inflater.inflate(
                R.layout.fragment_city_page2, container, false);
         cityNameText = (TextView)rootView.findViewById(R.id.city_name);
         populationText = (TextView)rootView.findViewById(R.id.population);

         setCityName(cityName);
         setPopulation(population)

         return rootView;
    }

    public void setCityName(String name){
        cityNameText.setText(name);
    }

    public void setPopulation(String population){
        populationText.setText(population);
    }

    public String getCityName(){
        return cityName;
    }  

    @Override
    public void onCityInfoChanged(String population) {
        //setCityName(cityName);
        setPopulation();
    }
}

【问题讨论】:

  • 发布代码。当用英语解释时,很难理解发生了什么。
  • 我发布了代码。请检查一下。谢谢!

标签: android multithreading android-fragments android-asynctask


【解决方案1】:

我建议从 MainActivity 中删除 AsyncTask AccessWebServiceTask 并将其放入 Fragment PageFragment。然后在这个片段中,覆盖onActivityCreated,启动AsyncTask。

[编辑]

这是您的代码的更新版本。测试它是否有效:

主活动

    public class MainActivity extends Activity{
    private List<City> cityList;
    private ViewPager mPager;  /*ViewPager for show the page of city info*/
    private ScreenSlidePagerAdapter mPagerAdapter;  /* Adapter for the ViewPager, 
            * ScreenSlidePagerAdapter is a subclass of FragmentStatePagerAdapter*/

     protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_city);

        cityList = new ArrayList<City>();
        mPager = (ViewPager) findViewById(R.id.pager);
        mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
        mPager.setAdapter(mPagerAdapter);
        init();
    }

    private void init(){
        loadCities();  //Load cities from database into cityList
        initFragments();

    }

    private void initFragments(){
        mPagerAdapter.removeAllFragments();
        for(City city: cityList){
            PageFragment fragment = PageFragment.newFragment(city.getName());
            mPagerAdapter.addFragment(fragment);       
        }
    }
}

ScreenSlidePagerAdapter:

    public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter{
    private List<PageFragment> fragmentsList;
    public ScreenSlidePagerAdapter(FragmentManager fm) {
        super(fm);
        fragmentsList = new ArrayList<PageFragment>();
    }

    public ScreenSlidePagerAdapter(FragmentManager fm, List<PageFragment> list){
        super(fm);
        fragmentsList = list;
    }

    @Override
    public PageFragment getItem(int position) {
        return fragmentsList.get(position);
    }

    @Override
    public int getCount() {
        return fragmentsList.size();
    }

    public void setFragmentsList(List<PageFragment> fragmentsList){
        this.fragmentsList = fragmentsList;
        notifyDataSetChanged();
    }

    public void addFragment(PageFragment f){
        fragmentsList.add(f);
        notifyDataSetChanged();
    }

    public void removeFragment(int position){
        fragmentsList.remove(position);
        notifyDataSetChanged();
    }

    public void removeAllFragments(){
        fragmentsList.clear();
        notifyDataSetChanged();
    }    
}

页面片段:

    public class PageFragment extends Fragment {
    private TextView cityNameText;
    private TextView populationText;
    private String cityName;
    private String population;

    public static final String CITY_NAME_KEY = "cityname"; 

    public PageFragment(){}


    public static PageFragment newFragment(String cityName){
      PageFragment fragment = new PageFragment();
      Bundle args = new Bundle();
      args.putString(CITY_NAME_KEY, cityName);
      fragment.setArguments(args);
      return fragment;
    }

    @Override
    public void onCreate (Bundle savedInstanceState){
       super.onCreate();
       if(savedInstanceState != null){
         this.cityName = savedInstanceState.getString(CITY_NAME_KEY);
       }       
    }   

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState){
         ViewGroup rootView = (ViewGroup) inflater.inflate(
                R.layout.fragment_city_page2, container, false);
         cityNameText = (TextView)rootView.findViewById(R.id.city_name);
         populationText = (TextView)rootView.findViewById(R.id.population);

         return rootView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState){
       if(this.cityName != null){
         String request = "http://example.abc.com/" + cityName + "&output=json";
         new AccessWebServiceTask().execute(request, cityName); 
       }
    }

    public void setCityName(String name){
        cityNameText.setText(name);
    }

    public void setPopulation(String population){
        populationText.setText(population);
    }

    public String getCityName(){
        return cityName;
    }  


    private class AccessWebServiceTask extends AsyncTask<String, Void, CityInfo>{
    @Override
    protected CityInfo doInBackground(String... urls) {
        String result = getWebContent(urls[0]);  /*Access web through HTTP*/
        String cityName = urls[1];
        /*Transform the String result to a CityInfo object containing information of a city*/
        CityInfo cityInfo = encodeJason(result, cityName);  

        return cityInfo;
    }

    protected void onPostExecute(CityInfo cityInfo){
            OnCityGot(cityInfo); 
     }
   }

  public void onCityGot(CityInfo cityInfo){
     if(cityInfo != null){  
        String population = cityInfo.getPopulation();
        setPopulation(population);
     }
  }
}

【讨论】:

    【解决方案2】:

    你可以试试ConditionVariable,它可以用来hold AsyncTask

    private ConditionVariable mCondition = new ConditionVariable(false);
    mCondition.block(); //used to block
    // your condition
    mCondition.open(); // used to open
    

    【讨论】:

    • 似乎无法解决问题。我在 MainActivity 中定义了 mCondition。在调用 OnCityGot() 之前,我在 onPostExecute() 中调用 mCondition.block()。并在返回语句之前在 onCreateView() 中调用 mCondition.open()。但结果是:程序没有响应。
    • 将条件变量声明为 volatile,因为它被许多线程共享。
    • 对不起,我刚才搞错了。结果和以前一样(没有任何区别)而不是没有响应。
    • 非常抱歉...您的解决方案似乎是正确的。谢谢!
    • 无需抱歉。很高兴能帮到你!
    【解决方案3】:

    首先,您不应该使用带参数的 Fragment 构造函数。您应该将参数发送到片段集的片段中的片段参数。见:https://stackoverflow.com/a/15392591/360211

    我会让片段在它的onCreate 中调用obtainCityInfo 方法。然后不再查找,我可以将OnCityInfoChanged 传递给异步任务,以便它调用onPostExecute

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState){
         ViewGroup rootView = (ViewGroup) inflater.inflate(
                R.layout.fragment_city_page2, container, false);
         cityNameText = (TextView)rootView.findViewById(R.id.city_name);
         populationText = (TextView)rootView.findViewById(R.id.population);
    
         setCityName(cityName);
         obtainCityInfo(cityName, this);
    
         return rootView;
    }
    
    private obtainCityInfo(String cityName, OnCityInfoChanged callback){
        String request = "http://example.abc.com/"
                + cityName + "&output=json";
        new AccessWebServiceTask(callback).execute(request, cityName); 
    }
    

    【讨论】:

    • 谢谢!我按你说的试了,解决了问题!
    • 如果有用,可以按向上的灰色箭头说声谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-02
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-20
    相关资源
    最近更新 更多