在网络不好时,会发现ping命令一直没有输出内容,如下:
于是就想说,设置个超时时间吧,通过-W可以设置超时时间,这里我设置了超时为1000,我不知道这个1000是个什么单位,是毫秒啊,还是秒?测试发现在我网络不通的时候一直不结束,有可能这个单位是秒,所以超时时间就很长,那我就当它单位是秒吧,设置为10再试一下,发现还是没结果,如下:
那我设置ping的次数为4次,再试一下,如下:
我使用计时器看了一下,大概13秒结束返回结果,搞不懂这个-W设置的超时是个什么原理,跟Windows不一样啊,Windows是每发送一个数据包,如果超时了就会打印一行Request timed out,按照这个逻辑的话上面应该打印4行Request timed out,而且总时间应该是40秒,因为超时是10秒,ping 4次,如果4次都超时则为40秒,这才是正常行为,但是不知道为什么Android上这么奇怪,是因为Android修改过这个Linux底层了吗?带着这个疑问,我打开了我华为云上的Linux主机,发现效果是一样的,如下:
这说明Linux的超时设置就是有问题的!完全没有Windows的那个效果。这里有一篇文章在说为什么Linux ping超时了没有回显消息:blog.csdn.net/wj31932/art…,文章大长了,我也懒得去慢慢看,这个超时参数不管用那我只能不用它了。
那在做Android开发时,如果网络不好ping命令一直不返回也不行啊,怎么结束ping操作啊?,我发现把ping的那个线程中断也不管用。
只输入ping就按回车,可以看到ping命令的所有参数,如下:
如上图,有一个参数为:-w deadline,注,这是小写的w,deadline中文含义为“最后期限”,其作用就是设置整个ping过程的时间,这个功能非常符合我的项目需求,我就是想要设置ping多久,比如我想设置ping30秒,我不管你30秒ping了多少次,我也不管你超时时间是多少,我也不管你网络好不好,反正30秒之后你一定要给我结束,使用如下:
我开计时器了,确实是30秒之后就结束了。
=================================================================
ping命令的参数很多,但是真正在用的时候需要的参数也就一两个,所以掌握这一两个就够了。Windows中的ping用-l和-t参数就够了,Linux中的ping用-s和-w就够了,超时时间一般是不用设置的,用默认的就好了。
- Windows ping简单使用:ping -l 128 -t baidu.com
- l 设置数据包大小为128 bytes
- t 设置一直不停地ping
- 示例如下:
- Linux ping简单使用:ping -s 120 -w 20 baidu.com
- s 设置数据包大小为120 bytes,实际发送数据包时是128bytes,据说是会包含一些头信息什么的需要额外的8bytes,据我的实验,在Android手机中,默认是1秒ping一次,包含超时时间在里面,比如ping 20秒,不管网络好与不好,20秒后,看统计信息会显示发送的刚好是20个包。
- w设置总的ping时间为20秒
- 示例如下:
- 查看ping参数说明
- Windows直接输入ping即可,如下:
- Linux也是直接输入ping,如下:
如果是在Linux电脑(在Android系统上不行),还可以使用man ping查看ping命令的详细使用手册,如下:
这个手册非常详细,从这里可以看到-w和-W的单位为秒,百度里找到的文章大多数说是毫秒,真是一个个都是转载别人的,一个错个个错,所以,尽量找官网文档看,比较准确。
===================================================================================
重点:Android使用此函数来执行ping命令:Runtime.getRuntime().exec(“ping -s 56 -w 30 192.168.1.8”),这个函数执行之后会返回一个Process进程对象,通过读取这个进程的两个输入流即可获取到ping的结果,需要注意的是,系统是有可能交替往这两个流里面写入数据的,所以我们需要开两个线程同时读取这两个流(百度的文章里全是一个线程进行读取的),这里使用到了线程的合并功能(join()函数),通过join()函数可以实现让两个线程先执行完,所以这个示例中完美的展示了join()函数的使用,大家可以深刻体会到该函数的作用是怎样的,准确的说,join()函数并不是线程合并函数,而是一个等待函数,比如在A线程里调用了B线程的join()函数,则A线程就等着不动了,等到B线程执行结束了A线程才继续接着往下执行。
先上一个效果图:
这里设置ping的ip为百度的ip,ping 5秒钟,数据包大小为64字节,点击Start Ping按钮,效果如下:
代码如下:
1、首先开启ViewBinding功能,在build.gradle中配置,如下:
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
buildFeatures {
viewBinding true
}
}
2、界面布局:
<LinearLayout xmlns:android="schemas.android.com/apk/res/and…"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="schemas.android.com/tools"
android:orientation="vertical"
android:paddingTop="16dp"
android:paddingBottom="16dp"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16dp"
android:text="Ping IP: "
tools:ignore="SpUsage" />
<EditText
android:id="@+id/etIp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16dp"
android:inputType="phone"
android:text="39.156.69.79"
tools:ignore="Autofill,SpUsage" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16dp"
android:text="Ping多久(单位为秒): "
tools:ignore="SpUsage" />
<EditText
android:id="@+id/etTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16dp"
android:inputType="number"
android:text="5"
tools:ignore="Autofill,SpUsage" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16dp"
android:text="数据包大小(单位为字节): "
tools:ignore="SpUsage" />
<EditText
android:id="@+id/etSize"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16dp"
android:inputType="number"
android:text="64"
tools:ignore="Autofill,SpUsage" />
<Button
android:gravity="center"
android:id="@+id/btnStartPing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Ping"
android:textAllCaps="false"
android:textColor="@android:color/white"
android:textSize="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
tools:ignore="SpUsage" />
3、代码:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var mAdapter: MyAdapter
private val lines = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
mAdapter = MyAdapter()
binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = mAdapter
binding.btnStartPing.setOnClickListener {
thread { ping() }
}
}
private fun ping() {
val ip = binding.etIp.text.trim().toString()
val sizeStr = binding.etSize.text.trim().toString()
val time = binding.etTime.text.trim().toString()
if (ip.isBlank()) {
runOnUiThread { Toast.makeText(this, "请输入IP", Toast.LENGTH_SHORT).show() }
return
}
if (sizeStr.isBlank()) {
runOnUiThread { Toast.makeText(this, "请输入数据包大小", Toast.LENGTH_SHORT).show() }
return
}
if (time.isBlank()) {
runOnUiThread { Toast.makeText(this, "请输入要ping多久", Toast.LENGTH_SHORT).show() }
return
}
if (!isValidIpAddress(ip)) {
runOnUiThread {
AlertDialog.Builder(this)
.setTitle("提示")
.setMessage("您输入的IP地址格式有误,请修正!")
.show()
}
return
}
runOnUiThread {
binding.btnStartPing.isEnabled = false
lines.clear()
addData("Ping开始")
}
val size = sizeStr.toInt() - 8
val command = "ping -s time $ip"
// 注:正常ping数据和错误ping数据可能会交替输出,所以需要开两个线程同时读取
val process = Runtime.getRuntime().exec(command)
val inputStreamThread = readData(process.inputStream) // 读取正常ping数据
val errorStreamThread = readData(process.errorStream) // 读取错误ping数据
// 等待两个读取线程结束
inputStreamThread.join()
errorStreamThread.join()
runOnUiThread {
addData("Ping结束")
binding.btnStartPing.isEnabled = true
}
}
private fun readData(inputStream: InputStream?) = thread {
try {
BufferedReader(InputStreamReader(inputStream)).use { reader ->
var line: String?
while (reader.readLine().also { line = it } != null) {
val lineTemp = line!!
runOnUiThread { addData(lineTemp) } // 这里切换到了UI线程,子线程继续执行时可以已经把line对象又赋值为null了,所以使用了lineTemp来预防值被重新赋值
}
}
} catch (e: Exception) {
runOnUiThread { addData("出现异常:{e.message}") }
}
}
private fun addData(data: String) {
lines.add(data)
refreshListView()
}
最后送福利了,现在关注我并且加入群聊可以获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,欢迎加群探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿
点击GitHub领取