android - AsyncTask 和方向变化

标签 android android-asynctask progressdialog screen-orientation

更改屏幕方向时,我在重新创建 Activity 、进度对话框和异步任务时遇到问题。我在这里看到了一些解决方案,我决定使用 onRetainCustomNonConfigurationInstance() 来保存 AsyncTask 的实例。好吧,在 onCreate 中,我检查是否有 AsyncTask 的实例,如果存在,我会显示一个新的进度对话框。但是这个对话框会在无限循环中阻止 UI(在 onPostExecute 中有dialog.dismiss()),并且 Activity 当然不会显示结果。

创建时

getDirection = (GetDirection) getLastCustomNonConfigurationInstance();
if(getDirection != null) {
  dialog = new ProgressDialog(RouteActivity.this);
  dialog.setMessage(getResources().getString(R.string.loading_data));
  dialog.setIndeterminate(false);
  dialog.setCancelable(false);
  dialog.show();
} else {
  getDirection = new GetDirection();
  getDirection.execute();
}

onRetainCustomNonConfigurationInstance

@Override
public Object onRetainCustomNonConfigurationInstance() {
  return getDirection;
}

@Override
protected void onDestroy() {
  super.onDestroy();
  if (dialog.isShowing()) {
    dialog.dismiss();
  }
}

编辑#1: 我采用了Kingfisher with Fragment and Retained Fragment的解决方案,但是当我在AsyncTask的doInBackground期间改变方向时,Expandable Listview上有一个NPE。

08-14 12:50:40.866: E/AndroidRuntime(22739): FATAL EXCEPTION: main
08-14 12:50:40.866: E/AndroidRuntime(22739): java.lang.NullPointerException
08-14 12:50:40.866: E/AndroidRuntime(22739):    at com.pasquini.adapter.ListRouteExpandableAdapter.getGroupCount(ListRouteExpandableAdapter.java:78)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at android.widget.ExpandableListConnector.getCount(ExpandableListConnector.java:399)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at android.widget.ListView.setAdapter(ListView.java:460)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at android.widget.ExpandableListView.setAdapter(ExpandableListView.java:470)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at com.pasquini.activity.RouteActivity.onPostExecute(RouteActivity.java:792)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at com.pasquini.fragment.GetDirectionFragment$GetDirectionTask.onPostExecute(GetDirectionFragment.java:244)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at com.pasquini.fragment.GetDirectionFragment$GetDirectionTask.onPostExecute(GetDirectionFragment.java:1)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at android.os.AsyncTask.finish(AsyncTask.java:631)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at android.os.AsyncTask.access$600(AsyncTask.java:177)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at android.os.Handler.dispatchMessage(Handler.java:99)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at android.os.Looper.loop(Looper.java:137)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at android.app.ActivityThread.main(ActivityThread.java:4895)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at java.lang.reflect.Method.invokeNative(Native Method)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at java.lang.reflect.Method.invoke(Method.java:511)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:994)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:761)
08-14 12:50:40.866: E/AndroidRuntime(22739):    at dalvik.system.NativeStart.main(Native Method).

这里是 fragment 中的代码:

public class GetDirectionFragment extends Fragment {

private Map<Leg, List<Step>> legsCollection;
private ArrayList<Object> legsAndSteps;
private List<Leg> legs;
private List<Poi> pois;
private List<LatLng> polyz;

/**
   * Callback interface through which the fragment will report the
   * task's progress and results back to the Activity.
   */
  public static interface TaskCallbacks {
    void onPreExecute();
    void onPostExecute();
    List<Poi> getListPoiByRoute();
    void setLegs(List<Leg> legs, Map<Leg, List<Step>> legsCollection);
    void setPolyline(List<LatLng> polyz);

  }

  private TaskCallbacks callbacks;
  private GetDirectionTask getDirectionTask;

