美文网首页
安卓-面试篇 基础版 一

安卓-面试篇 基础版 一

作者: 呐呐呐nana | 来源:发表于2020-01-14 17:52 被阅读0次

    一、系统架构

    1,四大组件

    Activity、Service、broadcastReceiver、ContentProvider

    1.1 activity 生命周期 、 启动模式
    1.1.1 生命周期

    https://www.jianshu.com/p/b1ff03a7bb1f

    安卓生命周期
    纠正:onStart()时处于可见不可交互状态。onResume()处于可见可交互状态

    1.1.1.1 当前Activity为A,此时用户打开ActivityB后,那么A的onPause()和B的onResume()哪个方法先执行?
    答:先 A的onPause() ,再B的onResume()

    Activity的启动过程:由ActivityManagerService(AMS)对栈内的Activity状态进行同步管理 & 规定:新Activity启动前,栈顶的Activity必须先onPause(),才能启动新的Activity(执行onResume())
    注:为了让新的Activity尽快切换到前台,在 onPause()尽量不要做耗时 / 重量级操作

    1.1.1.2 常见场景的生命周期调用


    生命周期II

    在清单文件中指定了屏幕方向,则Activity在锁屏和开启屏幕的时候执行的方法和顺序是:MainActivity onPause--->MainActivity onStop--->MainActivity onRestart--->MainActivity onStart--->MainActivity onResume

    如果在清单文件中没有对屏幕进行设置,则Activity在锁屏时候执行的方法和顺序是:MainActivity onPause--->MainActivity onStop--->MainActivity onDestory--->MainActivity onCreate--->MainActivity onStart--->MainActivity onResume--->MainActivity onPause销毁之后又新建。

    在开启屏幕的时候,Activity执行的方法及顺序是:MainActivity onResume--->MainActivity onPause--->MainActivity onStop--->MainActivity onDestory--->MainActivity onCreate--->MainActivity onStart--->MainActivity onResume。对于这种,锁屏后再次开启屏幕会销毁两次,重建两次。

    第二中情况的解决办法:在清单文件里activity标签下配置 android:configChanges="orientation|screenSize"。

    1.1.1.3 android:configChanges
    用于捕获手机状态的改变。在当所指定属性(Configuration Changes)发生改变时,通知程序调用onConfigurationChanged()函数。

    (1)、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

    (2)、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

    (3)、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

    但是,自从Android 3.2(API 13),在设置Activity的android:configChanges="orientation|keyboardHidden"后,还是一样 会重新调用各个生命周期的。

    因为screen size也开始跟着设备的横竖切换而改变。所以,在AndroidManifest.xml里设置的MiniSdkVersion和 TargetSdkVersion属性大于等于13的情况下,

    如果你想阻止程序在运行时重新加载Activity,除了设置"orientation", 你还必须设置"ScreenSize"。

    解决方法:

    AndroidManifest.xml中设置android:configChanges="orientation|screenSize“

    1.1.1.4 finish() onDestory()
    finish() 是结束一个activity的生命周期,而onDestory() 则是activity的一个生命周期

    finish()调用后,将此activity移出栈,并未及时调用onDestory()方法,释放资源。但因为已出栈,点击back键时,找不到此activity

    1.1.2 启动模式
    启动模式

    1.1.2.1 知识

    • 任务栈:管理activity,后进先出


      image.png
    • 可在 manifest中设置,launchMode。也可通过Intent设置标志位

      intent.addFlags(FLAG_ACTIVITY_NEW_TASK)
      
    • intent 设置优先级更高

    • intent 无法设置单例模式

    标记位属性 含义
    FLAG_ACTIVITY_SINGLE_TOP 指定启动模式为栈顶复用模式(SingleTop)
    FLAG_ACTIVITY_NEW_TASK 指定启动模式为栈内复用模式(SingleTask)
    FLAG_ACTIVITY_CLEAR_TOP 所有位于其上层的Activity都要移除,SingleTask模式默认具有此标记效果
    FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 具有该标记的Activity不会出现在历史Activity的列表中,即无法通过历史列表回到该Activity上

    1.2 fragment
    1.2.1 生命周期
    fragment生命周期 image.png
    • 创建时
      onAttach() onCreate() onCreateCiew() onActivityCreated()

    • 可见时
      onStart() onResume()

    • 进入后台时
      onPause() onStop()

    • fragment或activity被销毁
      onPause() onStop() onDestoryView() onDestory() onDetach()

    • 屏幕熄灭/回到桌面
      onPause() onSaveInstanceState() onStop()

    • 屏幕解锁/回到应用
      onStart() onResume()

    Activity中调用replace()方法和addToBackStack()方法时的生命周期
    新替换的Fragment(没有在BackStack中):
    onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
    新替换的Fragment(已经在BackStack中):
    onCreateView > onViewCreated > onActivityCreated > onStart > onResume
    被替换的Fragment:onPause > onStop > onDestroyView--> onDestroy --> onDetach

    Fragment以下4个生命周期方法将跟随所属的Activity一起被调用:
    onPause > onStop > onStart > onResume

    1.2.2 fragment 和 activity 通信
    • Handler
    • 回调
    • 广播或eventbus
    • 持有对象
    • bundle
    • findFrgamentById/Tag

    1.2.3 fragment 间通信

    • 通过activity操作
    • 回调、广播、eventbus
    1.2.4 使用

    静态添加: xml中作为view直接引用
    动态添加步骤:

    • 获取fragmentManager对象
    • 开启一个事务 beginTransaction
    • add remove replace
    • 可以调用addToBackStack(),加入回退栈,管控此fragment
    • 提交事务 commit()
    1.2.5 fragment 在viewpager容器的 resume 刷新

    setUserVisibleHint(true)

    • 在onResume的时候,如果getUserVisibleHint的值是false,不一定不是当前显示,如果为true就一定是当前显示的Fragment
    • setUserVisibleHint()在Fragment实例化时会先调用一次,并且默认值是false,当选中当前显示的Fragment时还会再调用一次。
    • setUserVisibleHint()可能会在Fragment的生命周期之外被调用,也就是可能在view创建前就被调用,也可能在destroyView后被调用,所以如果涉及到一些控件的操作的话,可能会报 null 异常,因为控件还没初始化,或者已经摧毁了。
    • 控制viewpager 加载数量 mPager .setOffscreenPageLimit(2);
    1.3 broadcastReceiver

    https://www.jianshu.com/p/ca3d87a4cdf3

    使用了观察者模式,将发送者和接收者解耦

    1.3.1 应用场景
    • 不同组件简通信(应用内、应用间)
    • 多线程通信
    • 系统通信 【电话呼入,网络可用】
    1.3.2 细解
    广播模型
    广播使用流程
    • 广播接收器运行在UI线程,因此 onReceive()不可执行耗时操作,否则ANR
    1.3.3 使用

    静态注册:manifest中通过receiver标签声明

    <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>
    
    • 当 app首启时,系统会自动实例化此 mBroadcastReceiver 类,并注册

    动态注册:代码中调用 .registerReceiver()方法

    • 需记得在对应位置【onPause】销毁广播
    • 当此 activity 实例化时,注册此广播。当activity销毁时,动态注册的广播将不再接收消息
    • 建议在 onResume()注册广播,onPause()注销广播,而非 onCreate() & onDestory() , 是因为当系统内存不足,需要回收资源时,activity执行完onPause()之后就会被销毁,此时还未执行 onStop() onDestory(),即广播还未注销,导致内存泄漏


      两种注册方式区别
    1.3.4 分类

    普通广播、系统广播、有序广播、粘性广播、local broadcast、粘性广播
    普通广播:开发者自定义 intent ,匹配接受者的intentFilter的action
    系统广播:仅需注册广播接收器,接收系统广播的action

    有序广播:优先级别高的广播接收器先接收,接收完了没有丢弃/修改(abortBroadcast()),传给次一级的广播接收器

    • 优先级相同的情况下,动态注册的广播优先

    粘性广播:发送之后一直存在于消息处理器中,等待对应的接收器去处理。如果接收器被销毁,则会在下次重建时自动接收消息数据

    • 需要 权限 BROADCAST_STICKY
    • 没有10秒限制,10秒限制是指普通的广播如果onReceive方法执行时间太长,超过10秒的时候系统会将这个广播置为可以干掉的candidate,一旦系统资源不够的时候,就会干掉这个广播而让它不执行。
    • 5.0后已 不建议使用

    应用内广播
    存在原因:

    • 其他App发出与当前app广播接收器相匹配的广播,导致当前app不断接收广播信息
    • 其他app注册与当前app一直的接收器,接收当前app的广播,出现安全问题

    应用内广播保证发送者和接受者属于同一个app
    LocalBroadcastManager。接受者只能动态注册,不能静态注册

    全局广播 -> 应用内广播 :

    • exported=false 使得非本app发送的广播不被接收
    • 增加permission
    • 发送广播时,指定包名 intent.setPackage(包名)
    1.3.5 onReceive()参数context上下文区别

    不同注册方式的广播接收器回调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;
    1.4 service 使用和生命周期、跨进程通信
    1.5 contentProvider

    https://www.jianshu.com/p/ea8bc4aaf057
    结合消息模块message,存储数据库交互一起看

    作用:进程间数据共享和交互


    作用

    原理: binder机制,分析见后文

    1.5.1 contentProvider 使用

    URI:统一资源标识符,外部线程通过URI找到对应的ContentProvider和其中的数据,并对数据进行操作
    分为系统预置 和 自定义URI

    Mime:指定数据类型,及打开的程序 。 = 类型+子类型

     text/html
     application/pdf
    

    ContentProvider 以表格的形式,组织和管理数据【增删改查】
    用于外部进程对数据进行操作的 实现。但不会直接和外部进程交互。

     ContentProvider.getType(uri)
    
    • 需注意线程同步
    • 若配合SQLite使用,则不需要考虑同步,因为SQLite内部实现了线程同步。 但使用多个SQLite时仍需考虑同步
    • 若存储数据到内存等,需手动实现线程同步

    ContentResolver:通过Uri操作ContentProvider的数据
    外部进程,通过ContentResolver , 与 ContentProvider 交互

    为何不允许直接交互?
    一款应用若使用多个contentProvider,直接交互需了解每个contentProvider的不同实现,成本高

    提供了增删改查四个外部类
    提供了三个辅助工具类: ContentUris、UriMatcher、ContentObserver

    1.5.2 ContentResolver 辅助工具类

    ContentUris : 操作URI

          parseId()   withAppendedId()
    

    UriMatcher:
    向ContentProvider注册URI : matcher.addURI()
    根据URI匹配ContentProvider中对应的数据表:matcher.match()

    ContentObserver:内容观察者。观察指定URI引起的ContentProvider的数据变化,通知外界【即ContentObserver注册者】。即当ContentProvider的数据发生变化时,会触发ContentObserver的监听

        getContentResolver().registerContentObserver(URI)
        @Override onChange()
    

    相关文章

      网友评论

          本文标题:安卓-面试篇 基础版 一

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