安卓 - android.os.NetworkOnMainThreadException

50

我遇到了这个异常,并且我正在阅读一个关于它的线程,但是它看起来很令人困惑:

如何修复android.os.NetworkOnMainThreadException?

我已经在我的清单文件中添加了这一行:

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

他们在讨论应用程序的主执行线程无法进行网络操作。我想知道如何重新构建我的代码以符合Android良好实践。

这是我的 Activity 类:

package com.problemio;

import java.io.InputStream;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class LoginActivity extends Activity 
{
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);

        // Show form for login_email
        final EditText loginEmail = (EditText) findViewById(R.id.login_email);  
        String name = loginEmail.getText().toString();  

        // Show field for password  
        final EditText password = (EditText) findViewById(R.id.password);  
        String text = password.getText().toString();                  

        // Show button for submit
        Button submit = (Button)findViewById(R.id.submit);   




        // Show options for create-profile and forgot-password




        submit.setOnClickListener(new Button.OnClickListener() 
        {  
           public void onClick(View v) 
           {
              String email = loginEmail.getText().toString();
              String pass = password.getText().toString(); 
              sendFeedback(pass, email);
            }
        });        
    }


    public void sendFeedback(String pass , String email) 
    {  
        Log.d( "1" , pass );
        Log.d( "1" , email );

        // Go to db and check if these r legit
        // How do I do that? :)
        ArrayList<NameValuePair> postParameters = new ArrayList<NameValuePair>();  
        postParameters.add(new BasicNameValuePair("username", email ));  
        postParameters.add(new BasicNameValuePair("password", pass ));

        String responseString = null;

        try 
        {  
            HttpClient httpclient = new DefaultHttpClient();
            HttpPost httppost = new HttpPost("myUrl");

            // no idea what this does :)
            httppost.setEntity(new UrlEncodedFormEntity(postParameters));

            // This is the line that send the request
            HttpResponse response = httpclient.execute(httppost);

            HttpEntity entity = response.getEntity();            

            InputStream is = entity.getContent();
        } 
        catch (Exception e) 
        {     
            Log.e("log_tag", "Error in http connection "+e.toString());
        }        
    }          
}

我在这里做错了什么,我该如何修复它?:) 谢谢!!


3
可能是android.os.NetworkOnMainThreadException的重复问题。 - Peter O.
使用OkHTTP库:http://square.github.io/okhttp/ 或者AsyncTask(正如其他人所说) - Yousha Aleayoub
10个回答

59

NetworkOnMainThreadException:当应用程序尝试在其主线程上执行网络操作时抛出的异常。

您应该在asynctask上调用sendfeedback方法,然后才能使上述代码起作用。由于Web服务器需要很长时间才能响应,因此主线程变得无响应。为了避免这种情况,您应该在另一个线程上调用它。因此,asynctask更好。

这里是链接,说明如何使用asynctask


2
AsyncTask现已被弃用,更好的替代方案是使用java.util.concurrent.Executors在另一个线程中执行网络任务。 - Daniel C Jacobs

36

NetworkOnMainThreadException是在应用程序尝试在主线程进行网络操作时抛出的异常。

为了解决这个问题,您可以在Activity内使用一个继承了android.os.AsyncTask<Params, Progress, Result>的私有内部类来执行服务器调用相关的操作。

代码示例如下:

private class SendfeedbackJob extends AsyncTask<String, Void, String> {

    @Override
    protected String doInBackground(String[] params) {
        // do above Server call here
        return "some message";
    }

    @Override
    protected void onPostExecute(String message) {
        //process message
    }
}

然后从 submit.setOnClickListener 中调用上述类,代码如下:

SendfeedbackJob job = new SendfeedbackJob();
job.execute(pass, email);

AsyncTask

参考资料

AsyncTask文档

AsyncTask Android示例


5
加油伙计,那真是太美了 :) - prayagupa

14
if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

7
不是特别需要,当你只需要进行小型测试并为此创建一个新的小型Android项目时... :) - Henrique de Sousa
1
这是一个不错的想法,如果你只是为了数据操作而测试应用程序,并且暂时不想烦恼线程。 - Muhammad
严格来说不建议这样做,但如果你正在测试某些功能并需要更快地完成,那么可以使用它。强烈建议您不要在生产环境中使用此功能。我们不希望出现设计不良的应用程序,对吧? - 0xC0DED00D
更多讨论(45条评论!)请参见此处:https://dev59.com/-mw15IYBdhLWcg3w3vpe#9289190 - RenniePet

