2

開始;是的,我到處都在尋找答案,但沒有人遇到同樣的問題。這個問題看起來可能與此處的另一個問題相同,但它絕對不是:)Android ViewPager,碎片和AsyncTask。如何保持AsyncTask在方向更改上的狀態?

基本上我想創建的是一個「首次啓動」活動,由4個不同佈局的片段組成。這我已經做到了。我還有一個AsyncTask,通過調用遠程服務器來檢查一個片段是否存在於數據庫中。這也適用。但是,只要我旋轉手機和方向更改FragmentActivity重新創建(顯然=))。如果我在解僱AsyncTask時這樣做,它會崩潰或在某些情況下完成用戶檢查。

所以我的問題是:如果方向更改,我如何保留AsyncTask的狀態? 我見過一些使用回調的基本解決方案,並將AsyncTask作爲內部類。由於我的尋呼機適配器的佈局以及所創建的所有四個片段都使用了相同的Fragments類,因此它們不適用於我的情況。

任何幫助,非常感謝。示例或解決方案更值得讚賞! =)

請參閱下面的(醜陋)代碼,使所有這些都變成現實。

FirstRunActivity.java

import android.content.Intent; 
import android.os.Bundle; 
import android.support.v4.view.ViewPager; 

import com.actionbarsherlock.app.SherlockFragmentActivity; 
import com.actionbarsherlock.view.Menu; 
import com.actionbarsherlock.view.MenuInflater; 
import com.actionbarsherlock.view.MenuItem; 
import com.iqqn.***.R; 
import com.viewpagerindicator.IconPageIndicator; 
import com.viewpagerindicator.PageIndicator; 

public class FirstRunActivity extends SherlockFragmentActivity { 

private FragmentAdapter mAdapter; 
private ViewPager mPager; 
private PageIndicator mIndicator; 
private boolean userExit = false; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.first_run); 

    mAdapter = new FragmentAdapter(getSupportFragmentManager()); 

    mPager = (ViewPager)findViewById(R.id.pager); 
    mPager.setAdapter(mAdapter); 

    mIndicator = (IconPageIndicator)findViewById(R.id.indicator); 
    mIndicator.setViewPager(mPager); 
} 

@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    MenuInflater inflater = getSupportMenuInflater(); 
    inflater.inflate(R.menu.first_run, menu); 
    return super.onCreateOptionsMenu(menu); 
} 

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    switch (item.getItemId()) { 
    case R.id.menu_exit: 
     userExit = true; 
     this.finish(); 
     return true; 
    } 
    return super.onOptionsItemSelected(item); 
} 

@Override 
public void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 
}; 

@Override 
public void finish() { 
    // Prepare data intent 
    Intent data = new Intent(); 
    if(userExit) { 
     data.putExtra("exit", true); 
     setResult(RESULT_CANCELED, data); 
    } else { 
     data.putExtra("signedIn", true); 
     data.putExtra("registered", true); 
     data.putExtra("acceptedEULA", true); 
     // Activity finished ok, return the data 
     setResult(RESULT_OK, data); 
    } 
    super.finish(); 
} 
} 

FragmentAdapter.java

import android.support.v4.app.Fragment; 
import android.support.v4.app.FragmentManager; 
import android.support.v4.app.FragmentPagerAdapter; 

import com.iqqn.***.R; 
import com.viewpagerindicator.IconPagerAdapter; 

class FragmentAdapter extends FragmentPagerAdapter implements IconPagerAdapter { 
    protected static final int[] CONTENT = new int[] { R.layout.***_welcome_1, R.layout.***_welcome_2, R.layout.***_welcome_3, R.layout.***_welcome_4}; 
    protected static final String[] CONTENTTITLE = new String[] { "Welcome","Account","Intro","Let's start!" }; 
protected static final int[] ICONS = new int[] { 
    R.drawable.perm_group_calendar, 
    R.drawable.perm_group_camera, 
    R.drawable.perm_group_device_alarms, 
    R.drawable.perm_group_location 
}; 

private int mCount = CONTENT.length; 

public FragmentAdapter(FragmentManager fm) { 
    super(fm); 
} 

@Override 
public Fragment getItem(int position) { 
    return Fragments.newInstance(CONTENT[position % CONTENT.length],position); 
} 

@Override 
public int getCount() { 
    return mCount; 
} 

@Override 
public CharSequence getPageTitle(int position) { 
    return FragmentAdapter.CONTENTTITLE[position % CONTENT.length]; 
} 

@Override 
public int getIconResId(int index) { 
    return ICONS[index % ICONS.length]; 
} 

public void setCount(int count) { 
    if (count > 0 && count <= 10) { 
     mCount = count; 
     notifyDataSetChanged(); 
    } 
} 
} 

