在doInBackground函数内部从一个函数中调用publishProgress?

24

我使用AsyncTask执行一个长时间的过程。

我不想直接在doInBackground中放置我的长时间处理代码。相反,我的长时间处理代码位于另一个类中,我在doInBackground中调用该类。

我想能够从longProcess函数内部调用publishProgress。

在C++中,我会将回调指针传递给longProcess函数的publishProgress。

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

但这并没有解决我的问题。

5个回答

35

问题在于publishProgressprotected final的,所以即使你将this传递到静态方法调用中,仍然不能直接调用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
    }
}

如果这个可行,请告诉我!请注意,从除了由doInBackground调用的方法之外的任何地方调用doProgress 都几乎肯定会导致错误。

对我来说感觉很糟糕,还有其他更好的方法吗?


如果我的LongOperation类没有调用xmlReader.parse(其中包含耗时的代码),那么它可能会起作用。看起来我将被困在一个简单的旋转进度对话框中。 - Regis St-Gelais
@Regis St-Gelais如果您正在尝试监视解析的进度,可以尝试此方法这个方法 - dave.c
难以置信我浪费了很多时间来寻找解决方案,现在,多亏了你,我找到了一个非常简单的解决方案!非常感谢;) - kinghomer
确切地说,感觉相当肮脏2。 - thecr0w

5

一种解决方案是在AsyncTask中放置一个简单的public类(确保你定义的任务也是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
    }
}

这对我很有效。

+1 对于完美的答案...对我有用...谢谢 :) - AL̲̳I

2

longProcess()函数拆分成更小的函数。

示例代码:

@Override
protected Boolean doInBackground(Void... params) {
    YourClass.yourStaticMethodOne();
    publishProgress(1);
    YourClass.yourStaticMethodTwo();
    publishProgress(2);
    YourClass.yourStaticMethodThree();
    publishProgress(3);

    // And so on...
    return true;
}

@Regis St-Gelais:什么是复杂的?如果您从这些静态方法之一获取了一个值,您只需捕获它并将其传递到下一个函数中即可。据我所知,没有其他可用的解决方案。 - Wroclai
@Regis St-Gelais:详细说明使用此方法的问题。 - Wroclai

1
如果这个有效,请让我知道!请注意,从任何不是从doInBackground调用的方法中调用doProgress几乎肯定会导致错误。
是的,它有效。我扩展了它,以便您不需要将AsyncTask作为参数传递给您的方法。如果(像我一样)您已经编写了所有方法,然后才决定实际上需要发布一些进度,或者在我的情况下,从AsyncTask更新UI,这特别有用:
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)。它可以从任何地方调用而不会产生错误(如果线程在hashMap中没有相应的AsyncTask,则不会调用publishProgress)。缺点是您必须在线程启动后自己添加Thread-AsyncTask映射。(很遗憾,您无法覆盖AsyncTask.execute(),因为这是final)。我在超类中重写了doInBackground(),这样任何扩展它的人只需将super.doInBackground()放在自己的doInBackground()的第一行即可。
我不了解线程和AsyncTask的足够知识,不知道HashMap引用在线程和/或AsyncTask结束时会发生什么。我怀疑会发生糟糕的事情,所以我不建议任何人在自己的代码中尝试我的解决方案,除非他们知道得更好。

这样做是非常糟糕的想法。如果调用线程不同,它将无法工作。此外,您正在将线程放入HashMap中,但没有将它们删除。 - xmen

0
当你说“我的长时间处理代码位于另一个类中,我在doInBackground中调用它”时,你是指“位于另一个方法中,我在doInBackground中调用它”吗?
如果是这样,你可以将该方法作为AsynTask类的私有方法。然后,每当需要时,你可以在该方法内部调用publishProgress。

不是的。它是一个静态方法,位于AsyncTask类之外的静态类中。 - Regis St-Gelais

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接