首页 文章 精选 留言 我的

精选列表

搜索[组件化],共10000篇文章
优秀的个人博客,低调大师

Android应用程序组件Content Provider的启动过程源代码分析(3)

Step 7.ActivityManagerService.getContentProviderImpl 这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中: publicfinalclassActivityManagerServiceextendsActivityManagerNative implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{ ...... privatefinalContentProviderHoldergetContentProviderImpl( IApplicationThreadcaller,Stringname){ ContentProviderRecordcpr; ProviderInfocpi=null; synchronized(this){ ProcessRecordr=null; if(caller!=null){ r=getRecordForAppLocked(caller); ...... } //Firstcheckifthiscontentproviderhasbeenpublished... cpr=mProvidersByName.get(name); if(cpr!=null){ ...... }else{ try{ cpi=AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS|PackageManager.GET_URI_PERMISSION_PATTERNS); }catch(RemoteExceptionex){ } ...... } cpr=mProvidersByClass.get(cpi.name); finalbooleanfirstClass=cpr==null; if(firstClass){ try{ ApplicationInfoai= AppGlobals.getPackageManager(). getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS); ...... cpr=newContentProviderRecord(cpi,ai); }catch(RemoteExceptionex){ //pmisinsameprocess,thiswillneverhappen. } } if(r!=null&&cpr.canRunHere(r)){ //Ifthisisamultiprocessprovider,thenjustreturnits //infoandallowthecallertoinstantiateit.Onlydo //thisiftheprovideristhesameuserasthecaller's //process,orcanrunasroot(socanbeinanyprocess). returncpr; } ...... //Thisissingleprocess,andourappisnowconnectingtoit. //Seeifwearealreadyintheprocessoflaunchingthis //provider. finalintN=mLaunchingProviders.size(); inti; for(i=0;i<N;i++){ if(mLaunchingProviders.get(i)==cpr){ break; } } //Iftheproviderisnotalreadybeinglaunched,thengetit //started. if(i>=N){ finallongorigId=Binder.clearCallingIdentity(); ProcessRecordproc=startProcessLocked(cpi.processName, cpr.appInfo,false,0,"contentprovider", newComponentName(cpi.applicationInfo.packageName, cpi.name),false); ...... mLaunchingProviders.add(cpr); ...... } //Makesuretheproviderispublished(thesameproviderclass //maybepublishedundermultiplenames). if(firstClass){ mProvidersByClass.put(cpi.name,cpr); } cpr.launchingApp=proc; mProvidersByName.put(name,cpr); ...... } //Waitfortheprovidertobepublished... synchronized(cpr){ while(cpr.provider==null){ ...... try{ cpr.wait(); }catch(InterruptedExceptionex){ } } } returncpr; } ...... } 这个函数比较长,我们一步一步地分析。 函数首先是获取调用者的进程记录块信息: ProcessRecordr=null; if(caller!=null){ r=getRecordForAppLocked(caller); ...... } 在我们这个情景中,要获取的就是应用程序Article的进程记录块信息了,后面会用到。 在ActivityManagerService中,有两个成员变量是用来保存系统中的Content Provider信息的,一个是mProvidersByName,一个是mProvidersByClass,前者是以Content Provider的authoriry值为键值来保存的,后者是以Content Provider的类名为键值来保存的。一个Content Provider可以有多个authority,而只有一个类来和它对应,因此,这里要用两个Map来保存,这里为了方便根据不同条件来快速查找而设计的。下面的代码就是用来检查要获取的Content Provider是否已经加存在的了: //Firstcheckifthiscontentproviderhasbeenpublished... cpr=mProvidersByName.get(name); if(cpr!=null){ ...... }else{ try{ cpi=AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS|PackageManager.GET_URI_PERMISSION_PATTERNS); }catch(RemoteExceptionex){ } ...... } cpr=mProvidersByClass.get(cpi.name); finalbooleanfirstClass=cpr==null; if(firstClass){ try{ ApplicationInfoai= AppGlobals.getPackageManager(). getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS); ...... cpr=newContentProviderRecord(cpi,ai); }catch(RemoteExceptionex){ //pmisinsameprocess,thiswillneverhappen. } } 在我们这个情景中,由于是第一次调用ArticlesProvider接口,因此,在mProvidersByName和mProvidersByClass两个Map中都不存在ArticlesProvider的相关信息,因此,这里会通过AppGlobals.getPackageManager函数来获得PackageManagerService服务接口,然后分别通过它的resolveContentProvider和getApplicationInfo函数来分别获取ArticlesProvider应用程序的相关信息,分别保存在cpi和cpr这两个本地变量中。这些信息都是在安装应用程序的过程中保存下来的,具体可以参考 Android应用程序安装过程源代码分析 一文。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966983,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序组件Content Provider的启动过程源代码分析(2)

Step 5.ActivityThread.getProvider 这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中: publicfinalclassActivityThread{ ...... privatefinalIContentProvidergetExistingProvider(Contextcontext,Stringname){ synchronized(mProviderMap){ finalProviderClientRecordpr=mProviderMap.get(name); if(pr!=null){ returnpr.mProvider; } returnnull; } } ...... privatefinalIContentProvidergetProvider(Contextcontext,Stringname){ IContentProviderexisting=getExistingProvider(context,name); if(existing!=null){ returnexisting; } IActivityManager.ContentProviderHolderholder=null; try{ holder=ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(),name); }catch(RemoteExceptionex){ } IContentProviderprov=installProvider(context,holder.provider, holder.info,true); ...... returnprov; } ...... } 这个函数首先会通过getExistingProvider函数来检查本地是否已经存在这个要获取的ContentProvider接口,如果存在,就直接返回了。本地已经存在的ContextProvider接口保存在ActivityThread类的mProviderMap成员变量中,以ContentProvider对应的URI的authority为键值保存。在我们这个情景中,因为是第一次调用ArticlesProvider接口,因此,这时候通过getExistingProvider函数得到的IContentProvider接口为null,于是下面就会调用ActivityManagerService服务的getContentProvider接口来获取一个ContentProviderHolder对象holder,这个对象就包含了我们所要获取的ArticlesProvider接口,在将这个接口返回给调用者之后,还会调用installProvider函数来把这个接口保存在本地中,以便下次要使用这个ContentProvider接口时,直接就可以通过getExistingProvider函数获取了。 我们先进入到ActivityManagerService服务的getContentProvider函数中看看它是如何获取我们所需要的ArticlesProvider接口的,然后再返回来看看installProvider函数的实现。 Step 6.ActivityManagerService.getContentProvider 这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中: publicfinalclassActivityManagerServiceextendsActivityManagerNative implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{ ...... publicfinalContentProviderHoldergetContentProvider( IApplicationThreadcaller,Stringname){ ...... returngetContentProviderImpl(caller,name); } ...... } 它调用getContentProviderImpl函数来进一步执行操作。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966982,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序组件Content Provider的启动过程源代码分析(4)

接下去这个代码判断当前要获取的Content Provider是否允许在客户进程中加载,即查看一个这个Content Provider否配置了multiprocess属性为true,如果允许在客户进程中加载,就直接返回了这个Content Provider的信息了: if(r!=null&&cpr.canRunHere(r)){ //Ifthisisamultiprocessprovider,thenjustreturnits //infoandallowthecallertoinstantiateit.Onlydo //thisiftheprovideristhesameuserasthecaller's //process,orcanrunasroot(socanbeinanyprocess). returncpr; } 在我们这个情景中,要获取的ArticlesProvider设置了要在独立的进程中运行,因此,继续往下执行: //Thisissingleprocess,andourappisnowconnectingtoit. //Seeifwearealreadyintheprocessoflaunchingthis //provider. finalintN=mLaunchingProviders.size(); inti; for(i=0;i<N;i++){ if(mLaunchingProviders.get(i)==cpr){ break; } } 系统中所有正在加载的Content Provider都保存在mLaunchingProviders成员变量中。在加载相应的Content Provider之前,首先要判断一下它是可否正在被其它应用程序加载,如果是的话,就不用重复加载了。在我们这个情景中,没有其它应用程序也正在加载ArticlesProvider这个Content Provider,继续往前执行: //Iftheproviderisnotalreadybeinglaunched,thengetit //started. if(i>=N){ finallongorigId=Binder.clearCallingIdentity(); ProcessRecordproc=startProcessLocked(cpi.processName, cpr.appInfo,false,0,"contentprovider", newComponentName(cpi.applicationInfo.packageName, cpi.name),false); ...... mLaunchingProviders.add(cpr); ...... } 这里的条件i >= N为true,就表明没有其它应用程序正在加载这个Content Provider,因此,就要调用startProcessLocked函数来启动一个新的进程来加载这个Content Provider对应的类了,然后把这个正在加载的信息增加到mLaunchingProviders中去。我们先接着分析这个函数,然后再来看在新进程中加载Content Provider的过程,继续往下执行: //Makesuretheproviderispublished(thesameproviderclass //maybepublishedundermultiplenames). if(firstClass){ mProvidersByClass.put(cpi.name,cpr); } cpr.launchingApp=proc; mProvidersByName.put(name,cpr); 这段代码把这个Content Provider的信息分别保存到mProvidersByName和mProviderByCalss两个Map中去,以方便后续查询。 因为我们需要获取的Content Provider是在新的进程中加载的,而getContentProviderImpl这个函数是在系统进程中执行的,它必须要等到要获取的Content Provider是在新的进程中加载完成后才能返回,这样就涉及到进程同步的问题了。这里使用的同步方法是不断地去检查变量cpr的provider域是否被设置了。当要获取的Content Provider在新的进程加载完成之后,它会通过Binder进程间通信机制调用到系统进程中,把这个cpr变量的provider域设置为已经加载好的Content Provider接口,这时候,函数getContentProviderImpl就可以返回了。下面的代码就是用来等待要获取的Content Provider是在新的进程中加载完成的: //Waitfortheprovidertobepublished... synchronized(cpr){ while(cpr.provider==null){ ...... try{ cpr.wait(); }catch(InterruptedExceptionex){ } } } 下面我们再分析在新进程中加载ArticlesProvider这个Content Provider的过程。 Step 8. ActivityManagerService.startProcessLocked Step 9. Process.start Step 10. ActivityThread.main Step 11. ActivityThread.attach Step 12. ActivityManagerService.attachApplication 这五步是标准的Android应用程序启动步骤,具体可以参考 Android应用程序启动过程源代码分析 一文中的Step 23到Step 27,或者 Android系统在新进程中启动自定义服务过程(startService)的原理分析 一文中的Step 4到Step 9,这里就不再详细描述了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966987,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序组件Content Provider的共享数据更新通知机制分析(2)

Step 1. ContentResolver.registerContentObserver 这个函数定义在frameworks/base/core/java/android/content/ContentResolver.java文件中: publicabstractclassContentResolver{ ...... publicfinalvoidregisterContentObserver(Uriuri,booleannotifyForDescendents, ContentObserverobserver) { try{ getContentService().registerContentObserver(uri,notifyForDescendents, observer.getContentObserver()); }catch(RemoteExceptione){ } } ...... } 当参数notifyForDescendents为true时,表示要监控所有以uri为前缀的URI对应的数据变化。这个函数做了三件事情,一是调用getContentService函数来获得前面已经启动起来了的ContentService远程接口,二是调用从参数传进来的ContentObserver对象observer的getContentObserver函数来获得一个Binder对象,三是通过调用这个ContentService远程接口的registerContentObserver函数来把这个Binder对象注册到ContentService中去。 Step 2.ContentResolver.getContentService 这个函数定义在frameworks/base/core/java/android/content/ContentResolver.java文件中: publicabstractclassContentResolver{ ...... publicstaticIContentServicegetContentService(){ if(sContentService!=null){ returnsContentService; } IBinderb=ServiceManager.getService(CONTENT_SERVICE_NAME); ...... sContentService=IContentService.Stub.asInterface(b); ...... returnsContentService; } privatestaticIContentServicesContentService; ...... } 在ContentResolver类中,有一个静态成员变量sContentService,开始时它的值为null。当ContentResolver类的getContentService函数第一次被调用时,它便会通过ServiceManager类的getService函数来获得前面已经启动起来了的ContentService服务的远程接口,然后把它保存在sContentService变量中。这样,当下次ContentResolver类的getContentService函数再次被调用时,就可以直接把这个ContentService远程接口返回给调用者了。 Step 3.ContentObserver.getContentObserver 这个函数定义在frameworks/base/core/java/android/database/ContentObserver.java文件中: publicabstractclassContentObserver{ ...... privateTransportmTransport; ...... privatestaticfinalclassTransportextendsIContentObserver.Stub{ ContentObservermContentObserver; publicTransport(ContentObservercontentObserver){ mContentObserver=contentObserver; } ...... } ...... publicIContentObservergetContentObserver(){ synchronized(lock){ if(mTransport==null){ mTransport=newTransport(this); } returnmTransport; } } ...... } ContentObserver类的getContentObserver函数返回的是一个成员变量mTransport,它的类型为ContentObserver的内部类Transport。从Transport类的定义我们可以知道,它有一个成员变量mContentObserver,用来保存与对应的ContentObserver对象。同时我们还可以看出,ContentObserver类的成员变量mTransport是一个Binder对象,它是要传递给ContentService服务的,以便当ContentObserver所监控的数据发生变化时,ContentService服务可以通过这个Binder对象通知相应的ContentObserver它监控的数据发生变化了。 Step 4. ContentService.registerContentObserver 这个函数定义在frameworks/base/core/java/android/content/ContentService.java文件中: publicfinalclassContentServiceextendsIContentService.Stub{ ...... privatefinalObserverNodemRootNode=newObserverNode(""); ...... publicvoidregisterContentObserver(Uriuri,booleannotifyForDescendents, IContentObserverobserver){ ...... synchronized(mRootNode){ mRootNode.addObserverLocked(uri,observer,notifyForDescendents,mRootNode); ...... } } ...... } 它调用了ContentService类的成员变量mRootNode的addObserverLocked函数来注册这个ContentObserver对象observer。成员变量mRootNode的类型为ContentService在内部定义的一个类ObserverNode。 Step 5.ObserverNode.addObserverLocked 这个函数定义在frameworks/base/core/java/android/content/ContentService.java文件中: publicfinalclassContentServiceextendsIContentService.Stub{ ...... publicstaticfinalclassObserverNode{ ...... privateStringmName; privateArrayList<ObserverNode>mChildren=newArrayList<ObserverNode>(); privateArrayList<ObserverEntry>mObservers=newArrayList<ObserverEntry>(); publicObserverNode(Stringname){ mName=name; } privateStringgetUriSegment(Uriuri,intindex){ if(uri!=null){ if(index==0){ returnuri.getAuthority(); }else{ returnuri.getPathSegments().get(index-1); } }else{ returnnull; } } privateintcountUriSegments(Uriuri){ if(uri==null){ return0; } returnuri.getPathSegments().size()+1; } publicvoidaddObserverLocked(Uriuri,IContentObserverobserver, booleannotifyForDescendents,ObjectobserversLock){ addObserverLocked(uri,0,observer,notifyForDescendents,observersLock); } privatevoidaddObserverLocked(Uriuri,intindex,IContentObserverobserver, booleannotifyForDescendents,ObjectobserversLock){ //Ifthisistheleafnodeaddtheobserver if(index==countUriSegments(uri)){ mObservers.add(newObserverEntry(observer,notifyForDescendents,observersLock)); return; } //Looktoseeiftheproperchildalreadyexists Stringsegment=getUriSegment(uri,index); if(segment==null){ thrownewIllegalArgumentException("InvalidUri("+uri+")usedforobserver"); } intN=mChildren.size(); for(inti=0;i<N;i++){ ObserverNodenode=mChildren.get(i); if(node.mName.equals(segment)){ node.addObserverLocked(uri,index+1,observer,notifyForDescendents,observersLock); return; } } //Nochildfound,createone ObserverNodenode=newObserverNode(segment); mChildren.add(node); node.addObserverLocked(uri,index+1,observer,notifyForDescendents,observersLock); } ...... } ...... } 从这里我们就可以看出,注册到ContentService中的ContentObserver按照树形来组织,树的节点类型为ObserverNode,而树的根节点就为ContentService类的成员变量mRootNode。每一个ObserverNode节点都对应一个名字,它是从URI中解析出来的。 在我们这个情景中,传进来的uri为"content://shy.luo.providers.articles/item",从Step 3调用mRootNode的addObserverLocked函数来往树上增加一个ObserverNode节点时,传进来的参数index的值为0,而调用countUriSegments("content://shy.luo.providers.articles/item")函数的返回值为2,不等于index的值,因此就会往下执行,而通过调用getUriSegment("content://shy.luo.providers.articles/item", 0)函数得到的返回值为"shy.luo.providers.articles"。假设这里是第一次调用树的根节点mRootNode来增加"content://shy.luo.providers.articles/item"这个URI,那么在接下来的for循环中,就不会在mRootNode的孩子节点列表mChildren中找到与名称"shy.luo.providers.articles"对应的ObserverNode,于是就会以"shy.luo.providers.articles"为名称来创建一个新的ObserverNode,并增加到mRootNode的孩子节点列表mChildren中去,并以这个新的ObserverNode来开始新一轮的addObserverLocked函数调用。 第二次进入到addObserverLocked函数时,countUriSegments("content://shy.luo.providers.articles/item")的值仍为2,而index的值为1,因此就会往下执行,这时候通过调用getUriSegment("content://shy.luo.providers.articles/item", 1)函数得到的返回值为"item"。假设这时候在以"shy.luo.providers.articles/item"为名称的ObserverNode中不存在名称为"item"的孩子节点,于是又会以"item"为名称来创建一个新的ObserverNode,并以这个新的ObserverNode来开始新一轮的addObserverLocked函数调用。 第三次进入到addObserverLocked函数时,countUriSegments("content://shy.luo.providers.articles/item")的值仍为2,而index的值也为2,因此就会新建一个ObserverEntry对象,并保存在这个以"item"为名称的ObserverNode的ContentObserver列表mObervers中。 最终我们得到的树形结构如下所示: mRootNode("") -- ObserverNode("shy.luo.providers.articles") --ObserverNode("item") , which has a ContentObserver in mObservers 这样,ContentObserver的注册过程就完成了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967018,如需转载请自行联系原作者

优秀的个人博客,低调大师

网站前端_EasyUI.基础入门.0008.带你玩转jQuery EasyUI Layout组件?

简单介绍: 说明: ($.fn.layout)布局,依赖panel和resizable,默认分为5个区东east,西west,南south,北north,中center,中间的区域面板是必须的,边缘区域面板是可选的,每个边缘区域面板可通过拖拽边框调整尺寸,也可以通过点击折叠触发器来折叠面板.布局可嵌套,因此用户可建立复杂的布局. 基础用法: <divid="l"class="easyui-layout"> <divdata-options="region:'east',width:100,title:'easttitle'"></div> <divdata-options="region:'west',width:200,title:'westtitle'"></div> <divdata-options="region:'south',width:100,height:100,title:'southtitle'"></div> <divdata-options="region:'north',width:100,height:100,title:'northtitle'"></div> <divdata-options="region:'center',title:'centertitle'"> <divid="ll"class="easyui-layout"data-options="fit:true"> <divdata-options="region:'west',title:'subwesttitle',width:200"></div> <divdata-options="region:'center',title:'subcentertitle',collapsed:true"></div> </div> </div> </div> <!--说明:加载jquery-easyui脚本文件--> <scriptsrc="js/jquery-easyui/jquery.min.js"></script> <scriptsrc="js/jquery-easyui/jquery.easyui.min.js"></script> <scriptsrc="js/jquery-easyui/locale/easyui-lang-zh_CN.js"></script> <styletype="text/css"> body{ padding:0; margin:0; } </style> <scripttype="text/javascript"> varwidth=$(document).width(); varheight=$(document).height(); $('#l').layout({ width:width, height:height, }); </script> 布局属性: fit -> boolean 说明: 当设置为true时,就设置布局的尺寸适应它的父容器,当在body标签上创建布局时,它将自动最大化到整个页面的全部尺寸 <bodyclass="easyui-layout"data-options="fit:true"> <divdata-options="region:'east',width:100,title:'easttitle'"></div> <divdata-options="region:'west',width:200,title:'westtitle'"></div> <divdata-options="region:'south',width:100,height:100,title:'southtitle'"></div> <divdata-options="region:'north',width:100,height:100,title:'northtitle'"></div> <divdata-options="region:'center',title:'centertitle'"> <divid="ll"class="easyui-layout"data-options="fit:true"> <divdata-options="region:'west',title:'subwesttitle',width:200"></div> <divdata-options="region:'center',title:'subcentertitle',collapsed:true"></div> </div> </div> <!--说明:加载jquery-easyui脚本文件--> <scriptsrc="js/jquery-easyui/jquery.min.js"></script> <scriptsrc="js/jquery-easyui/jquery.easyui.min.js"></script> <scriptsrc="js/jquery-easyui/locale/easyui-lang-zh_CN.js"></script> </body> 布局接口: resize -> function 说明: 设置面板尺寸并且做布局,需要传入包含width/height/left/top的对象 panel -> function 说明: 返回指定面板,需要传递region参数,参数支持north,south,east,west,center collapse -> function 说明: 折叠指定的面板,需要传递region参数,参数支持north,south,east,west,center expand -> function 说明: 展开指定的面板,需要传递region参数,参数支持north,south,east,west,center add -> function 说明: 添加一个指定的面板,options参数一个配置对象 remove -> function 说明: 移除指定的面板,需要传递region参数,参数支持north,south,east,west,center <bodyclass="easyui-layout"data-options="fit:true"> <divdata-options="region:'east',width:100,title:'easttitle'"> </div> <divdata-options="region:'west',width:200,title:'westtitle'"></div> <divdata-options="region:'south',width:100,height:100,title:'southtitle'"> </div> <divdata-options="region:'north',width:100,height:100,title:'northtitle'"> <div> <fieldset> <legend>操作区</legend> <ahref="#"class="easyui-linkbutton"onclick="javascript:westLoad(this);">左侧加载远程数据</a> <ahref="#"class="easyui-linkbutton"onclick="javascript:westCollapse(this);">左侧面板缓慢合并</a> <ahref="#"class="easyui-linkbutton"onclick="javascript:westExpand(this);">左侧面板缓慢展开</a> <ahref="#"class="easyui-linkbutton"onclick="javascript:centerRemove(this);">删除中间左边面板</a> <ahref="#"class="easyui-linkbutton"onclick="javascript:centerAdd(this);">添加中间左边面板</a> </fieldset> </div> </div> <divdata-options="region:'center',title:'centertitle'"> <divid="ll"class="easyui-layout"data-options="fit:true"> <divdata-options="region:'west',title:'subwesttitle',width:200"></div> <divdata-options="region:'center',title:'subcentertitle',collapsed:true"></div> </div> </div> <!--说明:加载jquery-easyui脚本文件--> <scriptsrc="js/jquery-easyui/jquery.min.js"></script> <scriptsrc="js/jquery-easyui/jquery.easyui.min.js"></script> <scriptsrc="js/jquery-easyui/locale/easyui-lang-zh_CN.js"></script> <scripttype="text/javascript"> functionwestLoad(that){ var$wpanel=$('body').layout('panel','west'); $wpanel.panel({ extractor:function(data){ varh='<ul>'; vard=JSON.parse(data); for(vari=0;i<d['total'];i++){ h+='<li><ahref="#">'+d['rows'][i]['name']+'</a></li>' }; h+='</ul>'; returnh; }, }); $wpanel.panel('refresh','/easyui/data.json'); }; functionwestCollapse(that){ $('body').layout('collapse','west'); }; functionwestExpand(that){ $('body').layout('expand','west'); }; functioncenterRemove(that){ $('#ll').layout('remove','west'); }; functioncenterAdd(that){ $('#ll').layout('add',{region:'west',title:'subwesttitle',width:200}) }; </script> </body> 面板扩展: title -> string 说明: 布局面板的标题文本. region -> string 说明: 定义布局面板的位置,其值可以为north,south,east,west,center border -> boolean 说明: 当设置为true时,就显示布局面板的边框 split -> boolean 说明: 当设置为true时,就显示拆分栏,用户可以改变面板的尺寸. iconCls -> string 说明: 当面板头部显示一个图表的css class href -> string 说明: 从远程站点加载数据的url collapsible -> boolean 说明: 定义是否显示可折叠按钮. minWidth -> number 说明: 面板的最小宽度 maxWidth -> number 说明: 面板的最大宽度 minHeigit -> number 说明: 面板的最小高度 maxHeight -> number 说明: 面板的最大高度

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册