Fragments.java

public final class Fragments extends SherlockFragment { 
    private static final String KEY_CONTENT = "Fragments:Content"; 
private int position = -1; 
private RegisterAsyncTaskHelper registerTask = null; 

public static Fragments newInstance(int content, int position) { 
    Fragments fragment = new Fragments(); 
    fragment.mContent = content; 
    fragment.position = position; 
    return fragment; 
} 

private int mContent = -1; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    setRetainInstance(true); 

    if ((savedInstanceState != null) && savedInstanceState.containsKey(KEY_CONTENT)) { 
     mContent = savedInstanceState.getInt(KEY_CONTENT); 
    } 
} 

@Override 
public void onPause() { 

    super.onPause(); 
} 

@Override 
public void onResume() { 

    super.onResume(); 
} 

@Override 
public void onDestroy() { 
    if (registerTask != null) { 
     registerTask.cancel(false); 
    } 
    super.onDestroy(); 
} 

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 

    final View view = inflater.inflate(mContent, container, false); 

    switch(position) { 
    case 0: 
     break; 
    case 1: 
     final Button btnRegister; 
     final Button btnLinkToLogin; 

     btnRegister = (Button) view.findViewById(R.id.btnRegister); 
     btnLinkToLogin = (Button) view.findViewById(R.id.btnLinkToLoginScreen); 
     // Register Button Click event 
     btnRegister.setOnClickListener(new View.OnClickListener() {   
      public void onClick(View localview) { 
       // Execute register task 
       registerTask = new RegisterAsyncTaskHelper(getActivity(), view); 
       if(registerTask != null) 
        registerTask.execute(); 
      } 
     }); 

     // Link to Login Screen 
     btnLinkToLogin.setOnClickListener(new View.OnClickListener() { 

      public void onClick(View view) { 
       // TODO Login 
      } 
     }); 
     break; 
    case 2: 
     break; 
    case 3: 
     break; 
    default: 
     break; 
    } 

    return view; 
} 


@Override 
public void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 
    outState.putInt(KEY_CONTENT, mContent); 
} 

} 

RegisterAsyncTaskHelper.java

import org.json.JSONException; 
import org.json.JSONObject; 

import android.os.AsyncTask; 
import android.support.v4.app.FragmentActivity; 
import android.view.View; 
import android.widget.EditText; 
import android.widget.ProgressBar; 
import android.widget.TextView; 

import com.iqqn.***.R; 
import com.iqqn.***.utils.DatabaseHandler; 
import com.iqqn.***.utils.UserFunctions; 

