InetAddress.getByName引起了NetworkOnMainThreadException异常。

3

意识到网络线程可能会对我的UI线程造成破坏,我经历了创建自定义处理程序和自定义线程的整个过程,用于在我的应用程序和远程服务器之间进行TCP/IP网络ping pong命令和响应。效果很好。(您将在下面的代码中看到它被引用为mainCommunicationThread(controlCommands);

然而,在触发该线程/处理程序之前,我从SQL中提取IP或主机名(称为currIPstr),然后将其与其他变量一起发送到线程/处理程序。

但是,我需要做的一件事是确保它要么是A)有效的IP地址,要么是B)主机名已解析--否则,甚至开始尝试都没有意义。

好的,没问题,我只需调用此函数:

currIP = InetAddress.getByName(currIPstr).toString().split("/")[1];

我刚刚发现的是,如果运行Gingerbread或StrictMode,上面的代码会触发令人恐惧的NetworkOnMainThreadException。

对,就是那个简单的InetAddress.getByName调用就足够了。我一直在试图找到解决方法,所以我将整个函数移动到了一个可运行的内部:

private String currStatIP = null;
private String currentStatIPstr = null;
private Integer currentStatIDstr = -1;
private String currentPort = null;
private String currentLocation = null;
private String currUserName = null;
private String currPassword = null;
private Integer currStatID = -1;
public void StatControl(Cursor c, final String[] commandtosend){


    /*the object will always start off with the first 4 fields being the current login information.
     * [0] = IP Address
     * [1] = port number
     * [2] = username
     * [3] = password
     * [4] = COMMAND -- this will be used in a switchcase inside the pingpong to decide what to do.
     *               -- if the Socket is not open, the first 4 fields will be used to re-initiate the connection
     *               -- otherwise, the command is drawn from this field and sent directly.
     * */


    currentStatIDstr = c.getInt(0);
    currentStatIPstr = c.getString(1);
    currentPort = c.getString(2);
    currentLocation = c.getString(3);
    currUserName = c.getString(4);
    currPassword = c.getString(5);
    currStatID = currentStatIDstr;


    Handler networkLookupHandler = new Handler();
    Runnable networkLookupRunnable = new Runnable() {
         public void run() {
             try {
                    currStatIP = InetAddress.getByName(currentStatIPstr).toString().split("/")[1];
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
             int portNumber = 0;
                try {
                    portNumber = Integer.parseInt(currentPort);
                } catch(NumberFormatException nfe) {
                   nfe.printStackTrace();
                }
                String userName = currUserName;
                String passWord = currPassword;
                String[] command = commandtosend;

                Object[] controlCommands = new Object[]{
                        currStatID,
                        currStatIP, //InetAddress of String representing the IP address
                        portNumber,     //int representation of the port number, taken from String
                        currentLocation,
                        userName,
                        passWord,
                        command
                        };
                /*for(int i=0;i<controlCommands.length;i++){
                    Log.d("object work","controlCommands[" + i + "] is " + controlCommands[i]);
                }*/

                mainCommunicationThread(controlCommands);
         }   
    };
    networkLookupHandler.post(networkLookupRunnable);
}

太好了,耶!它奏效了!我不再遇到NetworkOnMainThreadException异常了。 哦...等等..是的,我还是遇到了。

上述代码在API 10、12、14甚至16模拟器中没有触发异常,但如果我将其加载到运行Jellybean 4.1.1的三星谷歌Galaxy Nexus上,就会导致NetworkOnMainThreadException崩溃。即使上面的代码被包装在runnable里面。

我完全不知道如何修复这个问题。


你到底如何在没有网络连接的情况下解析域名呢?(有时候可能会出现这种情况,即当域名已经被缓存或者你为其提供了一个IP地址时) - njzk2
1
在大多数情况下,handler.post将在UI线程上运行(除非您为其创建了一个looper)。 - njzk2
认真的吗 @njzk2?我(显然)还没有深入研究线程/处理程序的所有注意事项。有一个“为handler.post创建looper”的示例吗? - Octoth0rpe
不需要,因为大多数情况下您不需要这样做。如果您只想在线程中运行可运行项,则使用new Thread(runnable)。start()就足够了。如果您需要访问UI(似乎不需要),则需要asynctasks或handlers。 - njzk2
是的,我确实需要UI访问权限,但我已经为HandlerThread和WorkerThread编写了自定义类(这就是上面mainCommunicationThread()调用的内容)。最奇怪的是,这在4.1模拟器上可以运行,但在我的4.1手机上却不行。 - Octoth0rpe
显示剩余5条评论
3个回答

5

我的解决方案:

public static InetAddress getInetAddressByName(String name)
{
    AsyncTask<String, Void, InetAddress> task = new AsyncTask<String, Void, InetAddress>()
            {

                @Override
                protected InetAddress doInBackground(String... params)
                {
                    try
                    {
                        return InetAddress.getByName(params[0]);
                    }
                    catch (UnknownHostException e)
                    {           
                        return null;
                    }                       
                }           
            };
    try
    {
        return task.execute(name).get();
    }
    catch (InterruptedException e)
    {
        return null;
    }
    catch (ExecutionException e)
    {
        return null;
    }

}

如何使用这个? - Codelaby

5

您正在尝试在可运行的网络操作中运行,因此请尝试将以下代码添加到onCreate方法中:

     StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
     StrictMode.setThreadPolicy(policy); 

我建议您使用AsyncTask而不是使用Runnable线程。

那个可行,但让我有点担心。就像当你没有系安全带时超越那令人烦恼的汽车铃声一样。 我想我得更深入地研究整个 looper.prepare() 这个东西。还是,你建议我将整个 StatControl 函数移动到 asynctask 的 doInBackground() 中? - Octoth0rpe
我建议您将整个StatsControl函数移动到AsyncTask的doInBackground中。 - G M Ramesh

0

我通过在新线程中运行Inet6Address.getLocalHost()解决了NetworkOnMainThreadException问题。


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