1

我实现了代码来处理异步任务,它运行良好,但如果用户退出应用程序,它会很快被杀死,所以我决定用服务尝试它,它运行良好,但它使应用程序冻结。

所以这是我的解压类:

    public class Decompress {
    private String _zipFile;
    private String _location;
    ZipEntry ze = null;

    public Decompress(String zipFile, String location) {
        _zipFile = zipFile;
        _location = location;

        _dirChecker("");
    }

    public void unzip() {
        try  {
            FileInputStream fin = new FileInputStream(_zipFile);
            ZipInputStream zin = new ZipInputStream(fin);
            while ((ze = zin.getNextEntry()) != null) {
                //Log.v("Decompress", "Unzipping " + ze.getName()); 

                if(ze.isDirectory()) {
                    _dirChecker(ze.getName());
                } else {
                    FileOutputStream fout = new FileOutputStream(_location + ze.getName());
                    for (int c = zin.read(); c != -1; c = zin.read()) {
                        fout.write(c);
                    }

                    zin.closeEntry();
                    fout.close();
                } 

            }
            zin.close();
        } catch(Exception e) {
            Log.e("Decompress", "unzip", e);
        }

    }

    private void _dirChecker(String dir) {
        File f = new File(_location + dir);

        if(!f.isDirectory()) {
            f.mkdirs();
        }
    }
}

这是我的解压缩服务调用:

@Override
public void onStart(Intent intent, int startid)
{

    try
    {
        zipFile = intent.getStringExtra("zipFile");
        zipLocation = intent.getStringExtra("unzipLocation");
        String fileS = intent.getStringExtra("file");
        file = new File(fileS);
        fin = new FileInputStream(zipFile); 
        zin = new ZipInputStream(fin);

        while (zin.getNextEntry() != null) {
            numFiles++;
        }
    }
    catch (FileNotFoundException e)
    {}
    catch (IOException e)
    {}

    d = new Decompress(zipFile, zipLocation);
    d.unzip();

}

现在这是我使用异步任务调用它的方式:

@Override
    protected Void doInBackground(Void... params) {

        d.unzip();

        return null;
    }

现在我的问题是,为什么使用 async tsk 我的应用程序不会冻结,它会继续解压缩,让我用一个按钮取消它,但是使用服务它会使应用程序滞后?我什至收到一条关于 MyApp 没有响应的消息,您要关闭它吗?

编辑:我的服务呼叫开始

@Override
    protected Void doInBackground(Void... params) {

        Intent intent = new Intent(DownloadFiles.this, MyService.class);
        String unzipLocation = Environment.getExternalStorageDirectory().toString()+"/Android/data/";
        String zipFile = Environment.getExternalStorageDirectory().toString()+"/Android/data/test.zip"; 
        intent.putExtra("zipFile", zipFile);
        intent.putExtra("unzipLocation", unzipLocation);
        intent.putExtra("file", Environment.getExternalStorageDirectory().toString()+"/Android/data/");
        startService(intent);

        try {
            FileInputStream fin = new FileInputStream(zipFile); 
            ZipInputStream zin = new ZipInputStream(fin);

            while (zin.getNextEntry() != null) {
                 numFiles++;
                }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }
4

3 回答 3

3

服务也在UI(主)线程中运行,因此您也需要在那里实现 AsyncTask 或单独的线程方法。

文档在什么是服务?

服务不是线程。它本身并不是一种脱离主线程工作的方法(以避免应用程序无响应错误)。

编辑:请注意,如果您从后台线程启动服务,该服务仍在主线程中运行。按照这个 SO answer。正如文档所说,这应该是有道理的:

当一个 Service 组件被实际创建时,出于上述任一原因,系统实际所做的只是实例化该组件并在主线程上调用其 onCreate() 和任何其他适当的回调。由服务以适当的行为来实现这些,例如创建一个辅助线程来执行其工作。

这最终意味着无论您如何启动服务,您都应该始终在服务中实现单独的 AsyncTask/Thread 方法。

于 2013-01-07T02:34:28.233 回答
1

如果您不需要 AsyncTask 的onPreExecute()onPostExecute()方法,请尝试在单独的后台线程中运行它,但仍然存在阻塞 UI 线程的操作的问题。

Thread t = new Thread() {
    public void run() {
        d = new Decompress(zipFile, zipLocation);
        d.unzip();
    }
};
t.start();

简单地从后台线程启动服务并不意味着它将从主 UI 线程启动。这是其启动的默认设置,您必须在 Service 中创建一个新线程才能绕过它。

于 2013-01-07T02:43:16.473 回答
1

扩展AC的观点:您需要创建后台线程以从服务内部解压缩文件,因为服务是在主线程上创建并运行的,无论您是否在另一个线程中启动它。

您基本上需要在服务中完全按照您在服务外所做的操作(即将“解压缩”调用放入 AsyncTask 中,然后执行任务)。

(附录)使用服务的目的不是创建单独的线程,而是将耗时的处理从基于 UI 的应用程序中分离出来。这意味着 UI 可以被操作系统破坏并恢复资源,而服务仍在运行。因此,关于是在应用程序本身还是在服务中使用 AsyncTask(或线程)的决定实际上是关于该操作是否应该独立于应用程序界面继续进行。使用 TitaniumBackup 恢复应用程序就是一个很好的例子:一旦开始恢复,应用程序 UI 就不再需要了。

于 2013-01-07T02:52:45.497 回答