2012-11-11 81 views
0

我已经创建了一个远程服务,负责所有客户端 - 服务器通信。 我使用服务,因为很少分离的应用程序将使用相同的通信套接字,并且没有其他方式在应用程序之间“共享”套接字(据我所知)。使android活动等待服务回复

该服务很好,可以启动一个套接字连接并通过它发送int/String,但我不能将它用作像readString()这样的输入。

我认为问题的发生是因为活动永远不会等待来自服务的回复。 我在我的服务的readString方法的每个部分返回自定义字符串时对其进行了测试。

和代码...

ConnectionRemoteService:

public class ConnectionRemoteService extends Service { 

private String deviceID; 
private ConnectionThread ct; 

@Override 
public void onCreate() { 
    super.onCreate(); 
    //Toast.makeText(this, "Service On.", Toast.LENGTH_LONG).show(); 

} 

@Override 
public void onDestroy() { 
    //Toast.makeText(this, "Service Off.", Toast.LENGTH_LONG).show(); 
    if(ct != null) 
     ct.close(); 
} 

@Override 
public IBinder onBind(Intent intent) { 
    return myRemoteServiceStub; 
} 

private ConnectionInterface.Stub myRemoteServiceStub = new ConnectionInterface.Stub() { 
    public void startConnection(){ 
     WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE); 
     deviceID = wm.getConnectionInfo().getMacAddress(); 
     ct = new ConnectionThread(deviceID); 
     ct.start(); 
    } 

    public void closeConnection(){ 
     if(ct != null) 
      ct.close(); 
    } 

    public void writeInt(int i) throws RemoteException { 
     if(ct != null) 
      ct.writeInt(i); 
    } 

    public int readInt() throws RemoteException { 
     if(ct != null) 
      return ct.readInt(); 
     return 0; 
    } 

    public void writeString(String st) throws RemoteException { 
     if(ct != null) 
      ct.writeString(st); 
    } 

    public String readString() throws RemoteException { 
     if(ct != null) 
      return ct.readString(); 
     return null; 
    } 

    public String deviceID() throws RemoteException { 
     return deviceID; 
    } 

    public boolean isConnected() throws RemoteException { 
     return ct.isConnected(); 
    } 

}; 

}

解释:

,你可以看到,我只启动一个 “空” 服务,等待应用程序与它绑定。 绑定后,我创建ConnectionThread,将照顾套接字等... 所有方法通过套接字调用输入\输出的线程方法。

ConnectionThread:

public class ConnectionThread extends Thread { 

private static final int SERVERPORT = 7777; 
private static final String SERVERADDRESS = "192.168.1.106"; 

private String deviceID; 
private Socket socket; 
private DataInputStream in; 
private DataOutputStream out; 
private ObjectInputStream inObj; 
private ObjectOutputStream outObj; 
private boolean isConnected = false; 

PingPongThread ppt; 

public ConnectionThread(String deviceID) { 
    super(); 
    this.deviceID = deviceID; 
} 

@Override 
public void run() { 
    super.run(); 
    open(); 

} 

void open(){ 
    try{ 
     socket = new Socket(SERVERADDRESS,SERVERPORT); 
     out = new DataOutputStream(socket.getOutputStream()); 
     out.flush(); 
     in = new DataInputStream(socket.getInputStream()); 
     outObj = new ObjectOutputStream(out); 
     outObj.flush(); 
     inObj = new ObjectInputStream(in); 
     out.writeUTF(deviceID); 
     isConnected = true; 
     ppt = new PingPongThread(SERVERADDRESS, SERVERPORT); 
     ppt.start(); 
    } 
    catch(Exception e){ 
     isConnected = false; 
     System.err.println(e.getMessage()); 
    } 
} 

public void close(){ 
    try { 
     if(ppt!=null){ 
      ppt.stopThread(); 
      ppt.notify(); 
     } 
     if(in!=null) 
      in.close(); 
     if(out!=null) 
      out.close(); 
     if(socket!=null) 
      socket.close(); 
    } 
    catch(Exception e){ 
     System.err.println(e.getMessage()); 
    } 
    isConnected = false; 
    socket=null; 

} 

public void writeInt(int i){ 
    try { 
     out.writeInt(i); 
     out.flush(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

public int readInt(){ 
    try { 

     int i = in.readInt(); 
     return i; 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    return 0; 
} 

public void writeString(String st){ 
    try { 
     out.writeUTF(st); 
     out.flush(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

public String readString(){ 
    String st = ""; 
    try { 
     st = in.readUTF(); 
     return st; 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    return st; 
} 

public boolean isConnected(){ 
    return isConnected; 
} 

}

解释:

在我的线程,我创建了插座和初始化所有输入/输出对象以后使用。 (忽略“PingPongThread”,它只是一个简单的线程来检查连接,它使用不同的端口,所以它不会是问题...) 所有其他方法都非常简单,只需使用输入/输出对象。 ..

和主要活动:

public class MainLauncherWindow extends Activity { 
private ConnectionInterface myRemoteService; 
private boolean isServiceBinded = false; 
private OnClickListener onclicklistener; 

final ServiceConnection conn = new ServiceConnection() { 
    public void onServiceConnected(ComponentName name, IBinder service) { 
     myRemoteService = ConnectionInterface.Stub.asInterface(service); 
    } 
    public void onServiceDisconnected(ComponentName name) { 
     myRemoteService = null; 
    } 
}; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main_launcher_window); 

    final Button connectButton = (Button)findViewById(R.id.Connect); 
    final Button disconnectButton = (Button)findViewById(R.id.Disconnect); 


    startService(new Intent(getApplicationContext(), ConnectionRemoteService.class)); 
    isServiceBinded = bindService(new Intent("com.mainlauncher.ConnectionRemoteService"),conn,Context.BIND_AUTO_CREATE); 

    //Connect button 
    onclicklistener = new OnClickListener(){ 
     public void onClick(View v) { 
      try { 
       if(isServiceBinded){ 
        myRemoteService.startConnection(); 
        connectButton.setEnabled(false); 
        disconnectButton.setEnabled(true); 
       } 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 

     } 
    }; 
    connectButton.setOnClickListener(onclicklistener); 

    //Disconnect button 
    onclicklistener = new OnClickListener(){ 
     public void onClick(View v) { 
      connectButton.setEnabled(true); 
      disconnectButton.setEnabled(false); 
      try { 
       if(isServiceBinded) 
        myRemoteService.closeConnection(); 
      } catch (RemoteException e) { 
       e.printStackTrace(); 
      } 
     } 
    }; 
    disconnectButton.setOnClickListener(onclicklistener); 


    //read test button 
    final Button bt1 = (Button)findViewById(R.id.bt1); 

    onclicklistener = new OnClickListener(){ 
     public void onClick(View v) { 
      try { 
       if(isServiceBinded){ 
        myRemoteService.writeString("Testing"); 
        Toast.makeText(v.getContext(), myRemoteService.readString(), Toast.LENGTH_LONG).show(); 
       } 
      } catch (RemoteException e) { 
       e.printStackTrace(); 
      } 
     } 
    }; 
    bt1.setOnClickListener(onclicklistener);   
} 




@Override 
public void onBackPressed() { 
    super.onBackPressed(); 
    if(isServiceBinded){ 
     unbindService(conn); 
     stopService(new Intent(getApplicationContext(), ConnectionRemoteService.class)); 
     isServiceBinded = false; 
    } 
} 

@Override 
protected void onDestroy() { 
    super.onDestroy(); 
    if(isServiceBinded){ 
     unbindService(conn); 
     stopService(new Intent(getApplicationContext(), ConnectionRemoteService.class)); 
     isServiceBinded = false; 
    } 
} 



} 

在我的主要活动,我创建按钮连接\断开和测试按钮。 测试按钮将“测试”字符串发送到服务器端。 服务器工作正常,获取“测试”字符串并将其他字符串返回给客户端。 但“Toast”味精总是空白。

  • 我测试服务器端没有服务,它工作正常,所以不用担心。
  • 我对ConnectionThread进行了测试,从它的readString方法返回测试字符串,它运行良好,意味着线程通过服务返回客户端(所有链运行良好)的答案。

我现在唯一想到的就是活动永远不会等待服务返回的字符串,这就是导致问题的原因。

有什么想法?

谢谢, Lioz。

回答

0

尝试使用,而不是线程的AsyncTask或通过这样做,你可以让你的应用程序等待你的结果像这样的的AsyncTask包裹你的线程(如果可能的话,我没有尝试过)

class YourClass extends AsyncTask <Bla,Object,Bla>{ 

doInBackGround(Bla blah){ 
    //do the stuff here 
    return result; 
} 


onPostCalculate(Object result){ 

    // use the result. 
    } 
} 
+0

不能在我的类中使用“扩展AsyncTask”,第一个扩展Service,第二个扩展Thread ...任何其他想法使AsyncTask工作? – HFDO5

+0

好吧,也许让内部类可以做到这一点。在内部类doInBackGround()中移动该操作并执行它。 或者只是将您的线程更改为AsyncTask。它跟线程一样。 – Ercan

+0

经过多次测试后,我无法找到使其正常工作的方法。任何想法的帮助? – HFDO5

0

看看我的帖子:

Display an alert or a view on receiving a notification

这将为您提供一种使用BroadcastReciever收听活动的活动方式。如果你想等待阻止用户做其他事情,你也可以调出一个简单的模态对话框,在超时或收到意图时可以取消。

+0

这不是我所描述的问题,我不希望阻止用户...问题是该活动不是来自我的服务(没有用户相关的事情“等待”的字符串回复)。我没有理由“阻止”活动或使用BroadcastReciever ... – HFDO5

1

终于明白了。 我使用“Ercan”方式与AsyncTask。

最终的解决方案是在我的活动(而不是在我的线程/服务)中实现它。 现在它工作得很好。 感谢您的帮助。

最终代码:

class ReadString extends AsyncTask <String,Object,String>{ 
    @Override 
    protected String doInBackground(String... params) { 
     try { 
      if(isServiceBinded){ 
       String st = myRemoteService.readString(); 
       return st; 
      } 
     } catch (RemoteException e) { 
      e.printStackTrace(); 
     } 
     return "Nothing"; 
    } 
} 

class ReadInt extends AsyncTask <Integer,Object,Integer>{ 
    @Override 
    protected Integer doInBackground(Integer... params) { 
     try { 
      if(isServiceBinded){ 
       int i = myRemoteService.readInt(); 
       return i; 
      } 
     } catch (RemoteException e) { 
      e.printStackTrace(); 
     } 
     return 0; 
    } 
} 
由Android

Adnroid内链接提供

1

使用handlerThread类:http://developer.android.com/reference/android/os/HandlerThread.html

教程链接:

  1. http://stephendnicholas.com/archives/42

使用这个可以为处理程序创建一个新线程并将其传递给服务。因此,所有由服务发送的消息都将在新线程中处理,并且您可以在活动中使用wait/notify。

例如:

/* This is running in hadlerThread */ 

static class IncomingHandler extends Handler 
{ 

    public IncomingHandler(Looper looper) 
    { 
     super(looper); 
    } 

    @Override 
    public void handleMessage(Message msg) { 

     switch (msg.what) { 
     case DATA: 
      //Your code 
      synchronized (sync) { 
       if(gotReply == false){ 
        gotReply = true; 
        sync.notify(); 
       } 
      } 
      break; 
     default: 
      super.handleMessage(msg); 
     } 
    } 
} 

/* This is running in main thread */ 

void foo(){ 
    synchronized (sync) { 
     gotReply = false; 
    } 
    Message msg = Message.obtain(null, DATA);  
    sendMessage(msg); 

    synchronized (sync) { 
     if(gotReply == false){ 
      try { 
       sync.wait(); 
      } catch (InterruptedException e) { 

       e.printStackTrace(); 
      } 
     } 
    } 
}