Android: Как обновить пользовательский интерфейс из AsyncTask, если AsyncTask находится в отдельном классе?
Я ненавижу внутренний класс.
У меня есть основной вид деятельности, который запускает AsyncTask с коротким сроком службы.
AsyncTask находится в отдельном файле, не является внутренним классом основной активности
Мне нужно асинхронное задание обновляет текстовое представление из основной деятельности.
Я знаю, что могу обновить TextView из onProgressUpdate, если AsyncTask является внутренним классом
Но как из внешней, независимой, асинхронной задачи?
ОБНОВЛЕНИЕ: Это похоже на работу:
В деятельности я называю задачу
backgroundTask = new BackgroundTask(this);
backgroundTask.execute();
В конструкторе я
public BackgroundTask(Activity myContext)
{
debug = (TextView) myContext.findViewById(R.id.debugText);
}
где отладка была частным полем AsyncTask.
Так что на ProgressUpdate я могу
debug.append(text);
Спасибо за все ваши предложения
7 ответов
РЕДАКТИРОВАТЬ Я отредактировал ответ, чтобы использовать WeakReference
AsyncTask всегда отдельный класс от Activity
, но я подозреваю, что вы имеете в виду, что он находится в другом файле, чем ваш файл класса активности, поэтому вы не можете извлечь выгоду из того, что он является внутренним классом деятельности. Просто передайте контекст Activity в качестве аргумента вашей асинхронной задаче (то есть ее конструктору)
class MyAsyncTask extends AsyncTask<URL, Integer, Long> {
WeakReference<Activity> mWeakActivity;
public MyAsyncTask(Activity activity) {
mWeakActivity = new WeakReference<Activity>(activity);
}
...
и используйте, когда вам это нужно (не забывайте использовать во время doInBackground()
) т.е. так, когда вы обычно звоните
int id = findViewById(...)
в AsyncTask вы звоните, т.е.
Activity activity = mWeakActivity.get();
if (activity != null) {
int id = activity.findViewById(...);
}
Обратите внимание, что наш Activity
может быть в то время как doInBackground()
выполняется (поэтому возвращаемая ссылка может стать null
), но с помощью WeakReference
мы не запрещаем GC собирать его (и тратить память), и когда активность исчезает, обычно бессмысленно даже пытаться обновить его состояние (тем не менее, в зависимости от вашей логики вы можете захотеть сделать что-то вроде изменения внутреннего состояния или обновления БД, но трогательный пользовательский интерфейс должен быть пропущен).
Использование интерфейса 1) Создайте один интерфейс
public interface OnDataSendToActivity {
public void sendData(String str);
}
2) Реализует это в своей деятельности
public class MainActivity extends Activity implements OnDataSendToActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
new AsyncTest(this).execute(new String[]{"AnyData"}); // start your task
}
@Override
public void sendData(String str) {
// TODO Auto-generated method stub
}
}
3) Создать конструктор в AsyncTask(Activity Activity){} Зарегистрируйте свой интерфейс в файле AsyncTask и вызовите метод интерфейса, как показано ниже.
public class AsyncTest extends AsyncTask<String, Integer, String> {
OnDataSendToActivity dataSendToActivity;
public AsyncTest(Activity activity){
dataSendToActivity = (OnDataSendToActivity)activity;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
dataSendToActivity.sendData(result);
}
}
Здесь ваш OnPostExecute будет вызывать после всей задачи, выполненной AsyncTask, и получит "result" в качестве параметра, возвращаемого doInBackground(){ return "";}.
Хотя "dataSendToActivity.sendData(result);" он будет вызывать переопределенный метод действия "public void sendData(String str) {}".
Крайний случай, чтобы помнить: обязательно пройти this
т. е. контекст вашей текущей деятельности AsyncTask
а не создавать другой экземпляр вашей деятельности, иначе ваш Activity
будет уничтожен и создан новый.
Создайте статическую функцию в вашем классе активности, передавая ему контекст, чтобы обновить текстовое представление, а затем вызовите эту функцию в вашем классе AsynkTask для обновления.
В классе Activity: открытая статическая void updateTextView () {
// ваш код здесь}
В классе AynckTask вызывайте эту функцию.
Просто передайте контекст (действие или что-то еще) вашей AsyncTask в конструкторе, а затем в onSuccess или onProgressUpdate вызовите все, что вам нужно в контексте.
Я написал небольшое расширение для AsyncTask для такого сценария. Это позволяет вам хранить AsyncTask в отдельном классе, но также предоставляет удобный доступ к выполнению задач:
public abstract class ListenableAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result>{
@Override
protected final void onPostExecute(Result result) {
notifyListenerOnPostExecute(result);
}
private AsyncTaskListener<Result> mListener;
public interface AsyncTaskListener<Result>{
public void onPostExecute(Result result);
}
public void listenWith(AsyncTaskListener<Result> l){
mListener = l;
}
private void notifyListenerOnPostExecute(Result result){
if(mListener != null)
mListener.onPostExecute(result);
}
}
Итак, сначала вы расширяете ListenableAsyncTask вместо AsyncTask. Затем в своем коде пользовательского интерфейса создайте конкретный экземпляр и установите listenWith(...).
Вопрос уже был дан ответ, я все еще пишу, как это должно быть сделано, я думаю..
Класс основной деятельности
public class MainActivity extends Activity implements OnClickListener
{
TextView Ctemp;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Ctemp = (TextView) findViewById(R.id.Ctemp);
doConv = (Button) findViewById(R.id.doConv);
doConv.setOnClickListener(this);
}
@Override
public void onClick(View arg0) // The conversion to do
{
new asyncConvert(this).execute();
}
}
сейчас в асинхронном классе
public class asyncConvert extends AsyncTask<Void, Void, String>
{
SoapPrimitive response = null;
Context context;
public asyncConvert(Context callerclass)
{
contextGUI = callerclass;
}
.
.
.
.
protected void onPostExecute(String result)
{
((MainActivity) contextGUI).Ctemp.setText(result); // changing TextView
}
}
/**
* Background Async Task to Load all product by making HTTP Request
* */
public static class updateTExtviewAsyncTask extends AsyncTask<String, String, String> {
Context context;
ProgressDialog pDialog;
String id, name;
String state_id;
//--- Constructor for getting network id from asking method
public updateTExtviewAsyncTask(Context context,String id,String city)
{
context = context;
state_id = id;
city_name = city;
}
/* *
* Before starting background thread Show Progress Dialog
* */
@Override
protected void onPreExecute()
{
super.onPreExecute();
pDialog = ProgressDialog.show(context, "","Please wait...", true, true);
pDialog.show();
}
/**
* getting All products from url
* */
protected String doInBackground(String... args)
{
return null;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String file_url) {
YourClass.UpdateTextViewData("Textview data");
}
}
// помещаем этот код в ваш класс активности, а также объявляем обновление статического textview
public static void UpdateTextViewData(String tvData)
{
tv.setText(tvData);
}