刷新 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!");
    }
}