@VidarWahlberg,你的回答很好,但是假设你有一个FragmentActivity
,在其中运行任务,并打开新的FragmentActivity
,在异步完成之前或者你的应用程序在后台运行(例如:你点击了主页)。我建议你创建一个BaseActivity
,在其中扩展FragmentActivity
,并且所有的活动都应该扩展它。
什么很重要?
在BaseActivity
中,你需要有static FragmentManager fm;
,正如@VidarWahlberg在他的回答中提到的那样,并在子活动中使用它来显示DialogFragment
。
有什么区别?
不要在onStart()
方法中初始化fm = getSupportFragmentManager();
,而是需要在onResume()
方法中进行初始化。这是必需的,因为当您启动线程运行活动时,它会创建其新实例,并且当启动下一个活动时,它会创建BaseActivity
的新实例,其中将初始化FragmenManager
,当您回到线程启动活动时,fm
对象将与第二个活动(已暂停)实例一起。应用程序将崩溃,因为您将尝试在已暂停的活动视图中显示对话框。您无法更改已暂停活动的视图,因此需要在每次调用onResume()
时进行初始化。onResume()
也在活动启动时调用,因此在那里初始化FragmentManager
是安全的!
我们还需要担心什么?
你可能认为一切都很好,但实际上并非如此。你可能一开始没有意识到,但如果你的应用程序不在前台(而是在后台),所有的活动都会被暂停,但线程已经启动并正在运行。这是一个问题,因为线程将继续运行,并尝试显示
DialogFragmernt
(或者说更改未处于焦点的活动视图)。因此,解决方案是跟踪应用程序是否在前台。可以使用
PackageManager
来完成这个任务,但这将需要在清单文件中添加新的权限(用户不喜欢这样做,他们可能不想安装你的应用程序)。解决方案是拥有一个
static boolean isOnBackground = false;
字段,在每个活动的
onResume()
中使用
isOnBackground = false;
进行更改,在
onPause()
中使用
isOnBackground = true;
进行更改。这不是一个大的变化,因为
BaseActivity
是我们可以这样做的地方。这是安全的,因为每次启动新活动都会创建
BaseActivity
类的新实例,但由于该字段是
static
,它只创建一次,并且对所有实例都相同。在启动线程后方法的主体代码之前,我们需要检查
isOnBackground
的状态。
你可能会说因为onPause()
和onResume()
之间的延迟可能会出问题,但你的应用程序需要设计成快速启动活动并不要在UI线程上执行长时间任务(需要在后台线程上完成)。因此,如果你的应用程序设计得好,你就不会有问题 :)
当应用程序不在前台时,可以使用线程运行方法的递归调用。这是安全的,不会导致循环,因为当应用程序的状态被重置(需要内存)或应用程序被用户或Android本身停止时,循环将停止,因为所有应用程序实例将从内存中释放!
注意:使用android支持库!
以下是代码:
BaseActivity.java
.......................
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
.......................
public class BaseActivity extends FragmentActivity {
public static FragmentManager fm;
public static boolean isOnBackground;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onResume() {
super.onResume();
BaseActivity.isOnBackground = false;
BaseActivity.fm = getSupportFragmentManager();
}
@Override
protected void onPause() {
super.onPause();
BaseActivity.isOnBackground = true;
}
}
MainActivity.java
.....................
import android.support.v4.app.DialogFragment;
.....................
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startNewActivity = (Button) findViewById(R.id.helloWorld);
startNewActivity.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
SecondActivity.class);
startActivity(intent);
}
});
showEditDialog();
}
private void showEditDialog() {
(new Handler()).postDelayed(new Runnable() {
@Override
public void run() {
if(!BaseActivity.isOnBackground) {
DialogFragment myFragmentDialog = new DialogFragment();
myFragmentDialog.show(BaseActivity.fm,
"fragment_my_dialog");
} else {
showEditDialog();
}
}
}, 15000);
}
}
setRetainInstance(true)
方法。然后在onPostExecute()
方法中调用getSupportFragmentManager()
方法以获取SupportFragmentManager
对象。然而,在英语中,“很多”意味着不止一个情况,这就是为什么我建议你提出一个新的 StackOverflow 问题,并提供示例,这样人们可以帮助你找到更好的解决策略。 - CommonsWareAsyncTask
内部类和onRetainNonConfigurationInstance()
,但这已经被弃用,以引导人们使用保留的片段:https://dev59.com/t2865IYBdhLWcg3wa980#3821998。关于您的其他答案,请勿将`View`放入静态数据成员中,因为这会引入内存泄漏。 - CommonsWare