Android
- Activity与Fragment的生命周期。
- 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)的区别:
- 后者优先级高于前者,两者同时存在时后者为准
- 前者无法为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
- Service的生命周期,两种启动方法,有什么区别。
- 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));
}
}
}
5.怎么保证service不被杀死。
-
白色保活
白色保活手段非常简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。如下方的LBE和QQ音乐这样:
灰色保活
这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的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函数源代码中可以看到这样的一行注释
使用灰色保活并不代表着你的Service就永生不死了,只能说是提高了进程的优先级。如果你的app进程占用了大量的内存,按照回收进程的策略,同样会干掉你的app。
http://www.diycode.cc/topics/45
Low Memory Killer 决定是否杀进程除了内存大小,还有进程优先级(数值越小,优先级越高):
- 启动一个纯C/C++ 的进程,没有Java run time ,内存使用极低。(5.0之后失效,可通过5.0提供的JobScheduler解决,且不受 forcestop 影响)
- onStartCommand()中返回START_STICKY,实现进程拉活
问题:
1.Service 第一次被异常杀死后会在5秒内重启,第二次被杀死会在10秒内重启,第三次会在20秒内重启,一旦在短时间内 Service 被杀死达到5次,则系统不再拉起。
2.进程被取得 Root 权限的管理工具或系统工具通过 forestop 停止掉,无法重启。