刷新Android片段视图时的奇怪行为

时间:2015-01-13 20:05:58

标签: android android-fragments android-asynctask refresh

我很难完成一项应该很容易的任务。我不确定是不是因为Android平台设计不好或者我错过了什么。我只想在简历上刷新片段的视图。以下是详细信息:

我有两个活动,一个SplashActivity,它从服务器检索数据(使用AsyncTask)并将数据传递给我的MainActivity。在我的MainActivity中,我获取此数据并将其传递给名为SummaryFragment的片段。我有一些片段和一个导航抽屉(在我的MainActivity中)。第一个可见片段是SummaryFragment,它读取从MainActivity传递给它的数据,从而绘制图形。

当应用程序启动时,可能没有活动的Internet连接,在这种情况下,在我的摘要片段中,我要求用户启用WiFi。我想要做的是在用户启用WiFi后返回应用程序后刷新此SummaryFragment的视图。我现在做的是在我的MainActivity的onResume()中,我检查SummaryFragment是否可见,如果是,我再次启动SplashActivity(并关闭当前活动)。 SplashActivity必须获取数据(就像应用程序启动时一样)并启动MainActivity(用获取的数据提供),它加载摘要片段并显示图形。

问题是,在应用程序恢复后,需要相当长的时间(30-40秒)才能从SplashActivity转到MainActivity并显示图表(同时用户会看到启动屏幕),而当应用程序启动时,这需要1-2秒。在使用当前解决方案(将用户重定向到SplashActivity)之前,在MainActivity.onResume()中,我尝试使用我在SplashScreen中使用的相同AsyncTask类来获取数据并在之后显示摘要片段,但结果是相同的,有一个显着的延迟。

以下代码是我的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();
    }       
}

PrefetchData:

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现在可以从传递给它的包中检索所需的数据。

1 个答案:

答案 0 :(得分:0)

问题在于Android的AsyncTask的(相当)未记录的行为。 AsyncTask 不在单独的线程中运行,它在与其他AsyncTasks共享的线程中运行,因此如果您有其他AsyncTasks(或者甚至是普通线程),如果它们在当前之前开始运行AsyncTask,这个AsyncTask 等待让他们完成他们的行动。我明确指出我希望我的AsyncTask在一个单独的线程中运行,这样就可以将提取JSON对象的延迟从大约20-25秒减少到3-4秒(我正在进行其他并行的网络访问调用)。我在我的主要活动的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!");
    }
}