我创建了一个小的测试应用程序,它代表了我的问题。 我使用ActionBarSherlock实现与(福尔摩斯)片段的标签。
我的代码: 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.lang.IllegalStateException:片段MyFragment {410f6060}不附着到活动
我想这是因为新的MyFragment
已经在此期间被创建,并连接到活动的前AsyncTask
完成。 在代码onPostExecute
呼吁一个独立的MyFragment
。
但是,我怎么能解决这个问题?
Answer 1:
我发现一个非常简单的答案: isAdded()
返回true
,如果该片段正在增加其活性。
@Override
protected void onPostExecute(Void result){
if(isAdded()){
getResources().getString(R.string.app_name);
}
}
为了避免onPostExecute
从当被称为Fragment
不附接到所述Activity
是取消AsyncTask
暂停或停止时Fragment
。 然后isAdded()
就没有必要了。 然而,最好是保持这个检查到位。
Answer 2:
我在这里遇到两种不同的情况:
1)当我想要异步任务仍要完成:想象我onPostExecute没有收到存储数据,然后调用一个监听器来更新视图,因此,更有效率,我希望任务反正做完,所以我有数据准备好时,用户卡梅斯背部。 在这种情况下,我通常这样做:
@Override
protected void onPostExecute(void result) {
// do whatever you do to save data
if (this.getView() != null) {
// update views
}
}
2)当我想要异步任务只完成意见时,可以更新:你在这里提议的情况下,任务仅更新的意见,不需要数据存储,所以它有没有线索的任务完成,如果意见不再被显示。 我这样做:
@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();
}
我发现这个没有问题,虽然我也用,包括从活动,而不是碎片发射任务的(也许)更复杂的方式。
希望这可以帮助别人! :)
Answer 3:
问题是,你正在使用getResources试图访问资源(在这种情况下,字符串)()的getString(),它会尝试从活动获得的资源。 见片段这个类的源代码:
/**
* 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()调用将抛出一个异常。
接受的解决方案恕我直言,是不是要为你只是隐藏问题的方式。 正确的方法是刚刚从别的地方,所以总能保证存在,如应用程序上下文获得资源:
youApplicationObject.getResources().getString(...)
Answer 4:
与您的代码的问题是,你正在使用的AsyncTask,因为当你睡眠的线程在旋转屏幕的方式:
Thread.sleep(2000)
在的AsyncTask仍在工作,那是因为你没有的onDestroy()适当地消除的AsyncTask实例中的片段重建之前(当你旋转)当这个相同的AsyncTask实例(旋转后)运行onPostExecute(),这个试图找到与getResources()与旧片段实例的资源(无效的情况下):
getResources().getString(R.string.app_name)
这相当于:
MyFragment.this.getResources().getString(R.string.app_name)
因此,最终的解决方案是管理的AsyncTask实例(取消的话,这是仍在工作)之前,当你旋转屏幕的片段重建,如果在过渡期间取消,一个布尔标志的帮助下重建后重新启动的AsyncTask:
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;
}
}
}
Answer 5:
他们是该片段从活动相当招解决方案和泄漏。
因此,在这取决于活动场景从片段访问的getResource或任何一个的情况下,经常检查活动状态和片段状态如下
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
}
}
Answer 6:
我面临同样的问题,我刚才添加的singletone实例来获得资源由埃里克称为
MainFragmentActivity.defaultInstance().getResources().getString(R.string.app_name);
你也可以使用
getActivity().getResources().getString(R.string.app_name);
我希望这将有所帮助。
Answer 7:
if (getActivity() == null) return;
工作也是在某些情况下。 刚刚打破了代码执行的,并确保应用程序不会崩溃
Answer 8:
我面临着类似的问题时,与加载的喜好应用程序设置活动是可见的。 如果我想改变的喜好之一,然后使显示内容旋转并再次更改优先级,将与该片段(我的首类)没有连接到一个活动的消息崩溃。
当调试看上去该PreferencesFragment的onCreate()方法时的显示内容旋转正在调用两次。 这是自叹不如了。 然后,我添加了isAdded()检查块哪里会指出崩溃之外,它解决了这个问题。
下面是更新喜好总结,以显示新的条目听者的代码。 它坐落在我的Preferences类的onCreate()方法扩展了PreferenceFragment类:
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");
}
}
}
};
// ...
}
我希望这会帮助别人!
Answer 9:
如果您扩展Application
类和维持静“全球化”语境对象,如下所示,那么你可以用这个来代替活动加载字符串资源。
public class MyApplication extends Application {
public static Context GLOBAL_APP_CONTEXT;
@Override
public void onCreate() {
super.onCreate();
GLOBAL_APP_CONTEXT = this;
}
}
如果你使用这个,你可以逃脱Toast
和资源加载,而不必担心生命周期。
Answer 10:
在我的情况片段方法已经经过被称为
getActivity().onBackPressed();
Answer 11:
旧的文章,但我很惊讶最先进的投票答案。
这个正确的解决方案应该是取消(在你的片段或在适当情况下)中的onStop中的AsyncTask。 这样,你不引入内存泄漏(一的AsyncTask保持你的破坏片段的引用),你有什么是你的片段事情的更好的控制。
@Override
public void onStop() {
super.onStop();
mYourAsyncTask.cancel(true);
}
文章来源: Fragment MyFragment not attached to Activity