安卓基础组件概念篇|青训营笔记

123 阅读6分钟

安卓基础组件概念篇|青训营笔记

这是我参与「第四届青训营 」笔记创作活动的第1天

1.Android 需求引入

需求-图片浏览器

  • 实现一个图片浏览app(Activity+Fragment)
  • 旋转屏幕场景(Activity生命周期)
  • 内置升级能力(Service使用)
  • 单一首页(SingleTask)
  • 相关图片(SingleTop)
  • 扫描所有图片(ContentProvider)
  • 提供图片选择能力给系统(Intent)

1.1 Activity基本用法:

  • 声明在程序清单里(Androidmanifest.xml)
  • kotlin或java将MainActivity的类文件写出
  • 声明布局文件layout/activity.main.xml 注册->布局->绑定

相册列表页:GridView+Adaptor

相册图片页:GridView+Adaptor(换数据源)

大图页:Viewpager+Adaptor

问题:接个电话就crash了?->生命周期

原因:页面被回收导致本地变量被置空

问题解决:

添加判空逻辑避免空指针问题

在页面回收生命周期中储存数据,页面重建时进行恢复

1.1.1 Activity生命周期
  • onCraete():创建时回调,一般在此处创建视图和绑定数据
  • onStart(): 已启动,即将进入前台
  • onResume(): 与用户开始交互,位于Activity栈顶(程序开始可见)
  • onPause(): Activity失去焦点或已暂停,Activity页面部分可见,下一个生命周期是onResume()或onStop()
  • onStop(): Activity不再可见,下一个回调是onRestart()或onDestory()
  • onRestart():重启已停止的Activity,下一个回调是onStart()
  • onDestory(): 销毁Activity,释放该Activity的所有资源
  • onSaveInstanceState(): 在非正常关闭时回调,用于保存数据,不支持持久化数据(系统回收界面)
  • onRestoreInstanceState()/onCreate():用于恢复数据
Case1部分遮挡:

部分遮挡:弹出dialog或请求授权等等 ->onPause() 遮挡恢复:重新回到onResume()。

Case2全遮挡:

页面全遮挡:->onStop() 遮挡恢复: ->onRestart()->onStart()->onResume()

Case3+4:配置改变场景

Case3配置改变场景(1):

销毁:Resumed()->onSaveIntanceState()->onPause()->onStop()->onDestory()

Case4配置改变场景(2):

重建:onCreate()->onStart()->onRestpreInstanceState()->onResume()

配置改变但不重建Activity:AndroidManifest配置Activity节点的configChange属性

  • local:语言改变
  • fontScale:字体大小改变
  • orientation:旋转屏幕
  • keyboardHidden:键盘显示隐藏 配置改变:onConfigurationChanged()

引子:加入主页按钮功能

问题:不特殊处理情况下,加入首页按钮之后,点击会在当前页面栈加入新首页,返回后页面会消失

预期:点击首页按钮回到首页,上面的页面全部退出

解决:将首页的launchmode设置为singleTask

1.1.2Activity启动模式
-standard:默认模式,允许重复
-singleTop:不允许连续重复 ->onNewIntent()
-singleTask: 不允许同个栈内重复
-singleInstance:整个系统不允许重复 (整个安卓系统只有一个)

例子:通讯录,银行登录界面

注:引自Android 关于SingleInstance Activity的注意事项 - 掘金 (juejin.cn)

  • 启动顺序为:ActivityA->ActivityB(SingleInstance )->ActivityC ,点击Back退出顺序:C-》A-》B !!!

  • 启动顺序为:ActivityA->ActivityB(SingleInstance ),点击HOME键再切回应用,展示A而不是B。

  • 以该模式启动的Activity单独位于一个栈

思考:图库app需要每一个页面都是activity吗?

1.2Fragment

原因:解决页面碎片化 优点:速度快,进行组件分离

1.2.1Fragment基本用法
  • 创建Fragment布局文件

  • 创建Fragment子类,加载布局文件

  • Activity加载Fragment

    静态加载:布局中绑定

    动态加载:FragmentManager加载

1.2.2Fragment生命周期
  • onAttach(): Fragment和Activity关联时调用
  • onCreateView():当Fragment创建视图时调用
  • onActivityCreated():Activity的onCreate()方法已返回时调用
  • onDestoryView():当Fragment中的视图被移除时调用
  • onDetach():Fragment和Activity取消关联时调用
CASE1启动:

启动:onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()->Resumed

CASE2退出:

退出: Resumed->onPause()->onStop()->onDestoryView()->onDestory()->onDetach()

CASE3部分遮挡:

部分覆盖:Resumed->onPause()->Paused

CASE4部分遮挡恢复:

部分遮挡恢复:Paused->onResume()->Resumed

CASE5完全覆盖:

完全覆盖:Resumed->onPause()->onStop()->onDestoryView()

CASE6完全遮挡恢复:

完全遮挡恢复: onCreateView()->onActivityCreated()>onStart()->onResume()->Resumed

注:Fragment生命周期可以通过FragmentTransaction.setMaxLifecycle手动干预

1.2.3Fragment与Activity交互
组件获取
  • Fragement获取Activity中的组件:getActivity().finViewById(R.id.xxx)
  • Activity获取fragment中的组件:getFragmentManager.findFragmentById(R.id.fragment_xxx)
