这是我参与「第四届青训营 」笔记创作活动的第1天。
本课讲述了客户端开发的一些基础知识,包括四大组件、通信组件等等。
1. 基础组件
1.1 界面组件
1.1.1 Activity(界面容器)
作用
Activity用于前端交互,提供程序入口,是布局容器。
基本用法
在AndroidManifest.xml中声明,使用Java或kotlin编写类文件;声明布局文件(activity_main.xml);在类文件中进行绑定。
生命周期
onCreate():创建时回调,再次创建试图和绑定数据
onStart():已启动,但界面依旧不可见
onResume():与用户开始交互,界面可见
onPause():Activity暂停,界面依旧可见
onStop():Activity不可见
onRestart():重启已停止的Activity
onDestory():销毁Activity,释放资源
onSaveInstanceState():非正常关闭时回调,保存数据
onRestoreInstanceState():恢复数据
启动退出:
打开页面:onCreate()-onStart()-onResume()
关闭页面:onPause()-onStop()-onDestroy()(可能走不到onDestory(),因此一些数据的保存应放在onPause()或onStop()中,若需兼容Android4,结束计时应写在onPause()中,若不兼容,则需写在onStop())
部分遮挡(弹出对话窗、请求权限):-onPause()-onResume()-
页面全遮挡:-onStop()-onRestart()-onStart()-onResume()-
配置改变(语言发生变化、全局系统设置发生变化):
未配置:
销毁:Resumed-onSaveInstanceState()-onPause()-onStop()-onDestory()
重建:onCreate()-onStart()-onRestoreInstanceState()-onResume()
已配置:
若不想重建Activity,则可在AndroidManifest中配置Activity节点的configChange属性local(语言改变)、fontScale(字体大小改变)、orientation(旋转屏幕)、keybordHidden(键盘显示隐藏),此时Activity经过的过程为onConfigurationChanged()
后台回收:
销毁时系统不会通知,若要存数据可在onStop()或onPause()进行存储或者适配系统提供的生命周期方法onSaveInstanceState()
重建:onCreate()-onStart()-onRestoreInstanceState()-onResume()
在基本了解Activity的生命周期之后,我们来看一个案例。
案例:接电话Crash问题
问题原因:页面被回收导致本地变量被置空
解决方案:
1添加判空逻辑避免空指针
2在页面回收生命周期中存储数据,页面重建时恢复
启动模式
standard:默认模式,允许重复
single Top:栈顶复用,不允许连续重复
single Task:栈内复用,不允许同个栈内重复
singleInstance:全局复用,整个系统不允许重复
1.1.2 Fragment
作用
轻量级页面容器,可与用户进行交互,解决页面碎片化问题,速度快,可做一部分的组件分离。
基本用法
1 创建Fragment布局文件;
2 创建Fragment子类,加载布局文件;
3 Activity加载Fragment(与Activity进行绑定)。
两种方法:静态加载:布局中绑定;
动态绑定:FragmentManager加载
生命周期
onAttach():Fragment和Activity建立关联时调用
onCreateView():建立视图时调用
onActivityCreated():Activity的onCreate()方法已返回时调用
onDestroyView():视图被移除时调用
onDetach():Fragment和Activity取消关联时调用
启动:
onAttach()-onCreate()-onCreateView()-onActivityCreated()-onStart()-onResume()-Resumed
退出:Resumed-onPause()-onStop()-onDestoryView()-onDestroy()-onDetach()
部分覆盖:Resumed-onPause()-Paused
部分覆盖恢复:Paused-onResume()-Resumed
完全覆盖:Resumed-onPause()-onStop()-onDestroyView()
完全遮挡恢复:onCreateView()-onActivityCreated()-onStart()-onResume()-Resumed
ps:Fragment生命周期可通过FragmentTransaction.setMaxLifecycle()手动干预
组件获取
Fragment获取Activity中的组件(须先绑定):getActivity().findViewById(R.id.XXX)
Activity获取Fragment中的组件:getFragmentManager.findFragmentById(R.id.fragment_XXX)
数据获取
Activity传数据给Fragment:setArguments(Bundle bundle)(数据封装在Bundle,常用于启动时给Fragment一些初始化参数、数据)
Fragment传数据给Activity:
a 直接getActivity拿对象,拿到对象后直接调用对象方法;或让Activity实现一个接口,通过接口调用
b 通过viewmodel/handler/broadcast/eventbus等通信
1.2服务组件
service:无界面的后台任务
基本用法
1.注册:AndroidManifest中使用<service.../>标签
2.创建:建立相应service类
3.加载:startService()/bindService()
生命周期(两种模式)
常用:
onStart()、onBind()(绑定)、onCreate()、onDestory
与Activity通信
1.定义Binder子类,实现getService()方法,返回service对象
2.实现Service类onBind()方法,返回上述Binder对象
3.实例化ServiceConnection对象,实现onServiceConnected()方法,从中获取Service实例
4.Activity中调用bindService()方法,并传递第三步的ServiceConnection对象
5.Activity既可通过调用Service实例中的方法进行直接通信
1.3广播组件
作用
将系统事件通知给应用进行响应。
基本用法
静态广播
1.注册:AndroidManifest中使用<receiver.../><intent-filter.../>
2.创建:建立相应的BroadcastReceiver实现类
3.接收:在第二步的类onReceive()中接收广播
4.发送:Context.sendBroadcast()
动态广播(有生命周期限制)
1.注册:Context.registerReceiver()
常用系统广播
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.4 数据组件
作用
用于数据通信,拿系统数据
基本用法
生产者
注册:AndroidManifest中使用<provider.../>
(属性:authorities/exported/readPermission/writePermission)
创建:建立相应的ContentProvider实现类(类里实现的方法:onCreate/getType/insert(增)/delete(删)/update(改)/query(查))
消费者
声明:AndroidManifest声明权限
使用:context.getContentResolver() 方法:insert/delete/update/query
案例:扫描系统现有图片
cur = MediaStore.Images.Thumbnails.queryMiniThumbnails(
context.getContentResolver(),//获取相册缩略图对象
MediaStore.Images.Thumbnails.EXTRNAL_CONTENT_URI,
MediaStore.Images.Thumbnails.MINI_KIND,projection);//Thumbnails指缩略图,context.getContentResolver()获取相册缩略图对象
if(cur != null && cur.moveToFirst()){
do{
String imageId = cur.getString(cur.getColumnIndex(MediaStore.Images.Thumbnails.IMAGE_ID));
String imagePath = cur.getString(cur.getColumnIndex(MediaStore.Images.Thumbnails.DATA));
thumbnailMap.put(imageId,imagePath);
}while(cur.moveToNext() && !cur.isLast());//while循环遍历缩略图对象
}
1.5 意图组件
作用
描述需求让系统进行处理。
1.Context.startActivity(Intent)
2.Context.startService(Intent)
3.Context.startBroadcast(Intent)
基本用法
显示Intent
setComponent/setClass指定意图处理的类对象,将其实例化并调用
隐式Intent
Action(动作)
Data(数据)
Category(类别)
Type(数据类型)
Component(组件)
Extra(扩展信息)
Flag(标志位)
系统能力
电话:Intent(Intent.ACTION_DIAL,Uri.parse("tel:10010"))
短信:Intent(Intent.ACTION_SENDTO,Uri.parse("smsto:10010"))
网页:Intent(Intent.ACTION_VIEW,Uri.parse("[http://www.baidu.com](http://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)实现
2 通信组件
2.1 Handler
作用
处理主线程之间通信
基本用法
1.创建:新建Handler,实现handlerMessage(Message)//处理给主线程发的事件、通知等消息,进行响应
2.构造Message:what/setData()
3.发送:子线程调用Handler.sendMessage(Message)发送Message
4.处理:在Handler的handleMessage(Message msg)主线程更新UI
核心原理
消息队列
2.2 Binder
作用
进程间通信
常用IPC方案
| IPC方式 | 数据拷贝次数 |
|---|---|
| 共享内存 | 0 |
| Binder | 1 |
| Socket/管道/消息队列 | 2 |
为何Binder不共享内存
Linux的进程隔离,两个进程内存空间是独立的,之间有权限控制。
基本用法
服务端:
1.定义一个AIDL文件
2.实现描述的接口,编写service
3.若有实体类,需提供实体类(jar包形式)
客户端:
1.拿到AIDL文件
2.绑定服务,获得接口持有对象
核心原理
匿名共享内存、Binder驱动、ServiceManager
通信过程
APP启动场景Binder处理方式
AMS SystemService与Zygote为何是使用Sokect通信?
- Binder通讯是需要多线程操作的,代理对象对Binder的调用是在Binder线程,需要再通过Handler调用主线程来操作。
- 若父进程(如zygote)binder线程有锁,然后子进程的主线程一直在等其子线程(从父进程拷贝过来的子进程)的资源,但是其实父进程的子进程并没有被拷贝过来,造成死锁,所以fork不允许存在多线程。而非常巧的是Binder通讯偏偏就是多线程,所以干脆父进程(Zgote)这个时候就不使用binder线程。
参考文章:Android Framework层学习——为什么SystemServer进程与Zygote进程通讯采用Socket而不是Binder_Kyrie_Wangyz的博客-CSDN博客_zygote 为什么用socket