Tags:

คือตอนนี้ ผมเขียนฝัง Server ด้วย Java(รันด้วย Editplus) แล้วตัว Client ก็เขียนด้วย Java (รันด้วย Editplus) สามารถ Connect แล้ว Chat กันไปมาได้ปกติครับ ไม่มีปัญหา

แต่พอผมมาเขียนฝั่ง Client บน Android มันมีปัญหาเกี่ยวกับ Thread ตอนอ่านข้อมูลกลับจากฝั่ง Server ครับตัว Client บน Android สามารถส่งข้อความหา Server ได้ปกติเลครับ

แต่ไม่สามารถอ่าน Buffer จาก Serverได้ มันจะเด้ง Error ถ้าผม Print Log ดูตรง
catch(Exception E){
Log.i(this.toString(), "error : " +E.toString());
}

มันมันจะมีปัญหากับ Class Thread ที่ผมเขียนไว้อ่านข้อมูลจาก Server ครับ แก้มาหลายวันลองมาหลายแบบแล้ว แต่ก็ไม่ได้เรื่องเลย ฝากผู้รู้ หรือเคยเขียน แนะนำ หาทางออกให้ผมที่นะคร้าบบบ

//Code ตอนเรียก Class Theard เพื่อรออ่าน Buffer จาก Server

threadrecieve tr = new threadrecieve(S);
tr.start();

//Code Class threadrecieve

public class threadrecieve extends Thread
{
    Socket SS;
    String msg;
    BufferedReader br;
    threadrecieve(Socket r)
    {
        SS=r;
    }

    public void run()
    {
        try
        {
            System.out.println("rock");
            br = new BufferedReader(new InputStreamReader(SS.getInputStream()));
                            while((msg=br.readLine())!=null)
            {               
                projectPCZ.this.mainText.append(msg+"\n"); // Show MSG on Mainboard

                if(msg.equals("Server is Shutting Down"))
                {
                    userText.setEnabled(true);
                    disconnectBtn.setEnabled(false);
                    connectBtn.setEnabled(true);
                    mainText.setText("");
                    serverText.setText("Server is Shutting Down .. .");
                    break;
                }

            }
            SS.close();
            br.close();
        }
        catch(IOException E){ 
            Log.i(this.toString(), "error: " +E.toString());

        }
    }
}

//Error by Eclipse

Thread [<7> Thread-8] (Suspended (exception

ViewRoot$CalledFromWrongThreadException))

ViewRoot.checkThread() line: 2802

ViewRoot.invalidateChild(View, Rect) line: 607  

ViewRoot.invalidateChildInParent(int[], Rect) line: 633 

TableLayout(ViewGroup).invalidateChild(View, Rect) line: 2505   

TextView(View).invalidate() line: 5139  

TextView.updateAfterEdit() line: 4734   

TextView.handleTextChanged(CharSequence, int, int, int) line: 6158  
TextView$ChangeWatcher.onTextChanged(CharSequence, int, int, int) line: 6316    

SpannableStringBuilder.sendTextChange(TextWatcher[], int, int, int) line: 889   

SpannableStringBuilder.change(boolean, int, int, CharSequence, int, int) line: 352  

SpannableStringBuilder.change(int, int, CharSequence, int, int) line: 269   

SpannableStringBuilder.replace(int, int, CharSequence, int, int) line: 432  

SpannableStringBuilder.append(CharSequence, int, int) line: 259 

SpannableStringBuilder.append(CharSequence, int, int) line: 28  

SpannableStringBuilder(TextView).append(CharSequence, int, int) 

line: 2240

TextView.append(CharSequence) line: 2227    

projectPCZ$threadrecieve.run() line: 173    

//อันนีั้ มัน Error ตอนกด Send อยู่ใน LogChat ของ Android

08-01 10:40:02.604: INFO/global(425): Default buffer size used in BufferedWriter constructor. It would be better to be explicit if an 8k-char buffer is required.

08-01 10:41:00.075: DEBUG/SntpClient(59): request time failed: java.net.SocketException: Address family not supported by protocol

ขอคำแนะนำด้วยนะครับบบบ ทำมาหลายวันแล้วววว

Get latest news from Blognone
By: PaPaSEK
ContributorAndroidWindowsIn Love
on 1 August 2011 - 14:17 #316922
PaPaSEK's picture

ได้ประกาศ permission ใน manifest หรือยังครับ

By: bobe
iPhoneWindows PhoneAndroid
on 1 August 2011 - 15:27 #316929
bobe's picture

ประกาศครับ

จริงๆมัน connect server และส่งข้อมูลได้ แต่มีปัญหาตอนอ่านข้อมูล ด้วย Class นี้แหละครับ

By: PaPaSEK
ContributorAndroidWindowsIn Love
on 1 August 2011 - 16:04 #316951
PaPaSEK's picture

ถ้าเข้าใจไม่ผิด... ปัญหามันเกิดจากคุณอ้างถึง Object ที่ถูกสร้างจากคนละ Thread กันครับ

การเรียกข้าม Thread นั้นต้องใช้ Runnable + View.Post ครับ (อ่านเพิ่มเติมตรงนี้)

หรือไม่ก็ต้องใช้ Class android.os.Handler ครับ

เกรงว่าตัวผมเองอธิบายเป็นภาษาคนไม่ได้ดี ผมขอแปะอันนี้ละกันครับ

By: bobe
iPhoneWindows PhoneAndroid
on 1 August 2011 - 17:18 #317022
bobe's picture

