#android
#Android
Вопрос:
Я создаю приложение для чата по локальной сети для Android (клиент) и ПК (java_server), оба из которых работают в eclipse.Проблема в том, что Android очень нестабилен и очень часто выходит из строя, однако он очень хорошо работает на моей Java-версии клиента и сервера. пожалуйста, помогите!
вот мой код:
package android.client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.PriorityQueue;
import java.util.Queue;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
общедоступный класс Chat_client расширяет Activity реализует Runnable {
private Socket socket = null;
//private Thread thread = null;
private DataOutputStream requestOut;
private int clientID;
private String playerName;
private DataInputStream requestIn;
private EditText text;
private ListView msgView;
private ArrayAdapter<String> msgArrayAdapter;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
socket = new Socket("192.168.2.2", 5558);
Toast.makeText(this,
"Connected to " this.socket.getInetAddress(),
Toast.LENGTH_LONG 10).show();
this.playerName = "wei";
this.clientID = socket.getLocalPort();
requestOut = new DataOutputStream(socket.getOutputStream());
requestIn = new DataInputStream(socket.getInputStream());
text = (EditText) findViewById(R.id.editText1);// black textfield
msgArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);
msgView = (ListView) findViewById(R.id.in);
msgView.setAdapter(this.msgArrayAdapter);
new Thread(this).start();
} catch (UnknownHostException uke) {
Toast.makeText(this, "Host unknown:" uke.getMessage(),
Toast.LENGTH_LONG).show();
} catch (IOException ioe) {
Toast.makeText(this, "Unexpected exception: " ioe.getMessage(),
Toast.LENGTH_LONG).show();
}
}
// This method is called at button click because we assigned the name to the
// "On Click property" of the button
public void myClickHandler(View v) {
Toast.makeText(this, "sending", Toast.LENGTH_LONG/2).show();
TextView view = (TextView) findViewById(R.id.editText1);
String message = view.getText().toString();
text.setText("");
Packet p;
if (message.startsWith("#")) {
p = new Packet(playerName, 1, clientID,message.substring(1, message.length()));
}
else
p = new Packet(playerName, -1, clientID,message);
try {
this.requestOut.writeUTF(p.convertToSendFormat());
requestOut.flush();
} catch (IOException e) {
e.printStackTrace();
}
msgArrayAdapter.add("Me: <" message ">");
}
public void run() {
while (true) {
String r;
try {
r = this.requestIn.readUTF();
if (r != null) {
Packet p = convert(r);
if (p != null)
handle(p);
}
} catch (IOException ioe) {
System.exit(0);
}
}
}
private synchronized Packet convert(String n) {
String data = "";
String[] p = n.split(" ", 4);
if (p.length < 4)
return null;
data = p[3];
return new Packet(p[0], Integer.parseInt(p[1]), Integer.parseInt(p[2]),
data);
}
public synchronized void handle(Packet re) {
if (re.getChannelID() == clientID){
msgArrayAdapter.add("Me" re.toString());}
else{
msgArrayAdapter.add(re.getSenderName() re.toString());
}
}
}
package android.client;
import java.io.Serializable;
/**
* This class handles the networking packet
* @author Wei
*/
public class Packet implements Serializable {
private static final long serialVersionUID = 1509287973845358190L;
private String senderName;
private int actionID;
private long channelID;
private int sent;
private String data;
public Packet(String name, int actionID,int cid, String action2) {
this.senderName=name;
this.channelID = cid;
this.actionID = actionID;
this.data = action2;
this.sent = -1;
}
public String getSenderName(){
return this.senderName;
}
public int getActionID(){
return this.actionID;
}
public String getData(){
return this.data;
}
protected void setChannelID(int id){
this.channelID = id;
}
public long getChannelID(){
return this.channelID;
}
protected void setSent(boolean b){
if(b)
this.sent = 1;
else
this.sent=-1;
}
protected int checkSent(){
return this.sent;
}
public String toString(){
return (" : " this.data);
}
public void addTimeStamp(String time){
this.data =time;
}
public String convertToSendFormat(){
return this.senderName " " this.actionID " " this.channelID " " this.data;
}
public int getFlag() {
return 0;
}
public void setFlag(boolean response) {
}
}
есть журнал:
10-12 12:54:22.763: DEBUG/AndroidRuntime(360): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
10-12 12:54:22.763: DEBUG/AndroidRuntime(360): CheckJNI is ON
10-12 12:54:22.895: DEBUG/AndroidRuntime(360): --- registering native functions ---
10-12 12:54:23.513: INFO/ActivityManager(59): Force stopping package android.client uid=10033
10-12 12:54:23.513: INFO/ActivityManager(59): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=android.client/.Chat_client }
10-12 12:54:23.633: DEBUG/AndroidRuntime(360): Shutting down VM
10-12 12:54:23.645: INFO/ActivityManager(59): Start proc android.client for activity android.client/.Chat_client: pid=366 uid=10033 gids={3003}
10-12 12:54:23.664: DEBUG/jdwp(360): Got wake-up signal, bailing out of select
10-12 12:54:23.664: DEBUG/dalvikvm(360): Debugger has detached; object registry had 1 entries
10-12 12:54:24.024: WARN/ActivityThread(366): Application android.client is waiting for the debugger on port 8100...
10-12 12:54:24.044: INFO/System.out(366): Sending WAIT chunk
10-12 12:54:24.054: INFO/dalvikvm(366): Debugger is active
10-12 12:54:24.244: INFO/System.out(366): Debugger has connected
10-12 12:54:24.244: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:24.444: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:24.644: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:24.843: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:25.053: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:25.254: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:25.454: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:25.654: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:25.866: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:26.075: INFO/System.out(366): waiting for debugger to settle...
10-12 12:54:26.284: INFO/System.out(366): debugger has settled (1322)
10-12 12:54:27.544: INFO/ActivityManager(59): Displayed activity android.client/.Chat_client: 3912 ms (total 3912 ms)
10-12 12:54:33.313: DEBUG/dalvikvm(124): GC_EXPLICIT freed 642 objects / 35976 bytes in 203ms
10-12 12:54:38.335: DEBUG/dalvikvm(225): GC_EXPLICIT freed 152 objects / 11144 bytes in 177ms
10-12 12:54:43.384: DEBUG/dalvikvm(261): GC_EXPLICIT freed 249 objects / 11840 bytes in 205ms
10-12 12:55:14.583: WARN/KeyCharacterMap(108): No keyboard for id 0
10-12 12:55:14.583: WARN/KeyCharacterMap(108): Using default keymap: /system/usr/keychars/qwerty.kcm.bin
10-12 12:55:37.295: WARN/dalvikvm(366): threadid=7: thread exiting with uncaught exception (group=0x4001d800)
Комментарии:
1. Возможно ли, что ваше приложение выходит из строя при / после вращения устройства?
2. Не уверен в этом, так как я использую виртуальное устройство (эмулятор)
Ответ №1:
Я решил проблему, добавив метод обработчика:
private Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg){
String str= (String) msg.obj;
if(msg.what==1){
msgArrayAdapter.add(str);
}
}
};`
и, конечно, я понятия не имею, почему это работает, но это просто работает.лол, еще раз спасибо за помощь @Knickedi
Ответ №2:
Вот самый простой способ сделать это (я не знаю, решает ли это вашу реальную проблему). Но обновление массива адаптеров из другого потока в любом случае не является хорошей идеей. И если ваше устройство вращается, вместо этого вы создадите другой поток, используя уже запущенный. И не забудьте обновить свой список, позвонив msgArrayAdapter.notifyDataSetChanged()
, когда вы изменили массив (сообщения). Вот план и идея…
Ваша активность. Он будет реагировать на изменения конфигурации и фактическое уничтожение правильным способом:
private MessagePollThread thread;
public void onCreate(Bundle b) {
super.onCreate(b);
thread = (MessagePollThread) getLastNonConfigurationInstance();
if (thread == null) {
// activity runs for first time - create thread
thread = new MessagePollThread();
thread.activity = this;
thread.start();
} else {
// just update the thread with the new activity - it's started already
thread.activity = this;
}
}
public Object onRetainNonConfigurationInstance() {
// retain the thread for configuration changes e.g. orientation change (see onCreate)
return thread;
// you could also retain your old received messages here
// just return new Object [] {thread, messageArray} and handle that in onCreate
}
public void onDestroy() {
super.onDestroy();
if (isFinishing()) {
// activity is about to destroy itself
// shutdown the running thread
thread.run = false;
thread.interrupt(); // break the sleep
}
}
Фактический поток, который обрабатывает запросы (как внутренний статический класс вашей активности):
private static class MessagePollThread extends Thread {
public volatile boolean run = true;
public volatile Chat_client activity;
private Handler handler = new Handler();
public void run() {
// setup socket connection here
while (run) {
try {
// you should / could also process send requests here
// just use a synchronized list to check in your
// activity (e.g. activity.messages) for new messages in queue
// http://developer.android.com/reference/java/util/Collections.html#synchronizedList(java.util.List)
final String r = this.requestIn.readUTF();
if (!run) {
// the thread could be shut down meanwhile
continue;
}
handler.post(new Runnable() {
public void run() {
activity.handleResult(r);
}
}
} catch (IOException e) {
handler.post(new Runnable() {
public void run() {
activity.finish();
}
}
}
try {
// sleep for a while
// non stop polling will drain your battery very fast
sleep(1000);
} catch (InterruptException e) {
// do nothing, just proceed
}
}
// destroy socket connection here
}
}
Комментарии:
1. Все еще есть проблема (случайно), в любом случае спасибо за помощь. Будет ли ошибка, если я не использую ArrayAdapter? или есть способ не использовать поток для этого?
2. Не должно иметь значения, что вы используете. Использование потока для сетевой задачи настоятельно рекомендуется и в этом случае даже необходимо. Вы просто должны убедиться, что вы изменяете данные, связанные с пользовательским интерфейсом, в потоке пользовательского интерфейса. Извините, в данный момент я действительно не могу сказать, что вызывает ваше исключение…