写点什么

2020 阿里巴巴,字节跳动,京东,android 驱动开发环境搭建

发布于: 23 小时前
  • 可以在 xml 中设置 Service 所在的进程,让 Service 在另外的进程中执行。

  • Service 执行的操作最多是 20s,BroadcastReceiver 是 10s,Activity 是 5s。

  • Activity 通过 bindService(Intent,ServiceConnection,flag)与 Service 绑定。

  • Activity 可以通过 startService 和 bindService 启动 Service。

IntentService

IntentService 是一个抽象类,继承自 Service,内部存在一个 ServiceHandler(Handler)和 HandlerThread(Thread)。IntentService 是处理异步请求的一个类,在 IntentService 中有一个工作线程(HandlerThread)来处理耗时操作,启动 IntentService 的方式和普通的一样,不过当执行完任务之后,IntentService 会自动停止。另外可以多次启动 IntentService,每一个耗时操作都会以工作队列的形式在 IntentService 的 onHandleIntent 回调中执行,并且每次执行一个工作线程。IntentService 的本质是:封装了一个 HandlerThread 和 Handler 的异步框架。

2.1、生命周期示意图

Service 作为 Android 四大组件之一,应用非常广泛。和 Activity 一样,Service 也有一系列的生命周期回调函数,具体如下图。


通常,启动 Service 有两种方式,startService 和 bindService 方式。

2.2、startService 生命周期

当我们通过调用了 Context 的 startService 方法后,我们便启动了 Service,通过 startService 方法启动的 Service 会一直无限期地运行下去,只有在外部调用 Context 的 stopService 或 Service 内部调用 Service 的 stopSelf 方法时,该 Service 才会停止运行并销毁。

onCreate

onCreate: 执行 startService 方法时,如果 Service 没有运行的时候会创建该 Service 并执行 Service 的 onCreate 回调方法;如果 Service 已经处于运行中,那么执行 startService 方法不会执行 Service 的 onCreate 方法。也就是说如果多次执行了 Context 的 startService 方法启动 Service,Service 方法的 onCreate 方法只会在第一次创建 Service 的时候调用一次,以后均不会再次调用。我们可以在 onCreate 方法中完成一些 Service 初始化相关的操作。

onStartCommand

onStartCommand: 在执行了 startService 方法之后,有可能会调用 Service 的 onCreate 方法,在这之后一定会执行 Service 的 onStartCommand 回调方法。也就是说,如果多次执行了 Context 的 startService 方法,那么 Service 的 onStartCommand 方法也会相应的多次调用。onStartCommand 方法很重要,我们在该方法中根据传入的 Intent 参数进行实际的操作,比如会在此处创建一个线程用于下载数据或播放音乐等。


public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {}


当 Android 面临内存匮乏的时候,可能会销毁掉你当前运行的 Service,然后待内存充足的时候可以重新创建 Service,Service 被 Android 系统强制销毁并再次重建的行为依赖于 Service 中 onStartCommand 方法的返回值。我们常用的返回值有三种值,START_NOT_STICKYSTART_STICKYSTART_REDELIVER_INTENT,这三个值都是 Service 中的静态常量。


START_NOT_STICKY


如果返回 START_NOT_STICKY,表示当 Service 运行的进程被 Android 系统强制杀掉之后,不会重新创建该 Service,当然如果在其被杀掉之后一段时间又调用了 startService,那么该 Service 又将被实例化。那什么情境下返回该值比较恰当呢?如果我们某个 Service 执行的工作被中断几次无关紧要或者对 Android 内存紧张的情况下需要被杀掉且不会立即重新创建这种行为也可接受,那么我们便可将 onStartCommand 的返回值设置为 START_NOT_STICKY。举个例子,某个 Service 需要定时从服务器获取最新数据:通过一个定时器每隔指定的 N 分钟让定时器启动 Service 去获取服务端的最新数据。当执行到 Service 的 onStartCommand 时,在该方法内再规划一个 N 分钟后的定时器用于再次启动该 Service 并开辟一个新的线程去执行网络操作。假设 Service 在从服务器获取最新数据的过程中被 Android 系统强制杀掉,Service 不会再重新创建,这也没关系,因为再过 N 分钟定时器就会再次启动该 Service 并重新获取数据。


