我正在开发一个应用程序,该应用程序将与 UDP 服务器进行通信以获取其数据。显然,这将是一个长期运行的过程(即活动的生命周期),所以我想在后台运行这个过程。当应用程序从 UDP 服务器获取数据时,我显然会想要操作数据,以便可以在应用程序的 UI 中显示它。
据我所知,在后台运行长时间运行的进程有 3 种方法:
- 标准 Java 线程
- 异步任务
- 服务(有界)
对于标准的 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 应用程序的“面包和黄油”变得如此困难?我知道我不能成为第一个想要这样做的人。
谢谢你的帮助。