Activity
onResume():在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。
onPause():此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容(例如camera、GPS),它应该非常迅速地执行所需操作。因为它返回后,下一个 Activity 才能继续执行。当按powerOff 锁屏时只回调此方法,而不掉onStop(),自然进入休眠会顺序调用这俩。
onStop():释放所有用户不再使用的资源。也是一个释放可能造成内存泄露资源的最佳时机,因为当系统直接杀死进程来结束程序时最后一步调用的是此方法而不会走到onDestroy()。也是执行CPU密集型关闭操作的好时机,例如将信息存入数据库。当activity进入此状态时,是仍在内存中的,但不再依附window manager了,也可能因为内存不足而进程被kill掉,这时可以通过存入信息至Bundle来恢复【一个疑问:系统如何判断当前Activity在之前被存过Bundle,而再次重启进程时需要记住那个Activity?这个记忆的时间是多少?】
onDestroy():当调用了finish()方法或进程被kill用以节省内存时调用,可以通过isFinishing()来区分这两种情况。当屏幕方向发生变化时,也回调此方法然后立刻再回调onCreate()。在onCreate()方法中可以通过参数Bundle来恢复数据,也可以选择onRestoreInstanceState()来实现,这个方法会在onStart()后被调用(仅当存储过Bundle时才会被系统调用,因此不需要判断是否为null),需要为恢复数据的view设置id来让系统自动恢复数据。执行时机是在onStart()与onPostCreate()之间。当用户按下返回键从B回到A时,不会调用B的onRestoreInstanceState()方法,因为B永远不会被恢复,因此系统也就不会调用它。
- 跳转,例Aactivity ->Bactivity:
- Aactivity.onPause()
- Bactivity.onCreate() -> Bactivity.onStart() -> Bactivity.onResume()
- Aactivity.onStop()
Service
- 有两种形式:
(1) 启动:Context.startService(),一旦启动服务可在后台无限期运行,即使启动服务的组件被销毁也不受影响。例:网络下载、上传文件。
(2) 绑定:Context.bindService(),绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信(IPC)跨进程执行这些操作。多个组件可以同时绑定该服务,但全部取消绑定后,该服务即被销毁。 - 服务在其托管进程的主线程中运行,它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。 这意味着,如果服务将执行任何 CPU 密集型工作或阻止性操作(例如 MP3 播放或联网),则应在服务内创建新线程来完成这项工作。通过使用单独的线程,可以降低发生“应用无响应”(ANR) 错误的风险,而应用的主线程仍可继续专注于运行用户与 Activity 之间的交互。
- 服务与线程的区别?与AsyncTask区别?
Service is like an activity without UI,service默认在其托管进程的主线程运行,它既不创建自己的线程,也不在单独的进程中运行,在其内部可以创建线程。service有着自己的生命周期,不依赖于组件。而线程,应用启动时,系统会为应用创建一个名为“主线程”的执行线程,负责将事件分发给相应的用户界面小部件,为了不阻塞主线程发生ANR,可以创建工作线程,子线程的声明周期是要依托于创建它的组件的且不能与UI交互,如果在activity.onDestroy()中停止线程,activity将失去对它的控制。AsyncTask简化了异步任务的操作,override它的内部方法可以实现后台操作&UI控制,无需再创建thread&handler。
ContentProvider
- 读取系统APP,信息,联系人, 多媒体信息等,也可以通过URI向外界程序暴露自己的数据,可以有增-删-改-查的overrider方法。
- 自定义ContentProvider的步骤
Broadcast
- 类似于订阅-发布的设计模式,应用能发送或接受来自于系统或其他应用的广播。
- 注册广播的两种方式:
1.应用级别的注册广播,此方式在app安装时就注册了,这个receiver成为了一个连接该app的单独入口点,即使当前程序并没有运行系统也可以启动程序传递广播。方式如下:
(1) 在manifest中声明receiver结点
<receiver android:name=".MyBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>
(2) extends BroadcastReceiver,实现onReceive(Context context, Intent intent)方法
2.通过上下文 context 注册广播,步骤如下:
(1) 声明
BroadcastReceiver br = new MyBroadcastReceiver();
(2) 创建intent-filter并且注册receiver
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
this.registerReceiver(br, filter);
(3) 不再使用时,需要调用** unregisterReceiver(br)解除注册
值得注意的是接收者能否接收广播取决于发布者context是否存活,如果是activity.reregisterReceiver,那么当activity执行了onDestroy()**
之后接收者就收不到广播了。如果使用 Application context来注册,那么在程序运行阶段都可以收到。
broadcast 对进程状态的影响
执行着onReceive()的进程具有较高优先级被认为是前台进程,不会被系统kill,当从该方法返回时,广播将失活,其宿主进程不再被认定为具有高优先级,随时可能被kill用以释放内存。因此不能在onReceive()中执行耗时操作(系统只允许10s),即使另开线程用以执行耗时操作也不可以,因为宿主进程一旦被kill,在其内部生产的线程也会被kill。要想让广播组件在onReceive()返回后仍能进行长时间操作,有2种方案:(1)在onReceive()方法内调用PendingResult pendingResult = goAsync();,在子线程完成操作后必须再调用pendingResult.finish(),这个pendingResult里有着失活的broadcastReceiver的intent数据。官方文档有示例代码。(2)使用JobScheduler调度JobService
JobService的五个约束条件如下:a.最小延时 b.最晚执行时间 c.需要充电 d.需要设备为idle(空闲)状态 e.联网状态(NETWORK_TYPE_NONE--不需要网络,NETWORK_TYPE_ANY--任何可用网络,NETWORK_TYPE_UNMETERED--不按用量计费的网络)。如果后台任务满足以上的一个或多个条件,就可以考虑是不是应该用JobService来执行。发送广播,有3种方式:
1.sendOrderedBroadcast(Intent, String),有序广播,为不同 broadcastReceiver 定义优先级顺序,receiver间可以传递数据,也可以阻拦优先级低的receiver接收广播。
2.sendBroadcast(Intent),普通广播
3.LocalBroadcastManager.sendBroadcast(Intent),本地广播,在应用所在的进程内发送,有以下优点:a.发送的广播数据只在当前应用的进程中,无需担心泄露私密数据 b.其他应用不可能将此类广播发送至当前的应用,无需担心有可被利用的安全漏洞 c.相对于全局广播,本地广播更高效(无需进程间通信)使用permisson限制广播的发送和接收,发送时指定permission,只有声明了permission的receiver(通过context注册或是在manifest的receiver结点)并且application也声明了同样的permission的应用才能收到广播。
-
关于使用的几点安全性建议:
- 如果不需要发送广播到当前app以外的组件,尽量使用本地广播LocalBroadcastManager.sendBroadcast(Intent)
- 如果很多app通过manifest注册了相同的broadcastReceiver,当触发时可能会启动多个app,这对用户体验不好,因此尽量使用通过context注册广播的方式而取代manifest,android系统对此也做了一些限制,比如检测网络连接的广播 CONNECTIVITY_ACTION 就只能通过context注册。
- 发送广播的安全性做法:发送时指定permission、4.0以上可以指定package、使用本地广播
- 接收广播的安全性做法: 注册时可指定permission、在receiver结点声明android:exported= false,可不接收外部广播、使用本地广播
- 广播的 actions 是系统全局的,因此需要使用包命名的action
- onReceive()最多10s内执行完成,不要做耗时操作
- 不要在onReceive()中打开其他activity,尤其有多个receiver,可以考虑使用Notification。
Intent
- 基本用图:启动Activity、启动服务、传递广播
- Intent类型:
1.显示Intent(Explicit intents):指定完全限定类名的接收组件,比如启动activity,启动服务下载文件。
2.隐式 Intent(Implicit intents):声明要执行的 action,接收的组件通过声明 intent-filter 来匹配,比如通过打开其他地图类app来显示位置。
出于安全性考虑,启动服务要通过显示intent,因为你不知道什么服务会响应该意图并且用户也不知道什么服务被启动了。在 API 5.0+时,当使用隐式intent启动服务时,当调用bindService()时系统会抛出 SecurityException。 - PendingIntent 的使用场景:当Intent用于 执行Notification的操作、执行App Widget的操作、在指定的未来某一时间执行操作(AlarmManager)。
- 就像 Intent 被设计的要由特定组件接收那样,PendingIntent 也需要指明处理它的组件类型,因此它的实例化有以下3种入口:PendingIntent.getActivity()、PendingIntent.getService()、PendingIntent.getBroadcast()。
- Intent 与 PendingIntent 的区别?
1.Intent立即执行,PendingIntent 可以等到事件发生后触发,还可以取消
2.Intent 需要在某个 context 内使用,而 PendingIntent 自带 context,当程序终止时 Intent 失效,而PendingIntent 依然有效
3.Intent 在原 task 中运行,PendingIntent 在新的 task 中运行 - Intent matching:intent 与 intent filters 匹配,意义不只是找出目标组件来处理,还能找出整个系统中匹配 intent filters的组件集合。例如Home app 就是通过找出匹配了action为ACTION_MAIN和 category为CATEGORY_LAUNCHER 的activities,利用 PackageManager 的一系列query...()方法也能实现主界面程序的功能,利用queryIntentActivities、queryIntentServices等方法来查看系统中有哪些应用和服务,在 PackageManager 中与之对应的一系列resolve...()方法就是列出匹配了意图的组件们,列表中的第一个就是最佳匹配的要被激活的。
如何创建一个开机自启动的service和应用?:
1.手机在启动的过程中会触发一个Standard Broadcast Action,名字叫android.intent.action.BOOT_COMPLETED,通过构建一个广播接收者来接收这个这个action
public class BootBroadcastReceiver extends BroadcastReceiver {
//重写onReceive方法
@Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context,XXService.class);
context.startService(service);
//启动应用,参数为需要自动启动的应用的包名
Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
context.startActivity(intent);
}
}
2.在manifest中为这个BroadcastReceiver添加intent-filter
<receiver android:name="BootBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</receiver>
3.在manifest中添加权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />