如何在主UI线程之外编写程序
如何在主UI线程之外编写程序
我不知道是否有默认的UI线程,如果没有,我也不知道如何创建它,或者如何确定它的开始和结束时间。我如何确保PacketListener不是主UI线程的一部分?
我一直遇到NetworkOnMainThreadException异常。我知道这意味着我在主UI线程中尝试做一些应该有自己独立线程的事情。然而,我找不到很多关于如何实际做到这一点的信息。
我原以为MainActivity类就是UI线程,因为它有一个UI并且工作正常。然而,我创建了一个实现了Runnable接口的独立类,并在其中放置了网络连接的内容,但仍然遇到异常,所以它仍然是主线程的一部分,对吗?我可以看到MainActivity没有继承Thread,所以也许这是一个愚蠢的假设。如果我添加另一个实现Runnable/继承Thread的类,我没有感觉任何事情会改变。会吗?
MainActivity.java
public class MainActivity extends AppCompatActivity { ArrayListlistItems; ArrayAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listItems = new ArrayList<>(); adapter = new ArrayAdapter (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 { ArrayAdapteradapter; public PacketListener(ArrayAdapter 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。我不再太在乎它是否"脏"。
如何在主UI线程之外进行编程
问题的出现原因:
- 代码中的线程没有被启动,需要调用start()
方法来启动线程
- 主线程不能执行耗时操作,否则会抛出NetworkOnMainThreadException
异常
解决方法:
- 在需要在主UI线程之外执行的代码块中,创建一个新的线程,并在该线程中执行代码
- 在新线程中,通过post()
方法将需要在主UI线程中执行的代码封装成一个Runnable
对象,并使用listview.post()
方法将其提交给主UI线程执行
- 最后,调用start()
方法启动新线程
以下是示例代码:
public void sendAnother(View view) { new Thread(new Runnable() { public void run() { listview.post(new Runnable() { public void run() { adapter.add("inside worker thread"); } }); } }).start(); }
除了以上解决方法外,还需要注意:
- 避免在主UI线程中执行耗时操作,可以使用多线程来处理耗时任务,以避免阻塞主线程导致界面卡顿或无响应的情况发生
- 如果出现其他问题,可以检查代码是否有其他错误,或者尝试创建一个全新的项目来排除可能的问题
希望以上解决方法能够帮助解决问题。如果问题仍然存在,请持续跟踪并解决其他可能的错误或异常。
在这段代码中,我们可以看到在MainActivity的onCreate方法中,创建了一个AsyncTask子类的对象UpdateView,并调用了其execute方法。在UpdateView的doInBackground方法中,执行了一些耗时的任务,包括发送和接收数据报文。然后在doInBackground方法中,通过adapter.add方法更新了UI界面。
这段代码的问题在于,耗时的任务是在UI线程中执行的,而不是在主UI线程之外的线程中执行。这可能导致UI界面的卡顿或响应延迟。为了解决这个问题,我们需要将耗时的任务放在主UI线程之外的线程中执行。
解决这个问题的方法是使用Handler或Runnable。下面是一个使用Handler的示例代码:
public class MainActivity extends AppCompatActivity { ArrayAdapter<String> adapter; private static final int PORT= 8888; private static final int TIMEOUT_MS = 500; private Handler handler = new Handler(); 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); //just to show these parts are in different threads String temp = Thread.currentThread().getName(); adapter.add("onCreate: " + temp); new Thread(new UpdateViewRunnable()).start(); } private class UpdateViewRunnable implements Runnable { public void run() { //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(); } handler.post(new Runnable() { public void run() { adapter.notifyDataSetChanged(); } }); } } }
在这个示例代码中,我们创建了一个Handler对象,并在主UI线程中初始化。然后,在onCreate方法中创建一个新的线程,并在该线程中执行耗时的任务。在耗时的任务执行完毕后,我们使用handler.post方法来将更新UI界面的代码放在主UI线程中执行。
这样,我们就解决了在主UI线程之外执行耗时任务的问题,并避免了UI界面的卡顿或响应延迟。
在Android开发中,UI线程是主线程,负责处理用户界面的更新和事件响应。然而,有时候我们需要在应用程序的其他线程中执行一些耗时的操作,以避免阻塞UI线程,提高用户体验和应用程序的响应性。
为了解决这个问题,我们可以使用以下几种方法来在主UI线程之外编程:
1. AsyncTask(异步任务):AsyncTask是Android提供的一种用于在后台执行异步操作的工具类。它允许我们在UI线程之外执行耗时的操作,并在操作完成后更新UI线程。可以通过继承AsyncTask类并实现它的几个方法来使用它。
2. Services(服务):Service是一种在后台运行的组件,没有用户界面。我们可以创建一个Service来执行需要在后台进行的任务,并通过与UI线程进行通信来更新UI。
3. ExecutorService(执行器服务):ExecutorService是Java提供的一个用于管理线程池的接口。通过创建一个ExecutorService对象,并将任务提交给它来执行,我们可以在UI线程之外创建新的线程,并在需要时控制线程的数量。
4. RxJava:RxJava是一个用于异步编程和事件驱动的库。它提供了丰富的操作符和函数式编程的方式来处理异步操作,使得在UI线程之外编程变得更加简单和可读。
选择使用哪种方法取决于你的需求和应用程序的复杂性。你可以通过参考这个链接来获取更多信息:https://stackoverflow.com/a/4722985/6540997
如果你想了解如何在Android中创建一个新的线程,可以尝试实现Runnable接口来创建一个新的线程。然而,这种方式可能不是最佳实践,因为它需要手动管理线程的生命周期和与UI线程的通信。
如果你想深入学习Java和Android中的多线程编程,可以通过谷歌搜索“Multithreading in Java”来找到相关的教程和资料。
希望这些信息对你有所帮助。如果你有其他问题,请随时提问。