android解决dialog和popupwindow的BadTokenException:Unable to add window
案例:今天修改公司一个项目BUG,有个效果是进APP首页,APP弹出弹窗广告,广告内容是实时从服务器获取的,(一般APP不推荐这么做,很多都是每次次进入获取下次要显示的广告),因为是实时获取,可能在这过程中网络不好,用户误点到某个模块进行跳转,那么已经开始show的dialog或者popupwindow就会出现BadTokenException.
异常信息:android.view.WindowManager&BadTokenException:Unable to add window这个错误,说reference on a null performance
正确异常出现原因:
1.由于dialog和popupwindow都要依附于activity,所以dialog和popupwindow在show之前,activity的实例都要存在,不能被销毁.
2.因为是开启线程进行联网耗时操作,耗时操作完毕再showdialog和popupwindow.用户在实际操作的时候,很有可能没有等到dialog和popupwindow在show之前就进行页面跳转,销毁上一个依赖的activity实例.导致在showdialog的时候报错.
因此解决思路就很明朗了.那就是:在activityfinish进行跳转的时候,在onDestroy里面去killprogess(); 即杀掉进程下所有的线程.
解决方法:
1.看了网上杀线程的方法.很多都在说android.os.Process.killProcess(android.os.Process.myPid());这个方法是杀掉当前所有的进程和线程.直接上代码,在跳转的时候直接全杀,在onDestroy的时候去实现跳转.
因为笔者,只想到了这么做,无论是跳转前和跳转后去杀线程,都会导致整个APP程序关闭.只有这样才能实现杀了在跳转的效果.
但是,这样做非常不好.第一.用户体验很差,跳转的时候会出现一瞬间黑屏.第二,跳转完成之后,你还得考虑之前被误杀的在后台跑的线程重启的问题,如果是需要在后台挂长连接的APP,这么做会很影响性能,且效果很低,所以,相当不推荐这个做法.简直是误导人.
protected void killprogess() {android.os.Process.killProcess(android.os.Process.myPid());}
@Overrideprotected void onDestroy() {super.onDestroy();startActivity(new Intent(MainActivity.this, SecondActivity.class));finish();}2 .第二种方法,笔者经过综合考虑,还是觉得比较合适.推荐用这种方法.主要还是通过主线程的handler去remove掉当前跑的runnable.这样做不会有连带的影响,直接上代码,很简单,相信大家都能看懂.不解释了.
/** * 进主界面有个广告弹窗,弹窗内容需要联网获取. * --------------这边可以设计成,首次登陆去获取下次要弹出的广告,一般的APP都推荐这么做, 不过我们公司有点逆天,反正需求是要每次都实时去获取------ * 在跳转的时候,关闭activity下面的线程,避免出现异常 * 有两种实现方式,推荐用handler.removeCallbacks关闭执行的线程 * @author max * */public class MainActivity extends Activity {private static final String TAG = "com.max.threadshutdowndemo";private Handler mHandler = new Handler();Runnable runnable = new Runnable() {@Overridepublic void run() {//显示广告弹窗的dialogshowAD();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//我用postdelayed来模拟联网获取广告的告示操作mHandler.postDelayed(runnable, 3000);Button button = (Button) findViewById(R.id.first_button);//如果用户在中途误点了某个模块,发生了页面跳转.那么久先啥子当前activity的线程,在跳转,避免出现异常崩溃button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {killprogess();}});}/** * 第一种方式,杀死当前的进程以及下面的线程 */protected void killprogess() {startActivity(new Intent(MainActivity.this, SecondActivity.class));finish();}/** * 显示一个dialog或者popupwindow * 弹窗显示的内容有可能是需要用thread去联网获取的耗时操作 * 这里为了方便用postDelayed来表示耗时操作 */protected void showAD() {//这边为了保险起见,加入了try catch捕获异常,避免崩溃,不过已经remove了子线程,这样做是多余的.有兴趣的童鞋可以尝试哈try {Dialog dialog = new AlertDialog.Builder(MainActivity.this).create();dialog.show();dialog.setCanceledOnTouchOutside(false);Window window = dialog.getWindow();window.setContentView(R.layout.dialog_main);} catch (Exception e) {Log.e(TAG, e.getMessage());e.printStackTrace();}}/** * 在ondestroy中执行关闭子线程的操作 */@Overrideprotected void onDestroy() {super.onDestroy();//这个方法极力不推荐.//android.os.Process.killProcess(android.os.Process.myPid());//关键的操作,当前页面finish的时候,关闭下面还在跑得线程,防止出现异常.//也可以用try catch捕获异常防止crash掉mHandler.removeCallbacks(runnable);}}
最后是源码,效果我就不截图了,这边有兴趣的童鞋,可以下源码自己泡一下.源码下载请点击这里.
更多相关文章
- Android(安卓)NDK——必知必会之Native线程操作及线程同步全面详
- Android(安卓)ANR分析
- android并发网络请求的处理
- android 四大控件之ContentProvider
- 线程方法Android:异步调用详解
- 金九银十中,看看这31道Android面试题
- Google 发布 Android(安卓)性能优化典范
- Android(安卓)Handler 作用以及使用
- 移动端启动速度