public class RegisterAsyncTaskHelper extends AsyncTask<Void, Integer, Void> { 

// JSON Response node names 
private static final String KEY_SUCCESS = "success"; 
private static final String KEY_ERROR = "error"; 
private static final String KEY_ERROR_MSG = "error_msg"; 
private static final String KEY_UID = "uid"; 
private static final String KEY_NAME = "name"; 
private static final String KEY_EMAIL = "email"; 
private static final String KEY_CREATED_AT = "created_at"; 

private View view = null; 
private FragmentActivity activity = null; 
private ProgressBar mProgress; 

public RegisterAsyncTaskHelper(FragmentActivity activity, View view) { 
    this.view = view; 
    this.activity = activity; 
    this.mProgress = (ProgressBar) view.findViewById(R.id.registerProgress); 
} 

@Override 
protected void onPreExecute() { 
    mProgress.setVisibility(View.VISIBLE); 
} 

@Override 
protected Void doInBackground(Void... params) { 

    final EditText inputFullName; 
    final EditText inputEmail; 
    final EditText inputPassword; 
    final TextView registerErrorMsg; 

    // Importing all assets like buttons, text fields 
    inputFullName = (EditText) view.findViewById(R.id.registerName); 
    inputEmail = (EditText) view.findViewById(R.id.registerEmail); 
    inputPassword = (EditText) view.findViewById(R.id.registerPassword); 
    registerErrorMsg = (TextView) view.findViewById(R.id.register_error); 


    String name = inputFullName.getText().toString(); 
    String email = inputEmail.getText().toString(); 
    String password = inputPassword.getText().toString(); 
    UserFunctions userFunction = new UserFunctions(); 
    JSONObject json = userFunction.registerUser(name, email, password); 

    // check for login response 
    try { 
     if (json.getString(KEY_SUCCESS) != null) { 
      activity.runOnUiThread(new Runnable() { 
       @Override 
       public void run() { 
        registerErrorMsg.setText(""); 
       } 
      }); 

      String res = json.getString(KEY_SUCCESS); 
      if(Integer.parseInt(res) == 1){ 
       // user successfully registred 
       // Store user details in SQLite Database 
       DatabaseHandler db = new DatabaseHandler(activity.getApplicationContext()); 
       JSONObject json_user = json.getJSONObject("user"); 

       // Clear all previous data in database 
       userFunction.logoutUser(activity.getApplicationContext()); 
       db.addUser(json_user.getString(KEY_NAME), json_user.getString(KEY_EMAIL), json.getString(KEY_UID), json_user.getString(KEY_CREATED_AT));       

       /* 
       // Launch Dashboard Screen 
       Intent dashboard = new Intent(ctx, DashboardActivity.class); 
       // Close all views before launching Dashboard 
       dashboard.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
       startActivity(dashboard); 
       // Close Registration Screen 
       finish(); 
       */ 

      }else{ 
       // Error in registration 
       activity.runOnUiThread(new Runnable() { 
        @Override 
        public void run() { 
         registerErrorMsg.setText("Error occured in registration"); 
        } 
       }); 
      } 
     } 
    } catch (JSONException e) { 
     e.printStackTrace(); 
    } 
    return null; 
} 

@Override 
protected void onProgressUpdate(final Integer... values) { 

} 

@Override 
protected void onPostExecute(final Void result) { 
    mProgress.setVisibility(View.GONE); 
} 
} 

只是爲了澄清。截至目前,該代碼絕不應該看起來不錯。但它會在我得到這個工作之後。

//亞歷山大

回答

2

如果方向更改,如何保留AsyncTask的狀態?

是否有AsyncTask由保留片段(即動態片段自身調用setRetainInstance(true))管理。該片段將在配置更改後繼續存在。

由於我的尋呼機適配器的佈局以及所創建的所有四個片段都使用了相同的碎片類,所以它們不起作用。

然後加入第五片段爲AsyncTask。它沒有參加UI:

if (getSupportFragmentManager().findFragmentByTag(MODEL)==null) { 
    model=new ModelFragment(); 
    getSupportFragmentManager().beginTransaction().add(model, MODEL) 
          .commit(); 
} 

這裏,ModelFragment負責AsyncTask(在內存其他地方無法舉行的活動所需要的數據模型的任何部分)Fragment。我們只創建片段(如果它不存在於其標籤下)(MODEL)。

+0

謝謝CommonsWare!這個答案讓我明白,問題本身就是AsyncTask片段是不可保留片段的一部分。我最終使用了你提供的方法,並且完美:)我將編輯我的主帖,並在代碼看起來可用時立即提供解決方案。 –

+0

當方向變化時重新創建Activity時,我們是否還應該在AsyncTask中更改Activity實例,並在Activity被銷燬時清除舊的? – Piotr

+0

@Piotr:只要'AsyncTask'不直接保存到'Activity'實例,就會自動爲您處理。如果'AsyncTask'爲片段的活動詢問其封閉片段,它將會得到正確的片段。 – CommonsWare

0

我在這種情況下所做的是使用BroadcastReceiverAsyncTask與您的FragmentActivity緊密耦合,我建議使用Thread來運行後臺操作,並調用context.sendBroadcast(...),然後在您的FragmentActivity中接收該廣播。