[英]Socket closing during lifecycle changes
我正在嘗試使用setRetainInstance(true);在無頭片段中的生命周期更改期間保持套接字打開。 在onCreate中。 但是,當我的應用返回時,會發生以下異常。
E/Client: Receiving thread loop error
java.net.SocketException: Socket closed
at libcore.io.Posix.recvfromBytes(Native Method)
at libcore.io.Posix.recvfrom(Posix.java:189)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:250)
at libcore.io.IoBridge.recvfrom(IoBridge.java:549)
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:481)
at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:37)
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:237)
at java.io.InputStreamReader.read(InputStreamReader.java:233)
at java.io.BufferedReader.fillBuf(BufferedReader.java:145)
at java.io.BufferedReader.readLine(BufferedReader.java:397)
at com.gm.popper_6.ConnectionFragment$Client$ReceivingThread.run(ConnectionFragment.java:183)
at java.lang.Thread.run(Thread.java:818)
這是片段的代碼
public class ConnectionFragment extends Fragment {
private InetAddress mGoAddress;
private int mGoPort;
private Client mClient;
private static final String TAG = "Connection";
private Server mServer;
private Socket mSocket;
private ConnectionFragmentListener listener;
private String mMessage;
public static ConnectionFragment newInstance(InetAddress address, int port){
Bundle bundle = new Bundle();
bundle.putSerializable("GoAddress", address);
bundle.putInt("GoPort", port);
ConnectionFragment fragment = new ConnectionFragment();
fragment.setArguments(bundle);
return fragment;
}
public interface ConnectionFragmentListener{
void onMessageRcvd(String message);
}
public void setConnectionFragmentListener(ConnectionFragmentListener listener){
this.listener = listener;
}
private void readBundle(Bundle bundle){
if (bundle != null){
mGoAddress = (InetAddress)bundle.getSerializable("GoAddress");
mGoPort = bundle.getInt("GoPort");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
readBundle(getArguments());
mGoAddress = (InetAddress) getArguments().getSerializable("GoAddress");
mGoPort = getArguments().getInt("GoPort");
mServer = new Server();
}
@Override
public void onStart() {
super.onStart();
}
// THE SERVER CLASS
private class Server{ //DECLARATION
ServerSocket mServerSocket = null;
Thread mThread = null;
public Server(){ //CONSTRUCTOR
mThread = new Thread(new ServerThread());
mThread.start();
}
public void tearDown(){
mThread.interrupt();
try {
mServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Error closing server socket");
}
}
class ServerThread implements Runnable{
@Override
public void run() {
//REMOVE OR COMMENT OUT FOR FINAL
//android.os.Debug.waitForDebugger();
try {
mServerSocket = new ServerSocket(mGoPort, 50, mGoAddress);
} catch (IOException e) {
e.printStackTrace();
}
while (!Thread.currentThread().isInterrupted()){
try {
mSocket = mServerSocket.accept();
Log.d(TAG, "Connected");
if (mClient == null){
mClient = new Client();
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
}
}
//THE CLIENT CLASS
private class Client { //DECLARATION
private final String CLIENT_TAG = "Client";
private Thread mSendThread;
private Thread mRecThread;
public Client() { //CONSTRUCTOR
Log.d(CLIENT_TAG, "Creating Client");
mSendThread = new Thread(new SendingThread());
mSendThread.start();
}
class SendingThread implements Runnable { //an inner class of Client
BlockingQueue<String> mMessageQueue;
private int QUEUE_CAPACITY = 10;
public SendingThread() {
mMessageQueue = new ArrayBlockingQueue<String>(QUEUE_CAPACITY);
}
@Override
public void run() {
mRecThread = new Thread(new ReceivingThread());
mRecThread.start();
while (true){
try {
String msg = mMessageQueue.take();
sendMessage(msg);
} catch (InterruptedException e) {
Log.d(CLIENT_TAG, "Sending loop interrupted, exiting");
}
}
}
} //closes SendingThread, an inner class of Client
class ReceivingThread implements Runnable{ //an inner class of Client
@Override
public void run() {
BufferedReader input;
try {
//android.os.Debug.waitForDebugger();
input = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
while (!Thread.currentThread().isInterrupted()){
String messageStr = null;
messageStr = input.readLine(); //Line 183
if (messageStr!= null){
Log.d(CLIENT_TAG, "Read from the stream: " + messageStr);
mMessage = messageStr;
updateMessages(false);
}
else{
Log.d(CLIENT_TAG, "The null!!!");
}
}
input.close();
} catch (IOException e) {
Log.e(CLIENT_TAG, "Receiving thread loop error", e);
e.printStackTrace();
}
} //closes run method
} //closes ReceivingThread, an inner class of Client
public void tearDown(){ //a method of Client
try {
getSocket().close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendMessage(String msg){ //a method of Client
try {
Socket socket = getSocket(); //should return mSocket
if (socket == null) {
Log.d(CLIENT_TAG, "Socket is null");
} else if (socket.getOutputStream() == null) {
Log.d(CLIENT_TAG, "Socket output stream in null");
}
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(getSocket().getOutputStream())), true);
out.println(msg);
out.flush();
mMessage = msg;
updateMessages(true);
} catch (UnknownHostException e){
Log.d(CLIENT_TAG, "Unkown host", e);
} catch (IOException e) {
Log.d(CLIENT_TAG, "I/O exception", e);
} catch (Exception e){
Log.d(CLIENT_TAG, "Error 3", e);
}
Log.d(CLIENT_TAG, "Message sent: " + msg);
} //closes sendMessage, a method of the inner Client class
} //closes Client class, an inner class of Connection
private Socket getSocket() {
return mSocket;
}
public synchronized void updateMessages(boolean local){
Log.i(TAG, "Updating message: " + mMessage);
if (local){
mMessage = "me: " + mMessage;
}
else{
mMessage = "them: " + mMessage;
}
if (listener!= null){
listener.onMessageRcvd(mMessage);
}
} //closes updateMessages
public void sendMessage(String msg){ //CALL FROM MAIN ACTIVITY
if(mClient != null){ //TO SEND A STRING MESSAGE
mClient.sendMessage(msg);
}
}
public void tearDown(){
mServer.tearDown();
mClient.tearDown();
}
@Override
public void onDestroy() {
tearDown();
super.onDestroy();
}
} //closes class declaration
這是主要活動
public class MainActivity extends Activity implements ChannelListener, DeviceActionListener,
ConnectionInfoListener, ConnectionFragment.ConnectionFragmentListener{
//CLASS DECLARATIONS
public static final String TAG = "Popper";
private WifiP2pManager manager;
private Boolean isWifiP2pEnabled = false;
ArrayList<Target> mTargets = new ArrayList<Target>(0);
Target mTarget;
TextView rcvd;
TextView ip;
EditText mssg;
String goAddress = "";
InetAddress goInetAddress;
int prefixedPort;
//declare and initialize an intent filter
private final IntentFilter intentFilter = new IntentFilter();
//private final IntentFilter serverFilter = new IntentFilter();
private Channel channel;
private BroadcastReceiver receiver = null;
private ConnectionInfoListener infoListener;
private Intent serverServiceIntent;
ConnectionFragment mConnection;
//????
public void setIsWifiP2pEnabled(boolean isWifiP2pEnabled) {
this.isWifiP2pEnabled = isWifiP2pEnabled;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//register app w/p2p framework with call to initialize
//channel is my apps connection to the p2p framework
manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
channel = manager.initialize(this, getMainLooper(), null);
receiver = new P2pReceiver(manager, channel, this);
rcvd = (TextView)findViewById(R.id.rcvd);
rcvd.setMovementMethod(new ScrollingMovementMethod());
//initialize filter and setup to listen for the following broadcast intents
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
Resources res = getResources();
prefixedPort = res.getInteger(R.integer.GOport);
}
@Override
public void onMessageRcvd(String message) {
addLine(message);
}
@Override
protected void onResume() {
super.onResume();
receiver = new P2pReceiver(manager, channel, this);
registerReceiver(receiver, intentFilter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.action_items, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.atn_direct_discover:
if (!isWifiP2pEnabled) {
NotificationToast.showToast(MainActivity.this, "Enable P2P!!!");
return true;
}
final TargetListFragment fragment = (TargetListFragment) getFragmentManager()
.findFragmentById(R.id.frag_list);
fragment.onInitiateDiscovery();
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
NotificationToast.showToast(MainActivity.this, "Discovery initiated");
}
@Override
public void onFailure(int reason) {
NotificationToast.showToast(MainActivity.this, "Discovery failed");
}
});
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override //this is associated with ChannelListener
public void onChannelDisconnected() { //removal causes error.
}
@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
goAddress = info.groupOwnerAddress.getHostAddress(); //this returns a string rep of add.
goInetAddress = info.groupOwnerAddress; //this returns actual inet add.
ip = (TextView) findViewById(R.id.ip);
mssg = (EditText) findViewById(R.id.mssg);
ip.setText(goAddress + ":" + "8080"); //display GO address and IP
startConnectionFragment();
}
//this override method is triggered by TargetListFragment's DeviceActionListener
@Override
public void connect(WifiP2pConfig config) {
manager.connect(channel, config, new ActionListener() {
@Override
public void onSuccess() {
//maybe use this to gray out and disable the listview object that connected
}
@Override
public void onFailure(int reason) {
}
});}
public void startConnectionFragment(){
mConnection = ConnectionFragment.newInstance(goInetAddress, prefixedPort);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(mConnection, "TAG_1");
ft.commit();
mConnection.setConnectionFragmentListener(this);
}
public void addLine(String line){
final String msg = line;
runOnUiThread(new Runnable(){
@Override
public void run() {
rcvd.append("\n" + msg);
}
});
}
@Override
public void onTargetListClick(Target target) {
mTarget = target;
}
public void stopServer() {
if(serverServiceIntent != null)
{
stopService(serverServiceIntent);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mConnection != null){
//mConnection.tearDown();
}
stopServer();
}
public void SendMessage(View v){
EditText txt = (EditText) this.findViewById(R.id.mssg);
String str = txt.getText().toString();
mConnection.sendMessage(str);
txt.getText().clear();
}
}
這可能與應用在暫停或停止時分離而又在重新恢復運行時不重新附加有關系嗎? 還有其他我應該考慮的事情嗎?
最終,應用程序需要通過p2p保持與大約5或6個設備的通信打開。 如果有更好的策略,我歡迎您提出建議。 謝謝。
更新-所以我已經確認,當主要活動進入onStop時,片段的onDestroy和onDetach方法都將觸發。 由於我有一種方法可以在片段死亡時關閉套接字,因此它們正在關閉。 現在最大的問題是如何使片段保持活力?
您也許應該創建一個幫助程序類,它將代表其他類(例如該片段)打開/關閉套接字,該幫助程序類將不受任何生命周期事件的影響,並且只要Application進程處於運行狀態,就可以保持運行狀態。活
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.