START_STICKY


如果返回 START_STICKY,表示 Service 运行的进程被 Android 系统强制杀掉之后,Android 系统会将该 Service 依然设置为 started 状态(即运行状态),但是不再保存 onStartCommand 方法传入的 intent 对象,然后 Android 系统会尝试再次重新创建该 Service,并执行 onStartCommand 回调方法,但是 onStartCommand 回调方法的 Intent 参数为 null,也就是 onStartCommand 方法虽然会执行但是获取不到 intent 信息。如果你的 Service 可以在任意时刻运行或结束都没什么问题,而且不需要 intent 信息,那么就可以在 onStartCommand 方法中返回 START_STICKY,比如一个用来播放背景音乐功能的 Service 就适合返回该值。


START_REDELIVER_INTENT


如果返回 START_REDELIVER_INTENT,表示 Service 运行的进程被 Android 系统强制杀掉之后,与返回 START_STICKY 的情况类似,Android 系统会将再次重新创建该 Service,并执行 onStartCommand 回调方法,但是不同的是,Android 系统会再次将 Service 在被杀掉之前最后一次传入 onStartCommand 方法中的 Intent 再次保留下来并再次传入到重新创建后的 Service 的 onStartCommand 方法中,这样我们就能读取到 intent 参数。只要返回 START_REDELIVER_INTENT,那么 onStartCommand 重的 intent 一定不是 null。如果我们的 Service 需要依赖具体的 Intent 才能运行(需要从 Intent 中读取相关数据信息等),并且在强制销毁后有必要重新创建运行,那么这样的 Service 就适合返回 START_REDELIVER_INTENT。

onBind

Service 中的 onBind 方法是抽象方法,所以 Service 类本身就是抽象类,也就是 onBind 方法是必须重写的,即使我们用不到。在通过 startService 使用 Service 时,我们在重写 onBind 方法时,只需要将其返回 null 即可。onBind 方法主要是用于给 bindService 方法调用 Service 时才会使用到。

onDestroy

onDestroy: 通过 startService 方法启动的 Service 会无限期运行,只有当调用了 Context 的 stopService 或在 Service 内部调用 stopSelf 方法时,Service 才会停止运行并销毁,在销毁的时候会执行 Service 回调函数。

2.3、bindService 生命周期

bindService 方式启动 Service 主要有以下几个生命周期函数:

onCreate():

首次创建服务时,系统将调用此方法。如果服务已在运行,则不会调用此方法,该方法只调用一次。

onStartCommand():

当另一个组件通过调用 startService()请求启动服务时,系统将调用此方法。

onDestroy():

当服务不再使用且将被销毁时,系统将调用此方法。

onBind():

当另一个组件通过调用 bindService()与服务绑定时,系统将调用此方法。

onUnbind():

当另一个组件通过调用 unbindService()与服务解绑时,系统将调用此方法。

onRebind():

当旧的组件与服务解绑后,另一个新的组件与服务绑定,onUnbind()返回 true 时,系统将调用此方法。

3、fragemnt

3.1、创建方式

(1)静态创建


首先我们需要创建一个 xml 文件,然后创建与之对应的 java 文件,通过 onCreatView()的返回方法进行关联,最后我们需要在 Activity 中进行配置相关参数即在 Activity 的 xml 文件中放上 fragment 的位置。


<fragmentandroid:name="xxx.BlankFragment"android:layout_width="match_parent"android:layout_height="match_parent"></fragment>


(2)动态创建


