2023年11月27日发(作者:)

Android源码解析--ClipBoardService(粘贴板)服务详解

ClipBoardService是Android的粘贴板服务,我们的复制粘贴都需要通过这个服务来完成。

1、与ClipBoardService相关的类

如下图所⽰, ClipBoardService服务核⼼的⼏个类:

ardManager: 继承⾃ardManager, 这是⼀个兼容性的设计, 早期android只⽀持

text复制。APP应⽤就是拿到这个对象来调⽤粘贴板相关的服务。

ClipBoardService: 粘贴板服务的服务端,各个应⽤的调⽤的复制粘贴都要到这个服务来处理。

ClipData: 顾名思义,就是管理保存粘贴数据的, 具体的数据存储在成员变量mItems中, 这个变量是⼀个Item类型的数组, 每⼀个

Item表⽰⼀项数据。

ClipDataDescription: ⽤来描述ClipData中数据的类型,Android剪切板⽀持三种类型:Text、Intent、以及URI。

从⼀个应⽤复制数据,然后被封装成ClipData对象传输给ClipboardService,粘贴的应⽤从ClipboardService获取到复制数据的应⽤上传

的ClipData对象,然后把数据解析出来,基本的复制粘贴就可以完成了。但是Android的ClipboardService所提供的功能远远不⽌这些,

既然ClipboardService可以传输Uri和Intent,那么要实现复制粘贴什么数据,便可以由APP本⾝发挥巨⼤的想象空间。如复制⼀张图⽚,

可以先把图⽚的Uri通过复制粘贴到⽬标应⽤程序,⽬标应⽤程序接收到这个Uri,通过content provider就可以凭Uri取得图⽚。

2、在SystemServer中添加ClipBoardService服务

ClipBoardService⼀样是在中添加的:

public ClipboardService(Context context) {

mContext = context;

mAm = ault();//获取ActivityManager对象

mPm = kageManager();//获取PackageManager

mUm = (IUserManager) vice(_SERVICE);//获取UserManager⽤户管理类

mAppOps = (AppOpsManager)temService(_OPS_SERVICE);//获取权限管理类

IBinder permOwner = null;

try {

permOwner = PermissionOwner("clipboard");

} catch (RemoteException e) {

Slog.w("clipboard", "AM dead", e);

}

mPermissionOwner = permOwner;

//注册了⼀个⼴播, Android⽀持多⽤户,当某个⽤户被删除后, 需要在ClipBoardService中移除此⽤户

IntentFilter userFilter = new IntentFilter();

ion(_USER_REMOVED);

erReceiver(new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

String action = ion();

if (_USER_(action)) {

removeClipboard(Extra(_USER_HANDLE, 0));

}

}

}, userFilter);

Intent appIntent = new Intent(this, );

...

ClipData clip = ent("Intent",appIntent);

maryClip(clip);

4APP从粘贴板获得数据进⾏粘贴

4.1 粘贴⽂本

// 获取Clipboard Manager

ClipboardManager clipboard = (ClipboardManager) getSystemService(ARD_SERVICE);

String pasteData = "";

if (!(maryClip())) {

//假设此应⽤程序⼀次只能处理⼀个项⽬。

item = maryClip().getItemAt(0);

// 假设只有⽂本,只处理⽂本

pasteData = t();

}

public void setPrimaryClip(ClipData clip, String callingPackage) {

synchronized (this) {

if (clip != null && mCount() <= 0) {

throw new IllegalArgumentException("No items");

}

//1、检查相关权限 start

//获取进程UID 此时若是A进程通过Binder调⽤了setPrimaryClip⽅法,则获得的就是A进程的UID

final int callingUid = lingUid();

if ((_WRITE_CLIPBOARD, callingUid,

callingPackage) != _ALLOWED) {

return;

}

checkDataOwnerLocked(clip, callingUid);

//1、检查相关权限 end

final int userId = rId(callingUid);

PerUserClipboard clipboard = getClipboard(userId);

revokeUris(clipboard);

setPrimaryClipInternal(clipboard, clip);

List related = getRelatedProfiles(userId);//⽤来判断当前⽤户是否有复制权限

if (related != null) {

int size = ();

if (size > 1) {

boolean canCopy = false;

try {

canCopy = !rRestrictions(userId).getBoolean(

public ClipData getPrimaryClip() {

try {

return getService().getPrimaryClip(ackageName());

} catch (RemoteException e) {

return null;

}

}

这⾥也是通过Binder 实际上调⽤的ClipBoardService的getPrimaryClip:

public ClipData getPrimaryClip(String pkg) {

synchronized (this) {

if ((_READ_CLIPBOARD, lingUid(),

pkg) != _ALLOWED) {

return null;

}

//赋予该pkg相应权限, 下⾯第6节会分析

addActiveOwnerLocked(lingUid(), pkg);

//返回ClipData给客户端

return getClipboard().primaryClip;

}

//2、如果包含uri 则返回uri相关内容

Uri uri = getUri();

if (uri != null) {

//2.1、如果能将uri作为纯⽂本流打开,这是最好的显⽰⽅式

FileInputStream stream = null;

try {

AssetFileDescriptor descr = tentResolver()

.openTypedAssetFileDescriptor(uri, "text/*", null);

stream = InputStream();

InputStreamReader reader = new InputStreamReader(stream, "UTF-8");

//2.2 得到⽂本流,将其返回

StringBuilder builder = new StringBuilder(128);

char[] buffer = new char[8192];

int len;

while ((len=(buffer)) > 0) {

(buffer, 0, len);

}

return ng();

} catch (FileNotFoundException e) {

//2.3 ⽆法作为⽂本流打开

1、CBS作为系统服务,是在SystemServer中被添加注册的;

-** 2、CBS可以复制粘贴的数据类型有三种:⽂本、URI、Intent;**