Подтвердить что ты не робот

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]);

Но это не меняет мою проблему.

4b9b3361

Ответ 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 внутри метода, когда это необходимо.