美文网首页
android 沉淀 - 四大组件

android 沉淀 - 四大组件

作者: 前行的乌龟 | 来源:发表于2019-06-01 22:50 被阅读0次

    Activity

    大家最熟悉的吧,但是熟悉并不代表了解一切,但往往越是被熟知的越是被问及的越深,面试时看=有的面试官可以喜欢在这里难住面试者的

    1. 生命周期
    • onCreate - Activity 被创建,一些初始化工作在这里(加载布局资源,初始化所需要的数据等),如果有耗时任务需开异步线程
    • onStart - Activity 启动中,个状态下 Activity 还在加载其他资源,用户还无法看到,不能交互
    • onResume - Activity创建完成,用户可看见界面,可交互
    • onPause - Activity 正在暂停,正常情况下接着会执行 onStop(),这时可以做数据的存储、动画停止的操作,尤其是不能太耗时,新的 Activity 必须要等当前 Activity 的 onPause 执行完才能开始创建的
    • onStop - Activity 即将停止,这时可以做一些回收工作,一样不能太耗时
    • onDestory - Activity即将被销毁,可以做一些工作和资源的回收,Service、BroadCastReceiver、Map、Bitmap、动画、handle、rxjava 回收
    • onRestart - Activity 正在重新启动,一般时当前 Activity 从不可见到可见状态时会执行这个方法,例如:用户按下 Home 键(锁屏)或者打开新 Activity 再返回这个 Activity

    以下的操作会触发 Activity 的生命周期函数:

    2. 启动模式

    启动模式这个一定会问,但是必会的

    • standard - 标准默认启动模式,Activity 可以有多个实例,每次启动 Activity,无论任务栈中是否已经有这个Activity 的实例,系统都会创建一个新的 Activity 实例
    • singleTop - 栈顶复用模式,当一个 singleTop 模式的 Activity 已经位于任务栈的栈顶,再去启动它时,不会再创建新的实例,如果不位于栈顶,就会创建新的实例
    • SingleTask - 栈复用模式,整个 Activity 栈只能有一个 SingleTask 的 Activity 实例存在,若是再启动相同的 Activity,会把栈中的该 Activity 实例置于栈顶,简单说就是该 Activity 实例上面的所有 Activity都会出栈,也就是退出了,之后该 Activity 实例自然就在栈顶了,注意栈的操作
    • singleInstance - Activity 实例复用模式,singleInstance 的 Activity 会自动生成一个 Activity 任务栈,该栈中只能切只有该 Activity 一个Activity,整个 app 的进程中只能使用这一个 Activity 实例,类似于 static 的概念

    除了 standard 之外其他3个模式,复用所在 Activity 时都会触发 onNewIntent 方法,这个方法接受一个 intent 参数,可以拿到用户传递过来的数据

    调用顺序如下:

    • onNewIntent() -> onRestart() -> onStart() -> onResume()
      需要特别注意的是, 如果在 onNewIntent(Intent) 中,不调用 setIntent(Intent) 方法对 Intent 进行更新的话,那么之后在调用 getIntent() 方法时得到的依然是最初的值
    3. 数据保存与恢复

    2 个方法:

    调用时机 : 注意是 Activity 容易被销毁的时候调用, 是容易被销毁, 但是也可能没有销毁就调用了

    onSaveInstanceState 方法总是在 onStop 之前调用,但是不去确定是在 onPause 之前或之后调用。另外 onSaveInstanceState 保存的数据有有效期和 app 进程相同,app 进程要是被销毁了那么保存的数据也就没了

    4. 系统 kill 时的生命周期

    非常郁闷的是当系统因为内存不足要回收 Activity 占用的资源时,有时 Activity 在 onPause() 之后就会被销毁,onStop(),onDestory() 根本不会执行,所以很多时候我们要在 onResume()注册监听,onPause() 注销监听也是没办法的事,尤其是对于广播接收器来说,这可能造成内存泄露问题


    Service

    Service 既服务,被认为是没有 UI 的 Activity,非常相似

    Service 的特点(可能被问及的点):

    • Service 同 Activity 一样都是运行在主线程上的
    • Service 的作用就是用来在可不见的后台默默执行一些任务
    • Service 有2种启动模式:StartService / bindService
    • StartService 启动的 Service,无法与外置直接通信(如传递接口),只能通过广播,handle,evenBus 等间接通讯方式。声明周期同 app 进程,比 app 本身要长,即便 app 退出了,该 Service 也会一直运行,直到内存不足时被系统 kill,并根据 Service 中 onStartCommand 方法的返回值采取不同的策略(比如重新启动该 Service )。可以多次启动,在该 Service 已经启动的情况下只会触发 onStartCommand 方法,可以在外部和 Service 内部关闭 Service
    • bindService 启动的 Service,可以与外界直接通信,可以通过 IBinder 接口获取 Service 实例,bindService 的生命周期和启动 Service 的 UI 页面等同,UI 页面关闭时,bindService 也会同步销毁,当然也可以在 UI 内主动解绑销毁 Service,当没有人与 bindService 捆绑时,bindService 也会自行销毁
    • 当前最常见的 Service 绑定方式:先 StartService 启动该 Service,在需要的时候 bindService,这样即可以让 Service 服务一直运行,也可以和 Service 进行通讯。
    • 设置为前台模式的 Service 会一直存活,理论上系统不会主动销毁该服务,除非用户手动 kill 该进程

    2种 Service 启动方式分别对应的生命周期:


    如何保证Service不被杀死:

    • onStartCommand方式中,返回START_STICKY,这样即便被系统 kill 也有东山再起的机会
    • 提高Service的优先级,AndroidManifest.xml 中可以通过 android:priority = "1000" 这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播
    • 提升Service进程的优先级,比如前台进程
    • 在onDestroy方法里重启Service,onDestroy() 时发送一个自定义广播,重新启动service
    • 系统广播监听 Service 状态,利用时间广播 Intent.ACTION_TIME_TICK 该广播每分钟发送一次,如果已经被结束了,就重新启动 Service
    • 将APK安装到/system/app,变身为系统级应用

    更多更详细请看:


    广播

    1. 静态注册参数
    <receiver 
        android:enabled=["true" | "false"]
    //此broadcastReceiver能否接收其他App的发出的广播
    //默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
        android:exported=["true" | "false"]
        android:icon="drawable resource"
        android:label="string resource"
    //继承BroadcastReceiver子类的类名
        android:name=".mBroadcastReceiver"
    //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
        android:permission="string"
    //BroadcastReceiver运行所处的进程
    //默认为app的进程,可以指定独立的进程
    //注:Android四大基本组件都可以通过此属性指定自己的独立进程
        android:process="string" >
    
    //用于指定此广播接收器将接收的广播类型
    //本示例中给出的是用于接收网络状态改变时发出的广播
     <intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
    </receiver>
    
    2. 继承 BroadcastReceiver

    BroadcastReceiver 是官方组件,自然是要像 Activity 一样去继承的

    public class SdCardBroadcastReceiver extends BroadcastReceiver {
        
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction ();
            if ("android.intent.action.MEDIA_MOUNTED".equals(action)) {
                System.out.println("sd卡已挂载");
            } else if ("android.intent.action.MEDIA_UNMOUNTED".equals(action)) {
                System.out.println("sd卡已卸载");
            }
    
           String data = intent.getStringExtra("key");
        }
    }
    

    从 intent 中可以拿到 action 和 bundle 传递的数据

    3. 广播的类型
    • 普通广播(Normal Broadcast)
    • 系统广播(System Broadcast)
    • 有序广播(Ordered Broadcast)
    • 粘性广播(Sticky Broadcast)
    • App应用内广播(Local Broadcast)

    3.1 发送无序广播

    public void startBroadcast(View view){
            //开启广播
            //创建一个意图对象
            Intent intent = new Intent();
            //指定发送广播的频道
            intent.setAction("com.example.BROADCAST");
            //发送广播的数据
            intent.putExtra("key", "发送无序广播,顺便传递的数据");
            //发送
            sendBroadcast(intent);
        }
    

    3.2 发送有序广播

        public void sendOrderedBroad(View view) {
            Intent intent = new Intent();
            intent.setAction("com.example.ORDERED");
            // 发送无序广播
            sendOrderedBroadcast(intent,//意图动作,指定action动作
                    null, //receiverPermission,接收这条广播具备什么权限
                    new FinalReceiver(),//resultReceiver,最终的广播接受者,广播一定会传给他
                    null, //scheduler,handler对象处理广播的分发
                    0,//initialCode,初始代码
                    "每人发10斤大米,不得有误!", //initialData,初始数据
                    null//initialExtras,额外的数据,如果觉得初始数据不够,可以通过bundle来指定其他数据
                    );
        }
    

    有序广播的特点是按照 xml 中的优先级从高到底开始接受,先接受的 recriver 可以把数据处理之后再交给下一级或者不在传递,很想网络里的拦截器

    priority 越大优先级越高


    public class ShengReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent)        
    
            // 获取广播的数据
            String data = getResultData();       
     
            // 修改之后再给下一级
            setResultData("中央下达福利,每人5斤大米");
    
            // 终止广播,权限小的接收者就接收不到广播了
            abortBroadcast();
        }
    }
    
    4. 动态注册方法

    注意广播注册注销的最佳时机在 onResume、onPause,因为系统回收 Activity 时后面的生命周期可能就不会走了

    上面在 AndroidManifest 中声明的都叫做静态广播,动态注册的广播就是不在AndroidManifest 中声明,用代码启动,解绑,但是要注意自 8.0 开始,不在允许静态注册的广播,必须手动代码注册

    // 选择在Activity生命周期方法中的onResume()中注册
    @Override
      protected void onResume(){
          super.onResume();
    
            // 手动注册广播
            var intentFilter = IntentFilter()
            intentFilter.addAction("AAA")
            registerReceiver(receiver, intentFilter)
    
            // 发送给广播
            var intent = Intent("AAA")
            sendBroadcast(intent)
     }
    
     @Override
     protected void onPause() {
         super.onPause();
          // 解绑广播
         unregisterReceiver(mBroadcastReceiver);
         }
    }
    

    重复注册、重复注销也不允许,不注销会有内存泄露问题的

    5. 不同注册方式的广播回调 OnReceive

    对于不同注册方式的广播接收器回调 OnReceive(Context context,Intent intent)中的 context 返回值是不一样的:

    • 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext
    • 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context
    • 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context
    • 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context

    进程

    android 的进程主要了解有优先级,用来配合 Service 保活策略

    android 进程优先级5个档:

    • 前台进程
    • 可见进程
    • 服务进程
    • 后台进程
    • 空进程

    怎么理解呢,简单来说:

    • 前台进程 - 需要指定设置;
    • 可见进程 - Activity 可见时就是
    • 服务进程 - Service 启动时就是
    • 后台进程 - app 按 home 就是
    • 空进程 - 所有的页面都退出了,但 app 所在的进程不会第一时间就回收,为了放置用户短时间内再启动有一个缓冲,但是非常容易被回收

    上面是简单理解,详细请看:

    但是和理论不同的是,Service 优先级首先参考的不是自己的优先级,而是首先参考所在 app 进程的页面活动

    • 比如我们在一个页面中启动一个 Service 播放音乐,我们按 home 切到后台,按照理论此时 app 所在进程是服务进程的优先级,属于不容易被回收的那种,但是实际不是,app 进程是后台进程,优先级比服务进程第一个档次,属于容易被回收的进程,只有 Service 设置为前台时除外
    • 若是这个 Service 单独运行在一个进程中,那么就和理论情况一样

    具体可以参考:


    相关文章

      网友评论

          本文标题:android 沉淀 - 四大组件

          本文链接:https://www.haomeiwen.com/subject/pizctctx.html