  /**
   * Hold a reference to the parent Activity so we can report the
   * task's current progress and results. The Android framework 
   * will pass us a reference to the newly created Activity after 
   * each configuration change.
   */
  @Override
  public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        callbacks = (TaskCallbacks) activity;
    } catch(ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement TaskCallbacks");
    }
    pois = callbacks.getListPoiByRoute();
  }

  /**
   * This method will only be called once when the retained
   * Fragment is first created.
   */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Retain this fragment across configuration changes.
    setRetainInstance(true);

    // Create and execute the background task.
    getDirectionTask = new GetDirectionTask();
    getDirectionTask.execute();
  }

  /**
   * Set the callback to null so we don't accidentally leak the 
   * Activity instance.
   */
  @Override
  public void onDetach() {
    super.onDetach();
    callbacks = null;
  }

  private class GetDirectionTask extends AsyncTask<Void, Integer, Void> {

        @Override
        protected void onPreExecute() {
          if (callbacks != null) {
              callbacks.onPreExecute();
          }
        }

        /**
         * Note that we do NOT call the callback object's methods
         * directly from the background thread, as this could result 
         * in a race condition.
         */
        @Override
        protected Void doInBackground(Void... ignore) {
                List<String> lats = new ArrayList<String>();
                List<String> longs = new ArrayList<String>();

                for(Poi poi: pois) {
                    lats.add(poi.getCoordinates().getLatitude());
                    longs.add(poi.getCoordinates().getLongitude());
                }

                String stringUrl = "http://maps.googleapis.com/maps/api/directions/" +
                        "json?";
                //if(actualLanguage.equals("en")) {
                //  stringUrl += "language=en_EN";
                //} else {
                    stringUrl += "language=it";
                //}
                stringUrl+="&mode=walking&units=metric&origin=" +
                         lats.get(0) + "," +
                         longs.get(0);

                stringUrl += "&destination=" +
                        lats.get(pois.size()-1) + "," +
                        longs.get(pois.size()-1) + "&" +
                        "waypoints=";


                for(int i = 1; i<=lats.size()-2 && i<=longs.size()-2; i++) {
                    stringUrl += lats.get(i)+","+longs.get(i);
                    if(i==(lats.size()-2) && i==(longs.size()-2)) {
                        stringUrl += "&sensor=false";
                    } else {
                        stringUrl +="|";
                    }
                }

                Log.i("urlgoogle", stringUrl);

                StringBuilder response = new StringBuilder();
                try {
                    URL url = new URL(stringUrl);
                    HttpURLConnection httpconn = (HttpURLConnection) url
                            .openConnection();
                    if (httpconn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                        BufferedReader input = new BufferedReader(
                                new InputStreamReader(httpconn.getInputStream()),
                                8192);
                        String strLine = null;

                        while ((strLine = input.readLine()) != null) {
                            response.append(strLine);
                        }
                        input.close();
                    }

                    String jsonOutput = response.toString();

                    JSONObject jsonObject = new JSONObject(jsonOutput);


                    // routesArray contains ALL routes
                    JSONArray routesArray = jsonObject.getJSONArray("routes");
                    // Grab the first route
                    JSONObject route = routesArray.getJSONObject(0);

                    JSONArray legsArray = route.getJSONArray("legs");
                    legs = new ArrayList<Leg>();
                    legsAndSteps = new ArrayList<Object>();
                    String htmlInstructions;

                    legsCollection = new LinkedHashMap<Leg, List<Step>>();

                    for(int i=0; i<legsArray.length(); i++) {
                        List<Step> steps = new ArrayList<Step>();
                        Leg leg = new Leg();
                        //int idLeg = 0;
                        JSONObject legJson = legsArray.getJSONObject(i);
                        leg.setDistance(legJson.getJSONObject("distance").getString("text"));
                        leg.setDuration(legJson.getJSONObject("duration").getString("text"));
                        leg.setEndAddress(legJson.getString("end_address"));
                        leg.setStartAddress(legJson.getString("start_address"));
                        leg.setIdLeg(pois.get(i).getId());
                        leg.setStartPoiName(pois.get(i).getName());
                        leg.setEndPoiName(pois.get(i+1).getName());

                        legsAndSteps.add(leg);

                        JSONArray stepsArray = legJson.getJSONArray("steps");
                        for(int j=0; j<stepsArray.length(); j++) {
                            Step step = new Step();
                            JSONObject stepJson = stepsArray.getJSONObject(j);
                            step.setDistance(stepJson.getJSONObject("distance").getString("text"));
                            step.setDuration(stepJson.getJSONObject("duration").getString("text"));
                            htmlInstructions = android.text.Html.fromHtml(stepJson.getString("html_instructions")).toString();
                            step.setInstructions(htmlInstructions);
                            //step.setIdLeg(idLeg);

                            //Aggiunto per Exp
                            steps.add(step);

                            legsAndSteps.add(step);

                        }
                        legsCollection.put(leg, steps);


                        legs.add(leg);
                    }



                    JSONObject poly = route.getJSONObject("overview_polyline");
                    String polyline = poly.getString("points");
                    polyz = decodePoly(polyline);

                    callbacks.setLegs(legs, legsCollection);
                    callbacks.setPolyline(polyz);

                } catch (Exception e) {

                }

          return null;
        }



        @Override
        protected void onPostExecute(Void ignore) {
          if (callbacks != null) {
              callbacks.onPostExecute();
          }
        }
      }

  /* Method to decode polyline points */
    private List<LatLng> decodePoly(String encoded) {

        [code...]
    }

}

