【问题标题】:Problems with AsyncTask/Thread and networkonmainthreadexceptionAsyncTask/Thread 和 networkonmainthreadexception 的问题
【发布时间】:2013-02-27 15:54:22
【问题描述】:

大家好,
我想为我的 Android 制作一个聊天客户端。但我就是无法获得任何联系。
好吧,也许你可以帮我一点..
在我的 MainActivity.java 中,我调用:

//connect to the server
SocketTask connection = new SocketTask(this); 
connection.execute();

我的 SocketTask.java 如下所示:

package chat.client;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;

import android.os.AsyncTask;

public class SocketTask extends AsyncTask<Void, Void, Void>{
    private Socket socket;
    private DataInputStream input;
    protected PrintStream output;
    private MainActivity main;
    private Thread thrd;

    public SocketTask(MainActivity main){
        this.main = main;
    }

    @Override
    protected Void doInBackground(Void... params) {
        try {
            socket = new Socket("192.168.178.23", 1338);
            input = new DataInputStream(socket.getInputStream());
            output = new PrintStream(socket.getOutputStream());

            // submittes the name of the client
            output.println("Mobile");
            output.flush();
            thrd = new Thread(new Runnable() {
                public void run() {
                    while (!Thread.interrupted()) {
                        try {
                            final String data = input.readLine();
                            if (data != null)
                                main.runOnUiThread(new Runnable() {
                                    //new Runnable(){
                                    @Override
                                    public void run() {
                                        if(!data.equals("")){
                                            //main.chatHistory.append(data+"\n");
                                            main.addText(data);
                                        }else{
                                            //chatHistory.append("\n");
                                            main.addText("");
                                        }

                                    }
                                });
                            //};
                        } catch (IOException e) {
                            //chatHistory.append("Verbindung zum Server abgebrochen!");
                            main.addText("Verbindung zum Server abgebrochen!");
                        }
                    }
                }
            });
            thrd.start();
        } catch (Exception e) {
            //chatHistory.append("Es konnte keine Verbindung zum Server aufgebaut werden!");
            main.addText("Es konnte keine Verbindung zum Server aufgebaut werden!"+e);
        }
        return null;
    }
}

但是当我启动它时,我只得到:
02-27 16:32:05.875: E/SensorManager(26146): 线程开始

另外,当我尝试调用“connection.doInBackground()”而不是“connection.execute()”(我知道这根本没有任何意义)时,我得到一个“networkonmainthreadexception” - 但我以为我已经用 AsyncTask 解决了这个问题。好吧,我是 Android 新手,但我已经用谷歌搜索了很多,不知道我是不是那么愚蠢,但现在我放弃了,所以我在这里注册了我。也许你们中的某个人可以告诉我,我做错了什么..

(不确定我是否需要发布我的 MainActivity 类..)
谢谢! :)

编辑:感谢大家提供所有这些答案,我知道我有点过分了,请给我一些时间来尝试/理解您的所有提示,谢谢!

【问题讨论】:

  • 哪里抛出了异常?什么行号和类?越详细越好。
  • 如果您直接调用 doInBackground,您将绕过通常通过调用“执行”创建的实际线程部分。因此,您只是在调用它的线程上运行 doInBackground 作为任何其他方法调用,这就是您收到该错误的原因。
  • 您需要发布更多的堆栈跟踪信息以查看导致异常的原因。 connection.execute() 是正确的方法,但可能是其他原因导致了问题。
  • 你正在尝试从 doInBackground() 访问 UI 元素
  • 你真的需要重新设计你的 SocketTask。您在运行 doInBackground 的后台线程中启动另一个线程。通过正确实现 onPostExecute 和 onProgressUpdate 来解决此问题。

标签: android sockets android-asynctask


【解决方案1】:

在 AsyncTask 的实现上有很多概念上的错误,甚至很难弄清楚为什么会出错,所以我会在这里写一个秘诀和感觉提示如何编写一个 AsyncTask 并应用它们,如果仍然出现错误,请再次询问:

提示:

  • 不要在 AsyncTask 中创建新线程,doInBackground() 已在后台线程中调用
  • 不要在 AsyncTask 上调用 runOnUiThread,onPostExecute() 已经在 UI 线程上调用了
  • 如果你的活动暂停(onPause() 被调用)在你的 AsyncTask 上调用 .pause() 或者你可能会有一些NullPointerException
  • 不要像你建议的那样直接调用doInBackground()execute() 调用会创建一个新线程并为你调用它(这就是 AsyncTask 的用途)
  • 不要持有活动对象或任何其他持有 AsyncTask 上下文的对象。只需将 AsyncTask 类声明放在 Activity 本身中,出于组织目的,使 doInbackground() 调用不同类中的一个方法进行实际处理。这样,您的 onPostExecute 可以调用 UI 来更新它,但您将数据收集/处理与活动分开

食谱:

// put this class inside your Activity and
// just call a separate class (for organisation),
// or just put the code all in there (for no organisation)
private class SocketTask extends AsyncTask<Void, Void, String> {

@Override
protected String doInBackground(Void... params) {
    // Whatever you do inside here
    // DO NOT create a new thread!
    return APIConnection.callAPI();
}

@Override
protected void onPostExecute(String result) {
    if(result!=null){
    //Update your UI here
    }else{
    // Some error happen?
    }
}
}

【讨论】:

  • 是的!第一个问题是它甚至试图使用一个AT来达到这个目的!是谁说过:“对于 Android 程序员来说,每个并发问题看起来都像一个 AsyncTask?”
【解决方案2】:

Exception 表示您需要在后台线程上运行网络操作。直接调用 doInBackground() 只会使所有内容都在调用的同一线程中运行,显然是主线程。所以,你必须用connection.excute() 生成一个新的。 但是,您不能从后台线程更新 UI 元素。要在主线程上执行此操作,您需要为您的 AsyncTask 实现 onProgressUpdate(),如下所示:

    @Override
    protected void onProgressUpdate(String... arg) {            
        main.addText(arg[0]);
    }

现在,将doInBackground() 中的所有addText() 调用替换为publishProgress("whatever");。不要忘记将异步任务声明更改为AsyncTask&lt;Void, String, Void&gt;。 完成上述所有操作后,您无需在doInBackground() 中生成另一个线程,无论如何它都会在后台线程中执行。 虽然,您当前的代码的作用大致相同,但这将解决混乱(不再有 thrd = new Thread(new Runnable() {...main.runOnUiThread...)并可能解决您的问题。

【讨论】:

    【解决方案3】:

    您是否在清单中添加了所需的权限?

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    

    【讨论】:

    • 这个答案最适合作为评论。
    • 对不起,我回答这个问题是因为很多时候这是此类问题的根源。
    • @AdorjanPrincz 很多次。
    【解决方案4】:

    如果您需要在主线程(即 UI)上运行某些东西,您可以从 doInBackground(...) 调用 publishProgess(),而不是像使用 AsynTask 那样尝试在主线程上运行某些东西,进而调用onProgressUpdate(...)。不要直接调用doInBackgroundexecute() 是启动AsyncTask 的正确方法。当doInBackground() 返回时,onPostExecute() 将在主线程上被自动调用。

    Android docs on AsyncTask 很好,有很好的用法示例。

    【讨论】:

      【解决方案5】:

      您正在尝试在doInBackground() 中使用您的MainActivity,这需要在onPostExecute() 方法中运行,该方法在UI 线程上运行,而doInBackground() 方法则没有。

      AsyncTask

      四个步骤

      当一个异步任务被执行时,任务会经过4个步骤:

      onPreExecute(),在任务执行之前在 UI 线程上调用。 此步骤通常用于设置任务,例如通过显示 用户界面中的进度条。

      doInBackground(Params...),在后台线程上调用 在 onPreExecute() 完成执行后立即。这一步使用 执行可能需要很长时间的后台计算。这 异步任务的参数传递给这一步。这 此步骤必须返回计算结果,并将 回到最后一步。这一步也可以使用 publishProgress(Progress...) 发布一个或多个进度单位。 这些值在 UI 线程上发布,在 onProgressUpdate(Progress...) 步骤。

      onProgressUpdate(Progress...),调用后在 UI 线程上调用 发布进度(进度...)。执行时间为 不明确的。此方法用于显示任何形式的进度 后台计算仍在执行时的用户界面。 例如,它可以用于动画进度条或显示登录 一个文本字段。

      onPostExecute(Result),在后台后UI线程上调用 计算结束。背景计算的结果是 作为参数传递到这一步。

      【讨论】:

      • 是的,谢谢@MikeD,我只是不经常使用它,所以我习惯说onPostExecute()
      猜你喜欢
      • 2012-12-14
      • 2012-11-16
      • 2015-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多