首页 文章 精选 留言 我的

精选列表

搜索[关系代数],共10001篇文章
优秀的个人博客,低调大师

MongoDB 事务,复制和分片的关系

摘要:本文尝试对Mongo的复制和分布式事务的原理进行描述,在必要的地方,对实现的正确性进行论证,希望能为MongoDB内核爱好者提供一些参考。 1.前言 MongoDB基于wiredTiger提供的泛化SI的功能,重构了readHistory(readMajority)的能力 基于wiredTiger提供的AllCommittedTimestamp API,重构了前缀一致的主从复制(Prefix-Consistent-Replication) 引入混合逻辑时钟(HLC),每个节点(Mongos/Mongod)的逻辑时钟维持在接近的值,基于此实现ChangeStream, 结合HLC与CLOCK-SI,实现分布式事务,HLC和泛化SI,CLOCK-SI两篇Paper可以作为理解MongoDB的设计的理论参考(这里并没有说MongoDB是Paper的实现)。 本文尝试对Mongo的复制和分布式事务的原理进行描述,在必要的地方,对实现的正确性进行论证,希望能为MongoDB内核爱好者提供一些参考。 2.MongoDB副本集事务介绍 MongoDB 副本集的事务 MongoDB副本集的复制是基于raft协议,相比于Paxos,raft协议实现简单,但是raft协议只支持single-master,对应的,MongoDB的副本集是主从架构,而且只有主节点支持写入操作。MongoDB副本集的事务管理,包括冲突检测,事务提交等关键操作,都只在主节点上完成。也就是说副本集的事务在事务管理方面,跟单节点逻辑基本一致。 MongoDB的事务,仍然是实现了 ACID 四个特性, MongoDB使用 SI 作为事务的隔离级别。 3.SI的简介 SI,即SnapshotIsolation,中文称为快照隔离,是一种mvcc的实现机制,它在1995年的A Critique of ANSI SQL Isolation Levels中被正式提出。因快照时间点的选取上的不同,又分为Conventional Si 和 Generalized SI。 CSI(Convensional SI) CSI 选取当前最新的系统快照作为事务的读取快照 就是在事务开始的时候,获得当前db最新的snapshot,作为事务的读取的snapshot, snapshot(Ti) = start(Ti) 可以减少写事务冲突发生的概率,并且提供读事务读取最新数据的能力 一般我们说一个数据库支持SI隔离级别,其实默认是说支持CSI。比如RocksDB支持的SI就是CSI,WiredTiger在3.0版本之前支持的SI也是CSI。 GSI(Generalized SI) GSI选择历史上的数据库快照作为事务的读取快照,因此CSI可以看作GSI的一个特例。 在复制集的情况下,考虑 CSI, 对于主节点上的事务,每次事务的开始时间选取的系统最新的快照, 但是对于其他从节点来说, 并没有 统一的 “最新的” 快照这个概念。泛化的快照实际上是基于快照观测得到的,对于当前事务来说,我们通过选取合适的 更早时间的快照,可以让 从节点上的事务正确且无延迟的执行。 举例如下: 例如当前数据库的状态是, S={T1, T2, T3}, 现在要开始执行T4, 如果我们知道T4要修改的值,在T3上没有被修改, 那么我们在执行T4的时候, 就可以按照 T2 commit后的snashot进行读取。 如何选择更早的时间点,需要满足下面的规则, 符号定义 Ti: 事务i Xi: 被事务i修改过的X变量 snapshot(Ti): 事务i的选取的快照时间 start(Ti): 事务i的开始时间 commit(Ti): 事务i的提交时间 abort(Ti): the time when Ti is aborted. end(Ti): the time when Ti is committed or aborted. 公式解释 读规则 G1.1, 如果变量X被本事务修改了值且读取到了新的值, 那么 读操作一定在写操作后面; G1.2, 如果事务i读取了事务j更新的变量的X, 那么一定不会有事务i更新X的操作,在事务i读取了事务j更新的变量的X这个操作前面; G1.3, 事务j的提交时间早于事务i的快照时间; G1.4, 对于任意一个会更新变量X的事务k, 那么这个事务k一定满足, 要么事务k的提交时间小于事务j, 要么这个事务k的提交时间大于事务i。 写规则 G2, 对于任意已经在提交历史里的两个事务,Ci, Cj, 那么一定可以保证当 事务j的commit时间戳在 事务i的观测时间段内时(snapshot(Ti), commit(Ti)), 那么他们更新的变量交集一定为空。 PCSI(PREFIX-CONSISTENT SNAPSHOT ISOLATION SI) GSI 只是定义了一个范围的range,都可以作为SI使用,并没有定义具体应该选择哪个SI。 PCSI 是为了复制集而设计的。对于一个事务Ti 要开S节点开始运行, 那么 S节点将必须包含这个事务所需要的所有前置事务都必须运行且提交。 相比较于GSI, PCSI的读规则,额外增加了 P1.5 规则。 SI的提交时间戳设置,依据 A Critique of ANSI SQL Isolation Levels 中的描述, 提交时间戳的设置应该是单调递增的。新设置的时间戳,应该大于系统中已经存在的开始时间戳和提交时间戳。 SI 读取时间戳的设置,必须保证比当前系统中正在运行的事务的最小的提交时间戳还要小, 因为一旦大于当前系统中正在运行事务的最小的提交时间戳,那么这个读事务读取到的数据就是未定义的, 取决于读事务启动的时间,而不是snapshot的时间,这违背了 一致性的要求。举例如下 当前已经完成的事务是T1,正在运行的事务是T2, 将要运行的读事务是T3, 如果 T3的读时间戳大于T2事务提交时间戳, 并且T2事务正在运行,等到T2事务执行完后。我们观察这个 database,就会发现 他违背了GSI, 事务执行顺序如下所示是: T1 commited and commitTs(1) -> T2 start -> T2 set commitTs(2) -> T3 start -> T3 set snapshotTs(3) -> T3 commit -> pointA -> T2 commit -> pointB 那么可知, T3事务实际读取的值是 T1事务的值。但根据 pointB 点来看 GSI的读规则 1.4 的要求,会发现, 如果T3读到T1的事务的修改,那么必然要求, T3和T1之间没有空洞。但实际上 T2 是落在了 T3和T1之间的, 也就是说, 违反了 GSI 1.4的读规则。 所以我们必须规定,SI 读取时间戳的设置,必须保证比当前系统中正在运行的事务的最小的提交时间戳还要小。 4.MongoDB副本集时间戳应用 MongoDB 4.0的复制也是利用时间戳特性解决了3.x系列MongoDB从节点复制造成从节点性能下降的关键方案。 MongoDB oplog 乱序问题 MongoDB主备节点的数据同步并不基于WiredTiger的wal日志来做的。相反,mongodb会将每次操作的数据变更写入到一个叫做oplog的集合里。 oplog这个集合,虽然名字带有log,但实际上,它是一个MongoDB的表, 对oplog的写入,并不是 append的方式修改的, 而是呈现出一种尾部乱序的方式。 对于oplog来说, oplog的读取顺序是按照TS字段来排序的, 跟上层的提交顺序无关。所以存在后开始的事务,在oplog先读取的场景。 oplog 空洞 因为出现了乱序,所以从节点在读取oplog的时候,就会在某些时间点出现空洞。举例如下: 时间点1: oplog 顺序为: Ta -> Tb, 此时系统中还有一个事务Tc在运行 时间点2: oplog 顺序为: Ta -> Tc -> Tb, 当Tc运行结束后, 因为ts的顺序, 看起来是将Tc插入到了Ta和Tb之间。 那么当 从节点 在时间点1 reply 到 Tb的时候, 实际上是漏了 Tc的,这个就是oplog的空洞, 他产生的原因是因为,从节点如果每次读取oplog最新的数据,就有可能会得到一个不连续的数据, 例如 时间点1上 Ta-> Tb. 这就是oplog空洞。 在具体复制逻辑中,我们必须想办法来从节点读取到含有空洞的oplog数据。这也是GSI的要求, snapshot的选取不能含有空洞。 因为 oplog的Ts是mongo上层给的,我们很容易知道哪些事务有哪些ts, 我们再将这个ts 作为事务的commitTs 放到 oplog存储的事务里, 这样我们读取 oplog的顺序事务的可见性顺序相一致了,在这种情况下,我们就可以 根据 活跃事务列表, 就可以将oplog 分为两个部分, 假设活跃commitTs列表的事务是 {T10, T11, T12}, 活跃事务列表是 {T10, T11, T12, T13, T14}, 那么意味着, 目前有 T10, T11, T12, T13, T14 再运行,并且 T10, T11, T12 已经设置了 commitTs, 又因为 上面讨论的 commitTs 是单调递增的, 那么我们可知, T13, T14 的commitTs 一定大于 maxCommitTs(T10, T11, T12), 而且我们还可知, minCommitTs(T10,T11,T12) 就是全局最小的 commitTs, 而小于这些的 commitTs的事务,因为不在 活跃事务列表里了, 表示已经提交了, 那么我们可以知道, oplog ts 在 全局最小的 commitTs 之前的, 就是都提交了的, oplog 按照 commitTs 排序后,如下所示 … Tx | minCommitTs(T10,T11,T12) | … 我们可以知道 T9, 或者说小于 minCommitTs(T10,T11,T12) 都是无空洞,因为系统不会再提交小于 minCommitTs(T10,T11,T12) 的事务到oplog里了, 所以从节点可以直接恢复这里的数据。 上面说的oplog minCommitTs(T10,T11,T12) 在 mongodb里,就是特殊的timestamp, 这个后文会讲。 通过上面的方案,我们可以解决空洞的问题。这个时候,从节点每次恢复数据的时候,将读取的snapshot,设置为上一次恢复的Ts(同样也是无空洞的Ts), 这样的话, 从节点的恢复数据和读取数据也就做到了互不冲突。从而解决了 3.x系列的 从节点同步数据造成节点性能下降的问题。 点击关注,第一时间了解华为云新鲜技术~

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