这里是 Activity 中的代码:

 @Override
public void onPostExecute() {


     expListView = (ExpandableListView) findViewById(R.id.lv_routepoi);
     ListRouteExpandableAdapter expListAdapter = new ListRouteExpandableAdapter(
             getApplicationContext(), legs, legsCollection);
     expListView.setAdapter(expListAdapter);

     setGroupIndicatorToRight();
     expListView.setChildDivider(getResources().getDrawable(R.drawable.divider_route));

    [code...]

    dialog.dismiss();


}

@Override
public void setLegs(List<Leg> legs, Map<Leg, List<Step>> legsCollection) {
    this.legs = legs;
    this.legsCollection = legsCollection;


}

@Override
public void setPolyline(List<LatLng> polyz) {
    this.polyz = polyz;

}

编辑 #2:我尝试了 Kingfisher 的新解决方案,但 doInBackground 方法中始终存在 NPE。如果我在加载过程中不更改屏幕方向,应用程序不会崩溃并在 ListView 上显示数据。

08-14 17:27:44.897: I/AsyncTask(30604): java.lang.NullPointerException
08-14 17:27:44.897: E/AndroidRuntime(30604): FATAL EXCEPTION: main
08-14 17:27:44.897: E/AndroidRuntime(30604): java.lang.NullPointerException
08-14 17:27:44.897: E/AndroidRuntime(30604):    at com.pasquini.activity.RouteActivity.onPostExecute(RouteActivity.java:847)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at com.pasquini.fragment.GetDirectionFragment$GetDirectionTask.onPostExecute(GetDirectionFragment.java:253)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at com.pasquini.fragment.GetDirectionFragment$GetDirectionTask.onPostExecute(GetDirectionFragment.java:1)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at android.os.AsyncTask.finish(AsyncTask.java:631)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at android.os.AsyncTask.access$600(AsyncTask.java:177)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at android.os.Handler.dispatchMessage(Handler.java:99)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at android.os.Looper.loop(Looper.java:137)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at android.app.ActivityThread.main(ActivityThread.java:4895)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at java.lang.reflect.Method.invokeNative(Native Method)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at java.lang.reflect.Method.invoke(Method.java:511)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:994)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:761)
08-14 17:27:44.897: E/AndroidRuntime(30604):    at dalvik.system.NativeStart.main(Native Method)

这里是 fragment 中的代码:

public class GetDirectionFragment extends Fragment {

private Map<Leg, List<Step>> legsCollection;
private ArrayList<Object> legsAndSteps;
private List<Leg> legs;
private List<Poi> pois;
private List<LatLng> polyz;

/**
   * Callback interface through which the fragment will report the
   * task's progress and results back to the Activity.
   */
  public static interface TaskCallbacks {
    void onPreExecute();
    void onPostExecute();
    List<Poi> getListPoiByRoute();
    void setLegs(List<Leg> legs, Map<Leg, List<Step>> legsCollection);
    void setPolyline(List<LatLng> polyz);

  }

  private TaskCallbacks callbacks;
  private GetDirectionTask getDirectionTask;