แบบที่ท่านแนะนำ มันคล้ายๆ ตัวอย่างนี้ หรือปล่าวครับ

ตัวอย่างที่ผมลองใช้ Runable ครับ

ผมก็ลองทำดูแล้ว แบบที่ใช้ Runnable
มันก็ส่งได้อย่างเด่วครับ มันอ่าน Buffer จาก Server ไม่ได้

code ที่ใช้ส่งข้อความไป server ก็ตามนี้ครับ

    if(str.equals("Send")){
        try{
            PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(S                                    
                     .getOutputStream())), true);
            pw.println(msgText.getText());
            pw.flush();
            msgText.setText("");
        }
        catch(Exception E){
            serverText.setText("Error Send :"+E.toString());
        }
    }

ส่วน Code อ่าน Buffer จาก Server ก็ Class ข้างบนครับประมานนั้น

By: PaPaSEK
ContributorAndroidWindowsIn Love
on 1 August 2011 - 18:07 #317072 Reply to:317022
PaPaSEK's picture

ถ้าเขียนแบบในตัวอย่างถือว่าถูกต้องนะครับ

เพราะสังเกตุได้ว่าตัวอย่างจะมี Handler object อยู่ใน method run ด้วยซึ่ง handler object ตัวนี้เป็น Class ที่จะป้องกันภาวะ Racing (หลาย Thread แย่งกันใช้ resource)

แต่จาก Code ของคุณอันแรก ผมยังไม่เห็นว่าคุณมีการนำอะไรที่เป็นการป้องกัน racing มาใช้เลยครับ

By: bobe
iPhoneWindows PhoneAndroid
on 1 August 2011 - 17:41 #317048
bobe's picture

ตัวโปรแกรมที่ผมเขียนครับ
รูปภาพ ตัวโปรแกรม

Console สีดำฝั่งซ้าย เป้น Server ครับ สังเกตุว่า ส่งข้อความหา Server ได้ แต่หน้าจอ Android อ่าน Buffer จาก Server ไม่ได้ ที่เห้นขึ้น XXXXX มันขึ้นก่อนจะเด้งมาฟ้อง Error ข้างล่างของ Eclipse ครับ

คือกดส่งครั้งแรก ไม่ error และขอความหน้าจอ Android ไม่ขึ้น กดรอบสอง ข้อความขึ้นแล้วเด้งไป Error

แต่ก็ยังส่งได้ปกติ

By: bobe
iPhoneWindows PhoneAndroid
on 2 August 2011 - 10:44 #317433
bobe's picture

ต้องขอบคุนสำหรับคำแนะนำของท่านจริงๆครับ ตอนนี้ผมสามารถแก้ปัญหานี้ได้แล้วตามคำแนะนำของท่าน
เอะใจตรงที่ท่านบอกว่า " handler object ตัวนี้เป็น Class ที่จะป้องกันภาวะ Racing (หลาย Thread แย่งกันใช้ resource)"

รูปตัวโปรแกรมที่ ใช้งานได้แล้ววว ^^

ที่มันมีปํญหาตอนแรกที่ผมเปลี่ยนมาใช้ Runnable เพราะผมเอา สองส่วนนี้ มาร่วมไว้ใน Runnable เดี่ยวกัน มันเลยเด้งฟ้องทุกครั้งที่ผมกด Send

//ส่วนแรก

mainText.append(msg+"\n"); // Show MSG on Mainboard

//ส่วนสอง

if(msg.equals("Server is Shutting Down"))

{

userText.setEnabled(true);

disconnectBtn.setEnabled(false);

connectBtn.setEnabled(true);

mainText.setText("");

serverText.setText("Server is Shutting Down .. .");

break;

}

ผมเลยลองแยกมันออกมาใหม่ ให้มันอยู่คนล่ะ Runnable ปรากฏว่าใช้ได้
ผมเลยเปลี่ยน TextView เป้น Listview มันจะได้ดูง่ายๆหน่อย

Class สำหรับอ่าน Buffer จาก Server ที่แก้ใหม่ครับ เผื่อใครจะใช้เป้นแนวทาง ..

public class ClientThread implements Runnable {

    Socket socket = null;
    String msg;
    BufferedReader br;
    ClientThread(Socket r)
    {
        socket=r;
    }
    public void run() {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while ((msg = in.readLine()) != null) {
                Log.d("Server msg: ", msg);

                if(msg.equals("Server is Shutting Down"))
                {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            userText.setEnabled(true);
                            disconnectBtn.setEnabled(false);
                            connectBtn.setEnabled(true);
                            receivedMessages.add("Server is Shutting Down..");
                        }
                    });

                }else{
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            // Show Message in ListView
                            receivedMessages.add(msg);
                        }
                    });
                }
            }
            in.close();
            socket.close(); 

        } catch (Exception e) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    serverText.setText("Oops. Connection interrupted. Please reconnect your phones.");
                }
            });
            e.printStackTrace();
        }
    }
}

ขอบคุนมากๆครับ

By: PaPaSEK
ContributorAndroidWindowsIn Love
on 2 August 2011 - 11:20 #317472
PaPaSEK's picture

ยินดีครับที่ได้ใช้ความรู้ให้เป็นประโยชน์ ความจริงแล้วผมเพิ่งเคยเขียนแบบ Multi-thread โดยใช้ Handler แค่ครั้งเดียวเองครับ แต่ที่พอจะจำได้เพราะผมแกะ Code ของ Multitouch Keyboard ที่มากับ Gingerbread ครับ

แกะ Code สนุกกว่าเขียนเองเยอะ