数据获取

1.Activity传数据给Fragment:setArguments(Bundle bundle) 常用于启动

2.Fragment传数据给Activity:

   a.通过对象直接传递(方法调用/接口调用) 直接getActivity

   b.通过viewmodel/handler/broadcast/eventbus等通信
   

1.3 Service

服务

1.3.1 Service基本用法
  • 注册:AndroidManifest中使用<service.../>的标签
  • 创建:建立相应的Service实现类
  • 加载:startService()/bindService()
1.3.2 Service生命周期

常用

  • onStart()
  • onBind()
  • onCreate()
  • onDestory()
1.3.3 Service与Activity通信

1.定义Binder子类,并实现getService()方法,返回Service对象

2.实现Service类onBind()方法,返回上述Binder对象

3.实例化ServiceConnection对象,实现onServiceConnected()方法,从中获取到Service实例

4.Activity中调用bindService()方法,并传递步骤3的ServiceConnecttion对象,将流程跑起来

5.Activity既可以通过调用Service实例中的方法进行直接通信

1.4 Broadcast

WHAT:没电,充电等等提示广播

1.4.1 Broadcast基本用法

静态广播

  • 注册:AndroidManifest中使用<receiver.../><intent-filter.../>
  • 创建:建立相应的BroadcastReceiver实现类
  • 接受:在步骤2类onReceive()中接收广播
  • 发送:Context.sendBroadcast()

动态广播

注册:Context.registerReceiver() 有生命周期限制

1.4.2 Broadcast常用系统广播 配合权限实现
  • Intent.ACTION_CONNECTIVITY_CHANGE 网络发生变化
  • Intent.ACTION_BATTERY_CHANGED 电量发生变化
  • Intent.ACTION_SCREEN_ON 开屏
  • Intent.ACTION_SCREEN_OFF 关屏
  • Intent.ACTION_PACKAGE_INSTALL 安装应用程序
  • Intent.ACTION_BOOT_COMPLETED 系统启动完成
  • Intent.ACTION_PACKAGE_ADDED 安装程序
  • Intent.ACTION_PACKAGE_REPLACED 程序更新
  • Intent.ACTION_PACKAGE_REMOVED 卸载程序

1.5 ContentProvider

用于数据通信

1.5.1 基本用法

生产者

  • 注册:AndroidManifest中使用<provider.../>

    属性:anthorities/exported/readPermission/writePermission

  • 创建:建立相应的ContentProvider实现类

    方法:onCreate/getType/insert/delete/update/query

消费者

  • 声明:AndroidManifest中声明权限
  • 使用:context.getContentResolver() 方法:insert/delete/update/query 增删改查

Intent

部分控件通过Intent启动 “意图”

  • Context.startActivity(Intent)
  • Context.startService(Intent)
  • Context.startBroadcast(Intent)
1.6.1用法

1.显式Intent

setComponent/setClass指定具体类 2.隐式Intent

  • Action 动作
  • Data 数据
  • Category 类别
  • Type 数据类型
  • Component 组件
  • Extra 扩展信息
  • Flag 标志位 实现不了SingleInstance
1.6.2系统能力
  • 电话:Intent(Intent_Action_DIAL,Uri.parse("tel:10010"))

  • 短信:Intent(Intent_Action_SENDTO,Uri.parse("smsto:10010")) 数据头区别邮件短信

  • 网页:Intent(Intent_Action_View,Uri.parse("https:www.baidu.com"))

  • 邮件:Intent(Intent_Action_SENDTO,Uri.parse("mailto:someone@domain.com"))

  • 地图:Intent(Intent_Action_View,Uri.parse("geo:39.9,116.3")) 经纬度

  • 拍照:Intent(MediaStore.ACTION_IMAGE_CAPTURE)

  • 设置:Intent(android.provider.Settings.ACTION_SETTINGS)

  • 市场:Intent(Intent.ACTION_View,Uri.parse("market://details?id="+packageName))

    使用:startActivity(Intent)

Android通信组件

多线程/多进程通信

2.1Handler

线程间通信:主线程通信

2.1.1基本用法
  • 创建:新建Handler,实现handleMessage(Message)
  • 构建Message:what/setData()
  • 发送:子线程调用Handler.sendMessage(Message)发送Message
  • 处理: 在Handler的handleMessage(Message msg)主线程更新UI
2.1.2核心原理

message query: 消息队列

2.2Binder

进程间通信:进程绑定

共享内存:数据拷贝次数 0 Linux进程隔离 内核空间才可以

Binder: 数据拷贝次数 1 效率高 CS架构 通信时Binder也隔离

Android Binder通信一次拷贝你真的理解了吗? - IT先森的个人空间 - OSCHINA - 中文开源技术交流社区

Socket: 数据拷贝次数 2

2.2.1基本用法

服务端

  • 定义一个AIDL文件
  • 实现描述的接口,编写service
  • 如果有实体类,需要提供实体类(jar包形式)

客户端

  • 拿到AIDL文件
  • 绑定服务,获得接口持有对象
2.2.2核心原理

CS架构 Binder驱动 ServiceManager进程 代理模式 匿名共享内存

注:looper有唤醒机制,不会一直占用