Проблема с вашим кодом заключается в том, как вы используете AsyncTask, потому что когда вы поворачиваете экран во время спящего потока:
Thread.sleep(2000)
AsyncTask все еще работает, это потому, что вы не сделали t правильно отменить экземпляр AsyncTask в onDestroy () до перестройки фрагмента (при вращении) и при запуске этого же экземпляра AsyncTask (после вращения) с помощью onPostExecute (), он пытается найти ресурсы с помощью getResources () со старым экземпляром фрагмента ( недопустимый экземпляр):
getResources().getString(R.string.app_name)
, что эквивалентно:
MyFragment.this.getResources().getString(R.string.app_name)
Таким образом, окончательное решение заключается в управлении экземпляром AsyncTask (для отмены, если он все еще работает) до перестройки фрагмента когда вы поворачиваете экран и отменены во время перехода, перезапустите AsyncTask после реконструкции с помощью логического флага:
public class MyFragment extends SherlockFragment {
private MyAsyncTask myAsyncTask = null;
private boolean myAsyncTaskIsRunning = true;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState!=null) {
myAsyncTaskIsRunning = savedInstanceState.getBoolean("myAsyncTaskIsRunning");
}
if(myAsyncTaskIsRunning) {
myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("myAsyncTaskIsRunning",myAsyncTaskIsRunning);
}
@Override
public void onDestroy() {
super.onDestroy();
if(myAsyncTask!=null) myAsyncTask.cancel(true);
myAsyncTask = null;
}
public class MyAsyncTask extends AsyncTask<Void, Void, Void>() {
public MyAsyncTask(){}
@Override
protected void onPreExecute() {
super.onPreExecute();
myAsyncTaskIsRunning = true;
}
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {}
return null;
}
@Override
protected void onPostExecute(Void result){
getResources().getString(R.string.app_name);
myAsyncTaskIsRunning = false;
myAsyncTask = null;
}
}
}
В этом случае вам нужно заставить вашу функцию возвращать декоратор. (Все может быть решено с помощью другого уровня косвенного обращения ...)
from decorator import decorator
def substitute_args(arg_sub_dict):
@decorator
def wrapper(fun, arg):
new_arg = arg_sub_dict.get(arg, arg)
return fun(new_arg)
return wrapper
Это означает, что substitute_args
не сам декоратор, это фабрика декораторов . Вот эквивалент без модуля декоратора
.
def substitute_args(arg_sub_dict):
def my_decorator(fun):
def wrapper(arg):
new_arg = arg_sub_dict.get(arg, arg)
return fun(new_arg)
# magic to update __name__, etc.
return wrapper
return my_decorator
Три уровня в глубину не очень удобно, но помните, что два из них - это когда функция определена:
@substitute_args({}) # this function is called and return value is the decorator
def f(x):
return x
# that (anonymous) decorator is applied to f
Что эквивалентно:
def f(x):
return x
f = substitude_args({})(f) # notice the double call
Вот еще один способ, которым я только что обнаружил: проверьте, требуется ли первый (и только) аргумент для вашего декоратора; Если это так, вы сделаете и можете вернуть свой метод модификации поведения обертки (сам украшен Functools.wraws
, чтобы сохранить название и строку документации).
В другом случае следует присутствовать один или несколько именованных или позиционных аргументов; Вы можете собрать эти аргументы и вернуть Callable, который принимает Callable в качестве первого аргумента и возвращает метод обертки - и с тех пор описание соответствует описанию метода декоратора, верните этот метод очень декоратора! Я использовал
functools.partial
Здесь, чтобы получить версию своего декоратора, IS_GLOBAL_Method
(который я работаю прямо сейчас - его реализация, конечно, чепуха, как показано ниже, это только продемонстрировать декоративные работы).
Это решение, по-видимому, работает, но наверняка необходима больше тестирования. Если вы увидите наших глаз, вы можете увидеть, что трюк - всего три или четыре строки, чтобы помнить шаблон. Теперь мне интересно, могу ли я обернуть такого рода функциональность в другой декоратор? Ах, метанесса этого!
from functools import wraps
from functools import partial
_ = print
is_instance_of = isinstance
is_callable = lambda x: hasattr( x, '__call__' )
def is_global_method( x, *, name = None ):
if is_callable( x ):
@wraps( x )
def wrapper( *P, **Q ):
return { 'name': name, 'result': x( *P, **Q ), }
return wrapper
# assert is_instance_of( x, str ) # could do some sanity checks here
return partial( is_global_method, name = x )
@is_global_method
def f( x ):
"""This is method f."""
return x ** 2
@is_global_method( 'foobar' )
def g( x ):
"""This is method g."""
return x ** 2
_( f.__name__ )
_( f.__doc__ )
_( f( 42 ) )
_( g.__name__ )
_( g.__doc__ )
_( g( 42 ) )