2014-09-29 2 views
0

Я использую AsyncTask в одном из фрагментов действия.Ошибка приложения при повороте экрана AsyncTask

AsyncTask работает отлично, но при вращении на экране он теряет ссылку на activity, и переменная возвращает NullPointerException, таким образом приложение падает.

Я смотрел на подобные вопросы, как this, this, this и this, но я не думаю, что с помощью изменения конфигурации хака этого решения.

код, который может быть сбой приложения (по LogCat, то NullPointerException находится в следующей строке):

Context context = MyActivity.this.getApplicationContext(); 

Я передать контекст в другой функции, проживающих за пределами деятельности и фрагмент класса ,

Заранее спасибо.

Update: Мой LogCat

09-29 09:40:53.415: E/AndroidRuntime(21997): FATAL EXCEPTION: AsyncTask #2 
09-29 09:40:53.415: E/AndroidRuntime(21997): java.lang.RuntimeException: An error occured while executing doInBackground() 
09-29 09:40:53.415: E/AndroidRuntime(21997): at android.os.AsyncTask$3.done(AsyncTask.java:278) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at java.util.concurrent.FutureTask.setException(FutureTask.java:124) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at java.util.concurrent.FutureTask.run(FutureTask.java:137) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at java.lang.Thread.run(Thread.java:856) 
09-29 09:40:53.415: E/AndroidRuntime(21997): Caused by: java.lang.NullPointerException 
09-29 09:40:53.415: E/AndroidRuntime(21997): at com.example.CommonClasses.CommonFunctions.readFile(CommonFunctions.java:262) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at com.example.CommonClasses.CommonFunctions.readFileContents(CommonFunctions.java:308) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at com.example.android.AvailabilityFragment$AvailabilityData.doInBackground(AvailabilityFragment.java:160) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at com.example.android.AvailabilityFragment$AvailabilityData.doInBackground(AvailabilityFragment.java:1) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at android.os.AsyncTask$2.call(AsyncTask.java:264) 
09-29 09:40:53.415: E/AndroidRuntime(21997): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 
+0

Эй, вы должны проверить, не имеет ли значение null или нет перед выполнением какой-либо операции. – Panther

+0

Опубликовать трассировку логарифма, пожалуйста, – Aniruddha

+0

@Panther Это всегда было бы нулевым, потому что активность была разрушена после поворота экрана. Разве это не заставило AsyncTask не запускаться после вращения экрана? –

ответ

0

Есть множество решений этого. Один простой для вас - использовать так называемый фрагмент «Сохранить». Это подход, который используется в некоторых образцах Google. Удерживаемый фрагмент не имеет пользовательского интерфейса и сохраняется при изменении ориентации. Это, как правило, определяется как статический внутренний класс вашей деятельности, как так:

public static class RetainFragment extends Fragment { 
    private static final String TAG = "RetainFragment"; 

    public RetainFragment() {} 

    public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) { 
     RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG); 
     if (fragment == null) { 
      fragment = new RetainFragment(); 
     } 
     return fragment; 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setRetainInstance(true); 
    } 
} 

Затем положить AsyncTask внутри вашего RetainFragment. Когда вызывается onPostExecute, вы можете просто использовать метод фрагмента getActivity(), чтобы получить активность, к которой он присоединен.

Надеюсь, что это поможет, дайте мне знать, если еще есть какая-то путаница!

Edit:Here является образец Google из сохранения фрагмента

Edit 2: (согласно комментариям)

public class A { 

    // This class holds a reference to it's outer A instance. It can be 
    // accessed using A.this. 
    public class innerClassA { 
     //... 
    } 

    // This class does not hold a reference to it's outer A instance. 
    public static class innerClassB { 
     //... 
    } 
} 

Edit 3: В комментариях, я заканчивал написав полный код в любом случае. Для всех, кто интересуется:

public class MyActivity extends Activity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_my); 

     Button b = (Button) findViewById(R.id.aaa); 
     b.setOnClickListener(new View.OnClickListener() { 
      @Override public void onClick(View v) { 
       RetainFragment retainFragment = 
         RetainFragment.findOrCreateRetainFragment(getFragmentManager()); 
       retainFragment.loadAsync(); 
      } 
     }); 
    } 

    private void handleResponse(String response) { 
     // do something with the response... 
    } 

    public static class RetainFragment extends Fragment { 
     private static final String TAG = "RetainFragment"; 

     public RetainFragment() { 
     } 

     public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) { 
      RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG); 
      if (fragment == null) { 
       fragment = new RetainFragment(); 
       fm.beginTransaction().add(fragment, TAG).commit(); 
      } 
      return fragment; 
     } 

     @Override 
     public void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      setRetainInstance(true); 
     } 

     private void loadAsync() { 
      new AsyncTask<Void, Void, String>() { 
       @Override protected String doInBackground(Void... params) { 
        // Do some work... 
        return null; 
       } 

       @Override protected void onPostExecute(String response) { 
        MyActivity myActivity = (MyActivity)getActivity(); 
        if (myActivity != null) { 
         myActivity.handleResponse(response); 
        } 
       } 
      }.execute(); 
     } 
    } 
} 
+0

Это выглядит достаточно элегантно. Вопрос: Должен ли я использовать статический класс для того, чтобы это решение работало? –

+0

Если это не статично, у вас будут утечки памяти –

+0

Не сделает ли статический класс фрагмента статичным в некоторых непредвиденных случаях в будущем? Я не знаю, я просто бросаю его туда. –

0

Попробуйте этот путь

<activity 
    android:name=".ActivityName" 
    android:configChanges="orientation|screenSize|keyboardHidden"/> 
+1

Я бы предпочел не так, как это НЕ РЕКОМЕНДУЕТСЯ в руководстве Android Developers: http://developer.android.com/guide/topics/manifest/activity-element.html#config –

+0

Я знаю, что Android не рекомендуется, но он не отказывается использовать. Если для поддержки во время выполнения и жесткости клавиатуры вы не можете использовать несколько языков, вы можете использовать их. –

+0

Я использовал его во многих проектах, но у меня нет такой большой проблемы из-за этого подхода. –

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