Android 进阶——Framework核心 之Binder Java成员类详解(三)
文章大纲
- 引言
- 一、Binder Java家族核心成员关系图
- 二、Binder Java家族核心成员源码概述
- 1、android.os.IBinder
- 1.1、boolean transact(int code, Parcel data, Parcel reply, int flags) send a call to an IBinder object
- 1.2、String getInterfaceDescriptor()
- 1.3、boolean pingBinder() 检测Binder是否存活
- 1.4、boolean isBinderAlive()检测托管进程是否存在
- 1.5、 IInterface queryLocalInterface(String descriptor) 获取Binder对应的本地实现对象
- 1.6、android.os.IBinder.DeathRecipient
- 1.7、android.os.IBinder 小结
- 2、android.os.Binder 和 android.os.BinderProxy
- 2.1、Binder 构造方法
- 2.2、attachInterface(IInterface owner, String descriptor) 初始化
- 2.3、boolean onTransact(int code, Parcel data, Parcel reply,
- 2.4、android.os.BinderProxy
- 3、android.os.IInterface
- 4、Stub和Stub.Proxy角色
引言
上一篇文章解读了Binder native家族成员的关系和功能,不过呢,和这篇文章关系也不是很大,不存在先后的顺序,因为这篇文章是主要解读Binder 代码层面主要Java家族成员类功能和部分代码的,目的是从代码和逻辑设计对Binder 有更深的理解。不过呢,Android中Java 、Native层使用同一套架构实现Binder服务,而且Java层最终是通过JNI调用Native 层的实现的,因此可以对照native层学习Java层将更容易掌握,所以建议先看native层的。
一、Binder Java家族核心成员关系图
主要有IBinder、IInterface、 Binder、BinderProxy类和Stub、Stub.Proxy角色。
- IBinder接口——代表一种跨进程传输的能力,实现这个接口就能将这个对象进行跨进程传递。
- Binder类——Binder本地对象。
- BinderProxy类——代表服务进程的Binder对象的本地代理。
- Parcel类——主要用于存储序列化数据,然后可以通过Binder在进程间传递这些数据。
- IInterface接口——Client端与Server端的调用契约(提供什么样的能力),实现这个接口就代表远程Server对象具有什么能力,其asBinder方法可以将Binder本地对象或代理对象返回。
- Stub角色(Local-side IPC implementation stub class)——是AIDL接口的静态内部抽象类,继承Binder并实现了具体的IInterface派生接口,负责把Binder转为Stup.Proxy角色。
- Stub.Proxy角色——Binder代理对象,Stub的静态内部类同样实现了AIDL接口派生类,真正去实现Binder#transact方法去实现AIDL接口方法的跨进程通信逻辑。
asInterface方法——当客户端bindService的onServiceConnecttion的回调里面,通过asInterface方法获取远程的service的。其函数的参数IBinder类型的obj,这个对象是驱动给我们的,如果是Binder本地对象,那么它就是Binder类型,如果是Binder代理对象,那就是BinderProxy类型。asInterface方法中会调用queryLocalInterface,查找Binder本地对象,如果找到,说明Client和Server都在同一个进程,这个参数直接就是本地对象,直接强制类型转换然后返回,如果找不到,说明是远程对象那么就需要创建Binder代理对象,让这个Binder代理对象实现对于远程对象的访问首先用Parcel把数据序列化,然后调用mRemote.transact方法,mRemote就是new Stub.Proxy(obj)传进来的,即BinderProxy对象
二、Binder Java家族核心成员源码概述
Framework层的Binder通过JNI来调用(C/C++)层的Binder框架,从而为上层应用程序提供服务。而Java层在命名与架构上和Native也非常相近,基本上是无缝衔接,同样也实现了一套类似的IPC通信架构供应用开发使用,当然最后都是通过Native层的Binder框架实现的。
1、android.os.IBinder
IBinder
是远程对象的Base接口,不仅可以在跨进程可以调用,也可以在进程内部调用。IBinder接口代表一种跨进程传输的能力,实现这个接口(不过通常都是去继承其子类android.os.Binder)就能将这个对象进行跨进程传递,IBinder用于定义了与远程对象进行交互的抽象协议。
1.1、boolean transact(int code, Parcel data, Parcel reply, int flags) send a call to an IBinder object
public boolean transact(int code, Parcel data, Parcel reply, int flags)
transact
方法是Binder IPC机制中真正去执行跨进程的方法,通过把序列化后的对象封装为Parcel 数据进行传递。换言之,所有跨进程的方法的调用最终都是会经过transact。通过**transact
方法可以向一个IBinder对象发送调用请求**,而在**Binder.onTransact
方法接收到一个Binder对象的调用**。transact()方法是同步调用,通常调用发出后会阻塞直到对方的Binder.onTransact()
方法调用完成后才会返回。
参数 | 说明 |
---|---|
code | “协议”代码,取值为android.os.IBinder#FIRST_CALL_TRANSACTION+N,用AIDL时与定义接口方法的顺序有关 |
data | 传输给服务端的数据,如果你没有传输任何数据,你必须创建一个空的Parcel实例。 |
reply | 从服务端接收的数据,如果不需要处理返回的值,传入null |
flags | IPC逻辑控制,如传入0表示是普通调用,而当且仅当调用者和响应者在不同进程时,传入android.os.IBinder#FLAG_ONEWAY时表示单向RPC,调用者会马上返回,而不必等结果从响应者响应之后再返回。 |
Parcel(一个通用缓冲区具体实现在Native层)除了有数据外还带有一些描述它内容的元数据(保存着IBinder对象的引用)。这样就能在缓冲区从一个进程移动到另一个进程时,保存这些引用。这确保了当一个 IBinder 在A进程内被写入到一个 Parcel 并发送到B进程后,B进程能将同一个 IBinder 的引用发送回A进程且确保A进程将收到相同的 IBinder 对象。因此可以将 IBinder/Binder 对象用作可以跨进程管理的唯一TOKEN。此外,系统为每一个进程维持一个Binder线程池,用于派发所有从其他进程发来的IPC调用。当进程A向进程B发起一个IPC 调用,A发出后,调用的线程就阻塞在transact()方法中了,进程B中Binder线程池的接收并调用Binder.onTransact(),完成后返回一个Parcel,然后A中的之前等待的线程在收到返回的Parcel才能继续执行,让你感觉像是在一个进程内线程之间的通信一样,实际上却是进程间的通信。
Binder机制还支持进程间的递归调用,transact()方法将在IBinder所在的进程不存在时抛出RemoteException异常
1.2、String getInterfaceDescriptor()
获取Binder 对应的标识,后续传入到queryLocalInterface中得到相应的IInterface对象。
1.3、boolean pingBinder() 检测Binder是否存活
如果目标进程不存在,那么调用pingBinder()时返回false,否则返回另一端的 pingBinder() 实现返回的结果(默认始终为 true)。
1.4、boolean isBinderAlive()检测托管进程是否存在
检查Binder所在的进程是否还活着,返回false则表示已经死亡,返回true有可能是正在返回结果或者活跃状态。
1.5、 IInterface queryLocalInterface(String descriptor) 获取Binder对应的本地实现对象
获取 Binder 对象的对应的本地实现对象, 返回null,则需要实例化一个代理类。
1.6、android.os.IBinder.DeathRecipient
监听当前进程所托管的Binder对象死亡的接口(只有一个方法android.os.IBinder.DeathRecipient#binderDied),可以通过linkToDeath(DeathRecipient recipient, int flags)方法注册监听IBinder生命周期,当Binder对象死亡时会被触发,而在不需要监听时调用unlinkToDeath(DeathRecipient recipient, int flags)。
1.7、android.os.IBinder 小结
总之,Binder 机制就是让我们感觉到跨进程的调用与进程内的调用没有什么区别。
- IBindre是远程对象的基接口,不仅可以在跨进程可以调用,也可以在进程内部调用
- 在远程调用的时候,一端用IBinder.transact()发送(封装为Parcel同步调用),另一端用Binder的Binder.onTransact()接受。
- 系统为每个进程维护一个进行跨进程调用的Binder线程池。
2、android.os.Binder 和 android.os.BinderProxy
android.os.Binder 和android.os.BinderProxy实现了android.os.IBinder接口,因而都可以被跨进程传输,在跨越进程的时候,Binder驱动会自动完成这两个对象的转换。Binder 是IBinder的标准实现,IPC 执行时直接继承的基类,但只是一个basic IPC primitive,它对应用程序的生命周期没有影响且只有在创建它的进程继续运行时才有效,因此要正确使用它必须在android.app.Service、android.app.Activity 或 android.content. ContentProvider里让创建它的进程一致存活。
2.1、Binder 构造方法
只有一个构造方法,由Native 进行,会在Stub 自动被调用。Binder实现了IBinder接口,但是一般不需要直接实现此类,而是跟据你的需要由开发包中的工具根据AIDL语法自动生成Binder的派生类。
2.2、attachInterface(IInterface owner, String descriptor) 初始化
初始化Binder和对应的标识,在Stub 构造函数中被调用并传入Stub自身对象和对应的全类名。
public static abstract class Stub extends android.os.Binder implements com.xxx.netservice.ICallback{
private static final java.lang.String DESCRIPTOR = "com.xxx.netservice.ICallback";
/** Construct the stub at attach it to the interface. */
public Stub(){this.attachInterface(this, DESCRIPTOR);
}
2.3、boolean onTransact(int code, Parcel data, Parcel reply,
int flags)
Binder中对应IBinder transact的实现,本质上就是调用Parcel 进行类“IO"操作。
2.4、android.os.BinderProxy
BinderProxy类也实现IBinder接口且是final类型的,主要API 的实现都是通过JNI调用Native 层的Binder,代表远程进程的Binder对象的本地代理,它的transact方法通过JNI去借助Binder驱动完成数据的传输,当完成数据传输后,会唤醒Server端,调用了Server端本地对象的onTransact函数,在Server进程里面,onTransact根据调用code会调用相关函数,接着将结果写入reply并通过super.onTransact返回给驱动,驱动唤醒挂起的Client进程里面的线程并将结果返回。
3、android.os.IInterface
Binder 的基类接口(定义的AIDL 实现时直接继承这个IInterface),主要是用于把IInterface 转为IBinder,以便确保代理对象可以返回正确的结果。
public interface IInterface{/*** Retrieve the Binder object associated with this interface.You must use this instead of a plain cast, so that proxy objects can return the correct result.*/public IBinder asBinder();
}
4、Stub和Stub.Proxy角色
Stub(抽象类)和Stub.Proxy(实体类) 是AIDL提供的标准实现定义的静态内部类,Stub.Proxy类和Stub类,都实现了AIDL接口,分别对应于本地进程和远程进程的Binder实例。其中Stub类在Server进程中实例化并通过 Service.onBind
方法传递给 Client 进程;而Client 进程接收到 IBinder 以后,通过 Stub.asInterface
方法转换成 AIDL接口之后使用。Stub.asInterface
在本地进程工作时直接返回 Stub 实例,否则创建一个 Proxy 实例来代理通讯。
Stub#onTransact方法该方法运行在服务端中的Binder线程池中,服务端通过code确定客户端所请求的目标方法是什么,从data中取出目标方法所需的参数,然后执行目标方法,并将目标方法的返回值写入reply。如果该方法的返回false,则表示客户端的请求回失败,可以利用这个特性来做权限验证。
Stub.Proxy实现aidl接口并将方法的标识,参数和返回值对象传入方法mRemote.transact中,发起远程调用请求,同时挂起当前线程,然后服务端的Stub#onTransact被调用,当onTransact返回true,调用成功,从reply对象中取出返回值,返回给客户端调用方,当Client 进程使用时通过 Stub.asInterface 创建,同时会将方法调用的数据,都通过mRemote 转发给Server端的 Binder 实例。
package com.xx.netservice;public interface IBaseIPC extends android.os.IInterface {public static abstract class Stub extends android.os.Binder implements com.xx.netservice.IBaseIPC {private static final java.lang.String DESCRIPTOR = "com.xx.netservice.IBaseIPC";public Stub() {this.attachInterface(this, DESCRIPTOR);}/*** 无论是哪个进程,拿到反序列化的 IBinder 对象以后,通过这个静态方法来获取ICallback接口。* 如果是 Server 进程(local),可以从 binder 中直接取出之前 attach的IInterface 实例,那么调用ICallback的方法,就相当于直接调用Stub 实例的方法了。* 如果是 Client 进程,IBinder 只是系统在远程创建的一个 Proxy 类并无实现,因此iin 将变成 null,此时 asInterface 会创建一个Stub.Proxy 代理类,来实现 IBaseIPC 接口。*/public static com.xx.netservice.IBaseIPC asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.xx.netservice.IBaseIPC))) {return ((com.xx.netservice.IBaseIPC) iin);}return new com.xx.netservice.IBaseIPC.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {java.lang.String descriptor = DESCRIPTOR;switch (code) {// Server 进程的 Binder,在这里接收 transaction,并将数据反序列化以后,调用 IBaseIPC 抽象方法。case TRANSACTION_baseIPC: {data.enforceInterface(descriptor);java.lang.String _arg0;_arg0 = data.readString();int _arg1;_arg1 = data.readInt();boolean _arg2;_arg2 = (0 != data.readInt());this.IBaseIPC(_arg0, _arg1, _arg2);reply.writeNoException();return true;}default: {return super.onTransact(code, data, reply, flags);}}}/*** Client 进程持有的代理类,通过 Stub.asInterface 创建,Proxy 也实现了 IBaseIPC,会将方法调用的数据,都通过 mRemote 转发给远程的 Binder 实体。*/private static class Proxy implements com.xx.netservice.IBaseIPC {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic void baseIPC(java.lang.String type, int deviceType, boolean status) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeString(type);_data.writeInt(deviceType);_data.writeInt(((status) ? (1) : (0)));mRemote.transact(Stub.TRANSACTION_baseIPC, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}}static final int TRANSACTION_baseIPC = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public void baseIPC(java.lang.String type, int deviceType, boolean status) throws android.os.RemoteException;
}
未完待续…
相关文章:
Android 进阶——Framework核心 之Binder Java成员类详解(三)
文章大纲引言一、Binder Java家族核心成员关系图二、Binder Java家族核心成员源码概述1、android.os.IBinder1.1、boolean transact(int code, Parcel data, Parcel reply, int flags) send a call to an IBinder object1.2、String getInterfaceDescriptor()1.3、boolean ping…...
Maven
Maven 1.什么是Maven 官方网站 https://maven.apache.org/ Maven是一款服务于Java平台的自动化构建工具,它可以帮助我们更方便的对项目进行构建、管理项目jar包 ,包括: bulid 项目,切换 jar 版本,添加 jar, 删除 jar 包等 1.…...
1947抓住那头牛(队列 广度优先搜索)
目录 题目描述 解析 解题思路 代码部分 代码部分 运行结果 看看len数组中各个位置的标记值 为什么这样做一定是最短路径: 题目描述 农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上,农夫起始位于点N(0<N<100000)&…...
基于linux5.15.5的IMX 参考手册 ---21
基于linux5.15.5的IMX 参考手册 — 21 10.5.2高清多媒体接口(HDMI)和显示端口(DP)概述 10.5.2.1测试名称 •mxc_cec_test.out 10.5.2.1.1位置 /unit_tests/HDMI/ 10.5.2.1.2功能 验证HDMI CEC功能并向HDMI接收器发送断电命令。 1…...
Android Dalvik虚拟机 堆初始化流程
前言 上篇文章介绍了dalvik虚拟机启动流程,在dalvik虚拟机启动时调用了dvmGcStartup来启动堆。 本文介绍我们在日常开发使用Java时的堆创建流程。 Dalvik堆介绍 Dalvik虚拟机中,堆是由heap[0] Active堆和heap[1] Zygote堆两部分组成的。其中ÿ…...
0讲(补)——开发前必备基本常识
前言 专栏内容持续补充更新,目前正在进行优惠活动 目录 前言 一、函数的声明和定义 二、预编译 三、串口打印中的printf函数的使用...
JS学习笔记
1.WebAPIs简介导读Web APIs 和JS 基础关联性JS 基础阶段以及 Web APIs 阶段JS基础学习 ECMAScript 基础语法为后面作铺垫,Web APIs 是JS 的应用,大量使用JS基础语法做交互效果①JS 基础阶段我们学习的是ECMAScript 标准规定的基本语法要求同学们掌握JS 基…...
linux005之用户、组管理
linux用户管理简介: 任何使用linux系统的用户,都必须使用一个合法的账号和密码,账号和密码一般都是超级管理员创建,当然普通用户也可以创建用户,前提是必须拥有创建用户权限。 root是linux系统中默认创建的超级用户 创…...
列线图工具_Nomogram
定义 列线图是一种相对传统的分析方法,用于展示自变量和因变量的线性关系,及其特征的重要程度。 现在用SHAP,和机器学习库中的 Feature importance 工具可以实现类似甚至更好效果。不过很多传统的研究领域比较认这种方法。 列线图工具建立在…...
【C++】类和对象(一)
目录一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装4.1、访问限定符4.2、封装五、类的作用域六、类的实例化七、类对象的大小八、this指针8.1、this指针的引出8.2、this指针的特性8.3、C语言和C实现Stack的对比一、面向过程和面向对象初步认…...
Python获取搜索引擎结果
前言 想快速获取各个高校的博士招生网站,于是通过python先获取出有可能包含高校博士招生网站的URL,然后通过人为筛选得到了想要的招生网站(注意,并非直接爬取,是间接获取的)。 整理了一份网站名单&#x…...
2.4.8 PCIe——物理逻辑层——REFCLK
一、概述 pcie的参考时钟由板级输入,提供给IP内PHY层的PLL使用,由PLL产生core_clk和pipe_clk。 二、REFCLK产生方式 Serdes 所用时钟由 PHY 模块内的PLL生成,PLL的参考时钟可以由common clock(外部背板提供)、separ…...
树莓派4B arm64 搭建 docker+drone+gitea
树莓派4B arm64 搭建 dockerdronegitea 记录时间: 2023年02月10日 树莓派烧录 如何用树莓派搭建一台永久运行的个人服务器? https://mp.weixin.qq.com/s?__bizMzI5NjA0ODkwNA&mid2651847658&idx1&sn267a1257b43d4a76f2a081ed157b77f9&chksmf7b11…...
Java的JDBC编程
目录 1. 打开IDEA,新建Project 2. 引入依赖 (1)下载驱动包 (2)将驱动包导入Project 3. 编写代码 (1)创建数据源 (2)让代码和数据库服务器建立联系 (3&…...
CSS:块格式化上下文(BFC)
块格式化上下文是块级盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。 块格式化上下文(BFC)的创建 满足以下条件将创建块格式化上下文: 根元素()浮动元素(float 值不为 none)绝对定位元素…...
paddle表情识别部署
表情识别模块1.环境部署1.1同样采用fastDeploy库1.2相关模型2.封装成静态库2.1参考[百度Paddle中PP-Mattingv2的部署并将之封装并调用一个C静态库](https://blog.csdn.net/weixin_43564060/article/details/128882099)2.2项目依赖添加2.3生成成功3.test3.1创建emotion_test项目…...
Python-第五天 Python函数
Python-第五天 Python函数一、函数介绍1. 什么事函数二、函数的定义1.函数的定义:2.案例三、函数的参数1.函数的传入参数2.案例升级四、函数的返回值1.什么是返回值2.返回值的语法3.None类型4.None类型的应用场景五、函数说明文档1.函数的说明文档2.在PyCharm中查看…...
【Python学习笔记】28.Python3 错误和异常
前言 作为 Python 初学者,在刚学习 Python 编程时,经常会看到一些报错信息,在前面我们没有提及,这章节我们会专门介绍。 Python3 错误和异常 Python 有两种错误很容易辨认:语法错误和异常。 Python assert…...
SQLServer 迁移到 MySQL 工具对比
我之所以会写这篇对比文章,是因为公司新产品研发真实经历过这个痛苦过程(传统基于 SQL Server开发的C/S 产品转为 MySQL云产品)。首次需要数据转换是测试环节,当时为了快速验证新研发云产品性能与结果准确性(算法类&am…...
分析finebi5.x仪表板组件获取数据过程(数据是数据集或者sql的)
首先仪表板的公共连接类似:http://localhost:37799/webroot/decision/link/Bo6B 当我们访问这个连接时,会来到FineLinkAction的getShareReport方法。 public String getShareReport(HttpServletRequest req, HttpServletResponse res, @FinePathVariable("linkId"…...
设计模式--适配器模式 Adapter Pattern
设计模式--适配器模式 Adapter Pattern适配器模式 Adapter Pattern1.1 基本介绍1.2 工作原理类适配器模式对象适配器模式接口适配器模式小结适配器模式 Adapter Pattern 1.1 基本介绍 (1)适配器模式将某个类的接口转换成为客户端期望的另一个接口表示&…...
PVE虚拟机篇-rest api
rest api官方介绍 Proxmox VE API rest api文档 rest api文档 rest api token 调用pve rest api ,有两种认证方式 Ticket Cookie Ticket Cookie的方式是最为推荐的,获取的方式为,通过post请求,发送用户名和密码到pve的server端获取tok…...
2022-2025学年面向中小学生的白名单全国性竞赛活动清单及官网地址链接
**资料来源:爬虫爬取。** 教育部办公厅 工业和信息化部办公厅关于公布 首批特色化示范性软件学院名单的通知 教育部办公厅 工业和信息化部办公厅关于公布首批特色化示范性软件学院名单的通知 - 中华人民共和国教育部政府门户网站 教育部办公厅关于2022-2025学年面向中小学生…...
Python 高级编程之生成器与协程进阶(五)
文章目录一、概述二、生成器1)生成器和迭代器的区别2)生成器创建方式1、通过生成器函数创建2、通过生成器表达式创建3)生成器表达式4)yield关键字5)生成器函数6)return 和 yield 异同7)yield的使…...
Django框架之视图和URL
视图和URL 站点管理页面做好了, 接下来就要做公共访问的页面了.对于Django的设计框架MVT. 用户在URL中请求的是视图.视图接收请求后进行处理.并将处理的结果返回给请求者.使用视图时需要进行两步操作 1.定义视图2.配置URLconf 1. 定义视图 视图就是一个Python函数,…...
Python 的Tkinter包系列之七:好例子补充2
Python 的Tkinter包系列之七:好例子补充2 英汉字典(使用文本文件记录英语单词和解释)、简单的通信录(使用SQLite数据库记录人员信息) 一、tkinter编写英汉字典 先看效果图: 词典文件是一个文本文件&…...
每日一练-等差数列
等差数列🍀题目描述🌿解题思路🌸Python源码📧Summary📆Date: 2023年2月10日 🎬Author: 小 y 同 学 📃Classify: 蓝桥杯每日一练 🔖Language: Python 🍀题目描述 题意 …...
使用动态参数构建CUDA图
文章目录使用动态参数构建CUDA图使用显式 API 调用构建 CUDA 图使用流捕获构建 CUDA 图组合方法执行结果总结使用动态参数构建CUDA图 自从在 CUDA 10 以来,CUDA Graphs 已被用于各种应用程序。 上图将一组 CUDA 内核和其他 CUDA 操作组合在一起,并使用指…...
在Fortran中调用Python教程
前言Python是机器学习领域不断增长的通用语言。拥有一些非常棒的工具包,比如scikit-learn,tensorflow和pytorch。气候模式通常是使用Fortran实现的。那么我们应该将基于Python的机器学习迁移到Fortran模型中吗?数据科学领域可能会利用HTTP AP…...
04-PS人像磨皮方法
1.高斯模糊磨皮 这种方法的原理就是建立一个将原图高斯模糊后图层, 然后用蒙版加画笔或者历史画笔工具将需要磨皮的地方涂抹出来, 通过图层透明度, 画笔流量等参数来控制磨皮程度 1.新建图层(命名为了高斯模糊磨皮), 混合模式设置为正常, 然后选择高斯模糊, 模糊数值设置到看…...
网站改版建设征求意见书/国际军事形势最新消息
1. BBC — 透视未来 简介:这里满满都是有证有据的干货,每天你都能通过里面的文章透过现象看到事物的本质,让你每天都会比昨天的你变得更聪明!这里的文章包罗万象,涵盖了如《女人高潮之谜》的下里巴人,到…...
如果在浏览器上做一网站广告大约需要多少钱/百度快速排名培训
全球及中国稀土抛光材料行业趋势展望及发展规划研究报告2021-2027年版 2020年,全球稀土抛光材料市场规模达到了 百万美元,预计2027年可以达到 百万美元,年复合增长率(CAGR)为 % (2021-2027)。中国市场规模增长快速,预计将由202…...
ftp删除wordpress插件/网址搜索引擎入口
【实例简介】Android利用Timer和Handler实现倒计时功能,包含暂停倒计时、停止倒计时【实例截图】【核心代码】TimerDemo.tar└── TimerDemo├── AndroidManifest.xml├── bin│ ├── classes│ │ └── com│ │ └── snowdream│ │ └─…...
利用第三方做网站永久发布地址/一份完整的活动策划方案
一 . redis主从同步 准备三个配置文件,实现一主两从的redis数据库结构(这三个配置文件仅仅端口不一样) # redis-6379.conf 文件, 写入下面数据:port 6379 daemonize yes pidfile /data/6379/redis.pid loglevel notice logfile "/data/6379/redis.log" dbfilename du…...
企业网站自己可以做/网络营销图片
23种设计模式(2)-工厂模式定义:工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔…...
欧美农庄网站模板/设计网络营销方案
目录 条件渲染 与v-show的区别 列表渲染 条件渲染 如此使用: <script setup> import { computed,reactive } from vue; const poem reactive({ title: 古诗合集, author: 李商隐, poem_LiBai : [ 我歌月徘徊,我舞影零乱。,且放白鹿青崖间。,两…...