활동에 연결되지 않은 조각 MyFragment
저는 제 문제를 나타내는 작은 테스트 앱을 만들었습니다.ActionBarSherlock을 사용하여 (Sherlock)이 있는 탭을 구현하고 있습니다.파편.
코드: 내코드:TestActivity.java
public class TestActivity extends SherlockFragmentActivity {
private ActionBar actionBar;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupTabs(savedInstanceState);
}
private void setupTabs(Bundle savedInstanceState) {
actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
addTab1();
addTab2();
}
private void addTab1() {
Tab tab1 = actionBar.newTab();
tab1.setTag("1");
String tabText = "1";
tab1.setText(tabText);
tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "1", MyFragment.class));
actionBar.addTab(tab1);
}
private void addTab2() {
Tab tab1 = actionBar.newTab();
tab1.setTag("2");
String tabText = "2";
tab1.setText(tabText);
tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "2", MyFragment.class));
actionBar.addTab(tab1);
}
}
TabListener.java
public class TabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener {
private final SherlockFragmentActivity mActivity;
private final String mTag;
private final Class<T> mClass;
public TabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {
mActivity = activity;
mTag = tag;
mClass = clz;
}
/* The following are each of the ActionBar.TabListener callbacks */
public void onTabSelected(Tab tab, FragmentTransaction ft) {
SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
// Check if the fragment is already initialized
if (preInitializedFragment == null) {
// If not, instantiate and add it to the activity
SherlockFragment mFragment = (SherlockFragment) SherlockFragment.instantiate(mActivity, mClass.getName());
ft.add(android.R.id.content, mFragment, mTag);
} else {
ft.attach(preInitializedFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);
if (preInitializedFragment != null) {
// Detach the fragment, because another one is being attached
ft.detach(preInitializedFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// User selected the already selected tab. Usually do nothing.
}
}
MyFragment.java
public class MyFragment extends SherlockFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
return null;
}
@Override
protected void onPostExecute(Void result){
getResources().getString(R.string.app_name);
}
}.execute();
}
}
를 Thread.sleep
부분적으로 데이터 다운로드를 시뮬레이션합니다.에 있는 onPostExecute
의 사용을 시뮬레이션하는 것입니다.Fragment
.
가로와 세로 사이에서 화면을 매우 빠르게 회전하면 다음에서 예외가 발생합니다.onPostExecute
코드:
자바.java.java잘못된 상태 예외:활동에 연결되지 않은 조각 MyFragment{410f6060}
제 생각에는 새로운.MyFragment
그 사이에 생성되었으며, 이전에 활동에 첨부되었습니다.AsyncTask
끝났습니다.의 코드onPostExecute
.MyFragment
.
하지만 어떻게 고칠 수 있을까요?
저는 매우 간단한 답을 찾았습니다::
아다가
true
조각이 현재 활동에 추가된 경우.
@Override
protected void onPostExecute(Void result){
if(isAdded()){
getResources().getString(R.string.app_name);
}
}
피하려면onPostExecute
전화를 받았을 때부터Fragment
는 에연되있않다니에 .Activity
하는 것입니다.AsyncTask
또는 시Fragment
.그리고나서isAdded()
더 이상 필요하지 않을 것입니다.그러나 이 검사는 그대로 유지하는 것이 좋습니다.
문제는 getResources().getString()을 사용하여 리소스(이 경우 문자열)에 액세스하려고 시도하는 것입니다. 이 경우 활동에서 리소스를 가져오려고 시도합니다.Fragment 클래스의 다음 소스 코드를 참조하십시오.
/**
* Return <code>getActivity().getResources()</code>.
*/
final public Resources getResources() {
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
return mHost.getContext().getResources();
}
mHost
활동을 유지하는 개체입니다.
활동이 연결되지 않을 수 있으므로 getResources() 호출에서 예외가 발생합니다.
당신이 문제를 숨기는 것이기 때문에 승인된 해결책 IMHO는 방법이 아닙니다.올바른 방법은 애플리케이션 컨텍스트와 같이 항상 존재하도록 보장된 다른 곳에서 리소스를 가져오는 것입니다.
youApplicationObject.getResources().getString(...)
저는 여기서 두 가지 다른 시나리오에 직면했습니다.
비동기 작업을 완료할 때: 내 게시물을 상상합니다.Execute는 수신된 데이터를 저장한 다음 수신기를 호출하여 보기를 업데이트하므로 효율성을 높이기 위해 작업을 완료하여 사용자가 돌아올 때 데이터를 준비합니다.이 경우에는 대개 다음과 같이 합니다.
@Override
protected void onPostExecute(void result) {
// do whatever you do to save data
if (this.getView() != null) {
// update views
}
}
보기를 업데이트할 수 있을 때만 비동기 작업이 완료되기를 원하는 경우: 여기서 제안하는 경우 작업은 보기만 업데이트하고 데이터 저장소는 필요하지 않으므로 보기가 더 이상 표시되지 않는 경우 작업을 완료할 단서가 없습니다.사용자 작업을 수행합니다.
@Override
protected void onStop() {
// notice here that I keep a reference to the task being executed as a class member:
if (this.myTask != null && this.myTask.getStatus() == Status.RUNNING) this.myTask.cancel(true);
super.onStop();
}
단편 대신 활동에서 작업을 시작하는 (아마도) 더 복잡한 방법을 사용하기도 하지만, 저는 이것에 대해 아무런 문제가 없다는 것을 발견했습니다.
이것이 누군가에게 도움이 되길 바랍니다! :)
그들은 이것에 대한 꽤 속임수 해결책과 활동에서 파편의 유출입니다.
따라서 getResource 또는 Fragment에서 액세스하는 활동 컨텍스트에 따라 달라지는 경우 항상 활동 상태와 fragment 상태를 확인합니다.
Activity activity = getActivity();
if(activity != null && isAdded())
getResources().getString(R.string.no_internet_error_msg);
//Or any other depends on activity context to be live like dailog
}
}
코드의 문제는 비동기 작업을 사용하는 방식에 있습니다. 절전 스레드 중에 화면을 회전하면 다음과 같은 문제가 발생하기 때문입니다.
Thread.sleep(2000)
비동기 작업이 여전히 작동합니다. 이는 조각이 재구성되기 전(회전할 때)과 동일한 비동기 작업 인스턴스(회전 후)가 포스트에서 실행될 때 destroy()에서 비동기 작업 인스턴스를 제대로 취소하지 않았기 때문입니다.execute(): 이전 fragment 인스턴스(잘못된 인스턴스)가 있는 getResources()가 있는 리소스를 찾습니다.
getResources().getString(R.string.app_name)
이 값은 다음과 같습니다.
MyFragment.this.getResources().getString(R.string.app_name)
따라서 화면을 회전할 때 조각이 재구성되기 전에 비동기 작업 인스턴스를 관리하는 것이 마지막 해결책입니다(아직 작동 중이면 취소). 전환 중에 취소된 경우 부울 플래그를 사용하여 재구성 후 비동기 작업을 다시 시작합니다.
public class MyFragment extends SherlockFragment {
private MyAsyncTask myAsyncTask = null;
private boolean myAsyncTaskIsRunning = true;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState!=null) {
myAsyncTaskIsRunning = savedInstanceState.getBoolean("myAsyncTaskIsRunning");
}
if(myAsyncTaskIsRunning) {
myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("myAsyncTaskIsRunning",myAsyncTaskIsRunning);
}
@Override
public void onDestroy() {
super.onDestroy();
if(myAsyncTask!=null) myAsyncTask.cancel(true);
myAsyncTask = null;
}
public class MyAsyncTask extends AsyncTask<Void, Void, Void>() {
public MyAsyncTask(){}
@Override
protected void onPreExecute() {
super.onPreExecute();
myAsyncTaskIsRunning = true;
}
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {}
return null;
}
@Override
protected void onPostExecute(Void result){
getResources().getString(R.string.app_name);
myAsyncTaskIsRunning = false;
myAsyncTask = null;
}
}
}
if (getActivity() == null) return;
경우에 따라 작동하기도 합니다.코드 실행을 중단하고 앱이 충돌하지 않도록 하기만 하면 됩니다.
에릭이 참조한 대로 리소스를 얻기 위해 싱글톤 인스턴스를 추가하는 것과 같은 문제에 직면했습니다.
MainFragmentActivity.defaultInstance().getResources().getString(R.string.app_name);
사용할 수도 있습니다.
getActivity().getResources().getString(R.string.app_name);
이것이 도움이 되길 바랍니다.
기본 설정이 로드된 응용 프로그램 설정 작업이 표시되었을 때도 비슷한 문제에 직면했습니다.환경설정 중 하나를 변경한 후 표시 내용을 회전시키고 환경설정을 다시 변경하면 조각(내 환경설정 클래스)이 활동에 연결되지 않았다는 메시지와 함께 충돌합니다.
디버깅할 때 디스플레이 내용이 회전할 때 PreferencesFragment의 onCreate() 메서드가 두 번 호출되는 것처럼 보였습니다.그것은 이미 충분히 이상했습니다.그런 다음 충돌을 나타내는 블록 외부에 isAdded() 체크를 추가하여 문제를 해결했습니다.
다음은 환경설정 요약을 업데이트하여 새 항목을 표시하는 수신기 코드입니다.PreferencesFragment 클래스를 확장하는 myPreferences 클래스의 onCreate() 메서드에 있습니다.
public static class Preferences extends PreferenceFragment {
SharedPreferences.OnSharedPreferenceChangeListener listener;
@Override
public void onCreate(Bundle savedInstanceState) {
// ...
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// check if the fragment has been added to the activity yet (necessary to avoid crashes)
if (isAdded()) {
// for the preferences of type "list" set the summary to be the entry of the selected item
if (key.equals(getString(R.string.pref_fileviewer_textsize))) {
ListPreference listPref = (ListPreference) findPreference(key);
listPref.setSummary("Display file content with a text size of " + listPref.getEntry());
} else if (key.equals(getString(R.string.pref_fileviewer_segmentsize))) {
ListPreference listPref = (ListPreference) findPreference(key);
listPref.setSummary("Show " + listPref.getEntry() + " bytes of a file at once");
}
}
}
};
// ...
}
이것이 다른 사람들에게 도움이 되기를 바랍니다!
만약 당신이 그것을 연장한다면.Application
정적 '글로벌' 컨텍스트 개체를 다음과 같이 분류하고 유지합니다. 그러면 작업 대신 이 개체를 사용하여 문자열 리소스를 로드할 수 있습니다.
public class MyApplication extends Application {
public static Context GLOBAL_APP_CONTEXT;
@Override
public void onCreate() {
super.onCreate();
GLOBAL_APP_CONTEXT = this;
}
}
이걸 사용하면, 당신은 도망갈 수 있습니다.Toast
라이프사이클에 대한 걱정 없이 리소스를 로드할 수 있습니다.
저는 Xamarine Android에서 "Fragment MyFragment not attached to Context"라는 비슷한 오류 메시지를 받았습니다.
이 리소스 호출로 인해 이 오류 메시지가 표시됩니다.
button.Text = Resources.GetString(Resource.String.please_wait)
저는 Xamarine Android를 사용하여 이 문제를 해결했습니다.
if (Context != null && IsAdded){
button.Text = Resources.GetString(Resource.String.please_wait);
}
나의 경우, fragment 메서드는 다음과 같이 호출되었습니다.
getActivity().onBackPressed();
오래된 게시물이지만, 저는 가장 많이 투표된 답변에 놀랐습니다.
이에 대한 적절한 해결책은 onStop(또는 fragment에서 적절한 경우)의 비동기 작업을 취소하는 것입니다.이렇게 하면 메모리 누수(파괴된 조각에 대한 참조를 유지하는 비동기 작업)가 발생하지 않고 조각에서 진행 중인 작업을 더 잘 제어할 수 있습니다.
@Override
public void onStop() {
super.onStop();
mYourAsyncTask.cancel(true);
}
프래그멘트에 이것을 추가합니다.
Activity activity;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
activity = context instanceof Activity ? (Activity) context : null;
}
그런 다음 getContext(), getActivity(), requireActivity() 또는 requireContext()를 활동으로 변경합니다.
단순한 솔루션 및 작업 100%
if (getActivity() == null || !isAdded()) return;
언급URL : https://stackoverflow.com/questions/10919240/fragment-myfragment-not-attached-to-activity
'programing' 카테고리의 다른 글
CSS 강제 이미지 크기 조정 및 가로 세로 비율 유지 (0) | 2023.05.31 |
---|---|
날짜 시간.지금 대 날짜 시간.UtcNow (0) | 2023.05.31 |
OSX의 홈브루에서 "cannot load the file --utils/open"을 방지하는 방법 (0) | 2023.05.31 |
탐색 모음 표시/숨기기 (0) | 2023.05.31 |
Express.js를 사용하여 HTTP 오류 코드를 지정하는 방법은 무엇입니까? (0) | 2023.05.31 |