Как программировать вне основного потока пользовательского интерфейса
Я не знаю, существует ли поток пользовательского интерфейса по умолчанию, как его создать, если нет, или как сказать, когда я "в" это где это начинается и заканчивается, если так. Как мне убедиться PacketListener
не является частью основного потока пользовательского интерфейса?
Я продолжаю сталкиваться с этим NetworkOnMainThreadException. Я понимаю, что это означает, что я пытаюсь сделать что-то в главном потоке пользовательского интерфейса, который должен иметь свой отдельный отдельный поток. Тем не менее, я не могу найти много, чтобы сказать мне, как на самом деле это сделать.
Я думал, что класс MainActivity будет являться потоком пользовательского интерфейса, так как у меня был пользовательский интерфейс, и он работал. Тем не менее, я создал отдельный класс, который реализует Runnable, и поместил туда сетевые соединения, и все еще получаю исключение, так что он все еще является частью основного потока, да? Я вижу, что MainActivity не расширяет Thread, поэтому, возможно, это было глупое предположение. У меня нет ощущения, что что-то изменится, если я добавлю другой класс, который реализует Runnable/extends Thread. Будет ли он?
MainActivity.java
public class MainActivity extends AppCompatActivity {
ArrayList<String> listItems;
ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listItems = new ArrayList<>();
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, listItems);
ListView listview = (ListView) findViewById(R.id.listview);
listview.setAdapter(adapter);
(new Thread(new PacketListener(adapter))).run();
}
}
PackageListener.java:
public class PacketListener implements Runnable {
ArrayAdapter<String> adapter;
public PacketListener(ArrayAdapter<String> adapter) {
this.adapter = adapter;
}
public void run() {
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try {
DatagramSocket socket = new DatagramSocket();
socket.receive(packet);
}
catch(Exception e) {
adapter.add("Exception: " + e.toString() );
e.printStackTrace();
}
catch(Error e) {
adapter.add("Unspecified Error: " + e.toString() );
e.printStackTrace();
}
}
}
РЕДАКТИРОВАТЬ:
Моя последняя попытка, следуя примеру этой страницы, прошла путь большинства моих попыток и полностью сломала приложение. SendAnother
метод тестирования, связанный с кнопкой:
public void sendAnother(View view) {
adapter.add("button clicked");
new Thread(new Runnable() {
public void run() {
listview.post(new Runnable() {
public void run() {
adapter.add("inside worker thread");
}
});
}
});
}
Думаю дальше попробую наконец то AsyncTask
, Меня больше не волнует, грязно ли это.
3 ответа
Пользовательский интерфейс является основным потоком. Вы знаете о текущей теме
Thread.currentThread().getName();
извините, я не прочитал весь ваш вопрос, но вы должны заблокировать процесс в другом потоке, кроме mainThread.
Есть много способов сделать это: AsyncTask, Services, ExecutorService и RxJava . и какой из них выбрать, зависит от ваших потребностей
эта ссылка будет полезна:)
Я до сих пор не уверен, что мне нравится AsyncTask для этого проекта, поэтому, если кто-то может предложить совет или ссылку на учебник по использованию Handler или Runnable, ПОЖАЛУЙСТА, опубликуйте его!
Строго говоря, хотя, это, по крайней мере, наконец позволяет мне делать вещи вне потока пользовательского интерфейса, и это был вопрос. Оооо, что для всех остальных, имеющих эту проблему, вот то, что сначала сработало для меня.
public class MainActivity extends AppCompatActivity {
ArrayAdapter<String> adapter;
private static final int PORT= 8888;
private static final int TIMEOUT_MS = 500;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<String> listItems = new ArrayList<>();
ListView listview = (ListView) findViewById(R.id.lvMain);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, listItems);
listview.setAdapter(adapter);
UpdateView task = new UpdateView();
task.execute();
//just to show these parts are in different threads
String temp = Thread.currentThread().getName();
adapter.add("onCreate: " + temp);
}
private class UpdateView extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
//just to show these parts are in different threads
String temp = Thread.currentThread().getName();
adapter.add("UpdateView: " + temp);
byte[] buf = new byte[256];
byte[] ipAddress = {(byte)192,(byte)168,0,1};
try {
InetAddress address = InetAddress.getByAddress(ipAddress);
String message = "\"xyz\"";
DatagramSocket socket = new DatagramSocket(PORT);
DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), address, PORT);
socket.send(packet);
packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
}
catch(IOException e) {
adapter.add("IO Exception: " + e.toString() );
e.printStackTrace();
}
adapter.notifyDataSetChanged();
return 0L;
}
}
}
Ваш последний метод правильный, за исключением того, что поток еще не запущен. Так что вам нужно позвонить start()
:
public void sendAnother(View view) {
//adapter.add("button clicked");
new Thread(new Runnable() {
public void run() {
listview.post(new Runnable() {
public void run() {
adapter.add("inside worker thread");
}
});
}
}).start();
}