这两天做了一个项目是app通过socket连接自动炒菜机,给炒菜机发指令,炒菜机接收到指令会执行相应的操作。(程序虽然做的差不多了,然而我连炒菜机长什么样都没见过)
其实作为一个会做饭的程序猿,我坚信还是自己动手做的饭菜比较好吃,毕竟做饭还是很有趣的。
闲话不多说,因为是通过socket去连接炒菜机的,并且要求每两秒要给炒菜机发送一个指令,点击按钮的话也要发送相应的指令。
因为是要保证全局只能有一个连接,而且我们还需要在不同的Activity中发指令,因此肯定不能在需要发指令的界面中都去连接socket,这样一来不好管理,性能也不好,重复代码也会比较多,所以想了一下还是把socket放到service中比较好,发指令功能都放在service中即可。
记得要先给网络权限
复制代码
1
2
3<uses-permission android:name="android.permission.INTERNET" />
下面我们来看看Service中的代码,其中有些细节是需要注意的
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209package com.yzq.socketdemo.service; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.util.Log; import android.widget.TabHost; import android.widget.Toast; import com.yzq.socketdemo.common.Constants; import com.yzq.socketdemo.common.EventMsg; import org.greenrobot.eventbus.EventBus; import java.io.IOException; import java.io.OutputStream; import java.net.ConnectException; import java.net.InetSocketAddress; import java.net.NoRouteToHostException; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.Timer; import java.util.TimerTask; /** * Created by yzq on 2017/9/26. * <p> * socket连接服务 */ public class SocketService extends Service { /*socket*/ private Socket socket; /*连接线程*/ private Thread connectThread; private Timer timer = new Timer(); private OutputStream outputStream; private SocketBinder sockerBinder = new SocketBinder(); private String ip; private String port; private TimerTask task; /*默认重连*/ private boolean isReConnect = true; private Handler handler = new Handler(Looper.getMainLooper()); @Override public IBinder onBind(Intent intent) { return sockerBinder; } public class SocketBinder extends Binder { /*返回SocketService 在需要的地方可以通过ServiceConnection获取到SocketService */ public SocketService getService() { return SocketService.this; } } @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { /*拿到传递过来的ip和端口号*/ ip = intent.getStringExtra(Constants.INTENT_IP); port = intent.getStringExtra(Constants.INTENT_PORT); /*初始化socket*/ initSocket(); return super.onStartCommand(intent, flags, startId); } /*初始化socket*/ private void initSocket() { if (socket == null && connectThread == null) { connectThread = new Thread(new Runnable() { @Override public void run() { socket = new Socket(); try { /*超时时间为2秒*/ socket.connect(new InetSocketAddress(ip, Integer.valueOf(port)), 2000); /*连接成功的话 发送心跳包*/ if (socket.isConnected()) { /*因为Toast是要运行在主线程的 这里是子线程 所以需要到主线程哪里去显示toast*/ toastMsg("socket已连接"); /*发送连接成功的消息*/ EventMsg msg = new EventMsg(); msg.setTag(Constants.CONNET_SUCCESS); EventBus.getDefault().post(msg); /*发送心跳数据*/ sendBeatData(); } } catch (IOException e) { e.printStackTrace(); if (e instanceof SocketTimeoutException) { toastMsg("连接超时,正在重连"); releaseSocket(); } else if (e instanceof NoRouteToHostException) { toastMsg("该地址不存在,请检查"); stopSelf(); } else if (e instanceof ConnectException) { toastMsg("连接异常或被拒绝,请检查"); stopSelf(); } } } }); /*启动连接线程*/ connectThread.start(); } } /*因为Toast是要运行在主线程的 所以需要到主线程哪里去显示toast*/ private void toastMsg(final String msg) { handler.post(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show(); } }); } /*发送数据*/ public void sendOrder(final String order) { if (socket != null && socket.isConnected()) { /*发送指令*/ new Thread(new Runnable() { @Override public void run() { try { outputStream = socket.getOutputStream(); if (outputStream != null) { outputStream.write((order).getBytes("gbk")); outputStream.flush(); } } catch (IOException e) { e.printStackTrace(); } } }).start(); } else { toastMsg("socket连接错误,请重试"); } } /*定时发送数据*/ private void sendBeatData() { if (timer == null) { timer = new Timer(); } if (task == null) { task = new TimerTask() { @Override public void run() { try { outputStream = socket.getOutputStream(); /*这里的编码方式根据你的需求去改*/ outputStream.write(("test").getBytes("gbk")); outputStream.flush(); } catch (Exception e) { /*发送失败说明socket断开了或者出现了其他错误*/ toastMsg("连接断开,正在重连"); /*重连*/ releaseSocket(); e.printStackTrace(); } } }; } timer.schedule(task, 0, 2000); } /*释放资源*/ private void releaseSocket() { if (task != null) { task.cancel(); task = null; } if (timer != null) { timer.purge(); timer.cancel(); timer = null; } if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } outputStream = null; } if (socket != null) { try { socket.close(); } catch (IOException e) { } socket = null; } if (connectThread != null) { connectThread = null; } /*重新初始化socket*/ if (isReConnect) { initSocket(); } } @Override public void onDestroy() { super.onDestroy(); Log.i("SocketService", "onDestroy"); isReConnect = false; releaseSocket(); } }
好了,连接的service我们基本就做好了,先来看看效果,调试工具使用的是一个网络调试助手,免去我们写服务端的代码。
可以看到,断线重连,连接成功自动发送数据,连接成功发消息这些都有了,实际上数据发送失败重连也是有的,不过模拟器上间隔时间很长,不知道怎么回事,真机没有问题。
解决了service下面就是Activity于service通信的问题了。这个就简单了,我们在service中提供了一个binder,我们可以通过binder来拿到service,然后调service的sendOrder()即可
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62package com.yzq.socketdemo.activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.support.v7.app.AppCompatActivity; import android.widget.Button; import android.widget.EditText; import com.yzq.socketdemo.R; import com.yzq.socketdemo.service.SocketService; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; /** * Created by yzq on 2017/9/26. * <p> * mainActivity */ public class MainActivity extends AppCompatActivity { @BindView(R.id.contentEt) EditText contentEt; @BindView(R.id.sendBtn) Button sendBtn; private ServiceConnection sc; public SocketService socketService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindSocketService(); ButterKnife.bind(this); } private void bindSocketService() { /*通过binder拿到service*/ sc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { SocketService.SocketBinder binder = (SocketService.SocketBinder) iBinder; socketService = binder.getService(); } @Override public void onServiceDisconnected(ComponentName componentName) { } }; Intent intent = new Intent(getApplicationContext(), SocketService.class); bindService(intent, sc, BIND_AUTO_CREATE); } @OnClick(R.id.sendBtn) public void onViewClicked() { String data = contentEt.getText().toString().trim(); socketService.sendOrder(data); } @Override protected void onDestroy() { super.onDestroy(); unbindService(sc); Intent intent = new Intent(getApplicationContext(), SocketService.class); stopService(intent); } }
ok,大功告成
为了方便没有积分的大佬们下载,大家可以去下面的Github上下载
https://github.com/yuzhiqiang1993/SocketDemo
另外一种方式是采用Netty+Kotlin+RxJava方式实现的。使用起来更加简单,详情请看
Android Netty版本
如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!
最后
以上就是大方洋葱最近收集整理的关于Android Socket连接(模拟心跳包,断线重连,发送数据等)的全部内容,更多相关Android内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复