Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Android任务线程和UI_C#_Android_Multithreading_Asynchronous - Fatal编程技术网

C# Android任务线程和UI

C# Android任务线程和UI,c#,android,multithreading,asynchronous,C#,Android,Multithreading,Asynchronous,我正在尝试在后台/异步/运行大型XML文件的导入-换句话说,我正试图让它脱离UI线程 我的代码如下: private string ImportXML() { string returnFile = null; ParseXML.ParseXML parsexml = new ParseXML.ParseXML (chosenFile); ProgressDialog progress = new ProgressDialog(this)

我正在尝试在后台/异步/运行大型XML文件的导入-换句话说,我正试图让它脱离UI线程

我的代码如下:

private string ImportXML()
    {
        string returnFile = null;
        ParseXML.ParseXML parsexml = new ParseXML.ParseXML (chosenFile);

        ProgressDialog progress = new ProgressDialog(this);
        progress.SetMessage(GetString(Resource.String.ImportXML));
        progress.Indeterminate = true;
        progress.Show();

        Task xmlTask = new Task (() => returnFile = parsexml.ProcessXML());

        xmlTask.Start();

        while (!(xmlTask.IsCompleted || xmlTask.IsCanceled || xmlTask.IsFaulted))
            progress.Show();

        xmlTask.Wait(); 

        progress.Hide();

        return returnFile;
    } 
代码似乎是同步运行的,或者仍然在主UI线程中运行,因为我暂时尝试在while循环中进行的UI更新没有发生-我根本看不到进度

该操作可能需要几秒钟的时间——对于我的一些示例文件,最多需要10秒钟——因此我不喜欢使用AsyncTask,因为我认为不建议将其用于长时间的操作

有人能告诉我这个问题出在哪里吗?

我创建了一个示例AsyncTask,向您展示如何独立于ui线程处理数据。此AsyncTask将从资源中加载字符串并将其设置为TextView

重要提示:通常情况下,从资源中加载字符串永远不需要AsyncTask。我只是以这个为例来演示如何在单独的线程中执行工作

对于任何异步任务,只有一件事需要记住:您在单独的线程中执行工作,而不是在ui线程中。当独立线程完成时,无法保证您引用的ui元素仍然存在,甚至无法保证您的应用程序仍在运行。因此,如果您引用一个活动或上下文或某个ui元素,如TextView或Button或任何其他复杂对象,而这些对象在后台线程结束时可能不再存在,则必须使用WeakReference。WeakReferences是一种巧妙的方法,它允许对您正在引用的对象进行垃圾收集,从而绕过这个问题。在AsyncTask中,您可以检查所需的对象是否仍然存在,如果仍然存在,则可以在不造成内存泄漏的情况下执行工作。您可以使用这样的WeakReference:

WeakReference<TextView> textViewReference = new WeakReference<TextView>(textView);
TextView textView = textViewReference.get();
TextView textView = textViewReference.get();
if(textView != null) {
   // TextView still exists. 
} else {
   // TextView doesn't exist anymore. Most likely he has already been garbage collected.
}
如果您引用的TextView不再存在,get将简单地返回null。因此,要检查TextView是否仍然存在,您可以这样做:

WeakReference<TextView> textViewReference = new WeakReference<TextView>(textView);
TextView textView = textViewReference.get();
TextView textView = textViewReference.get();
if(textView != null) {
   // TextView still exists. 
} else {
   // TextView doesn't exist anymore. Most likely he has already been garbage collected.
}
如果您理解关于如何以及何时使用weakreference的这一简单原则,那么实现异步任务应该没有问题。我已经用注释解释了有关AsyncTask的任何其他重要内容,只需查看源代码即可。如果您有任何其他问题,请随时提问

public class ExampleTask extends AsyncTask<Void, Void, String> {

    // We use WeakReferences for all complex objects to prevent memory leaks
    private final WeakReference<TextView> textViewRefernece;
    private final WeakReference<Context> contextReference;

    // Primitives are fine. WeakReferences are not needed here.
    private int resourceId;

