java - Asynctask 中多线程意外停止

标签 java android multithreading android-asynctask

我正在尝试扫描 Android 设备中的所有文件。我使用了这样的多线程类:

public class FileScanner {

// subfolders to explore
private final Queue<File> exploreList = new ConcurrentLinkedQueue<File>();

private long fileCounter = 0;

List<File> listFile = new ArrayList<File>();

public void count() {
    fileCounter++;
}

public long getCounter() {
    return this.fileCounter;
}

public List<File> getListFile() {
    return this.listFile;
}

int[] threads;

public FileScanner(int numberOfThreads) {
    threads = new int[numberOfThreads];
    for (int i = 0; i < threads.length; i++) {
        threads[i] = -1;
    }
}

void scan(File file) {

    // add the first one to the list
    exploreList.add(file);

    for (int i = 0; i < threads.length; i++) {
        FileExplorer explorer = new FileExplorer(i, this);
        Thread t = new Thread(explorer);
        t.start();
    }

    Thread waitToFinish = new Thread(new Runnable() {

        @Override
        public void run() {

            boolean working = true;
            while (working) {
                working = false;

                for (int i = 0; i < threads.length; i++) {
                    if (threads[i] == -1) {
                        working = true;
                        break;
                    }
                }

                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    });

    waitToFinish.start();
}

public void done(int id, int counter) {
    threads[id] = counter;
}

public boolean isFinished() {
    for (int i = 0; i < threads.length; i++) {
        if (threads[i] == -1) {
            return false;
        }
    }
    return true;
}

class FileExplorer implements Runnable {

    public int counter = 0;
    public FileScanner owner;
    private int id;

    public FileExplorer(int id, FileScanner owner) {
        this.id = id;
        this.owner = owner;
    }

    @Override
    public void run() {
        while (!owner.exploreList.isEmpty()) {

            // get the first from the list
            try {
                File file = (File) owner.exploreList.remove();

                if (file.exists()) {

                    if (!file.isDirectory()) {
                        count();
                        listFile.add(file);
                    } else {

                        // add the files to the queue
                        File[] arr = file.listFiles();
                        if (arr != null) {
                            for (int i = 0; i < arr.length; i++) {
                                owner.exploreList.add(arr[i]);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                // silent kill :)
            }

            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        owner.done(id, counter);
    }

}

我在我的异步任务中调用它:

私有(private)类 FetchResidualAsynctask 扩展了 AsyncTask { FileScanner 文件机器;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        listResidualFileTemp.clear();
        listResidualFileThumbnail.clear();
        listResidualAppAds.clear();
        listResidualAppLeftOvers.clear();
        findAllStorage();
        for (int i = 0; i < listStorage.size(); i++) {
            fileMachine = new FileScanner(20);
            fileMachine.scan(listStorage.get(i));
            listFile.addAll(fileMachine.getListFile());
        }
    }

    @Override
    protected Void doInBackground(Void... params) { 
    numberOfFiles = listFile.size();
        Log.i("numberOfFiles", "NUmber: " + numberOfFiles);
        processindex = 0;
        getActivity().runOnUiThread(new Runnable() {
            public void run() {
                mBtnClean.setText(R.string.btn_rescan);
                mBtnClean.setEnabled(false);
                txtResidualFile.setText("");
                mProgressbar.setVisibility(View.VISIBLE);
                mProgressbar.setProgress(0);
                mBtnClean.setText(R.string.btn_stop);
                mBtnClean.setEnabled(true);
                mProgressbar.setMax(numberOfFiles);
            }
        });

        for (int i = 0; i < listFile.size(); i++) {
            getFilePath(listFile.get(i));
        }

    }

问题是返回的文件列表非常困惑。当我调试时,每次测试的结果都不一样。第一次它返回很少数量的文件(例如:160),下一次会返回很大的文件(1200)。

我认为 FileScanner fileMachine.scan() 尚未完成并强制停止运行到 DoInBackground。

有人可以帮我解决这个问题吗?

最佳答案

这看起来过于复杂并且充满了竞争条件。您的主要错误可能是线程在队列实际为空之前检测到队列为空(然后线程退出)...即在某个时刻队列暂时变为空(最后一个线程删除()d) item),但随后一个线程向其中添加了一些内容。

要等待您的工作人员完成...您可以使用 Thread.join() 或信号量,而不是使用那里的复杂的不安全轮询。

您确定并行化这样的事情有好处吗?我想象 20 个线程都试图同时攻击文件系统,但实际上并不能享受大量的同时执行。甚至可能是文件系统驱动程序序列化了所有 IO 请求!

关于java - Asynctask 中多线程意外停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15646895/

相关文章:

java - 在时间限制后停止/中断线程

c++ - 几个单例 : one for each task

java - Camera2API : Session has been closed; further changes are illegal

java - Swing 的骨架应用程序

java - 使用 Java Spark - kafka 直接流无法从 Kafka 主题获取值(value)

javascript - 强制在 Android/iOS 应用程序中打开 Youtube 链接

java - 为什么这不返回正确的数组(java)?

android - 如何使用 NewRelic 发送捕获的异常?

android - app:dexDebug在build.gradle Android Studio中出错

java - 无法正确跟踪连接的客户端 java