2017-09-14 62 views
0

我正在嘗試更新我的應用程序以手動處理配置更改(尤其是屏幕轉向)。Android:關於在線程運行時手動處理配置更改的問題

我對線程執行期間發生更改時發生的情況有一些疑問。

我創建了一個抽象類,我調用ThreadTask,它使用主線程的Looper的線程和處理程序向主線程發送更新。這是我的AsyncTask實現,但帶有線程,我更喜歡使用AsyncTask,因爲我對它有更多的控制。

它也有兩種方法來註冊一個觀察者上述事件,它使用這個接口:

public interface TaskObserver { 
    void pre_execute(); 
    void on_progress(ProgressData progress); 
    void finished(Object result); 
    void cancelled(Object result); 
} 

的子類必須實現的抽象成員是:

abstract Object do_in_background(); 

和一些具體的成員是:

synchronized void add_observer(TaskObserver observer){ 
    _observers.add(observer); 
} 

synchronized void remove_observer(TaskObserver observer){ 
    _observers.remove(observer); 
} 

synchronized void post_execute(Object result) { 
    int observers = _observers.size(); 
    for (int idx = 0; idx < observers; idx++) { 
     _observers.get(idx).finished(result); 
    } 
} 
///plus all the other methods of the interface 

所以當我實現一個具體的類時它會這樣mething這樣的:

public class MyThreadTask extends ThreadTask{ 

    @Override 
    Object do_in_background() { 
     progress.primary_max = 5; 
     for (int cur = 0 ; cur < 5 ; cur++) { 
      progress.primary = cur; 
      publish(); 
      Thread.Sleep(3000); 
     } 
    } 

} 

和我更新調用此像這樣的活動:

static final string TAG ="my_main_activity"; 

MyDataFragment _data; //this is a datafragment, a fragment with retaininstancestate , and a public `MyThreadTask task` variable 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    if (_data == null) { 
     _data = (MyDataFragment)getFragmentManager().findFragmentByTag(TAG + "_data"); 
     if (_data == null) { 
      _data = new MyDataFragment(); 
      getFragmentManager().beginTransaction().add(_data, TAG + "_data").commit(); 
     } 
    } 
    if (_data.task != null) { 
     _data.task.register(this); 
    } 
} 

@Override 
protected void onDestroy() { 
    super.onDestroy(); 
    if (_data.task != null) { 
     _data.task.remove(this); 
    } 
} 

這可以確保我總是有正確的線程

參考。當我想開始任務我這樣做:

button.setOnClickListener((v) -> { 
    if (_data.task != null) return; //to make sure only one thread starts at a time 
    _data.task = new MyThreadTask(); 
    _data.task.register(this); 
    _data.task.start(); //this is not thread's start, it is mine, it does other things too 
}) 

當線程完成它調用void f inished(對象結果),我處理,像這樣:

void finished(Object result) { 
    try { 
     //get result; 
    } finally { 
     _data.task.remove(this); 
     _data.task = null; 
    } 
} 

這裏是我的問題:

一)被宣告我的觀測方法爲synchronized有必要嗎?我只是爲了確保,但是當活動被銷燬並重新創建時,它是否發生在同一個線程上?有沒有機會例如在onDestroy期間觀察者被移除時progress_update可能發生?

b)如果線程在配置更改期間完成並調用post_execute(這很重要)會發生什麼?更新會丟失嗎?

c)如果確實更新因爲目前沒有觀察者而丟失,那麼在我的實現或其他方法中,是否有辦法處理上述問題?

預先感謝任何幫助,您可以提供

回答

0

的首選方式,讓一個後臺任務活着通過配置的變化是在retained fragment收留了它。片段的相同實例將持續通過配置更改。片段暫停時,請檢查活動的isChangingConfigurations,並且只有在該任務爲假時才取消該任務。

我不知道這是否記錄在任何地方,但似乎將整個配置更改發佈到主線程,以便在暫停舊活動和恢復新活動之間沒有任何其他操作可以運行。如果您在保留的片段中使用AsyncTask,則可以確保在配置更改期間無法運行其onPostExecute。用你的方法,當沒有觀察者時,任務可以輕鬆完成。

+0

我在保留片段收留了它,MyDataFragment _data是一個保留的片段。感謝isChangingConfigurations方法,它可能會有所幫助。另外感謝有關AsyncTask的信息,我會看看AsyncTask的代碼,看看我是否也可以在我的代碼中複製該行爲 – Cruces

+0

嗯,我檢查了AsyncTask的代碼,並且我沒有看到任何特殊的預防措施在配置更改期間運行postexecute。它似乎使用一個簡單的處理程序發佈完成數據到任務,然後調用onpostexecute,也許我錯過了什麼? – Cruces

+0

@Cruces它不在'AsyncTask'的代碼中。這是框架將配置更改的所有步驟發佈到整個主線程的結果。 –

0

Asynctask不能很好地處理配置更改。我認爲,而不是Asynctask你應該使用A synctaskLoader它可以很容易地處理配置變化,它的行爲在活動/片段的生命週期內。

當您運行的AsyncTask,如果Android系統殺死你的活動/片段(期間配置的變化或內存保護),那麼你doInBackground()方法仍不斷在後臺運行,這可能會導致不良的結果。

因此,與其使用的AsyncTask可以使用AsynctaskLoader,或者如果你是從SQLite的填充數據,那麼你可以使用CursorLoader

參考文獻:

  1. Guideline to choose among AsyncTaskLoader and AsyncTask to be used in Fragment

  2. https://developer.android.com/reference/android/content/AsyncTaskLoader.html

+0

對不起,我可能有misspoke,當我說我的AsyncTask的實現時,我的意思是我有與AsyncTask(pre_execute,do_in_background等)相同的方法,但我使用Thread來運行後臺任務和Handler以將消息發送到主線程。 – Cruces

+0

另外我的實現工作,它確實更新數據,我只是想知道當線程完成時生命週期方法運行時會發生什麼 – Cruces

+0

如果你想正確處理配置更改,那麼我建議你使用AsyncTaskLoader,它會處理配置改變你。 Android文檔也推薦相同。 –