我正在开发一个应用程序,它将与UDP服务器通信以获取其数据。显然,这将是一个长期运行的过程(即活动的生命周期),所以我想在后台运行这个过程。当应用程序从UDP服务器获取数据时,我显然会想要操纵数据,以便我可以在应用程序的UI中显示它。
据我所知,有三种方法可以在后台运行长时间运行的流程:
对于标准的Java线程,通过我在互联网上的搜索,我听到很多关于如何在Android中创建自己的线程不是一个好主意的抱怨。我之前尝试过这条路线并且确实有效,但是为了在应用程序的UI中使用线程的接收数据,它还需要相当多的操作。
对于AsyncTask,Android文档本身说:
理想情况下,AsyncTasks应该用于短操作(最多几秒钟。)
这对我来说并不好,因为我需要这个线程在整个活动期间运行。
最后,有绑定的服务。这似乎完全符合我的需求。再次,从Android文档:
服务是一种可以在后台执行长时间运行的应用程序组件,它不提供用户界面。另一个应用程序组件可以启动服务,即使用户切换到另一个应用程序,它也会继续在后台运行。此外,组件可以绑定到服务以与其交互,甚至可以执行进程间通信(IPC)。
读完这一点后,我继续沿着Android服务路径前进。事情进展顺利,直到我试图运行我的应用程序。
首先,这是我的服务:
Listener.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应用程序?我知道我不能成为第一个想要这样做的人。
感谢您的帮助。