Android坑(1)

Android

  1. Activity与Fragment的生命周期。
Android坑(1)_第1张图片
activity_fragment_cyclelife
  • A启动B:A-onPause->B-onCreate,B-onResume->A-onStop
  • B返回A:B-onPause->A-onRestart,A-onResume->B-onStop

2.Acitivty的四中启动模式与特点。

  • standard
    A启动(startActivty)B,则B在A所在的任务栈中
    当A是非Activity类型的context(如ApplicationContext)时,因为其没有任务栈,会crash。
    解决方法:为B指定FLAG_ACTIVITY_NEW_TASK标记位
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),则启动时会为B创建一个新的任务栈,效果等价于singleTask
  • singeTop
    在栈顶则复用,回调onNewIntent,onCreate,onStart不会调用
  • singleTask
    在栈内则复用,回调onNewIntent,注意有clearTop效果
    eg:
    前台task={A,B},SingleTask模式后台task={C,D},(后台task的Activity处于暂停状态)
    若B启动D => 前台task={A,B,C,D}
    若B启动C => 前台task={A,B,C} (clearTop)
  • singleInstance
    单独位于栈中

andoid:launchMode="XXX"和intent.addFlags(Intent.XXX)的区别:

  1. 后者优先级高于前者,两者同时存在时后者为准
  2. 前者无法为Activity设置FLAG_ACTIVITY_CLEAR_TOP标识,后者无法为Activity指定singleInstance

Flags:(格式:FLAG_ACTIVITY_XXX_XXX)

  • FLAG_ACTIVITY_NEW_TASK
    即singleTask
  • FLAG_ACTIVITY_SINGLE_TOP
    即singleTop
  • FLAG_ACTIVITY_CLEAR_TOP
    即clearTop,一般配合FLAG_ACTIVITY_NEW_TASK使用;singleTask默认具有此标识位的功能
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    具有该标记的Activity不会出现在历史Activity的列表中。
    等同于android:excludeFromRecents="true",应用:进程保活

1.直接调用Context类的startActivity方法:这种方式启动的Activity没有Activity栈,因此不能以standard方式启动,必须加上FLAG_ACTIVITY_NEW_TASK这个Flag。
2.调用被Activity类重载过的startActivity方法,通常在我们的Activity中直接调用这个方法就是这种形式;


3.Activity缓存方法。

  • Activity意外被销毁(如屏幕旋转)时会调用onSaveInstanceState保存当前Activity状态(调用时间:在onStop之前,在onPause前或后),当Activity重新创建后,onSaveInstanceState保存的Bundle数据作为参数同时传给onRestoreInstance(调用时间:在onStart之后,onResume之前)和onCreate来恢复之前保存的数据

  • 注:按home键退出不会销毁Activity(停留在onStop)但也会调用onSaveInstance,所以恢复时不会调用onCreate和onRestoreInstance,而是从onReStart开始

  • 设置 android:configChanges="orientation"可解决Activity旋转销毁的问题,这样设置后旋转时会调用onConfigurationChanged


  1. Service的生命周期,两种启动方法,有什么区别。
Android坑(1)_第2张图片
service_cyclelife
  • startService只是启动Service,启动它的组件(如Activity)和Service并没有关联,只有当Service调用stopSelf或者其他组件调用stopService服务才会终止;多次启动同一个Service只会调用一次onCreate(),第二次启动直接调用onStartCommand()
  • bindService方法启动Service,其他组件可以通过回调获取Service的代理对象和Service交互,而这两方也进行了绑定,当启动方销毁时,Service也会自动进行unBind操作,当发现所有绑定都进行了unBind时才会销毁Service
    -startService();bindService;分别调用:startservice->oncreate()->stopservice()->停止service但没有销毁->unbind()->调用unbind()方法调用destory()方法
  • IntentService:启动IntentService的方式和启动传统Service一样,有独立的worker线程,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent(Intent intent)回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。

Android 5.0(API=21) 禁止使用隐式Intent来启动Service.

private void validateServiceIntent(Intent service) { 
if (service.getComponent() == null && service.getPackage() == null) { 
    if (getApplicationInfo().targetSdkVersion >=Build.VERSION_CODES.LOLLIPOP) { 
     IllegalArgumentException ex = new IllegalArgumentException( "Service Intent must be explicit: " + service); 
        throw ex;
 } else {
       Log.w(TAG, "Implicit intents with startService are not safe: " + service + " " + Debug.getCallers(2, 3)); 
    }
   }
 }
Android坑(1)_第3张图片
大概是为了安全才取消隐式启动

5.怎么保证service不被杀死。

  • 白色保活
    白色保活手段非常简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。如下方的LBE和QQ音乐这样:


    Android坑(1)_第4张图片
  • 灰色保活
    这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。那么如何利用系统的漏洞呢,大致的实现思路和代码如下:

思路一:API < 18,调用startForeground(ID, new Notification()),发送空的Notification ,图标则不会显示;
思路二:API >= 18,在需要提优先级的service A启动一个InnerService,两个服务同时startForeground,且绑定同样的 ID。Stop 掉InnerService ,这样通知栏图标即被移除;
注:进程优先级降序排列:前台进程(Foreground process)、可见进程(Visible process)、服务进程(Service process)、后台进程(Background process)、空进程(Empty process); 详见Android 进程保活招式大全

public class GrayService extends Service {

    private final static int GRAY_SERVICE_ID = 1001;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT < 18) {
            startForeground(GRAY_SERVICE_ID, new Notification());//API < 18 ,此方法能有效隐藏Notification上的图标
        } else {
            Intent innerIntent = new Intent(this, GrayInnerService.class);
            startService(innerIntent);
            startForeground(GRAY_SERVICE_ID, new Notification());
        }

        return super.onStartCommand(intent, flags, startId);
    }

    ...
    ...

    /**
     * 给 API >= 18 的平台上用的灰色保活手段
     */
    public static class GrayInnerService extends Service {

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            startForeground(GRAY_SERVICE_ID, new Notification());
            stopForeground(true);
            //stopSelf()隐藏顶部的Notification!
            stopSelf();
            return super.onStartCommand(intent, flags, startId);
        }

    }
}

其实Google察觉到了此漏洞的存在,从Android5.0的ServiceRecord类的postNotification函数源代码中可以看到这样的一行注释

Android坑(1)_第5张图片

使用灰色保活并不代表着你的Service就永生不死了,只能说是提高了进程的优先级。如果你的app进程占用了大量的内存,按照回收进程的策略,同样会干掉你的app。
http://www.diycode.cc/topics/45

Low Memory Killer 决定是否杀进程除了内存大小,还有进程优先级(数值越小,优先级越高):


Android坑(1)_第6张图片
  • 启动一个纯C/C++ 的进程,没有Java run time ,内存使用极低。(5.0之后失效,可通过5.0提供的JobScheduler解决,且不受 forcestop 影响)
  • onStartCommand()中返回START_STICKY,实现进程拉活
    问题:
    1.Service 第一次被异常杀死后会在5秒内重启,第二次被杀死会在10秒内重启,第三次会在20秒内重启,一旦在短时间内 Service 被杀死达到5次,则系统不再拉起。
    2.进程被取得 Root 权限的管理工具或系统工具通过 forestop 停止掉,无法重启。

http://mp.weixin.qq.com/s?__biz=MzA3ODg4MDk0Ng==&mid=403254393&idx=1&sn=8dc0e3a03031177777b5a5876cb210cc&scene=1&srcid=0402fANUWIotbVLECw4Ytz4K#wechat_redirect


你可能感兴趣的:(Android坑(1))