动态创建 Fragment 主要有以下几个步骤:


  1. 创建待添加的 fragment 实例。

  2. 获取 FragmentManager,在 Activity 中可以直接通过调用 getSupportFragmentManager()方法得到。

  3. 开启一个事务,通过调用 beginTransaction()方法开启。

  4. 向容器内添加或替换 fragment,一般使用 repalce()方法实现,需要传入容器的 id 和待添加的 fragment 实例。

  5. 提交事务,调用 commit()


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


方法来完成。

3.2、Adapter 对比

FragmnetPageAdapter 在每次切换页面时,只是将 Fragment 进行分离,适合页面较少的 Fragment 使用以保存一些内存,对系统内存不会多大影响。


FragmentPageStateAdapter 在每次切换页面的时候,是将 Fragment 进行回收,适合页面较多的 Fragment 使用,这样就不会消耗更多的内存

3.3、Activity 生命周期

Activity 的生命周期如下图:


(1)动态加载:


动态加载时,Activity 的 onCreate()调用完,才开始加载 fragment 并调用其生命周期方法,所以在第一个生命周期方法 onAttach()中便能获取 Activity 以及 Activity 的布局的组件;

(2)静态加载:

1.静态加载时,Activity 的 onCreate()调用过程中,fragment 也在加载,所以 fragment 无法获取到 Activity 的布局中的组件,但为什么能获取到 Activity 呢?


2.原来在 fragment 调用 onAttach()之前其实还调用了一个方法 onInflate(),该方法被调用时 fragment 已经是和 Activity 相互结合了,所以可以获取到对方,但是 Activity 的 onCreate()调用还未完成,故无法获取 Activity 的组件;


3.Activity 的 onCreate()调用完成是,fragment 会调用 onActivityCreated()生命周期方法,因此在这儿开始便能获取到 Activity 的布局的组件;

3.4、与 Activity 通信

fragment 不通过构造函数进行传值的原因是因为横屏切换的时候获取不到值。

Activity 向 Fragment 传值:

Activity 向 Fragment 传值,要传的值放到 bundle 对象里; 在 Activity 中创建该 Fragment 的对象 fragment,通过调用 setArguments()传递到 fragment 中; 在该 Fragment 中通过调用 getArguments()得到 bundle 对象,就能得到里面的值。

Fragment 向 Activity 传值:

第一种:


在 Activity 中调用 getFragmentManager()得到 fragmentManager,,调用 findFragmentByTag(tag)或者通过 findFragmentById(id),例如:


FragmentManager fragmentManager = getFragmentManager();


Fragment fragment = fragmentManager.findFragmentByTag(tag);


第二种:


通过回调的方式,定义一个接口(可以在 Fragment 类中定义),接口中有一个空的方法,在 fragment 中需要的时候调用接口的方法,值可以作为参数放在这个方法中,然后让 Activity 实现这个接口,必然会重写这个方法,这样值就传到了 Activity 中

Fragment 与 Fragment 之间是如何传值的:

第一种:


通过 findFragmentByTag 得到另一个的 Fragment 的对象,这样就可以调用另一个的方法了。


第二种:


通过接口回调的方式。


第三种:


通过 setArguments,getArguments 的方式。

3.5、api 区别

add

一种是 add 方式来进行 show 和 add,这种方式你切换 fragment 不会让 fragment 重新刷新,只会调用 onHiddenChanged(boolean isHidden)。

replace

而用 replace 方式会使 fragment 重新刷新,因为 add 方式是将 fragment 隐藏了而不是销毁再创建,replace 方式每次都是重新创建。

commit/commitAllowingStateLoss

两者都可以提交 fragment 的操作,唯一的不同是第二种方法,允许丢失一些界面的状态和信息,几乎所有的开发者都遇到过这样的错误:无法在 activity 调用了 onSaveInstanceState 之后再执行 commit(),这种异常时可以理解的,界面被系统回收(界面已经不存在),为了在下次打开的时候恢复原来的样子,系统为我们保存界面的所有状态,这个时候我们再去修改界面理论上肯定是不允许的,所以为了避免这种异常,要使用第二种方法。