关系型数据抽取到hive中

操作步骤 1.建立外表 CREATE external TABLE t_rk_baseinfo_5kwexternal ( ROW_ID STRING, SYNC_SIGN STRING, SYNC_ERROR_DESC STRING, OperateType STRING, SYNC_Date STRING, XiaQuCode STRING, Name STRING, NameEN STRING, NameUsed STRING, Sex STRING, Nation STRING, Birthday STRING, BirthPlace STRING, Age STRING, Native STRING, Nationality STRING, IdcardType STRING, Idcard STRING, IdcardBeginDate STRING, IdcardEndDate STRING, Political STRING, MaritalStatus STRING, Faith STRING, MilitaryService STRING, Height STRING, Weight STRING, BloodType STRING, Mobile STRING, Telephone STRING, RegionAddress STRING, Address STRING, PostCode STRING, EMail STRING, Health STRING, FamliyDiseaseHis STRING, IdDeath STRING, DeathDate STRING, Type STRING, PersonGuid STRING, HabCode STRING, AgeArea STRING, HouseholdType STRING, Education STRING, baidulat STRING, baidulng STRING) row format delimited fields terminated by ‘\t’; ; 2.文件抽取至HDFS 3.创建事务表 clustered by (ROW_ID) into 3 buckets stored as orc TBLPROPERTIES (‘transactional’=’true’); 备用:LOAD DATA INPATH ‘/path/to/local/files’ into table t_rk_baseinfo; 4.hdfs到外表 5.外表到事务表 insert into 事务表 select * from 外表;

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

