刷新 Android 片段视图时的奇怪行为
Weird behavior when refreshing an Android fragment's view
我很难完成一项本应很容易的任务。我不确定是因为 Android 平台的糟糕设计还是我遗漏了什么。我只是想刷新一个片段对简历的看法。详情如下:
我有两个活动,一个 SplashActivity,它从服务器检索数据(使用 AsyncTask)并将数据传递到我的 MainActivity。在我的 MainActivity 中,我获取此数据并将其传递给名为 SummaryFragment 的片段。我有一些片段和一个导航抽屉(在我的 MainActivity 中)。第一个可见片段是 SummaryFragment,它读取从 MainActivity 传递给它的数据,然后绘制一个图形。
当应用程序启动时,可能没有活动的互联网连接,在这种情况下,在我的摘要片段中我要求用户启用 WiFi。我想要做的是在用户启用 WiFi 后返回应用程序后刷新此 SummaryFragment 的视图。我现在所做的是,在我的 MainActivity 的 onResume() 中,我检查 SummaryFragment 是否可见,如果是,我再次启动 SplashActivity(并关闭当前的 activity)。 SplashActivity 必须获取数据(就像它在应用程序启动时所做的那样)并启动 MainActivity(使用获取的数据提供)以加载摘要片段并显示图形。
问题是应用程序恢复后 需要相当长的时间(30-40 秒) 从 SplashActivity 转到 MainActivity 并显示图形(同时用户看到启动画面),而当应用程序启动时,它需要 1-2 秒才能完成。在使用当前解决方案(将用户重定向到 SplashActivity)之前,在 MainActivity.onResume() 中,我尝试使用我在 SplashScreen 中使用的相同 AsyncTask class 来获取数据并随后显示摘要片段,但是结果是一样的,有明显的延迟。
下面的代码是我的MainActivity的onResume():
Fragment fragment = getVisibleFragment();
if (fragment instanceof SummaryFragment) {
Intent intentSplashActvity = new Intent(this, SplashActivity.class);
Log.d(TAG, "about to start the splash activity");
startActivity(intentSplashActvity);
// close current activity
finish();
super.onResume();
return;
}
super.onResume();
SplashActivity:
public class SplashActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
new PrefetchData(this).execute();
}
}
预取数据:
public class PrefetchData extends AsyncTask<Void, Void, Void> {
String serverResponseJson = null;
private String data1 = null,
data2 = null,
data3 = null;
private SplashActivity mSplashActivity = null;
private MainActivity mMainActivity;
public PrefetchData(Activity sourceActivity){
if (sourceActivity.getClass() == SplashActivity.class) {
mSplashActivity = (SplashActivity) sourceActivity;
}
}
@Override
protected Void doInBackground(Void... arg0) {
JsonParser jsonParser = new JsonParser();
try {
if (CaratApplication.isInternetAvailable()) {
serverResponseJson = jsonParser
.getJSONFromUrl("http://aURL");
}
} catch (Exception e) {
Log.d("SplashActivity", e.getStackTrace().toString());
}
if (serverResponseJson != null && serverResponseJson != "") {
try {
JSONArray jsonArray = new JSONObject(serverResponseJson).getJSONArray("arrayName");
// Using Java reflections to set fields by passing their name to a method
try {
setFieldsFromJson(jsonArray, 0, "data1");
setFieldsFromJson(jsonArray, 1, "data2");
setFieldsFromJson(jsonArray, 2, "data3");
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
} catch (JSONException e) {
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (mSplashActivity != null) {
Intent intentMainActvity = new Intent(mSplashActivity, MainActivity.class);
if (gotDataSuccessfully()) {
intentMainActvity.putExtra("data1", data1);
intentMainActvity.putExtra("data2", data2);
intentMainActvity.putExtra("data3", data3);
} else {
intentMainActvity.putExtra("data1", Constants.DATA_NOT_AVAIABLE);
intentMainActvity.putExtra("data2", Constants.DATA_NOT_AVAIABLE);
intentMainActvity.putExtra("data3", Constants.DATA_NOT_AVAIABLE);
}
mSplashActivity.startActivity(intentMainActvity);
mSplashActivity.finish();
}
}
}
在 MainActivity 中,在导航抽屉中选择 "summary" 条目后,我初始化了 SummaryFragment,然后使用片段事务 (replaceFragment(mSummaryFragment, mSummaryFragmentLabel)).这是我用来初始化摘要片段的方法:
private void initSummaryFragment() {
if (mData1 == 0 && mData2 == 0 && mData3 == 0) {
Intent intent = getIntent();
String data1 = intent.getStringExtra("data1");
String data2 = intent.getStringExtra("data2");
String data3 = intent.getStringExtra("data3");
boolean isDataAvaiable = !data1.equals(Constants.DATA_NOT_AVAIABLE)
&& !data2.equals(Constants.DATA_NOT_AVAIABLE) && !data3.equals(Constants.DATA_NOT_AVAIABLE);
if (isDataAvaiable) {
mData1 = Integer.parseInt(data1);
mData2 = Integer.parseInt(data2);
mData3 = Integer.parseInt(data3);
}
}
mSummaryFragment = new SummaryFragment();
mArgs = new Bundle();
mArgs.putInt("data1", mData1);
mArgs.putInt("data2", mData2);
mArgs.putInt("data3", mData3);
mSummaryFragment.setArguments(mArgs);
mSummaryFragmentLabel = getString(R.string.summary);
}
SummaryFragment 现在可以从传递给它的包中检索它需要的数据。
问题出在 Android 的 AsyncTask 的(相当)未记录的行为上。 AsyncTask 不 运行 在单独的线程 中,它 运行 在与其他 AsyncTask 共享的线程中,因此如果您有其他 AsyncTask(甚至普通线程),如果它们在您当前的 AsyncTask 之前 运行ning 开始,则此 AsyncTask 等待 以等待它们完成操作。我明确指定我希望我的 AsyncTask 在单独的线程中 运行,这将获取 JSON 对象的延迟从大约 20-25 秒减少到 3-4 秒(我有其他网络并行访问正在进行的呼叫)。我 运行 以下代码作为我的主要 activity.
的 onCreate() 方法中 preInitFragments() 方法的一部分
// The following PrefetchData class is of type AsyncTask<void, void, void>. I moved this class from a stand-alone class to an inner class inside my main activity for easier refreshing of my fragment.
PrefetchData prefetchData = new PrefetchData();
// run this asyncTask in a new thread [from the thread pool] (run in parallel to other asyncTasks)
// (do not wait for them to finish, it takes a long time)
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
prefetchData.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
prefetchData.execute();
在此之后,我调用我的方法 initSummaryFragment()。请注意,在我的 AsyncTask 中,在 doInBackGround() 中,我设置了您在下面看到的两个字段(mField1 和 mField2)(我过去常常将它们作为一个包传递到摘要屏幕,但是当您只是分离和附加片段(以刷新其视图)而不将包传递给您的片段)。在我的 asyncTask 的 onPostExecute() 中,我调用了我的方法 refreshSummaryFragment():
private boolean isStatsDataAvailable() {
return mWellbehaved != 0 && mHogs != 0 && mBugs != 0;
}
private void initSummaryFragment() {
if (! isStatsDataAvailable()) {
mSummaryFragment = new SummaryFragment();
}
mSummaryFragmentLabel = "Summary";
}
为了刷新我的摘要片段,我简单地分离并附加我的摘要片段,然后提交挂起的片段事务:
public void refreshSummaryFragment() {
if (isStatsDataAvailable()) { // blank summary fragment already attached. detach and attach to refresh
FragmentManager manager = getSupportFragmentManager();
// init the fragment (for later use when user selects an item from the navigation drawer)
mSummaryFragment = getSupportFragmentManager().findFragmentByTag("Summary");
FragmentTransaction fragTransaction = manager.beginTransaction();
// refresh the summary fragment:
fragTransaction.detach(mSummaryFragment);
fragTransaction.attach(mSummaryFragment);
fragTransaction.commit();
} else {
Log.e(TAG, "refreshSummaryFragment(): stats data not avaiable!");
}
}
我很难完成一项本应很容易的任务。我不确定是因为 Android 平台的糟糕设计还是我遗漏了什么。我只是想刷新一个片段对简历的看法。详情如下:
我有两个活动,一个 SplashActivity,它从服务器检索数据(使用 AsyncTask)并将数据传递到我的 MainActivity。在我的 MainActivity 中,我获取此数据并将其传递给名为 SummaryFragment 的片段。我有一些片段和一个导航抽屉(在我的 MainActivity 中)。第一个可见片段是 SummaryFragment,它读取从 MainActivity 传递给它的数据,然后绘制一个图形。
当应用程序启动时,可能没有活动的互联网连接,在这种情况下,在我的摘要片段中我要求用户启用 WiFi。我想要做的是在用户启用 WiFi 后返回应用程序后刷新此 SummaryFragment 的视图。我现在所做的是,在我的 MainActivity 的 onResume() 中,我检查 SummaryFragment 是否可见,如果是,我再次启动 SplashActivity(并关闭当前的 activity)。 SplashActivity 必须获取数据(就像它在应用程序启动时所做的那样)并启动 MainActivity(使用获取的数据提供)以加载摘要片段并显示图形。
问题是应用程序恢复后 需要相当长的时间(30-40 秒) 从 SplashActivity 转到 MainActivity 并显示图形(同时用户看到启动画面),而当应用程序启动时,它需要 1-2 秒才能完成。在使用当前解决方案(将用户重定向到 SplashActivity)之前,在 MainActivity.onResume() 中,我尝试使用我在 SplashScreen 中使用的相同 AsyncTask class 来获取数据并随后显示摘要片段,但是结果是一样的,有明显的延迟。
下面的代码是我的MainActivity的onResume():
Fragment fragment = getVisibleFragment();
if (fragment instanceof SummaryFragment) {
Intent intentSplashActvity = new Intent(this, SplashActivity.class);
Log.d(TAG, "about to start the splash activity");
startActivity(intentSplashActvity);
// close current activity
finish();
super.onResume();
return;
}
super.onResume();
SplashActivity:
public class SplashActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
new PrefetchData(this).execute();
}
}
预取数据:
public class PrefetchData extends AsyncTask<Void, Void, Void> {
String serverResponseJson = null;
private String data1 = null,
data2 = null,
data3 = null;
private SplashActivity mSplashActivity = null;
private MainActivity mMainActivity;
public PrefetchData(Activity sourceActivity){
if (sourceActivity.getClass() == SplashActivity.class) {
mSplashActivity = (SplashActivity) sourceActivity;
}
}
@Override
protected Void doInBackground(Void... arg0) {
JsonParser jsonParser = new JsonParser();
try {
if (CaratApplication.isInternetAvailable()) {
serverResponseJson = jsonParser
.getJSONFromUrl("http://aURL");
}
} catch (Exception e) {
Log.d("SplashActivity", e.getStackTrace().toString());
}
if (serverResponseJson != null && serverResponseJson != "") {
try {
JSONArray jsonArray = new JSONObject(serverResponseJson).getJSONArray("arrayName");
// Using Java reflections to set fields by passing their name to a method
try {
setFieldsFromJson(jsonArray, 0, "data1");
setFieldsFromJson(jsonArray, 1, "data2");
setFieldsFromJson(jsonArray, 2, "data3");
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
} catch (JSONException e) {
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (mSplashActivity != null) {
Intent intentMainActvity = new Intent(mSplashActivity, MainActivity.class);
if (gotDataSuccessfully()) {
intentMainActvity.putExtra("data1", data1);
intentMainActvity.putExtra("data2", data2);
intentMainActvity.putExtra("data3", data3);
} else {
intentMainActvity.putExtra("data1", Constants.DATA_NOT_AVAIABLE);
intentMainActvity.putExtra("data2", Constants.DATA_NOT_AVAIABLE);
intentMainActvity.putExtra("data3", Constants.DATA_NOT_AVAIABLE);
}
mSplashActivity.startActivity(intentMainActvity);
mSplashActivity.finish();
}
}
}
在 MainActivity 中,在导航抽屉中选择 "summary" 条目后,我初始化了 SummaryFragment,然后使用片段事务 (replaceFragment(mSummaryFragment, mSummaryFragmentLabel)).这是我用来初始化摘要片段的方法:
private void initSummaryFragment() {
if (mData1 == 0 && mData2 == 0 && mData3 == 0) {
Intent intent = getIntent();
String data1 = intent.getStringExtra("data1");
String data2 = intent.getStringExtra("data2");
String data3 = intent.getStringExtra("data3");
boolean isDataAvaiable = !data1.equals(Constants.DATA_NOT_AVAIABLE)
&& !data2.equals(Constants.DATA_NOT_AVAIABLE) && !data3.equals(Constants.DATA_NOT_AVAIABLE);
if (isDataAvaiable) {
mData1 = Integer.parseInt(data1);
mData2 = Integer.parseInt(data2);
mData3 = Integer.parseInt(data3);
}
}
mSummaryFragment = new SummaryFragment();
mArgs = new Bundle();
mArgs.putInt("data1", mData1);
mArgs.putInt("data2", mData2);
mArgs.putInt("data3", mData3);
mSummaryFragment.setArguments(mArgs);
mSummaryFragmentLabel = getString(R.string.summary);
}
SummaryFragment 现在可以从传递给它的包中检索它需要的数据。
问题出在 Android 的 AsyncTask 的(相当)未记录的行为上。 AsyncTask 不 运行 在单独的线程 中,它 运行 在与其他 AsyncTask 共享的线程中,因此如果您有其他 AsyncTask(甚至普通线程),如果它们在您当前的 AsyncTask 之前 运行ning 开始,则此 AsyncTask 等待 以等待它们完成操作。我明确指定我希望我的 AsyncTask 在单独的线程中 运行,这将获取 JSON 对象的延迟从大约 20-25 秒减少到 3-4 秒(我有其他网络并行访问正在进行的呼叫)。我 运行 以下代码作为我的主要 activity.
的 onCreate() 方法中 preInitFragments() 方法的一部分// The following PrefetchData class is of type AsyncTask<void, void, void>. I moved this class from a stand-alone class to an inner class inside my main activity for easier refreshing of my fragment.
PrefetchData prefetchData = new PrefetchData();
// run this asyncTask in a new thread [from the thread pool] (run in parallel to other asyncTasks)
// (do not wait for them to finish, it takes a long time)
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
prefetchData.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
prefetchData.execute();
在此之后,我调用我的方法 initSummaryFragment()。请注意,在我的 AsyncTask 中,在 doInBackGround() 中,我设置了您在下面看到的两个字段(mField1 和 mField2)(我过去常常将它们作为一个包传递到摘要屏幕,但是当您只是分离和附加片段(以刷新其视图)而不将包传递给您的片段)。在我的 asyncTask 的 onPostExecute() 中,我调用了我的方法 refreshSummaryFragment():
private boolean isStatsDataAvailable() {
return mWellbehaved != 0 && mHogs != 0 && mBugs != 0;
}
private void initSummaryFragment() {
if (! isStatsDataAvailable()) {
mSummaryFragment = new SummaryFragment();
}
mSummaryFragmentLabel = "Summary";
}
为了刷新我的摘要片段,我简单地分离并附加我的摘要片段,然后提交挂起的片段事务:
public void refreshSummaryFragment() {
if (isStatsDataAvailable()) { // blank summary fragment already attached. detach and attach to refresh
FragmentManager manager = getSupportFragmentManager();
// init the fragment (for later use when user selects an item from the navigation drawer)
mSummaryFragment = getSupportFragmentManager().findFragmentByTag("Summary");
FragmentTransaction fragTransaction = manager.beginTransaction();
// refresh the summary fragment:
fragTransaction.detach(mSummaryFragment);
fragTransaction.attach(mSummaryFragment);
fragTransaction.commit();
} else {
Log.e(TAG, "refreshSummaryFragment(): stats data not avaiable!");
}
}