InetAddress.getByName вызывает исключение NetworkOnMainThreadException

понимая, что мне нужно беспокоиться о сетевом потоке, вызывающем хаос в моем потоке пользовательского интерфейса, я прошел весь процесс создания пользовательского обработчика и пользовательского потока для моей сети TCP/IP, пинг-понг команд и ответов между моим приложением и удаленным сервером. . Работал отлично. (вы увидите, что в приведенном ниже коде он упоминается как mainCommunicationThread(controlCommands);)

Однако до того, как этот поток/обработчик будет запущен, я извлекаю IP-адрес или имя хоста из SQL (называя его 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, отлично работает в эмуляторе. Однако, если я загружаю его на свой Samsung Google Galaxy Nexus с Jellybean 4.1.1, я получаю сбой NetworkOnMainThreadException. Даже с учетом вышеизложенного, завернутого внутрь runnable.

Я в полной растерянности, как это исправить.


person Octoth0rpe    schedule 05.10.2012    source источник
comment
как вы собираетесь разрешать доменные имена без подключения к сети? (может быть случай, когда это не так, когда домен находится в кеше или когда вы даете ему IP-адрес для имени)   -  person njzk2    schedule 05.10.2012
comment
в большинстве случаев handler.post будет работать в потоке пользовательского интерфейса (если вы не создадите для него цикл)   -  person njzk2    schedule 05.10.2012
comment
серьезно @njzk2 ? Я (очевидно) не слишком углублялся во все предостережения о потоках/обработчиках. Есть пример создания петлителя для handler.post?   -  person Octoth0rpe    schedule 05.10.2012
comment
нет, потому что в большинстве случаев вам не нужно этого делать. Если вы просто хотите запустить runnable в потоке, new Thread(runnable).start() будет достаточно. Если вам нужен доступ к пользовательскому интерфейсу (чего вам не кажется), вам понадобятся асинтаски или обработчики.   -  person njzk2    schedule 05.10.2012
comment
да, мне ДЕЙСТВИТЕЛЬНО нужен доступ к пользовательскому интерфейсу, но я написал пользовательские классы для handlerthread и workerthread (это то, что вызывается mainCommunicationThread() выше. Самое странное, что это работает на эмуляторе 4.1, но не на моем телефоне 4.1.   -  person Octoth0rpe    schedule 05.10.2012
comment
затем проверьте, как работает асинтаск. у вас будет фоновая работа в отдельном потоке и обратный вызов в потоке пользовательского интерфейса, когда вы закончите   -  person njzk2    schedule 05.10.2012
comment
да, около 6 месяцев назад эта функция использовала асинхронную задачу, но вышеупомянутый пинг-понг команд стал слишком большим, поэтому я перенес все это на пользовательские классы обработчиков/потоков. Однако на самом деле это не проблема, так как я либо дохожу до этого, либо нет - в зависимости от этого NetworkOnMainThread, кроме   -  person Octoth0rpe    schedule 05.10.2012
comment
Мой самый большой вопрос: работает ли вышеперечисленное ПО-ПРЕЖНЕМУ в потоке пользовательского интерфейса?   -  person Octoth0rpe    schedule 05.10.2012
comment
кажется, так. если вы не дадите обработчику специальный цикл   -  person njzk2    schedule 05.10.2012
comment
еще, так странно, что это работает на ВСЕМ, кроме моего телефона.   -  person Octoth0rpe    schedule 05.10.2012


Ответы (3)


мое решение:

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;
    }

}
person Rupert    schedule 25.07.2016
comment
как это использовать? - person Webserveis; 12.07.2018

вы пытаетесь запустить сетевую операцию в runnable, поэтому либо попробуйте добавить этот код в метод onCreate:

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

или я бы предложил вам использовать асинхронную задачу вместо использования потока Runnable

person G M Ramesh    schedule 05.10.2012
comment
хорошо, что сработало, но меня это беспокоит. Это как отключить надоедливый звонок в машине, когда ремень безопасности не пристегнут. Я думаю, мне придется глубже изучить всю эту looper.prepare() штуку.?? Или вы предлагаете мне переместить всю функцию StatControl внутрь doInBackground() асинхронной задачи? - person Octoth0rpe; 05.10.2012
comment
я бы посоветовал вам переместить всю функцию StatsControl внутрь doInBackground AsyncTask - person G M Ramesh; 06.10.2012

Я решил свою проблему с Inet6Address.getLocalHost() NetworkOnMainThreadException, запустив ее в новом потоке.

person Ben Rayfield    schedule 28.10.2015