2017-09-14 31 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文档也推荐相同。 –