首页 > 解决方案 > 在多用户聊天 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);
        }
    });
}

}

标签: javaandroidmultithreadingnetworking

解决方案


AsyncTask 的设计目的不是保持活动状态和接收命令,所以我不同意谁建议使用它。您需要一个在不确定的时间内保持活动状态并接收命令的线程。在物理上可以以类似的方式使用 AsyncTask,但这种情况类似于使用 Component 来完成完全不同的事情,例如使用 TextView 显示图像时(通过替换常见的 ImageView 并在 TextView 上手动绘制):这是可能的,但是....问题是:“为什么?”

使用它没有任何优势。


推荐阅读