💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
我们截取与Copy操作相关的代码: **sample** ~~~ //首先获取能与CBS交互的ClipboardManager对象 ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); //调用setPrimaryClip函数,参数是ClipData.newUri函数的返回值 clipboard.setPrimaryClip(ClipData.newUri( getContentResolver(),"Note",noteUri)); ~~~ ClipData的newUri是一个static函数,用于返回一个存储URI数据类型的ClipData,代码如下。根据前文所述可知,ClipData对象装载的就是可保存在剪贴板中的数据。 **ClipData.java** ~~~ static public ClipData newUri(ContentResolverresolver, CharSequence label, Uri uri) { Itemitem = new Item(uri); //创建一个Item,将Uri直接传给它的构造函数 String[] mimeTypes = null; /* 下边代码的功能是获取这个Uri代表的数据的MIME类型。先尝试利用ContentResolver 从ContentProvider那查询,如果查询不到,则设置mimeTypes为 MIMETYPES_TEXT_URILIST,它的定义是new String[“text/uri-list”] */ if("content".equals(uri.getScheme())) { String realType = resolver.getType(uri); //查询该uri所指向的数据的mimeTypes mimeTypes = resolver.getStreamTypes(uri, "*/*"); if (mimeTypes == null) { if (realType != null) { mimeTypes = new String[] { realType,ClipDescription.MIMETYPE_TEXT_URILIST }; } } else { ...... } if(mimeTypes == null) { mimeTypes = MIMETYPES_TEXT_URILIST; } //创建一个ClipData对象 return new ClipData(label, mimeTypes, item); } //ClipData的构造函数 public ClipData(CharSequence label, String[]mimeTypes, Item item) { mClipDescription = new ClipDescription(label, mimeTypes); ...... mIcon = null; mItems.add(item);//将item对象添加到mItems数组中 } ~~~ newUri函数的主要功能在于,获得URI所指向的数据的数据类型。对于使用剪切板服务的程序来说,了解剪切板中数据的数据类型相当重要,因为这样可以判断自己能否处理这种类型的数据。 URI和MIME的关系: URI指向数据的位置,这和PC机上文件的存储位置类似,例如c:/dfp。MIME则表示该数据的数据类型。在Windows平台上是采用后缀名来表示文件类型的,前面提到的c盘下的dfp文件,后缀是wav,表示该文件是一个wav音频。 对于剪切板来说,数据源由URI指定,数据类型由MIME表示,两者缺一不可。 获得一个ClipData后,将调用setPrimaryClip函数,将数据传递到CBS。setPrimaryClip的代码如下: ~~~ public void setPrimaryClip(ClipData clip) { try { //跨Binder调用,先要把参数打包。有兴趣的读者可以看看writToParcel函数 getService().setPrimaryClip(clip); }catch (RemoteException e) { } } ~~~ 通过Binder发送setPrimaryClip请求后,由CBS完成实际功能,代码如下: **ClipboardService.java** ~~~ public void setPrimaryClip(ClipData clip) { synchronized (this) { ...... //权限检查,后面会在3.7.3中单独分析 checkDataOwnerLocked(clip,Binder.getCallingUid()); */ //和权限相关,后续会分析 clearActiveOwnersLocked(); //保存新的clipData到mPrimaryClip中 mPrimaryClip = clip; / * mPrimaryClipListeners是一个RemoteCallbackList数组, 当CBS中的ClipData发生变化时,CBS需要向那些监控剪切板的 客户端发送通知。客户端通过addPrimaryClipChangedListener函数 注册回调 */ final int n = mPrimaryClipListeners.beginBroadcast(); for (int i = 0; i < n; i++) { try{ //通知客户端,剪切板的内容发生变化 mPrimaryClipListeners.getBroadcastItem(i). dispatchPrimaryClipChanged(); }...... } mPrimaryClipListeners.finishBroadcast(); } } ~~~ setPrimaryClip比较简单。但是由于新增支持Uri和Intent这两种数据类型,因此在安全性方面还有一些需要考虑的地方。这部分内容我们放到3.7.3小节去分析。 RemoteCallbackList是一个比较重要的常用类,很有必要掌握它的用法。