    public ExampleTask(Context context, TextView textView, int resourceId) {

        // We create the WeakReferences to our Context and to the TextView in which we want the result to appear.
        this.contextReference = new WeakReference<Context>(context);
        this.textViewRefernece = new WeakReference<TextView>(textView);

        this.resourceId = resourceId;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();

        // This method is executed on the ui thread just before the background thread starts.
        // You can perform some inital setup here but mostly you can leave this empty
    }

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

        // This method is executed in a background thread. Here we can do our work.
        // First we have to get the context from the WeakReference
        Context context = contextReference.get();

        // Now we check if the Context still exists. If the context is null it has already been garbage collected.
        // That would happen for example if the app has been closed or even if it crashed.
        if(context != null) {

            // The context is not null which means it still exists we can continue and load the required String and return it
            return context.getString(this.resourceId);
        }

        // If something went wrong we return null.
        return null;
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);

        // This method is executed on the ui thread after the background thread is finished.
        // Here we get our final result and can set it to our TextView

        // First we check if the String result is null. If it is not null the background thread has finished successfully and we can continue.
        if(result != null) {

            // Here we get our TextView from the WeakReference
            TextView textView = textViewRefernece.get();

            // Now we check if the TextView still exists. If it is null it has already been garbage collected.
            // That would happen for example if the app has been closed while the AsyncTask has been running.
            // Or simply if the Fragment has been replaced or the Activity has changed.
            if(textView != null) {

                // The TextView is not null which means it still exists. We can now set our text.
                textView.setText(result);
            }
        }
    }
}

是的,您可以创建一个任务来在非UI线程中执行某些工作。然后告诉UI线程坐在那里等待其他任务完成。在任务完成之前,不要让它继续或做任何其他工作。这会阻塞UI线程,这并不比只在UI线程中正确地执行工作好,开销实际上会使情况变得更糟

您需要做的是向任务添加一个continuation,它可以在任务完成时处理您想要做的任何工作,然后让该方法结束,以便UI线程可以继续执行其他工作

private Task<string> ImportXML()
{
    ParseXML.ParseXML parsexml = new ParseXML.ParseXML(chosenFile);

    ProgressDialog progress = new ProgressDialog(this);
    progress.SetMessage(GetString(Resource.String.ImportXML));
    progress.Indeterminate = true;
    progress.Show();
    var task = Task.Factory.StartNew(() => parsexml.ProcessXML());
    task.ContinueWith(t => progress.Hide());
    return task;
}

因为这个方法必须是异步的,允许UI线程继续工作,所以它需要返回一个任务,而不是实际的字符串。然后,此方法的调用方可以向此方法调用的结果添加一个延续以使用结果。

使用异步任务,它非常适合于此。您确定您的代码正在单独的线程中运行吗?我的意思是ImportXML方法是否在单独的线程中运行?当然,您不会看到使用非UI线程更新进度的更新。。。您必须调用UI线程才能执行此操作。@XaverKapeller感谢您的回复。我对Android和c开发有点陌生,所以我有这样的印象,通过以这种方式调用ImportXML,我会强制它位于一个单独的线程中。从你的回答来看,我想情况不是这样的?不,不是。我将用一个示例AsyncTask发布一个答案。@XaverKapeller所以它使用的是C1.0?为什么lambda不能工作?谢谢@Servy-我已经对ImportXML方法做了这些修改,使之成为一项任务。在调用此任务的方法中,我有以下内容:如果chosenFile.EndsWith.xml,StringComparison.OrdinalIgnoreCase dbFile=ImportXML.Result;但是通过追踪,我似乎再次通过这样做阻塞了线程。@Yromulus,这是正确的;结果块,直到有结果为止。您应该将需要使用此任务结果本身的工作作为此方法任务的延续。这就是异步程序的含义。谢谢@Servy-通过您的建议和更多的研究,我现在有了一个可行的解决方案-我非常感谢您的帮助。问题的标签是C,而不是Java。这是Java代码。我对这个问题看得不够清楚。他从来没有提到他正在使用Xamarin或任何类似的药物。但是,一旦您在获取后持有引用,它就不能再被垃圾收集,因为您再次直接持有引用。谢谢@XaverKapeller-很抱歉,我没有更详细地解释我使用的是Xamarin和C。我将尝试Servy之前给出的C示例 我尝试一下您给我的异步任务示例。