8
 try 
    {  
        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost("myUrl");

        // no idea what this does :)
        httppost.setEntity(new UrlEncodedFormEntity(postParameters));

        // This is the line that send the request
        HttpResponse response = httpclient.execute(httppost);

        HttpEntity entity = response.getEntity();            

        InputStream is = entity.getContent();
    } 
    catch (Exception e) 
    {     
        Log.e("log_tag", "Error in http connection "+e.toString());
    }        

您遇到的问题是,从api 11开始,这个异常会提示您在ui线程上运行长时间任务(您类中的http通信),而根据新的StrictGuard策略,这是不可能的。因此,您有两个不同的选择

  1. 使用线程或aynctask来执行您的长期任务(更好的方式)

1
关于更改StrictMode.setThreadPolicy,您可以阅读此文章:http://android-er.blogspot.com/2012/04/strictmodesetthreadpolicy-and.html - Nolesh
这段代码什么也没做?这怎么能算是一个答案呢? - Shawn Cicoria
我已经对这个问题进行了解释。这段代码片段的目的是指出错误,而不是解决 OP 的问题。@Cicorias - Blackbelt
我现在不能点赞,直到有编辑发生。我现在同意了。 - Shawn Cicoria

3
你可以按照以下方式创建Async类。
class Retrievedata extends AsyncTask<String, Void, String> {
@Override
    protected String doInBackground(String... params) {
         try{
                //Your code 
                }
        return null;
    }
}

你可以把所有的代码都放在doInBackground方法里。

使用Volley库-看一下这个-https://dev59.com/7nLYa4cB1Zd3GeqPbLoB - Mahalakshmi

3
你在主线程进行了网络调用,这是违反 Android 规则的,因此你需要在单独的线程中执行网络调用,例如 AsyncTask 或 Handler 等。

1

针对这种情况,请使用线程。

Thread thread = new Thread(new Runnable() {

    @Override
    public void run() {
        try  {
            //Code here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});

thread.start(); 


1
我喜欢这个答案,因为它使用了Java代码和一个线程。为什么呢?1. 因为所有这些转换成Kotlin的东西都很糟糕。2. 常量过时异步现在并发等等,所以我敢打赌,谷歌要废弃Thread会很困难。 - dcarl661
我喜欢这个答案,因为它使用了Java代码和线程。为什么呢?1. 因为所有这些转换成Kotlin的东西都很糟糕。2. 常量过时异步现在并发等等,所以我敢打赌,对于谷歌来说,废弃Thread也会太难了。 - dcarl661
@dcarl661 同意。 - Tigerrrrr

0
此处所示,AsyncTask已被弃用,Google建议您使用java.util.concurrent包或Kotlin协程
您可以使用java.util.concurrent.Executors轻松地在另一个线程中执行网络任务。
只需将这两个变量添加到您的LoginActivity类中即可:
val executor = Executors.newSingleThreadExecutor()

然后将您的网络调用放在executor.execute{}块中:
executor.execute {
    /**
     * Executes network task in background. You cannot
     * access view elements here.
     */
    
    HttpResponse response = httpclient.execute(httppost);
    
    HttpEntity entity = response.getEntity();            

    InputStream is = entity.getContent()

}

0

经过长时间的研究(持续半天),我找到了一个解决方案,它与此处指出的问题类似。我的Android Studio 2.3.3显示的异常是这样的:

android studio android.os.networkonmainthreadexception

问题基于在MainActivity中无法设置UI变量。所以我看了下面的视频并解决了我的问题。我希望它对其他人也有用:

如何避免android os NetworkOnMainThreadException


0
看一下这个链接:https://developer.android.com/reference/android/os/NetworkOnMainThreadException.html 当应用程序试图在其主线程上执行网络操作时抛出的异常。 早期SDK版本的应用程序允许在其主事件循环线程上进行网络操作,但强烈不建议这样做。
如果您设置minSdkVersion <11,则您的应用程序将正常工作,并且可以在主线程中运行网络操作。

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