PublishProgress изнутри функции в doInBackground?
Я использую AsyncTask для выполнения длительного процесса.
Я не хочу размещать свой длинный код процесса непосредственно внутри doInBackground. Вместо этого мой длинный код процесса находится в другом классе, который я вызываю в doInBackground.
Я хотел бы иметь возможность вызывать publishProgress внутри функции longProcess.
В С++ я бы передал указатель обратного вызова publishProgress для моей функции longProcess.
Как это сделать в java?
ИЗМЕНИТЬ:
Мой длинный код процесса:
public class MyLongProcessClass
{
public static void mylongProcess(File filetoRead)
{
// some code...
// here I would like to call publishProgress
// some code...
}
}
Мой код AsyncTask:
private class ReadFileTask extends AsyncTask<File, Void, Boolean>
{
ProgressDialog taskProgress;
@Override
protected Boolean doInBackground(File... configFile)
{
MyLongProcessClass.mylongProcess(configFile[0]);
return true;
}
}
ИЗМЕНИТЬ № 2
Метод длинных процессов также может быть нестационарным и называется так:
MyLongProcessClass fileReader = new MyLongProcessClass();
fileReader.mylongProcess(configFile[0]);
Но это не меняет мою проблему.
Ответы
Ответ 1
Трудность заключается в том, что publishProgress
равно protected final
, поэтому, даже если вы передаете this
в свой вызов метода static
, вы по-прежнему не можете напрямую вызвать publishProgress
.
Я не пробовал это сам, но как насчет:
public class LongOperation extends AsyncTask<String, Integer, String> {
...
@Override
protected String doInBackground(String... params) {
SomeClass.doStuff(this);
return null;
}
...
public void doProgress(int value){
publishProgress(value);
}
}
...
public class SomeClass {
public static void doStuff(LongOperation task){
// do stuff
task.doProgress(1);
// more stuff etc
}
}
Если это работает, пожалуйста, дайте мне знать! Обратите внимание, что вызов doProgress
из любого другого объекта, кроме метода, который был вызван из doInBackground
, почти наверняка вызовет ошибку.
Чувствует себя довольно грязным, у кого-нибудь есть лучший способ?
Ответ 2
Решением может быть размещение простого класса public
внутри AsyncTask (убедитесь, что заданная вами задача также является public
), которая имеет метод public
, который вызывает publishProgress(val)
. Передача этого класса должна быть доступна из любого другого пакета или класса.
public abstract class MyClass {
public MyClass() {
// code...
}
// more code from your class...
public class Task extends AsyncTask<String, Integer, Integer> {
private Progress progress;
protected Task() {
this.progress = new Progress(this);
}
// ...
@Override
protected Integer doInBackground(String... params) {
// ...
SomeClass.doStuff(progress);
// ...
}
// ...
@Override
protected void onProgressUpdate(Integer... progress) {
// your code to update progress
}
public class Progress {
private Task task;
public Progress(Task task) {
this.task = task;
}
public void publish(int val) {
task.publishProgress(val);
}
}
}
}
а затем в другом классе:
public class SomeClass {
public static void doStuff(Progress progress){
// do stuff
progress.publish(20);
// more stuff etc
}
}
Это сработало для меня.
Ответ 3
Разделите функцию longProcess()
на более мелкие функции.
Пример кода:
@Override
protected Boolean doInBackground(Void... params) {
YourClass.yourStaticMethodOne();
publishProgress(1);
YourClass.yourStaticMethodTwo();
publishProgress(2);
YourClass.yourStaticMethodThree();
publishProgress(3);
// And so on...
return true;
}
Ответ 4
Если это работает, пожалуйста, дайте мне знать! Обратите внимание, что вызов doProgress из любого другого места, кроме метода, который был вызван из doInBackground, почти наверняка вызовет ошибку.
Да, это работает. Я расширил его, так что вам не нужно передавать AsyncTask в качестве параметра для вашего метода. Это особенно полезно, если (как и я) вы уже написали все свои методы, прежде чем решить, что на самом деле вам нужно опубликовать некоторый прогресс или, в моем случае, обновить пользовательский интерфейс из AsyncTask:
public abstract class ModifiedAsyncTask<A,B,C> extends AsyncTask<A,B,C>{
private static final HashMap<Thread,ModifiedAsyncTask<?,?,?>> threads
= new HashMap<Thread,ModifiedAsyncTask<?,?,?>>();
@Override
protected C doInBackground(A... params) {
threads.put(Thread.currentThread(), this);
return null;
}
public static <T> void publishProgressCustom(T... t) throws ClassCastException{
ModifiedAsyncTask<?, T, ?> task = null;
try{
task = (ModifiedAsyncTask<?, T, ?>) threads.get(Thread.currentThread());
}catch(ClassCastException e){
throw e;
}
if(task!=null)
task.publishProgress(t);
}
}
public class testThreadsActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void Button1Clicked(View v){
MyThread mthread = new MyThread();
mthread.execute((Void[])null);
}
private class MyThread extends ModifiedAsyncTask<Void, Long, Void>{
@Override
protected Void doInBackground(Void... params) {
super.doInBackground(params);
while(true){
myMethod(System.currentTimeMillis());
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
return null;
}
}
}
protected void onProgressUpdate(Long... progress) {
//Update UI
((TextView) findViewById(R.id.textView2)).setText("The Time is:" + progress[0]);
}
}
private void myMethod(long l){
// do something
// request UI update
ModifiedAsyncTask.publishProgressCustom(new Long[]{l});
}
}
Чувствует себя довольно грязным, у кого-нибудь есть лучший способ?
Мой путь, вероятно, хуже. Я вызываю статический метод для doProgress (который я назвал publishProgressCustom). Его можно вызывать из любого места без возникновения ошибки (как будто поток не имеет соответствующей AsyncTask в hashMap, он не будет вызывать publishProgress). Нижняя сторона заключается в том, что вам нужно добавить отображение Thread-AsyncTask самостоятельно после начала потока. (Вы не можете переопределить AsyncTask.execute(), так как это окончательно). Я сделал это здесь, переопределив doInBackground() в суперклассе, так что любой, кто его расширяет, просто должен поставить super.doInBackground() в качестве первой строки в своей собственной doInBackground().
Я не знаю достаточно о Threads и AsyncTask, чтобы узнать, что происходит с ссылками HashMap после завершения Thread и/или AsyncTask. Я подозреваю, что произошли плохие вещи, поэтому я не предлагаю, чтобы кто-нибудь пытался решить мое решение как часть своей собственной, если они не знают лучше
Ответ 5
Когда вы говорите: "мой длинный код процесса находится в другом классе, который я вызываю в doInBackground", вы имеете в виду "расположенный в другом методе, который я вызываю в doInBackground"?
Если это так, вы можете сделать этот метод приватным методом класса AsynTask. Затем вы можете вызвать publishProgress внутри метода, когда это необходимо.