最近在做一个项目,项目其中一个需求是打开app自动连接tcp服务端,连接失败后自动尝试8次。尝试成功继续,不成功提示服务器未响应。
我想到了用多线程实现,其中一个用于显示尝试状态,另一个线程循环尝试socket=new Socket(),达到以下效果:
代码如下:
连接tcp用tcp_conn();
private void tcp_conn(){
socket=null;
if(socket!=null) {
if (socket.isConnected()) {
system.out.println("已连接");
}
}
else {
socket = new Socket();
SocketAddress socAddress = new InetSocketAddress(netAdress, port);
try {
socket.connect(socAddress, 1000);
} catch (IOException e) {
e.printStackTrace();
}
}
}
第一次连接失败后调用ReConnect()
//此线程用于重连尝试,cnt[0]为尝试次数,rctime=8表示最大重连次数
private void ReConnect() {
new Thread(new Runnable() {
@Override
public void run() {
//因为第一次连接使得socket!=null,所有此处没报错
//如果socket没有连接,循环尝试,直到连接上或尝试次数达到最大
while (!socket.isConnected() && cnt[0] < rctime) {
//调用tcp_conn();
tcp_conn();
try {
//线程延时5秒,等待socket反应,socket连接比较耗时
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//连接成功则跳出循环,cnt[0]=rctime+1为什么要等于它,往下看
if (socket.isConnected()) {
tv_setText("连接成功");
cnt[0] = rctime + 1;//不再重连
break;
} else {
//连接失败,继续尝试
cnt[0] += 1;
System.out.println("连接失败");
}
}
}
}).start();
}
另一个线程用于打印尝试状态,使用handler定时器,每隔1s检查连接状态,显示到屏幕TextView tv_tip中
//当未连接或尝试次数小于8:tv_tip.setText("连接失败,尝试重新连接:" + cnt[0] + "/" + rctime + "\n");并继续定时
//当尝试次数==8:tv_tip.setText("服务器未响应,请确定服务器已打开");并使cnt[0]=1,继续定时,以便下次进入时关闭定时器
//尝试次数>8:关闭定时器,上面的连接成功后也使cnt[0]>8,以关闭定时器
Handler handler = new Handler();
final Runnable runnable = new Runnable() {
@Override
public void run() {
//打开一个新的线程
new Thread(new Runnable() {
@Override
public void run() {
if (!socket.isConnected() && cnt[0] < rctime) {
tv_tip.setText("连接失败,尝试重新连接:" + cnt[0] + "/" + rctime + "\n");
handler.postDelayed(runnable, 1000);//每1s执行一次runnable.
} else if (cnt[0] == rctime) {
cnt[0] += 1;
tv_tip.setText("服务器未响应,请确定服务器已打开");
handler.postDelayed(runnable, 1000);//每两秒执行一次runnable.
} else {
handler.removeCallbacks(runnable);
}
}
}).start();
}
};
将所以步骤封装到SystemInit()
private void SystemInit() {
new Thread(new Runnable() {
@Override
public void run() {
cnt[0] = 0;
tcp_conn();
handler.postDelayed(runnable, 1000);//每两秒执行一次runnable.
if (socket.isConnected()) {
{
Tip("连接成功", Toast.LENGTH_LONG);
}
} else {
ReConnect();
}
}
}).start();
}
然后在onCreate中调用:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_tip = findViewById(R.id.tv_tip);
SystemInit();
}
其他布局代码就不贴出来了。
这只是实现了这一步功能,8次之后如果还没连上难道就不行了吗?还要重新打开app才能连?当然可以增加一个刷新按钮,在按钮点击事件中再次调用SystemInit();
所以未连接时要尝试8次,连接成功后跳出ReConnect和关闭定时器。8次后仍未连接放弃尝试,关闭定时器。
验证中,我先在网络调试助手中关闭tcp服务端,打开app,app打印出连接失败,尝试重新连接 2/8,此时我打开tcp服务端,等一会后显示连接成功。如果尝试8次之后还没成功,则显示出“服务器未响应,请确定服务器已打开”。
将所有内容封装成类,做了一些改动,UserTcp.java
package com.example.menu_tst;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.lang.Runnable;
import static android.widget.Toast.LENGTH_LONG;
public class UserTcp extends Activity {
public Socket socket = null;
public final TextView tv_tip;
final int[] cnt = {0};
public final int rctime = 8;
private Context context;
public UserTcp(TextView tv_tip,Context context){
super();
this.tv_tip=tv_tip;
this.context=context;
}
public void Tip(String str, int showTime) {
Looper.prepare();
Toast toast = Toast.makeText(context, str, showTime);
toast.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0); //设置显示位置
TextView v = (TextView) toast.getView().findViewById(android.R.id.message);
toast.show();
Looper.loop();
}
public void TcpInit() {
new Thread(new Runnable() {
@Override
public void run() {
cnt[0] = 0;
tcp_conn(context);
handler.postDelayed(runnable, 1000);//每两秒执行一次runnable.
if (socket.isConnected()) {
{
Tip("连接成功", LENGTH_LONG);
}
} else {
ReConnect(context);
}
}
}).start();
}
Handler handler = new Handler();
final Runnable runnable = new Runnable() {
@Override
public void run() {
new Thread(new Runnable() {
@Override
public void run() {
if (!socket.isConnected() && cnt[0] < rctime) {
System.out.println("连接失败,尝试重新连接:" + cnt[0] + "/" + rctime);
//Tip("连接失败,尝试重新连接:" + cnt[0] + "/" + rctime, Toast.LENGTH_LONG);
// tv_tip.setText("连接失败,尝试重新连接:" + cnt[0] + "/" + rctime + "\n");
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_tip.setText("连接失败,尝试重新连接:" + cnt[0] + "/" + rctime + "\n");
}
});
handler.postDelayed(runnable, 1000);//每两秒执行一次runnable.
} else if (cnt[0] == rctime) {
cnt[0] += 1;
System.out.println("服务器未响应,请确定服务器已打开");
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_tip.setText("服务器未响应,请确定服务器已打开");
}
});
handler.postDelayed(runnable, 1000);//每两秒执行一次runnable.
} else {
//Tip("timer",Toast.LENGTH_SHORT);
handler.removeCallbacks(runnable);
}
}
}).start();
}
};
public void ReConnect(Context context) {
new Thread(new Runnable() {
@Override
public void run() {
while (!socket.isConnected() && cnt[0] < rctime) {
tcp_conn(context);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (socket.isConnected()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_tip.setText("连接成功");
}
});
cnt[0] = rctime + 1;//不再重连
break;
} else {
cnt[0] += 1;
System.out.println("连接失败");
}
}
}
}).start();
}
public void tcp_conn(Context context) {
socket = null;
if (socket != null) {
if (socket.isConnected()) {
Tip("已连接!!!", Toast.LENGTH_SHORT);
}
} else {
socket = new Socket();
int port = 6000;
String netAdress = "192.168.1.7";
SocketAddress socAddress = new InetSocketAddress(netAdress, port);
try {
socket.connect(socAddress, 1000);
} catch (IOException e) {
e.printStackTrace();
}
if(socket.isConnected()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_tip.setText("连接成功");
}
});
}
}
}
public static View getRootView(Context context) {
Activity activity = (Activity) context;
return activity.getWindow().getDecorView().findViewById(android.R.id.content);
}
}
使用时只需调用TcpInit();tv_tip是TextView控件,需自己定义