3.懒加载

我们经常在使用 fragment 时,常常会结合着 viewpager 使用,那么我们就会遇到一个问题,就是初始化 fragment 的时候,会连同我们写的网络请求一起执行,这样非常消耗性能,最理想的方式是,只有用户点开或滑动到当前 fragment 时,才进行请求网络的操作。因此,我们就产生了懒加载这样一个说法。


Viewpager 配合 fragment 使用,默认加载前两个 fragment。很容易造成网络丢包、阻塞等问题。


在 Fragment 中有一个 setUserVisibleHint 这个方法,而且这个方法是优于 onCreate()方法的,它会通过 isVisibleToUser 告诉我们当前 Fragment 我们是否可见,我们可以在可见的时候再进行网络加载。


从 log 上看 setUserVisibleHint()的调用早于 onCreateView,所以如果在 setUserVisibleHint()要实现懒加载的话,就必须要确保 View 以及其他变量都已经初始化结束,避免空指针。


使用步骤:


申明一个变量 isPrepare=false,isVisible=false,标明当前页面是否被创建了 在 onViewCreated 周期内设置 isPrepare=true 在 setUserVisibleHint(boolean isVisible)判断是否显示,设置 isVisible=true 判断 isPrepare 和 isVisible,都为 true 开始加载数据,然后恢复 isPrepare 和 isVisible 为 false,防止重复加载。

4、Activity

4.1、 Activity 启动流程

用户从 Launcher 程序点击应用图标可启动应用的入口 Activity,Activity 启动时需要多个进程之间的交互,Android 系统中有一个 zygote 进程专用于孵化 Android 框架层和应用层程序的进程。还有一个 system_server 进程,该进程里运行了很多 binder service。例如 ActivityManagerService,PackageManagerService,WindowManagerService,这些 binder service 分别运行在不同的线程中,其中 ActivityManagerService 负责管理 Activity 栈,应用进程,task。

点击 Launcher 图标来启动 Activity

用户在 Launcher 程序里点击应用图标时,会通知 ActivityManagerService 启动应用的入口 Activity,ActivityManagerService 发现这个应用还未启动,则会通知 Zygote 进程孵化出应用进程,然后在这个 dalvik 应用进程里执行 ActivityThread 的 main 方法。应用进程接下来通知 ActivityManagerService 应用进程已启动,ActivityManagerService 保存应用进程的一个代理对象,这样 ActivityManagerService 可以通过这个代理对象控制应用进程,然后 ActivityManagerService 通知应用进程创建入口 Activity 的实例,并执行它的生命周期方法。

4.2、Activity 生命周期

(1)Activity 的形态

Active/Running:

Activity 处于活动状态,此时 Activity 处于栈顶,是可见状态,可与用户进行交互。

Paused:

当 Activity 失去焦点时,或被一个新的非全屏的 Activity,或被一个透明的 Activity 放置在栈顶时,Activity 就转化为 Paused 状态。但我们需要明白,此时 Activity 只是失去了与用户交互的能力,其所有的状态信息及其成员变量都还存在,只有在系统内存紧张的情况下,才有可能被系统回收掉。

Stopped:

当一个 Activity 被另一个 Activity 完全覆盖时,被覆盖的 Activity 就会进入 Stopped 状态,此时它不再可见,但是跟 Paused 状态一样保持着其所有状态信息及其成员变量。

Killed:

当 Activity 被系统回收掉时,Activity 就处于 Killed 状态。


Activity 会在以上四种形态中相互切换,至于如何切换,这因用户的操作不同而异。了解了 Activity 的 4 种形态后,我们就来聊聊 Activity 的生命周期。

评论

发布
暂无评论
2020阿里巴巴,字节跳动,京东,android驱动开发环境搭建