  /**
   * Hold a reference to the parent Activity so we can report the
   * task's current progress and results. The Android framework 
   * will pass us a reference to the newly created Activity after 
   * each configuration change.
   */
  @Override
  public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        callbacks = (TaskCallbacks) activity;
    } catch(ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement TaskCallbacks");
    }
    pois = callbacks.getListPoiByRoute();
  }

  /**
   * This method will only be called once when the retained
   * Fragment is first created.
   */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Retain this fragment across configuration changes.
    setRetainInstance(true);

    // Create and execute the background task.
    getDirectionTask = new GetDirectionTask();
    getDirectionTask.execute();
  }

  /**
   * Set the callback to null so we don't accidentally leak the 
   * Activity instance.
   */
  @Override
  public void onDetach() {
    super.onDetach();
    callbacks = null;
  }

  private class GetDirectionTask extends AsyncTask<Void, Integer, Void> {

        @Override
        protected void onPreExecute() {
          if (callbacks != null) {
              callbacks.onPreExecute();
          }
        }

        /**
         * Note that we do NOT call the callback object's methods
         * directly from the background thread, as this could result 
         * in a race condition.
         */
        @Override
        protected Void doInBackground(Void... ignore) {
                List<String> lats = new ArrayList<String>();
                List<String> longs = new ArrayList<String>();

                for(Poi poi: pois) {
                    lats.add(poi.getCoordinates().getLatitude());
                    longs.add(poi.getCoordinates().getLongitude());
                }

                String stringUrl = "http://maps.googleapis.com/maps/api/directions/" +
                        "json?";
                //if(actualLanguage.equals("en")) {
                //  stringUrl += "language=en_EN";
                //} else {
                    stringUrl += "language=it";
                //}
                stringUrl+="&mode=walking&units=metric&origin=" +
                         lats.get(0) + "," +
                         longs.get(0);

                stringUrl += "&destination=" +
                        lats.get(pois.size()-1) + "," +
                        longs.get(pois.size()-1) + "&" +
                        "waypoints=";


                for(int i = 1; i<=lats.size()-2 && i<=longs.size()-2; i++) {
                    stringUrl += lats.get(i)+","+longs.get(i);
                    if(i==(lats.size()-2) && i==(longs.size()-2)) {
                        stringUrl += "&sensor=false";
                    } else {
                        stringUrl +="|";
                    }
                }

                Log.i("urlgoogle", stringUrl);

                StringBuilder response = new StringBuilder();
                try {
                    URL url = new URL(stringUrl);
                    HttpURLConnection httpconn = (HttpURLConnection) url
                            .openConnection();
                    if (httpconn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                        BufferedReader input = new BufferedReader(
                                new InputStreamReader(httpconn.getInputStream()),
                                8192);
                        String strLine = null;

                        while ((strLine = input.readLine()) != null) {
                            response.append(strLine);
                        }
                        input.close();
                    }

                    String jsonOutput = response.toString();

                    JSONObject jsonObject = new JSONObject(jsonOutput);


                    // routesArray contains ALL routes
                    JSONArray routesArray = jsonObject.getJSONArray("routes");
                    // Grab the first route
                    JSONObject route = routesArray.getJSONObject(0);

                    JSONArray legsArray = route.getJSONArray("legs");
                    legs = new ArrayList<Leg>();
                    legsAndSteps = new ArrayList<Object>();
                    String htmlInstructions;

                    legsCollection = new LinkedHashMap<Leg, List<Step>>();

                    for(int i=0; i<legsArray.length(); i++) {
                        List<Step> steps = new ArrayList<Step>();
                        Leg leg = new Leg();
                        //int idLeg = 0;
                        JSONObject legJson = legsArray.getJSONObject(i);
                        leg.setDistance(legJson.getJSONObject("distance").getString("text"));
                        leg.setDuration(legJson.getJSONObject("duration").getString("text"));
                        leg.setEndAddress(legJson.getString("end_address"));
                        leg.setStartAddress(legJson.getString("start_address"));
                        leg.setIdLeg(pois.get(i).getId());
                        leg.setStartPoiName(pois.get(i).getName());
                        leg.setEndPoiName(pois.get(i+1).getName());

                        legsAndSteps.add(leg);

                        JSONArray stepsArray = legJson.getJSONArray("steps");
                        for(int j=0; j<stepsArray.length(); j++) {
                            Step step = new Step();
                            JSONObject stepJson = stepsArray.getJSONObject(j);
                            step.setDistance(stepJson.getJSONObject("distance").getString("text"));
                            step.setDuration(stepJson.getJSONObject("duration").getString("text"));
                            htmlInstructions = android.text.Html.fromHtml(stepJson.getString("html_instructions")).toString();
                            step.setInstructions(htmlInstructions);
                            //step.setIdLeg(idLeg);

                            steps.add(step);

                            legsAndSteps.add(step);

                        }
                        legsCollection.put(leg, steps);


                        legs.add(leg);
                    }



                    JSONObject poly = route.getJSONObject("overview_polyline");
                    String polyline = poly.getString("points");
                    polyz = decodePoly(polyline);

                    setLegs(legs, legsCollection);
                    setPolyline(polyz);

                } catch (Exception e) {
                            Log.i(TAG, e.toString());
                }

          return null;
        }



        @Override
        protected void onPostExecute(Void ignore) {
          if (callbacks != null) {
              callbacks.onPostExecute();
          }
        }
      }

  /* Method to decode polyline points */
    private List<LatLng> decodePoly(String encoded) {

        [code...]
    }

