0

我正在开发一个应用程序,该应用程序将与 UDP 服务器进行通信以获取其数据。显然,这将是一个长期运行的过程(即活动的生命周期),所以我想在后台运行这个过程。当应用程序从 UDP 服务器获取数据时,我显然会想要操作数据,以便可以在应用程序的 UI 中显示它。

据我所知,在后台运行长时间运行的进程有 3 种方法:

  1. 标准 Java 线程
  2. 异步任务
  3. 服务(有界)

对于标准的 Java 线程,从我在 Internet 上的搜索中,我听到了很多关于如何在 Android 中创建自己的线程不是一个好主意的抱怨。我之前尝试过这条路线并且它确实有效,但它也需要相当多的操作才能在应用程序的 UI 中使用线程接收到的数据。

对于 AsyncTask,Android 文档本身说:

理想情况下,AsyncTasks 应该用于短操作(最多几秒钟。)

这对我不利,因为我需要这个线程在活动的整个生命周期中运行。

最后是绑定服务。这似乎非常适合我的需求。同样,来自 Android 文档:

Service 是一个应用程序组件,可以在后台执行长时间运行的操作,它不提供用户界面。另一个应用程序组件可以启动一个服务,即使用户切换到另一个应用程序,它也会继续在后台运行。此外,组件可以绑定到服务以与其交互,甚至执行进程间通信 (IPC)。

读完这篇文章后,我继续走 Android 服务路径。在我尝试运行我的应用程序之前,一切都很顺利。


首先,这是我的服务:

监听器.java

public class Listener extends Service {

    private boolean keepWorking = true;
    private int localPort;
    private final IBinder iBinder = new LocalBinder();

    public class LocalBinder extends Binder {
        Listener getService(){
            return Listener.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }

    public void listenToServer(){
        try {
            byte[] buffer = new byte[16 * 1024];
            DatagramPacket recvPacket = new DatagramPacket(buffer, buffer.length);
            DatagramSocket server = new DatagramSocket(0);
            server.setSoTimeout(1000);
            localPort = server.getLocalPort();

            while (keepWorking) {
                try {
                    server.receive(recvPacket);
                    byte[] data = new byte[recvPacket.getLength()];
                    System.arraycopy(recvPacket.getData(), 0, data, 0, data.length);
                    // Do stuff with the data ...
                } catch ( IOException ioe) {
                    // something here
                }
            }
        } catch (SocketException se) {
            // something here
        }
    }

    public int getPort() {
        return this.localPort;
    }

    public void stopWorking() {
        this.keepWorking = false;
    }
}

而且,我的MainActivity.java绑定到这个服务:

public class MainActivity extends AppCompatActivity {

    Listener myListener;
    boolean isBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, Listener.class);
        startService(intent);
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if(isBound){
            unbindService(serviceConnection);
            isBound = false;
        }
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Listener.LocalBinder localBinder = (Listener.LocalBinder) service;
            myListener = localBinder.getService();
            myListener.listenToServer();
            isBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }
    };
}

我很快发现,即使我是从服务中执行此操作的,但它显然仍在从主线程运行,因为我收到此错误:

android.os.NetworkOnMainThreadException

从我的Listener.java中读取的行中server.receive(recvPacket);

在四处挖掘之后,我发现我可能需要一个线程或 AsyncTask 来处理这个问题。

所以,我的问题是:在 Android 应用程序中执行长时间运行的网络操作的权威、“正确”方式是什么?

为什么 Android 让真正的 Android 应用程序的“面包和黄油”变得如此困难?我知道我不能成为第一个想要这样做的人。

谢谢你的帮助。

4

0 回答 0