Android源码解析--ClipBoardService(粘贴板)服务详解
ClipBoardService是Android的粘贴板服务,我们的复制粘贴都需要通过这个服务来完成。
1、与ClipBoardService相关的类
如下图所⽰, ClipBoardService服务核⼼的⼏个类:端午节祝福语简短语句
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⼀样是在SystemServer.java中添加的:
ServiceManager.addService(Context.CLIPBOARD_SERVICE,
new ClipboardService(context));
看⼀下构造⽅法做了些什么:
public ClipboardService(Context context) {
mContext = context;
mAm = Default();//获取ActivityManager对象
mPm = PackageManager();//获取PackageManager
mUm = (IUserManager) Service(Context.USER_SERVICE);//获取UserManager⽤户管理类
mAppOps = (SystemService(Context.APP_OPS_SERVICE);//获取权限管理类
IBinder permOwner = null;
try {
permOwner = wUriPermissionOwner("clipboard");
} catch (RemoteException e) {
Slog.w("clipboard", "AM dead", e);
}
mPermissionOwner = permOwner;
//注册了⼀个⼴播, Android⽀持多⽤户,当某个⽤户被删除后,需要在ClipBoardService中移除此⽤户
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
@Override
public void onReceive(Context context, Intent intent) {
String action = Action();
if (Intent.ACTION_USER_REMOVED.equals(action)) {
IntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
}
}, userFilter);
}
构造⽅法⽅法中只是初始化了⼀些参数, 并注册了⼀个⼴播,⽤来监听Android多⽤户的移除操作。
下⾯将从⼀个APP使⽤粘贴板来进⼀步了解ClipBoardService
3、APP复制数据到粘贴板
不再让你孤单 歌词3.1 复制⽂本
APP在使⽤粘贴板时, ⾸先拿到ClipboardManager对象, 通过这个对象就可以和ClipBoardService进⾏IPC通信。
//获取ClipboardManager对象
ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
//把⽂本封装到ClipData中
ClipData clip = wPlainText("simple text","Hello, World!");
// Set the clipboard's primary clip.
赵丽颖恋情clipboard.setPrimaryClip(clip);
3.2 复制URI
//联系⼈的URI
private static final String CONTACTS = "content://acts";
private static final String COPY_PATH = "/copy";
Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName);
...
ClipData clip = wUri(getContentResolver(),"URI",copyUri);
clipboard.setPrimaryClip(clip);
3.3 复制Intent
Intent appIntent = new Intent(this, application.class);
.
相思成灾歌词..
ClipData clip = wIntent("Intent",appIntent);
clipboard.setPrimaryClip(clip);
4、APP从粘贴板获得数据进⾏粘贴
4.1 粘贴⽂本
// 获取Clipboard Manager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
吴奇隆现任老婆String pasteData = "";
if (!(clipboard.hasPrimaryClip())) {
//假设此应⽤程序⼀次只能处理⼀个项⽬。
ClipData.Item item = PrimaryClip().getItemAt(0);
// 假设只有⽂本,只处理⽂本
pasteData = Text();
}
4.2 粘贴URI
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
// Gets the clipboard data from the clipboard
ClipData clip = PrimaryClip();
if (clip != null) {
//这⾥只处理uri
ClipData.Item item = ItemAt(0);
Uri pasteUri = Uri();
}
4.3 粘贴Intent
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
//这⾥值处理intent
Intent pasteIntent = PrimaryClip().getItemAt(0).getIntent();
###4.4 第4⼩节 总结
上⾯的⽰例代码中都⽐较简单,实际使⽤中,可能需要同时判断从粘贴板蝴获得的数据属于text、URI、Intent中的哪⼀种,并且都要进⾏判空等处理。
5 复制和粘贴过程中ClipBoardService的源码分析
5.1 复制过程的setPrimaryClip⽅法
前⼀个⼩节已说到,数据赋值时, 会调⽤ClipBoardManager的setPrimaryClip, 看⼀下这⾥⾯的代码
逻辑:
public void setPrimaryClip(ClipData clip) {
try {
if (clip != null) {
clip.prepareToLeaveProcess();
}
getService().setPrimaryClip(clip, OpPackageName());
} catch (RemoteException e) {
}
}
这⾥⾯实际上就是通过Binder通信,把数据传递给ClipBoardService⽽已, 所以我们接着区ClipBoardService中查看对应逻辑:
public void setPrimaryClip(ClipData clip, String callingPackage) {
synchronized (this) {
if (clip != null && ItemCount() <= 0) {
throw new IllegalArgumentException("No items");
}
//1、检查相关权限 start
//获取进程UID,此时若是A进程通过Binder调⽤了setPrimaryClip⽅法,则获得的就是A进程的UID
final int callingUid = CallingUid();
if (Op(AppOpsManager.OP_WRITE_CLIPBOARD, callingUid,
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return;
}
checkDataOwnerLocked(clip, callingUid);
//1、检查相关权限 end
final int userId = UserId(callingUid);
PerUserClipboard clipboard = getClipboard(userId);
revokeUris(clipboard);
setPrimaryClipInternal(clipboard, clip);
List<UserInfo> related = getRelatedProfiles(userId);//⽤来判断当前⽤户是否有复制权限
if (related != null) {
int size = related.size();
if (size > 1) {
boolean canCopy = false;
try {
canCopy = !UserRestrictions(userId).getBoolean(
UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
} catch (RemoteException e) {
}
// 如果允许,将可以将剪辑数据复制到相关⽤户。
// 如果不允许,则删除相关⽤户中的主剪辑,以防⽌粘贴陈旧内容。
if (!canCopy) {
clip = null;
} else {
clip.fixUrisLight(userId);
}
for (int i = 0; i < size; i++) {
int id = (i).id;
if (id != userId) {
//需要通知其他所有注册了的客户端,粘贴板的内容变化了
setPrimaryClipInternal(getClipboard(id), clip);
}
}
}
}
}
}
setPrimaryClip ⽅法主要做了两件事:
1、就是做了⼀些权限判断;
2、通知所有客户端,粘贴板内容更新
权限判断的具体细节,其实主要是判断是否有赋值粘贴对应uri的权限,不然随意⼀个APP,都能根据uri去获取系统或者其他APP的隐私数据。
5.2 粘贴过程的getPrimaryClip⽅法
查看ClipBoardManager中的此⽅法:
public ClipData getPrimaryClip() {
try {
return getService().OpPackageName());
} catch (RemoteException e) {
return null;
}
}
这⾥也是通过Binder 实际上调⽤的ClipBoardService的getPrimaryClip:
public ClipData getPrimaryClip(String pkg) {
synchronized (this) {
if (Op(AppOpsManager.OP_READ_CLIPBOARD, CallingUid(),
pkg) != AppOpsManager.MODE_ALLOWED) {
return null;
}
/
/赋予该pkg相应权限,下⾯第6节会分析
CallingUid(), pkg);
//返回ClipData给客户端
return getClipboard().primaryClip;
房改房是什么}
}
5.3 重要⽅法 ToText()
在前⾯讲到,粘贴板的数据类型有text、Intent、URI三种, ⽽客户端并不知道粘贴板的内容是哪⼀个类型, 所以在粘贴的时候需要去判断。如果不想去判断, 那就可以⽤ClipData的⼀个⽅法:coreceToText, 它可以将粘贴板内容强制转换为String。⽰例如下:
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clipData = PrimaryClip();
//强制转为⽂本
String text = clipData。coreceToText(String());
我们看看这个⽅法的具体实现:
public CharSequence coerceToText(Context context) {
//1、如果当前ClipData包含text,则直返回
CharSequence text = getText();
if (text != null) {
return text;
}
发布评论