public void setLegs(List<Leg> legs, Map<Leg, List<Step>> legsCollection) {
            this.legs = legs;
            this.legsCollection = legsCollection;
        }

    public void setPolyline(List<LatLng> polyz) {
        this.polyz = polyz;

    }

    public List<Leg> getLegs() {
        return this.legs;
    }

    public Map<Leg, List<Step>> getMap() {
        return this.legsCollection;
    }

    public List<LatLng> getPolyline() {
        return this.polyz;
    }

}

这里是 Activity 中的代码:

@Override
    protected void onCreate(Bundle savedInstanceState) {
               [code...]
           FragmentManager fm = getSupportFragmentManager();
           getDirectionFragment = (GetDirectionFragment) fm.findFragmentByTag(TASK_FRAGMENT_TAG);

            // If the Fragment is non-null, then it is currently being
            // retained across a configuration change.
            if (getDirectionFragment == null) {
                getDirectionFragment = new GetDirectionFragment();
                fm.beginTransaction().add(getDirectionFragment, TASK_FRAGMENT_TAG).commit();

         [code...]
            }

 @Override
public void onPostExecute() {

           legs = getDirectionFragment.getLegs();
    legsCollection = getDirectionFragment.getMap();
    polyz = getDirectionFragment.getPolyline();

     expListView = (ExpandableListView) findViewById(R.id.lv_routepoi);
     ListRouteExpandableAdapter expListAdapter = new ListRouteExpandableAdapter(
             getApplicationContext(), legs, legsCollection);
     expListView.setAdapter(expListAdapter);

     setGroupIndicatorToRight();
     expListView.setChildDivider(getResources().getDrawable(R.drawable.divider_route));

    [code...]



}

最佳答案

检查此链接:Handling Configuration Changes with Fragments或者您可以使用AsyncTaskLoader 。我建议使用Retained Fragment来处理配置更改。仔细阅读文章。 编辑:不要在doInBackground中调用callback.set函数。当重新创建 Activity 时,您应该找到您的Retained Fragment ,并在onPost函数中尝试使用Retained Fragment的get函数来获取列表结果。

//Activity recreated
FragmentManager fm = getFragmentManager();
mTaskFragment = (TaskFragment) fm.findFragmentByTag("task");

//onPost method
yourList = mTaskFragment.getLegsAndMoveList();
// Do lisview data changing here

希望这有帮助。

关于android - AsyncTask 和方向变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18214293/

相关文章:

android - 避免 IllegalStateException “Can not perform this action after onSaveInstanceState"

android - 加载 Activity 时显示进度条

php - 获取具有相同值同表的SQL数据

android - Canvas 上的 TextView

android - 获取 adb shell 命令的结果代码

java - 在 onCreateView 中执行 AsyncTask 第一次尝试时不显示信息

java - ListView 在自定义适配器中重复项目

java - SeekBar - 无法设置起点

android - 将 36 MB JSON 文件写入 Realm 数据库时 UI 被阻塞,因此无法显示进度

java - 使用 SharedPreferences 覆盖后退按钮以保存数据