zoukankan      html  css  js  c++  java
  • HOME键与Notification配合使用的bug重现【原创】

    • 前言

      最近几天刚刚跳槽完毕。在家歇了快一个月了。重现开始上班后,还真有点不适应。上班实在是太辛苦了, 还是坐地铁13号线。但是变成反向乘坐了。 昨天才拿到的电脑,连代码还没看呢,就接到了新的任务:解决一个bug。 好了,废话不多说,先描述一下bug情况。

    • BUG描述     
    程序中在某个地方加入一个Notification。把程序全部退出(是finish的那种),用notification来启动程序,进行操作。随便进入了一个页面A,此时点击“home”,然后再长按“HOME”,回到该程序。 结果不能返回到之前的页面A了。
    简短分析: 刚开始我认为是程序的问题,加入很多log日志,也没能解决该问题。 最后,我怀疑是android系统的问题。下面我就写了一个简单的代码,来重现该问题。
    • DEMO实例
    先来个图,看看这个DEMO的演示情况:
      
    结合图像,展示功能列表:
      1. 该程序共有3个Activity。分别从title中进行区别:test,test2, test3.
      2. 消息栏中有个图片,可以使用该图标来启动程序。程序到达test2.
      3. 正常启动程序,到达的activity为test。
      4. test和test2的 Activity中,b按钮的功能是掉转到test3. 
      5. 在test的Activity中,a按钮的功能是生成notification。
    代码:
      
    public class TestActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    }

    public void a(View view) {
    //启动掉转到Test2
    Intent intent = new Intent(TestActivity.this, Test2Activity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP
    | Intent.FLAG_ACTIVITY_NEW_TASK);
    PendingIntent contentIntent = PendingIntent.getActivity(TestActivity.this, 0,
    intent,PendingIntent.FLAG_UPDATE_CURRENT);

    Notification notification = new Notification(R.drawable.icon, "ticker", System.currentTimeMillis());
    notification.setLatestEventInfo(TestActivity.this, "title", "text", contentIntent);

    NotificationManager notificationMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    notificationMgr.notify(1000, notification);
    }

    public void b(View view) {
    //掉转到Test3
    Intent intent = new Intent(TestActivity.this, Test3Activity.class);
    startActivity(intent);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
    if(keyCode == KeyEvent.KEYCODE_BACK) System.exit(0);
    return super.onKeyDown(keyCode, event);
    }
    }
    能靠notification启动的Activity:
    public class Test2Activity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    }

    public void a(View view) {}

    public void b(View view) {
    //掉转到Test3
    Intent intent = new Intent(Test2Activity.this, Test3Activity.class);
    startActivity(intent);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
    if(keyCode == KeyEvent.KEYCODE_BACK) System.exit(0);
    return super.onKeyDown(keyCode, event);
    }
    }
    Test3就没有什么代码可以展示了,就是一个表达的区别页面。
    • 重现操作流程
      1. 启动程序。页面为Test Activity。
      2. 点击a按钮,启动一个notification待用。
      3. 点击“back”,关闭该程序。
      
      4. 点击notification,启动程序到达Activity Test2.
      5. 简介b按钮,跳转页面到TEST3.
      6. 点击“HOME”键,回到主界面。
      7. 长按“HOME”,尝试回到之前的程序。bug发生(页面没有回到TEST3,而是到达页面TEST2.更让人奇特的是Test3执行力OnDestory方法)

    • 深入分析
      1。 首先要理解activity的launchMode的值。参考文章:http://marshal.easymorse.com/archives/2950
        我的解析: singleTask是用在一个应用中要共享一个activity。他们的taskId与之前是一样的。当被标记为singleTask的应用已经存在时,就直接跳转到该ACT,并清空上面的ACT。
              singleInstance是用在多个应用共享一个ACT。也就是该ACT的taskID和之前的是不一样的。并且该ACT的栈中,有且仅有那个一个ACT。
           2。 singleTask是否启动新的taskID。
        singleTask并不会每次都新启动一个task。如果已经存在一个task与新活动亲和度(taskAffinity)一样,该活动将启动到该task。如果不是,才启动一个新task。

    同一个application里面,每个activity的taskAffinity默认都是一样的。也就是说楼主代码里的ActivityTwo和ActivityMain的taskAffinity是一样的,假设这个taskAffinity是"com.test"。ActivityMain启动时,新建了一个task,这个task的taskAffinity就是"com.test"。ActivityTwo具有singleTask属性,启动时,会先寻找是否有相同taskAffinity的task存在,没有的话,才会启动一个新的task,但是这时发现已经有一个task存在了,它的taskAffinity也是"com.test",就不会启动新的task了,而是把ActivityTwo放入这个task的栈中,这时候task id是一样的。

    所以使用singleTask时,要想新建一个task,就得保证taskAffinity值不同,比如设置ActivityTwo的taskAffinity="com.test2",这时再运行楼主的代码,就会发现task id不一样了。

    以上是两个activity在同一个application中的情况。如果在某个application调用其他application里声明的singleTask模式的Activity呢。taskAffinity的值默认是包名,两个application一般包名都不一样,如果taskAffinity都是默认的话,它会重新创建一个Task,然后将该Activity实例化并压入task的栈。


      3。 理解了上面的内容。下面就来点关键的。
        在开发者文档中有这样写的内容:
        
      当Intent中包含有Flag_ACTIVITY_NEW_TASK时,
        当有了上面那个属性,只有affinity的值与之前的affinity的值不一致,才考虑去new一个新的之。


    • 解决办法:
    在其他的程序中加入:intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT|Intent.FLAG_ACTIVITY_NEW_T‌​ASK);  
    //如果activity在task存在,拿到最顶端,不会启动新的Activity
      intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
      //如果activity在task存在,将Activity之上的所有Activity结束掉
      intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
      //默认的跳转类型,将Activity放到一个新的Task中
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      //如果Activity已经运行到了Task,再次跳转不会在运行这个Activity
      intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

      
  • 相关阅读:
    ribbon--eureka注册中心消费者
    eureka注册中心
    spring cloud简介
    Quartz定时任务
    ThreadLocal
    分布式单点登录SSO
    dubbo框架
    注册中心
    centos安装zookeeper及搭建集群
    7.19 基础数据结构选讲
  • 原文地址:https://www.cnblogs.com/xitang/p/2196724.html
Copyright © 2011-2022 走看看