Pressed状态和clickable,duplicateParentState的关系

做Android开发的人都用过Selector,可以方便的实现View在不同状态下的背景。不过,相信大部分开发者遇到过和我一样的问题,本文会从源码角度,解释这些问题。 首先,这里简单描述一下,我遇到的问题: 界面上有个全屏的LinearLayout A,A中有一个TextView B和Button C,其中,A的clickable=true,并设置了pressed时,背景色为灰色,B设置了pressed时,背景色为蓝色 当手指触摸C下方的空白区域时,看到了这样的效果: 在这里看到,在没有触摸B的情况下,B的pressed = true,而C的pressed = false。 C的状态暂且不讨论,按照Android消息传递的原则,因为touch的point不在B内部,所以,touch消息应该不会交给B处理,那为什么B的pressed = true? 下面开始一步一步分析(本文分析的Android源码为4.2.2)。 Pressed状态的设定 从View.onTouchEvent函数看起(frameworks/base/core/java/android/view/View.java): [java] view plain copy /** *Implementthismethodtohandletouchscreenmotionevents. * *@parameventThemotionevent. *@returnTrueiftheeventwashandled,falseotherwise. */ publicbooleanonTouchEvent(MotionEventevent){ ...... if(((viewFlags&CLICKABLE)==CLICKABLE||//这里是为什么设置A的clickable为true的原因,否则,pressA的时候,没有界面元素处理touchevent,最终会由Activity的onTouchEvent函数处理 (viewFlags&LONG_CLICKABLE)==LONG_CLICKABLE)){ switch(event.getAction()){ caseMotionEvent.ACTION_UP: ...... break; caseMotionEvent.ACTION_DOWN: mHasPerformedLongPress=false; ...... //Walkupthehierarchytodetermineifwe'reinsideascrollingcontainer. booleanisInScrollingContainer=isInScrollingContainer();//A已经是顶层元素了,没有ScrollView之类的控件存在,所以,isInScrollingContainer=false //Forviewsinsideascrollingcontainer,delaythepressedfeedbackfor //ashortperiodincasethisisascroll. if(isInScrollingContainer){ mPrivateFlags|=PFLAG_PREPRESSED; if(mPendingCheckForTap==null){ mPendingCheckForTap=newCheckForTap(); } postDelayed(mPendingCheckForTap,ViewConfiguration.getTapTimeout()); }else{ //Notinsideascrollingcontainer,soshowthefeedbackrightaway setPressed(true);//A设置pressed=true checkForLongClick(0); } break; caseMotionEvent.ACTION_CANCEL: ...... break; caseMotionEvent.ACTION_MOVE: ...... break; } returntrue; } returnfalse; } 从上面的代码我们知道,当手指触摸A的时候,A的pressed被设置为true。 Pressed状态的传递 接着,我们看看setPressed函数的实现: [java] view plain copy /** *Setsthepressedstateforthisview. * *@parampressedPasstruetosettheView'sinternalstateto"pressed",orfalsetoreverts *theView'sinternalstatefromapreviouslyset"pressed"state. *@see#isClickable() *@see#setClickable(boolean) */ publicvoidsetPressed(booleanpressed){ finalbooleanneedsRefresh=pressed!=((mPrivateFlags&PFLAG_PRESSED)==PFLAG_PRESSED); if(pressed){ mPrivateFlags|=PFLAG_PRESSED; }else{ mPrivateFlags&=~PFLAG_PRESSED; } if(needsRefresh){ refreshDrawableState();//切换背景图片 } dispatchSetPressed(pressed); } setPressed函数内部调用了dispatchSetPressed函数,这个让人很在意(frameworks/base/core/java/android/view/ViewGroup.java): [java] view plain copy @Override protectedvoiddispatchSetPressed(booleanpressed){ finalView[]children=mChildren; finalintcount=mChildrenCount; for(inti=0;i<count;i++){ finalViewchild=children[i]; //Childrenthatareclickableontheirownshouldnot //showapressedstatewhentheirparentviewdoes. //Clearingapressedstatealwayspropagates. if(!pressed||(!child.isClickable()&&!child.isLongClickable())){ child.setPressed(pressed); } } } 原来,dispatchSetPressed函数会把pressed状态传递给所有clickable=false并且longclickable=false的子元素。 到这里,前面的现象就可以解释了,因为C是button类,clickable=true,而B的clickable=false。所以,当A被触摸时,B的pressed=true,而C的pressed=false。那么,可以回答下面几个小问题了: 如果不希望A的pressed=true时,B的pressed = true,该如何修改? 设置B的clickable=true 如果希望A的pressed = true时,C的pressed = true,那又该如何修改? 设置C的clickable = false. 但是,这里可能又存在问题了,设置C的clickable=false,会导致button按钮的onclicklistener无法工作,这是个严重的副作用。那么可以不修改clickable,而设置android:duplicateParentState为true。 那么duplicateParentState做了些什么呢?View.setDuplicateParentStateEnabled: [java] view plain copy publicvoidsetDuplicateParentStateEnabled(booleanenabled){ setFlags(enabled?DUPLICATE_PARENT_STATE:0,DUPLICATE_PARENT_STATE); } 再看看View.onCreateDrawableState() [java] view plain copy /** *Generatethenew{@linkandroid.graphics.drawable.Drawable}statefor *thisview.Thisiscalledbytheview *systemwhenthecachedDrawablestateisdeterminedtobeinvalid.To *retrievethecurrentstate,youshoulduse{@link#getDrawableState}. * *@paramextraSpaceifnon-zero,thisisthenumberofextraentriesyou *wouldlikeinthereturnedarrayinwhichyoucanplaceyourown *states. *@returnReturnsanarrayholdingthecurrent{@linkDrawable}stateof *theview. *@see#mergeDrawableStates(int[],int[]) */ protectedint[]onCreateDrawableState(intextraSpace){ if((mViewFlags&DUPLICATE_PARENT_STATE)==DUPLICATE_PARENT_STATE&& mParentinstanceofView){ return((View)mParent).onCreateDrawableState(extraSpace); } ...... } 从上面的代码,可以看到,当设置duplicateParentState为true时,View的DrawableState直接使用了其parent的。所以,他的drawable状态会一直保持与其parent同步。 题外,为什么当ListView中包含focusable为true的控件时,OnItemClickListener不会触发 因为ListView未重载onTouchEvent事件,所以,需要看其父类的AbsListView.onTouchEvent(frameworks/base/core/java/android/widget/AbsListView): [java] view plain copy @Override publicbooleanonTouchEvent(MotionEventev){ ...... switch(action&MotionEvent.ACTION_MASK){ caseMotionEvent.ACTION_DOWN:{ ...... break; } caseMotionEvent.ACTION_MOVE:{ ...... break; } caseMotionEvent.ACTION_UP:{ switch(mTouchMode){ caseTOUCH_MODE_DOWN: caseTOUCH_MODE_TAP: caseTOUCH_MODE_DONE_WAITING: finalintmotionPosition=mMotionPosition; finalViewchild=getChildAt(motionPosition-mFirstPosition); finalfloatx=ev.getX(); finalbooleaninList=x>mListPadding.left&&x<getWidth()-mListPadding.right; if(child!=null&&!child.hasFocusable()&&inList){ if(mTouchMode!=TOUCH_MODE_DOWN){ child.setPressed(false); } if(mPerformClick==null){ mPerformClick=newPerformClick(); } ...... performClick.run(); ...... } ...... caseTOUCH_MODE_SCROLL: ...... break; caseTOUCH_MODE_OVERSCROLL: ...... break; } ...... caseMotionEvent.ACTION_CANCEL:{ ...... break; } caseMotionEvent.ACTION_POINTER_UP:{ ...... break; } caseMotionEvent.ACTION_POINTER_DOWN:{ ...... break; } } returntrue; } 仅在child.hasFocusable()=false时, PerformClick对象才会执行ViewGroup.hasFocusable: [java] view plain copy /** *{@inheritDoc} */ @Override publicbooleanhasFocusable(){ if((mViewFlags&VISIBILITY_MASK)!=VISIBLE){ returnfalse; } if(isFocusable()){ returntrue; } finalintdescendantFocusability=getDescendantFocusability(); if(descendantFocusability!=FOCUS_BLOCK_DESCENDANTS){ finalintcount=mChildrenCount; finalView[]children=mChildren; for(inti=0;i<count;i++){ finalViewchild=children[i]; if(child.hasFocusable()){ returntrue; } } } returnfalse; } 仅在所有的clild的hasFocusable为false时,ListView才会执行performClick(AbsListView.PerformClick): [java] view plain copy privateclassPerformClickextendsWindowRunnnableimplementsRunnable{ intmClickMotionPosition; publicvoidrun(){ //Thedatahaschangedsincewepostedthisactionintheeventqueue, //bailoutbeforebadthingshappen if(mDataChanged)return; finalListAdapteradapter=mAdapter; finalintmotionPosition=mClickMotionPosition; if(adapter!=null&&mItemCount>0&& motionPosition!=INVALID_POSITION&& motionPosition<adapter.getCount()&&sameWindow()){ finalViewview=getChildAt(motionPosition-mFirstPosition); //Ifthereisnoview,somethingbadhappened(theviewscrolledoffthe //screen,etc.)andweshouldcanceltheclick if(view!=null){ performItemClick(view,motionPosition,adapter.getItemId(motionPosition)); } } } } 而PerformClick会调用performItemClick(AdsListView.performItemClick): [java] view plain copy @Override publicbooleanperformItemClick(Viewview,intposition,longid){ booleanhandled=false; booleandispatchItemClick=true; ...... if(dispatchItemClick){ handled|=super.performItemClick(view,position,id); } returnhandled; } AdapterView.preformItemClick: [java] view plain copy /** *CalltheOnItemClickListener,ifitisdefined. * *@paramviewTheviewwithintheAdapterViewthatwasclicked. *@parampositionThepositionoftheviewintheadapter. *@paramidTherowidoftheitemthatwasclicked. *@returnTrueiftherewasanassignedOnItemClickListenerthatwas *called,falseotherwiseisreturned. */ publicbooleanperformItemClick(Viewview,intposition,longid){ if(mOnItemClickListener!=null){ playSoundEffect(SoundEffectConstants.CLICK); if(view!=null){ view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); } mOnItemClickListener.onItemClick(this,view,position,id); returntrue; } returnfalse; } 所以,如果ListView item中包含focusable为true的控件(例如:button, radiobutton),会导致ItemClickListener失效。解决方案两个: 设置特定的控件focusable = false 不使用onItemClickListener,而直接在Item上设置onClickListener监听点击事件。 本文转自 一点点征服 博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/8328107.html,如需转载请自行联系原作者

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

谈谈Spark与Spark-Streaming关系

spark程序是使用一个spark应用实例一次性对一批历史数据进行处理,spark streaming是将持续不断输入的数据流转换成多个batch分片,使用一批spark应用实例进行处理,侧重点在Steaming上面。我们常说的Spark-Streaming依赖了Spark Core的意思就是,实际计算的核心框架还是spark。我们还是上一张老生常谈的官方图: 从原理上看,我们将spark-streaming转变为传统的spark需要什么? 需要构建4个东西: 一个静态的 RDD DAG 的模板,来表示处理逻辑; 一个动态的工作控制器,将连续的 streaming data 切分数据片段,并按照模板复制出新的 RDD DAG 的实例,对数据片段进行处理; Receiver进行原始数据的产生和导入;Receiver将接收到的数据合并为数据块并存到内存或硬盘中,供后续batch RDD进行消费;对长时运行任务的保障,包括输入数据的失效后的重构,处理任务的失败后的重调。 至于上述过程具体怎么实现,我们会在spark-streaming源码分析的文章中一一解决。本文中图片文字来自于网络。 原文发布时间为:2017-10-13 本文作者:佚名 本文来自云栖社区合作伙伴“51CTO”,了解相关信息可以关注。

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

iOS:UIToolBar、toolbarItems、BarButtonItem的几种关系

工具栏:ToolBar 工具栏项目:Bar Button Item 调节按钮位置的固定调节:Fixed Space Bar Button Item 调节按钮位置的灵活调节:Flexible Space Bar Button ToolBar工具栏是视图View的属性,可以在工具栏上添加工具栏按钮Bar Button Item(可以是自定义的Custom、也可以是系统自带的BarButtonSystemItem),视图控制器可以通过工具栏项对视图中内容进行操作。 按钮Bar Button Item有三种Style:Plain(可使用自定义图片),Boarded和Done。其他属性则望文生义即可。要使按钮不可用,更改Enable属性即可。而Tool Bar也可以做一些调整(主要也是调整style)。需要注意的是,Plain类型的按钮是没有title的,即为Null。 要说明的是,Fixed Space Bar Button Item是固定的(也可调)间距,而Flexible Space Bar Button Item则会自动调整现有按钮使最大限度占据工具栏的所有空间。 重点是:我们可以利用toolbarItems来设置工具栏上的按钮,一般APP下面看到有3-4个选项比如“首页”、“设置”这些都可以作为UIBarButtonItem对象被加载到这个工具栏上,当然,从toolbarItems名字看出来,它有个“s”,所以我们得单独的把UIBarButtonItem对象装到一个数组中,然后把这个数组赋值给toolbarItems。或者用setToolbarItems方法将所有的工具项添加到工具栏上,即最后将toolbaritems添加到toolbar工具栏中。 例如:通过toolbaritems来设置工具栏的显示项,数组中是UIBarButtonItem的实例 //barButtonitem实例 UIBarButtonItem *item1 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAddtarget:self action:nil]; UIBarButtonItem *item2 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarkstarget:self action:nil]; //item的间隔,不会显示出来,会自动计算间隔,就是上面的调节 UIBarButtonItem *spaceItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; //将工具栏项目:barButtonItem和调节实例放入数组中 NSArray *arr1=[[NSArray alloc]initWithObject:item1,spaceItem,item2,spaceItem,nil]; //通过toolbarItems属性将按钮放入到工具栏上 self.toolbarItems = arr; 或者通过setToolbarItems方法将按钮放入到工具栏上 [selfsetToolbarItems:arr]; 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4771811.html,如需转载请自行联系原作者

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

Style在Android中的继承关系

Android的Styles(样式)和Themes(主题)非常类似Web开发里的CSS,方便开发者将页面内容和布局呈现分开。Style和Theme在Android里的定义方式是完全一样的,两者只是概念上的区别:Style作用在单个视图或控件上,而Theme用于Activity或整个应用程序。由于作用范围的不同,Theme也就需要比Style包含更多的定义属性值的项目(item)。不过本文,我将Style和Theme都归为Style来称呼。 Android的Style和Web的CSS相比,有一个缺陷就是只能针对一个对象只能通过android:theme="@style/AppTheme"或style="@style/MyStyle"指定一个值。而CSS则可以通过class属性在DOM元素上定义多个样式来达到组合的效果。不过Style也有CSS没有的功能,那就是继承(Inheritance)。(当然CSS通过LESS和SASS这些工具也获得继承的能力。) Style继承简介 根据Android Developers官方文档的介绍,定义Style的继承有两种方式:一是通过parent标志父Style; <stylename="GreenText"parent="@android:style/TextAppearance"> <itemname="android:textColor">#00FF00</item> </style> 另一种则是将父Style的名字作为前缀,然后通过“.”连接新定义Style的名字: <stylename="CodeFont.Red"> <itemname="android:textColor">#FF0000</item> </style> 第二种方式可以无限连接子Style来实践多层继承: <stylename="CodeFont.Red.Big"> <itemname="android:textSize">30sp</item> </style> 相对第一种,Android对第二种方式做出的限制就是Style必须是由自己定义的,或者说父Style和子Style必须是定义在同一个程序内,不能是引用第三方或系统的Style。毕竟对于系统的Style的引用是需要加上android:前缀作为命名空间。 其次在使用Style时,对于第二种方式定义的Style,必须引用其完全的名字,也就是说必须要包含完整的前缀和名字: <EditText style="@style/CodeFont.Red.Big" .../> Android对于第一种定义方式并没用限制,所以所有以第二种方式定义的Style都可以转用第一种: <stylename="Big"parent="CodeFont.Red"> <itemname="android:textSize">30sp</item> </style> 只要parent中的名字对应上实际定义的Style名字即可。不过换成第一种后Style的名字如果太简洁就容易冲突了。 两种继承方式混合的效果 前面说到Style的两种继承方式的效果是一致的,那假如将两种方式混在一起定义一个Style又会是什么样的效果呢?下边就用实际例子来分析一下。 首先定义一些实验所需的自定义属性(attr),(这样可以减少系统属性的干扰,因为系统总是会为它的属性定义值,那样可能无法分辨最后的效果是来自系统还是定义的值) <?xmlversion="1.0"encoding="utf-8"?> <resources> <declare-styleablename="CustomStyle"> <attrname="customColor"format="color"/> <attrname="customText"format="string"/> <attrname="customSize"format="dimension"/> </declare-styleable> </resources> 接着定义一个TextView的子类,并在其中获取上边自定义属性的值并赋予TextView去呈现: importandroid.util.TypedValue; importandroid.widget.TextView; /** *@authorIder */ publicclassStyledTextViewextendsTextView{ publicStyledTextView(Contextcontext){ this(context,null); } publicStyledTextView(Contextcontext,AttributeSetattrs){ this(context,attrs,0); } publicStyledTextView(Contextcontext,AttributeSetattrs,intdefStyleAttr){ super(context,attrs,defStyleAttr); finalTypedArraya=context.getTheme() .obtainStyledAttributes(attrs,R.styleable.CustomStyle,defStyleAttr,0); finalCharSequencetext=a.getText(R.styleable.CustomStyle_customText); finalintcolor=a.getColor(R.styleable.CustomStyle_customColor,Color.RED); finalfloatsize=a.getDimensionPixelSize(R.styleable.CustomStyle_customSize,70); a.recycle(); setText(text); setTextColor(color); setTextSize(TypedValue.COMPLEX_UNIT_PX,size); } } 然后就是定义研究所需的Style <resources> <stylename="SuperStyleOne"> <itemname="customColor">@android:color/holo_orange_dark</item> <itemname="customText">HelloWorld</item> <itemname="customSize">30dp</item> </style> <stylename="SuperStyleTwo"> <itemname="customText">www.iderzheng.com</item> </style> <stylename="SuperStyleOne.SubOne"> <itemname="customColor">@android:color/holo_blue_dark</item> </style> <stylename="SuperStyleOne.SubTwo"parent="SuperStyleTwo"> </style> <stylename="SuperStyleOne.SubThree"parent="SuperStyleTwo"> <itemname="customText">blog.iderzheng.com</item> </style> </resources> 上边定义的Style里,SuperStyleOne将通过添加前缀的方式作用到子Style上,而SuperStyleTwo则通过指定到parent来其作用。可以看到SubTwo和SubThree混合了两种方式。 最后在Activity的布局视图里使用自定类并设定上不同的Style <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <com.ider.trial.styles.StyledTextView style="@style/SuperStyleOne" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <com.ider.trial.styles.StyledTextView style="@style/SuperStyleOne.SubOne" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <com.ider.trial.styles.StyledTextView style="@style/SuperStyleOne.SubTwo" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <com.ider.trial.styles.StyledTextView style="@style/SuperStyleOne.SubThree" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> 运行之后得到效果如下: 第一个和第二个都是Style标准的使用方式,也看到它们正确地获得了定义的属性值,子Style也正确的继承和覆盖了父Style的属性值。 对于第三个和第四个,它们呈现的颜色是代码中使用的默认红色(Color.RED),字体的值也是源自代码中的使用值,所以明显比前两者要小。这也就是说它们并没用继承下SuperStyleOne中定义的字体大小和颜色。但是SuperStyleTwo中定义的内容被第三个正确的显示了出来,也说明SubTwo成功继承通过parent指定的父Style的内容。而第四个呈现出来内容则说明覆盖的效果也是正确的。 在做这个试验之前,我一直以为两种方式会同时其作用,只是用parent指定比用前缀有高优先级。也就是说Android会先从当前Style定义中找某个属性的值,如果没有找到就转到parent指定的父Style中找,还没有则转到前缀指定的父Style中找。但是通过上边的结果表明:当使用parent指定父Style后,前缀方式则不在其作用,只是作为Style的名字。也就是说:Android的Style不支持多继承。Style的继承只能单线一层层下来。 反过来在看看系统定义的Style也更容易懂了,比如打开themes_holo.xml,会看到很多一样的内容被”冗余”地定义在Theme.Holo和Theme.Holo.Light两个Style下。但因为Theme.Holo.Light用parent指定了其父Style是Theme.Light,所以Theme.Holo.Light并没有从Theme.Holo继承任何属性值,也因此这样的冗余是必须的。 <stylename="Theme.Holo.Light"parent="Theme.Light"> ............ </style> 使用Theme.Holo.Light作为Style的名字只是为了名字更加的清晰明了。 References: Styles and Themes | Android Developers Android XML theme inheriting from two parent themes? – Stack Overflow xml – Reason why style attribute does not use the android: namespace prefix – Stack Overflow 本文作者:佚名 来源:51CTO

资源下载

更多资源
Nacos

Nacos

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

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

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部分的功能。

用户登录
用户注册