近期在测试蓝牙通信设备的app,需要了解TCP连接。简单了解一下基本概念。
什么是TCP连接?
为实现数据的可靠传输,TCP要在应用进程间建立传输连接。它是在两个传输用户之间建立一种逻辑联系,使得通信双方都确认对方为自己的传输连接端点。
建立连接
建立连接前,服务器端首先被动打开其熟知的端口,对端口进行侦听。当客户端要和服务器端建立连接时,发起一个主动打开端口的请求(该端口一般为临时端口);然后进入三次握手的过程。
三次握手的过程:
① A 的 TCP 向 B 发出连接请求报文段。
② B 的 TCP 收到连接请求报文段后,如同意,则发回确认。
③ A 收到此报文段后,向 B 给出确认。
A 的 TCP 通知上层应用进程,连接已经建立。
当运行服务器进程的主机 B 的 TCP 收到主机 A 的确认后,也通知其上层应用进程,连接已经建立。
释放连接
数据传输结束后,通信双方都可以释放连接。
四次分手的过程:
过程结束时,从 A 到 B 的连接就释放了,连接处于半关闭状态。
相当于 A 向 B 说:“我已经没有数据要发送了。但你如果还发送数据,我仍接收。”
整个过程结束后,至此,整个连接已全部释放。
TCP的半关闭
TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。这就是所谓的半关闭。
好了,基础简单介绍到这里。感兴趣的同学可以继续上网搜索。在测试基本功能过程中,发现设备断开连接,或者退出请求页面时,时不时地出现崩溃。它是长这个样子的:
Stack trace:
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@3c57f7c7 is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:592)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:285)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
at android.app.Dialog.show(Dialog.java:298)
......
经过多次再现并与开发同学沟通,可能原因在之前的线程池打开后,页面返回而没有关闭线程池造成的。笔者在网上搜集这方面的解决办法,摘录如下,供后来者参考:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
android.view.WindowManager$BadTokenException的4种情形:
- 1.Unable to add window --token null is not valid; is your activity running
- 2.Unable to add window --token null is not for an application
- Unable to add window -- token android.os.BinderProxy@XXX is not valid;
is your activity running - 4.Unable to add window -- token android.app.LocalActivityManager
$LocalActivityRecord @xxx is not valid; is your activity running
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
情形1.android.view.WindowManager$BadTokenException: Unable to add window --token null is not valid; is your activity running?异常处理。
该异常多见于Popup Window组件的使用中抛出。
原因:错误在PopupWindow.showAtLocation(findViewById(R.id.main), Gravity.BOTTOM,0,0); popwindow必须依附于某一个view,而在oncreate中view还没有加载完毕,必须要等activity的生命周期函数全部执行完毕,你需要依附的view加载好后才可以执行popwindow。
解决办法:showAtLocation()函数可以这样改:
总结: PopupWindow必须在某个事件中显示或者是开启一个新线程去调用,不能直接在onCreate方法中显示一个Popupwindow,否则永远会有以上的错误。
参考:
http://*.com/questions/4187673/problems-creating-a-popup-window-in-android-activity
情形2.android.view.WindowManager$BadTokenException: Unable to add window --token null is not for an application ?异常处理。
该异常多见于AlertDialog组件的使用中抛出。
原因:导致报这个错是在于new AlertDialog.Builder(mcontext),虽然这里的参数是AlertDialog.Builder(Context context),但我们不能使用getApplicationContext()获得的Context,而必须使用Activity,因为只有一个Activity才能添加一个窗体。
解决方法:将new AlertDialog.Builder(Context context)中的参数用Activity.this(Activity是你的Activity的名称)或者getActivity()来填充就可以正确的创建一个Dialog了。
参考:
http://*.com/questions/20779377/android-custom-dialog-gives-an-error
情形3.android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@XXX is not valid; is your activity running?异常处理。
原因:从错误信息我们也可以明白其原因,此问题根本原因就是由于将要弹出的dialog所要依附的View已经不存在导致的。当界面销毁后再弹出来;或者界面跳转时我们的view发生改变,dialog依附的context发生变化或者界面未运行了。
解决方法:界面已经销毁引起的错误就只能判断界面是否存在然后再弹出了。
https://github.com/VKCOM/vk-android-sdk/issues/21
情形4.android.view.WindowManager LocalActivityRecord @xxx is not valid; is your activity running? 异常处理。
原因:因为new对话框的时候,参数context 指定成了this,即指向当前子Activity的context。但子Activity是动态创建的,不能保证一直存在。其父Activity的context是稳定存在的,所以有下面的解决办法。
解决方法:将context替换为getParent()即可。 注意:要创建dialog对象,上下文环境必须是activity,同时若ActivityGroup中嵌套ActivityGroup,嵌套多少就该使用多少个getParent()。
参考:
http://*.com/questions/9914195/webview-in-activity-group-crashing-on-dialogs