java - 在多用户聊天 Android 客户端中使用 AsyncTask 是否比扩展连接线程更好?
问题描述
我正在使用 Java Server 和 Android 客户端练习多客户端网络。我以前用 Java 客户端做过这个,所以我主要围绕我在 Java 中使用多线程的知识来调整我的解决方案。这是为创建的每个客户端连接扩展一个线程。我通常在 Java 中有一个单独的 ClientConnection 类,但在这里我使用了一个受保护的内部类。
这个解决方案有效,但有人向我提到我可能想用 AsyncTask 来做这个。使用 AsyncTask 提供什么好处而不是像我在这里所做的那样扩展线程?
请注意,我不要求任何代码或我将如何去做。与这种方法相比,它将提供什么好处。
客户端布局
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="za.ac.nmu.wrap302.jan2015.MainActivity">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/loginPanel"
android:layout_alignParentBottom="true">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/etHandle"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Connect"
android:id="@+id/btnConnect"
android:layout_below="@+id/etHandle"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignRight="@+id/etHandle"
android:layout_alignEnd="@+id/etHandle"/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/chatPanel"
android:visibility="gone">
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/lvMessages"
android:layout_below="@+id/btnConnect"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_above="@+id/txtMessage"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Change Room"
android:id="@+id/btnRoom"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtMessage"
android:layout_above="@+id/btnDisconnect"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignRight="@+id/btnDisconnect"
android:layout_alignEnd="@+id/btnDisconnect"/>
</LinearLayout>
<LinearLayout
android:background="?android:colorBackground"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/roomPanel"
android:visibility="gone">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Disconnect"
android:id="@+id/btnDisconnect"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignRight="@+id/lvMessages"
android:layout_alignEnd="@+id/lvMessages"/>
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/lvRooms"/>
</LinearLayout>
客户代码
public class MainActivity extends AppCompatActivity {
EditText etHandle;
ConnectionThread connectionThread;
private ArrayList<String> messages;
private ArrayAdapter<String> adapter;
private ArrayList<String> rooms;
private ArrayAdapter<String> roomsAdapter;
private static final String SET_HANDLE = "#SetHandle";
private static final String SEND_MESSAGE = "#SendMessage";
private static final String DISCONNECT = "#Disconnect";
private static final String JOIN_ROOM = "#JoinRoom";
private static final String LIST_ROOMS = "#ListRooms";
ListView lvMessages;
ListView lvRooms;
EditText textMessage;
Button btnConnect;
Button btnDisconnect;
Button btnRoom;
LinearLayout chatPanel, roomPanel, loginPanel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etHandle = (EditText) findViewById(R.id.etHandle);
lvMessages = (ListView) findViewById(R.id.lvMessages);
textMessage = (EditText) findViewById(R.id.txtMessage);
btnConnect = (Button) findViewById(R.id.btnConnect);
btnDisconnect = (Button) findViewById(R.id.btnDisconnect);
chatPanel = (LinearLayout) findViewById(R.id.chatPanel);
roomPanel = (LinearLayout) findViewById(R.id.roomPanel);
loginPanel = (LinearLayout) findViewById(R.id.loginPanel);
lvRooms = (ListView) findViewById(R.id.lvRooms);
btnRoom = (Button) findViewById(R.id.btnRoom);
messages = new ArrayList<String>();
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_selectable_list_item, messages);
lvMessages.setAdapter(adapter);
rooms = new ArrayList<String>();
roomsAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_selectable_list_item, rooms);
lvRooms.setAdapter(roomsAdapter);
btnConnect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
connectionThread = new ConnectionThread(etHandle.getText().toString());
connectionThread.start();
}
});
btnDisconnect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Thread out = new Thread(new Runnable() {
@Override
public void run() {
try {
connectionThread.out.writeUTF(DISCONNECT);
} catch (IOException e) {
e.printStackTrace();
}
}
});
out.start();
hideRoomScreen();
showLoginScreen();
}
});
btnRoom.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showRoomScreen();
Thread out = new Thread(new Runnable() {
@Override
public void run() {
try {
connectionThread.out.writeUTF(LIST_ROOMS);
} catch (IOException e) {
e.printStackTrace();
}
}
});
out.start();
}
});
textMessage.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
final String text = textMessage.getText().toString();
Thread out = new Thread(new Runnable() {
@Override
public void run() {
try {
connectionThread.out.writeUTF(SEND_MESSAGE);
connectionThread.out.writeUTF(text);
} catch (IOException e) {
e.printStackTrace();
}
}
});
out.start();
textMessage.getText().clear();
return true;
}
return false;
}
});
lvRooms.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
final String roomName = roomsAdapter.getItem(i);
Thread out = new Thread(new Runnable() {
@Override
public void run() {
try {
connectionThread.out.writeUTF(JOIN_ROOM);
connectionThread.out.writeUTF(roomName);
} catch (IOException e) {
e.printStackTrace();
}
}
});
out.start();
hideRoomScreen();
showChatScreen();
}
});
}
public void addMessage(String message) {
adapter.add(message);
lvMessages.setSelection(adapter.getCount() - 1);
}
public void addRoom(final String roomName){
runOnUiThread(new Runnable() {
@Override
public void run() {
roomsAdapter.add(roomName);
}
});
}
protected class ConnectionThread extends Thread {
Socket connection;
DataInputStream in;
DataOutputStream out;
String handle;
boolean connected;
public ConnectionThread(String name) {
handle = name;
}
@Override
public void run() {
try {
connection = new Socket("10.0.0.10", 1234);
connected = true;
in = new DataInputStream(connection.getInputStream());
out = new DataOutputStream(connection.getOutputStream());
out.writeUTF(handle);
hideLoginScreen();
btnRoom.callOnClick();
String command = "";
while (connected) {
command = in.readUTF();
switch (command) {
case SEND_MESSAGE:
final String user = in.readUTF();
final String message = in.readUTF();
runOnUiThread(new Runnable() {
@Override
public void run() {
addMessage(user + ": " + message);
}
});
break;
case DISCONNECT:
showLoginScreen();
connected = false;
break;
case LIST_ROOMS:
int count = in.readInt();
runOnUiThread(new Runnable() {
@Override
public void run() {
roomsAdapter.clear();
}
});
for(int i = 0; i < count; i++){
String roomName = in.readUTF();
addRoom(roomName);
}
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void showLoginScreen() {
runOnUiThread(new Runnable() {
@Override
public void run() {
loginPanel.setVisibility(View.VISIBLE);
}
});
}
private void hideLoginScreen() {
runOnUiThread(new Runnable() {
@Override
public void run() {
loginPanel.setVisibility(View.GONE);
}
});
}
private void showChatScreen() {
runOnUiThread(new Runnable() {
@Override
public void run() {
chatPanel.setVisibility(View.VISIBLE);
}
});
}
private void hideChatScreen() {
runOnUiThread(new Runnable() {
@Override
public void run() {
chatPanel.setVisibility(View.GONE);
}
});
}
private void showRoomScreen() {
runOnUiThread(new Runnable() {
@Override
public void run() {
roomPanel.setVisibility(View.VISIBLE);
}
});
}
private void hideRoomScreen() {
runOnUiThread(new Runnable() {
@Override
public void run() {
roomPanel.setVisibility(View.GONE);
}
});
}
}
解决方案
AsyncTask 的设计目的不是保持活动状态和接收命令,所以我不同意谁建议使用它。您需要一个在不确定的时间内保持活动状态并接收命令的线程。在物理上可以以类似的方式使用 AsyncTask,但这种情况类似于使用 Component 来完成完全不同的事情,例如使用 TextView 显示图像时(通过替换常见的 ImageView 并在 TextView 上手动绘制):这是可能的,但是....问题是:“为什么?”
使用它没有任何优势。
推荐阅读
- python - 生成具有正确输出python的文件?
- javascript - 如何根据链接点击显示相同的DIV容器和不同的内容
- php - 使用 PHP 检查第一个单选按钮
- python - 试图在 numpy 数组中追加一列,得到 ValueError
- ruby - 用于活动存储的 FTP 服务
- python - 如何在 Spyder IDE 中逐行查找 IPython 输出?
- python - 使用 pygooglevoice 登录时如何修复 AssertionError
- c++ - 难以理解 C++ 依赖类型,以及当前实例化的内容
- php - 如何在 PDO 包装器中返回执行的值
- python - 如何使用 SparseVector 功能构建模式?