13.Android中touch事件的传递机制是怎样的?
1.Touch事件传递的相关API有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
2.Touch事件相关的类有View、ViewGroup、Activity
3.Touch事件会被封装成MotionEvent对象,该对象封装了手势按下、移动、松开等动作
4.Touch事件通常从Activity#dispatchTouchEvent发出,只要没有被消费,会一直往下传递,到最底层的View。
5.如果Touch事件传递到的每个View都不消费事件,那么Touch事件会反向向上传递,最终交由Activity#onTouchEvent处理.
6.onInterceptTouchEvent为ViewGroup特有,可以拦截事件.
7.Down事件到来时,如果一个View没有消费该事件,那么后续的MOVE/UP事件都不会再给它
TCP报文的header中有6个比特位,其中就有SYN和ACK位 (依次为URG,ACK,PSH,RST,SYN,FIN)
1. 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
2. 第二次握手:服务器收到SYN报文段。服务器将客户端发过来的报文段中的Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
3. 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
======================================================================
SYN-Flood正是利用了上文中TCP协议的设定,达到攻击的目的。攻击者伪装大量的IP地址给服务器发送SYN报文,由于伪造的IP地址几乎不可能存在,也 就几乎没有设备会给服务器返回任何应答了。因此,服务器将会维持一个庞大的等待列表,不停地重试发送SYN+ACK报文,同时占用着大量的资源无法释放。 更为关键的是,被攻击服务器的SYN_RECV队列被恶意的数据包占满,不再接受新的SYN请求,合法用户无法完成三次握手建立起TCP连接。
防御:启用SYN Cookie、设置SYN最大队列长度以及设置SYN+ACK最大重试次数 | 限制同时打开的Syn半连接数目,缩短Syn半连接的time out 时间
======================================================================
[ 为什么要三次握手 ]
概述:
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
在书中同时举了一个例子,如下:
“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”
=============================================================
v5.0之后引入RenderThread(openGL的):分担主线程的压力
=============================================================
-
防止内存抖动(GC执行时会使其他线程等待):用对象池模型,通过对象池来解决频繁创建与销毁的问题(volley中的ByteArrayPool)
-
SparseArray替代HashMap<Integer, V>
-
ArrayMap (5.1上Bundle.mMap就是ArrayMap)
-
FloatMath:替代Math类的float运算(乘方、开方、sin三角函数), 针对android的JIT做优化,native方法
-
android的String类的indexOf()和equals()是用native方法实现的
-
AtomicFile: 多线程读写文件
-
尽量为所有分辨率创建资源:
减少不必要的硬件缩放,这会降低UI的绘制速度,可借助Android asset studio
-
提前编译正则表达式
private final Pattern mPattern = Pattern.compile(_regex);
[ 网络优化 ]
-
OkHttp 支持在糟糕的网络环境下面更快的重试 (less_read: Okhttp调整图片的预先抓取算法,确保app中下载队列前面的图片被优先处理,防止队列阻塞时间过长)
-
http 304 if-None-Match__ETag
-
JobService (in API-21)
[ 调优 ]
-
adb shell procrank 分析内存占用
-
adb shell dumpsys meminfo package_name 分析内存占用
-
adb shell dumpsys batterystats 分析耗电量
<图片优化>
-
MipMap文件夹
-
WebP图片
<布局调优>
HierarchyViewer、LayoutOpt
-
少用layout_weight(导致measure两次)
-
尽量多使用RelativeLayout,不要使用绝对布局AbsoluteLayout;
-
< include /> 提高 内聚性(复用性);
-
使用< ViewStub />标签来加载一些不常用的布局;
-
使用< merge />标签减少布局的嵌套层次;
==========================================================
**********************************************************************************************
这个自然段-略:内存分析:MAT,DDMS,LeakCanary(Square),infer(Facebook)
静态分析:Find Bugs,Lint
压力测试:Monkey
自动化测试: UiAutomator,MonkeyRunner,Rubotium,Athrun(淘宝)
[ 调试 ]
-
traceview 和 dmTraceDump
{
Debug.startMethodTracing(“test”);
....
Debug.stopMethodTracing();
}
-
StrictMode: 规范化,ThreadPolicy/VmPolicy,dump_to_log,可供分析
-
HProf (Heap Profile)
- android.os.Debug.dumpHprofData(“/data/tmp/input.hprof”);
- 使用命令hprof-conv把input.hprof转成MAT识别的标准的output.hprof
- 用MAT查看
-
DDMS:
threads, heap, Allocation_Tracker子页面
-
BuildConfig.DEBUG: log开关,关闭log提升新能。
-
unCaughtExceptionHandler 发布后可用于收集
-
android_studio logcat 自动按包名过滤
-
android_studio crash 直接定位到代码行,看蓝色的,不像eclispe还有看是否是本程序的代码
-
Memory Monitor(Android-Studio上专有的)
=========================================================
缓存
a. 线程池
b. Android图片缓存,Android图片Sdcard缓存,数据预取缓存
c. 消息缓存
通过handler.obtainMessage复用之前的message,如下:
handler.sendMessage(handler.obtainMessage(0, object));
d. ListView缓存
e. 网络缓存
数据库缓存http response,根据http头信息中的Cache-Control域确定缓存过期时间。
f. 文件IO缓存
=========================================================
a. 数据类型选择
字符串拼接用StringBuilder代替String,
64位类型如long double的处理比32位如int慢
使用SoftReference、WeakReference相对正常的强应用来说更有利于系统垃圾回收
final类型存储在常量区中读取效率更高
LocalBroadcastManager代替普通BroadcastReceiver,效率和安全性都更高
逻辑优化
这个不同于算法,主要是理清程序逻辑,减少不必要的操作。
=========================================================
activity被onStop之后,不会被lowMemoryKiller蛮横的干掉,先兆是activity的onTrimMemory()被回调, 你应该释放UI持有的资源
当你启动一个服务时,系统一般会保持住它的进程。这使得这个服务进程开销很大,因为它所占用的内存不会被其他程序使用,也不能被换出。这回降低系统 在LRU缓存中能够保持的线程数,会使app切换变得很低效。当内存吃紧时也会造成系统的不稳定,因为系统可能不会为所有服务维持住足够的进程。
限制服务寿命的最好方法是使用IntentService,主要因为当捕获到某个Intent时就会启动,任务执行结束也会自杀。更多信息请阅读Running in a Background Service 。
if u wanna tell user about service's_exec_result, use NotificationBar
让你不再需要服务仍然处于运行状态是安卓内存管理中最最严重的错误。所以,不要贪图让你的服务一直运行着,这不仅不会增加你的App表现欠佳的风险,用户最终也会发现这些不良行为,然后把你的App卸载之。
================================================================================
-
“过度绘制(overdraw)”:这在GPU绘制完背景后再在上面绘制其它图形工件时就会发生。过度绘制太多会杀死应用的性能。Android支持“GPU过度绘制调试”模式,高亮过度绘制区域,使用不同的颜色展示重复绘制的次数。这样,开发者就有了线索,知道应该修改哪个区域。
0. 尽量多使用RelativeLayout和LinearLayout, 不要使用绝对布局AbsoluteLayout,
1. 在布局层次一样的情况下, 建议使用LinearLayout代替RelativeLayout, 因为LinearLayout性能要稍高一点.
2. 在完成相对较复杂的布局时,建议使用RelativeLayout,RelativeLayout可以简单实现LinearLayout嵌套才能实现的布局.
2. 将可复用的组件抽取出来并通过include标签使用;
3. 使用ViewStub标签来加载一些不常用的布局;
4. 动态地inflation view性能要比SetVisiblity性能要好.当然用VIewStub是最好的选择.
5. 使用merge标签减少布局的嵌套层次
6. 去掉多余的背景颜色
7. 对于有多层背景颜色的Layout来说,留最上面一层的颜色即可,其他底层的颜色都可以去掉
8. 对于使用Selector当背景的Layout(比如ListView的Item,会使用Selector来标记点击,选择等不同的状态),可以将normal状态的color设置为”@android:color/transparent”,来解决对应的问题
9. 内嵌使用包含layout_weight属性的LinearLayout会在绘制时花费昂贵的系统资源,因为每一个子组件都需要被测量两次。在使用ListView与GridView的时候,这个问题显的尤其重要,因为子组件会重复被创建.所以要尽量避免使用Layout_weight
10. 使得Layout宽而浅,而不是窄而深(在Hierarchy Viewer的Tree视图里面体现)
-
渲染管道:UI渲染耗时与层次中的视图数量成正比。Android SDK层次查看器可以帮助开发者检查视图层次,找出将其扁平化的方法。它还提供了有关每个视图渲染消耗多少时间的性能分析信息
多版本兼容 for Android
-
指定minSdkVersion和targetSdkVersion,如果开发的app不够新式,可指定maxSdkVersion
-
Build.VERSION.SDK_INT
-
support_v4(v1.6)_v7(v2.1)_v13(v3.2)_v17(4.2)
-
layout-v11 | style-v17 View.setElevation
-
<manifest ... >
<uses-feature android:name="android.hardware.sensor.compass"
android:required="true" /> (不同设备多样的硬件/软件特性)
-
</manifest>
-
PackageManager pm = getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_COMPASS)) {
// This device does not have a compass, turn off the compass feature
disableCompassFeature();
}
2.0:startService导致调用service导致调用onStart(), 2.0之后是onStartCommand()
在 Froyo(2.2) 之前,HttpURLConnection 有个重大 Bug,调用 close() 函数会影响连接池,导致连接复用失效,所以在 Froyo 之前使用 HttpURLConnection 需要关闭 keepAlive
3.0: propertyAnimation
fragment
4.4 ART vs davik 吹davik是JIT,运行会卡顿,art缺点是开机很慢
5.0 material_design, appCompatV7, Desgin-Support-Lib, TransitionAnim_is_not_support_under_5.0
公共技术点之View 绘制流程
公共技术点之View 绘制流程
|
setMeasuredDimension()
测量阶段终极方法,在 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法中调用,将计算得到的尺寸,传递给该方法,测量阶段即结束
公共技术点之View 绘制流程
|
在ViewRoot.java类的performTraversals()函数展开,该函数所做 的工作可简单概况为是否需要重新计算视图大小(measure)、是否需要重新安置视图的位置(layout)、以及是否需要重绘(draw)
公共技术点之View 绘制流程
|
-
invalidate()
请求重绘 View 树,即 draw 过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些调用了invalidate()方法的 View。
-
requestLayout()
当布局变化的时候,比如方向变化,尺寸的变化,会调用该方法,在自定义的视图中,如果某些情况下希望重新测量尺寸大小,应该手动去调用该方法,它会触发measure()和layout()过程,但不会进行 draw。
Handler Looper MessageQueue 详解
吹epoll_wait for sendMessageAtTime()
=====================================================
[Less important]
Looper和Handler这两个类实现了一种通用的并发模型,我把它叫做:Pipeline 线程。它是这样工作的:
-
Pipeline 线程持有一个任务队列(a queue of tasks),这些任务就是一些可以执行的工作单元(units of work)
-
其他线程可以自由的将任务加到Pipeline线程的任务队列中去
-
Pipeline线程就按次序一个一个执行任务,如果任务队列中没有任务了,它就会自动阻塞直到有任务到来
-
有些时候,任务可以叫做消息(messages)或者其他名字
Handler Looper MessageQueue 详解 - Cloud Chou's Tech Blog |
enqueueMessage用于将Message对象插入消息队列,消息队列的消息是按待处理时间排序的, handleCallback处理先于handleMessage
摘要 --- Handler Looper MessageQueue 详解 - Cloud Chou's Tech Blog |
Looper的构造器方法里会创建当前线程的MessageQueue实例,并将mThread指向当前线程
摘要 --- Handler Looper MessageQueue 详解 - Cloud Chou's Tech Blog |
Looper的设计使用了多线程设计模式里的Thread Specifical Storage模式,属性sThreadLocal为每个线程存储Looper对象
Handler Looper MessageQueue 详解 - Cloud Chou's Tech Blog |
使用Looper时,调用完Looper.prepare之后,还需要调用Looper.loop方法,该方法是一个死循环,会不断从MessageQueue里取出消息,并调用消息关联的Handler对象来处理该消息
Handler Looper MessageQueue 详解 - Cloud Chou's Tech Blog |
负责管理消息队列,实际上Message类有一个next字段,会将Message对象串在一起成为一个消息队列,所以并不需要LinkedList之类的数据结构将Message对象组在一起成为队列。
最重要的方法是MsgQueue.next(),用于获取下一个Message对象,如果没有需要处理的Message对象,该方法将阻塞
Handler Looper MessageQueue 详解 - Cloud Chou's Tech Blog |
管理Message对象时,Android采用了消息池,可以有效节省内存
Handler Looper MessageQueue 详解 - Cloud Chou's Tech Blog |
当前线程也只需要调用Looper.myLooper()即可得到当前线程的Looper对象
当前进程Write->唤醒目标进程,同时当前进程进入read等待,目标进程处理完毕后,唤醒当前进程,同时目标进程进入read等待。
-
mmap ServiceManger_Daemon
-
IPCThreadState接收到了Client处的请求后,就会调用 BBinder类的transact函数,并传入相关参数,BBinder类的transact函数最终调用BnMediaPlayerService类 的onTransact函数,于是,就开始真正地处理Client的请求了
-
在用户空间分配一个虚拟地址,然后让这个用户空间虚拟地址与 t->buffer->data 这个内核空间虚拟地址指向同一个物理地址
-
和进程外com组件的机制很相似,基于stub/proxy机制,在client和server进程分别提供stub/proxy,通过对象 的序列化和反序列化屏蔽进程,达到直接调用远程对象接口的效果,本质上还是把请求和应答通过RPC介质(例如共享内存,管道,Binder驱动)进行进程 间传递。
首先server和client都调用了ProcessState的构造函数从而都向驱动申请了一块物理内存用于通信数据的存放,然后:
1.server 调用SM的addService()传递一个字符串和实际Binder对象在自己虚拟地址空间的地址到Binder驱动,Binder驱动记录下该地址 值,在SM申请的物理内存中分配一个虚拟地址并连同字符串一起传递给SM,而且Binder驱动会记录下两个地址之间的对应关系.
2.client 调用SM的getService()传递一个字符串给SM,SM将相对应的虚拟地址值传递给Binder驱动,Binder驱动查询出实际对象的地址,在 client申请的物理内存中分配一个虚拟地址并传递给client,而且Binder驱动会记录下这个地址和实际Binder对象地址之间的对应关 系,client这里得到的就是实际Binder的引用了.
到了这一步,真正的Binder对象就拥有两个引用,一个在SM,一个client.
3.client通过得到的Binder引用调用server中的函数,驱动会根据传递过来引用值找到应该处理该请求的进程,并唤醒其中的Binder线程,调用BBinder对象的onTransaction函数,最终调用到实际的函数。
Svclist中存储了服务的名字和Handle,这个Handle作为Client端发起请求时的目标地址。服务通过add_service方法将自己的名字和Binder标识handle登记在svclist中。而服务请求者,通过check_service方法,通过服务名字在service list中获取到service 相关联的Binder的标识handle,通过这个Handle作为请求包的目标地址发起请求。
(2) 从c++空间传入内核Binder设备,使用ProcessState类完成工作。
===================================================================
[why choose Binder as IPC, not share_mem, msg_queue, etc ?]
Android的libc(a.k.a bionic)库不支持System V IPCs
socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。
-
copy-time-is-acceptable:
msg-queue && pipe采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。
share-mem虽然无需拷贝,但控制复杂,难以使用
binder 一次copy: 其实 binder 也是这样的,不过它把内核空间的地址和用户空间的虚拟地址映射到了同一段物理地址上,所以就只需要把数据从原始用户空间复制到内核空间,把目标进程用户空 间和内核空间映射到同一段物理地址,这样第一次复制到内核空间,其实目标的用户空间上也有这段数据了
Per-process thread pool for processing requests ( MediaPlayerService最多可同时启动15个线程来处理Client端的请求。)
-
Reference counting
-
Synchronous calls between processes
Binder的局限性
-
每个进程最多支持15个Binder线程
-
Binder限制运行在每个进程中的并发事务的缓存为1M
************************************************************************************************************************************
-
对象的索引和映射
Binder中的一个重要概念就是对象的映射和索引。就是要把对象从一个 进程映射到另一个进程中,以实现线程迁移的概念。前面描述过Binder的一个重要概念是进程/线程迁移,即当一个进程需要同另一个进程通信时,它可以 “迁移”远程的进程/线程到本地来执行。对于调用进程来说,看起来就像是在本地执行一样。这是Binder与其他IPC机制的不同点或者说是优点。当然迁 移的工作是由Binder驱动来完成的,而实现的基础和核心就是对象的映射和索引。
Binder通信操作类似线程迁移(thread migration),两个进程间IPC看起来就象是一个进程进入另一个进程执行代码然后带着执行的结果返回;
Binder的用户空间为每一个进程维护着一个可用的线程池,线程池用于处理到来的IPC以及执行进程本地消息,Binder通信是同步而不是异步。
========================================
Client、Server和Service Manager通过open和ioctl文件操作函数与Binder驱动程序进行通信
Service Manager是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力
========================================
总结一下,Service Manager是成为Android进程间通信(IPC)机制Binder守护进程的过程是这样的:
1. 打开/dev/binder文件:open("/dev/binder", O_RDWR);
2. 建立128K内存映射:mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
3. 通知Binder驱动程序它是守护进程:binder_become_context_manager(bs);
4. 进入循环等待请求的到来:binder_loop(bs, svcmgr_handler);
在这个过程中,在Binder驱动程序中建立了一个struct binder_proc结构、一个struct binder_thread结构和一个struct binder_node结构,这样,Service Manager就在Android系统的进程间通信机制Binder担负起守护进程的职责了。
========================================
BnMediaPlayerService并不是直接接收到Client处发送过来的请求,而是使用了IPCThreadState接收Client处发送过来的请求,而IPCThreadState又借助了ProcessState类来与Binder驱动程序交互
IPCThreadState接收到了Client处的请求后,就会调用 BBinder类的transact函数,并传入相关参数,BBinder类的transact函数最终调用BnMediaPlayerService类 的onTransact函数,于是,就开始真正地处理Client的请求了。
========================================
flat_binder_obj
唤醒Service Manager进程
Binder机制用的是类似浅拷贝的方法,通过在用户空间分配一个虚拟地址,然后让这个用户空间虚拟地址与 t->buffer->data 这个内核空间虚拟地址指向同一个物理地址,这样就可以实现浅拷贝了。怎么样用户空间和内核空间的虚拟地址同时指向同一个物理地址呢?
只要将t->buffer->data加上一个偏移值proc->user_buffer_offset就可以得到t->buffer->data对应的用户空间虚拟地址了
ONE_WAY为false,那就不需要等待回复了 (即给驱动程序一个确认)
实际上就是调用talkWithDriver来等待Client的请求,然后再调用executeCommand来处理请求,而在executeCommand函数中,最终会调用BBinder::transact来真正处理Client的请求
BnMediaPlayerService::onTransact函数(真正的业务逻辑):
-
status_t BnMediaPlayerService::onTransact(
-
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-
{
-
switch(code) {
-
case CREATE_MEDIA_RECORDER: {
-
......
-
} break;
binder线程通过一个while循环来不断进入binder驱动去轮循自己是否有新的binder进程间通信请求需要处理。
等到有新的binder进程间通信请求需要处理它或者它所属进程来处理时,binder线程就会被唤醒。
当调用从用户空间调用writeTransactionData函数进入到内核空间的binder_transaction函数的时候,就会从用户空间的 binder_transaction_data结构体缓冲区数据拷贝到空核空间的binder_transaction_data结构体缓冲区去
class BnBackupService: public BnInterface<IBackupService>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
const sp<IBackupService>& Backup::getBackupService()
{
Mutex::Autolock _l(sLock);
if (sBackupService.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("backupservice"));
if (binder != 0)
break;
MLOGW("BackupService not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
binder->linkToDeath(sBackup);
sBackupService = interface_cast<IBackupService>(binder);
}
ALOGE_IF(sBackupService==0, "no BackupService!?");
return sBackupService;
}
调用defaultServiceManager的目的就是为了在本进程内获得一个的Service Manager代理,更具体的说,是为了获得本进程内的Service Manager代理单例。
========================================
晰。不过感觉和进程外com组件的机制很相似,基于stub/proxy机制,在client和server进程分别提供stub/proxy,通过对象 的序列化和反序列化屏蔽进程,达到直接调用远程对象接口的效果,本质上还是把请求和应答通过RPC介质(例如共享内存,管道,Binder驱动)进行进程 间传递。当然Binder通过自己的方式解决了一次RPC需要2次内存拷贝的问题。
是的,Binder进程间通信机制和COM、CORBA这些进程间通信方式在方向上都是一致的,都是有Proxy和Stub的概念,所不同的是中间件的实 现方式,例如COM在用户空间提供了COM运行时来作为调用者和被调用者之间的桥梁,而Binder在内核空间提供了Binder驱动程序来作为调用者和 被调用者之间的桥梁
========================================
< 实例流程 >
[bk_jni.cpp]
static void native_setup(JNIEnv* env, jobject thiz, jobject weak_this) {
MLOGV("native_setup");
sp<Backup> mp = new Backup(); // IMPORTANT result-to-call: [binder = sm->getService(String16("backupservice")) ]
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// Stow our new C++ Backup in an opaque field in the Java object.
setBackup(env, thiz, mp);
}
static jboolean backup_data(JNIEnv* env, jobject thiz, jstring jPackageName, jstring jBackupDir)
{
sp<Backup> backup = getBackup(env, thiz);
if (backup == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return false;
}
if (jPackageName == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return false;
}
if (jBackupDir == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return false;
}
const char *packageName = env->GetStringUTFChars(jPackageName, NULL);
if (packageName == NULL) { // Out of memory
return false;
}
const char* backupDir = env->GetStringUTFChars(jBackupDir, NULL);
if (backupDir == NULL) { // Out of memory
env->ReleaseStringUTFChars(jPackageName, packageName);
packageName = NULL;
return false;
}
MLOGV("backupData: package %s, backupDir %s", packageName, backupDir);
status_t opStatus = backup->backupData(packageName, backupDir); // 线索
MLOGV("backupData return: %d", opStatus);
env->ReleaseStringUTFChars(jPackageName, packageName);
packageName = NULL;
env->ReleaseStringUTFChars(jBackupDir, backupDir);
backupDir = NULL;
return opStatus == 0;
}
========================================================
[bk.cpp]
Backup::Backup()
{
sBackup = this;
getBackupService();
}
const sp<IBackupService>& Backup::getBackupService()
{
Mutex::Autolock _l(sLock);
if (sBackupService.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("backupservice")); // IMPORTANT
if (binder != 0)
break;
MLOGW("BackupService not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
binder->linkToDeath(sBackup);
sBackupService = interface_cast<IBackupService>(binder);
}
ALOGE_IF(sBackupService==0, "no BackupService!?");
return sBackupService;
}
status_t Backup::backupData(const char* packageName, const char* backupDir) {
if (sBackupService == NULL) {
getBackupService();
}
// 拿到binder对象,就可以调用远程service的在本地的“代理者IBackupService”的方法了
return sBackupService->backupData(packageName, backupDir);
}
=============================================================================
[IBkService.cpp]
status_t backupData(const char* packageName, const char* backupDir)
{
Parcel data, reply;
data.writeInterfaceToken(IBackupService::getInterfaceDescriptor());
data.writeCString(packageName);
data.writeCString(backupDir);
remote()->transact(BACKUP_DATA, data, &reply); // IMPORTANT
return reply.readInt32();
}
// remote()可能就是BnBackupService,即BackupService(因为:BackupService继承自BnBackupService)
status_t BnBackupService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case BACKUP_DATA: {
CHECK_INTERFACE(IBackupService, data, reply);
const char* packageName = data.readCString();
const char* backupDir = data.readCString();
// client-passed-arg is serilized into "parcel"
int32 result = backupData(packageName, backupDir);
reply->writeInt32(result);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
========================================