首页 文章 精选 留言 我的

精选列表

搜索[服务器开发],共10000篇文章
优秀的个人博客,低调大师

Android开发实践:自定义带消息循环(Looper)的工作线程

1. 首先,我们完成一个简单的线程框架。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class LooperThread{ private volatile boolean mIsLooperQuit= false ; private ThreadmThread; public void start(){ if (mThread!= null ){ return ; } mIsLooperQuit= false ; mThread= new Thread(mLooperRunnable); mThread.start(); } public void stop(){ if (mThread== null ){ return ; } mIsLooperQuit= true ; mThread= null ; } protected RunnablemLooperRunnable= new Runnable(){ @Override public void run(){ while (!mIsLooperQuit){ } } }; } 如上述代码所示,mLooperRunnable.run()循环执行线程任务,mIsLooperQuit则是线程退出循环的条件。下面,我们将添加消息的发送和处理代码。 2. 添加线程循环的消息发送和处理代码 (1) 定义消息结构体,创建消息队列 1 2 3 4 5 6 7 8 public class LooperThread{ private Queue<Message>mMessageQueue= new LinkedList<Message>(); public static class Message{ int what; } } (2) 创建互斥锁和条件变量 1 2 3 4 5 public class LooperThread{ private LockmLock= new ReentrantLock(); private ConditionmCondition=mLock.newCondition(); } (3) 创建发送消息的函数 1 2 3 4 5 6 7 8 9 10 //发送消息,由外部其他模块调用,发送消息给线程 public void sendMessage(Messagemessage){ if (mThread== null ){ return ; } mLock.lock(); mMessageQueue.add(message); //添加消息到消息队列 mCondition.signal(); //通知线程循环,有消息来了,请立即处理 mLock.unlock(); } (4) 创建处理消息的函数 1 2 3 4 //处理消息,由线程内部调用 public void handleMessage(Messagemessage){ //这里可以通过一个Callback来回调监听者 } (5) 在mLooperRunnable.run()循环中解析消息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 protected RunnablemLooperRunnable= new Runnable(){ @Override public void run(){ while (!mIsLooperQuit){ mLock.lock(); Messagemessage= null ; try { while (!mIsLooperQuit&&mMessageQueue.isEmpty()){ mCondition.await(); //没有消息到来则休眠 } message=mMessageQueue.poll(); } catch (InterruptedExceptione){ e.printStackTrace(); } finally { mLock.unlock(); } handleMessage(message); } }; } (6) 修改线程的Stop()函数,唤醒休眠的消息循环 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public void stop(){ if (mThread== null ){ return ; } mIsLooperQuit= true ; mLock.lock(); mCondition.signal(); mLock.unlock(); mMessageQueue.clear(); mThread= null ; } 到这里,一个基本的带有消息循环的线程类封装就完成了,相信大家应该从编写这段代码的过程中,理解了系统是如何实现消息循环的。 本文转自 Jhuster 51CTO博客,原文链接:http://blog.51cto.com/ticktick/1565272,如需转载请自行联系原作者

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

开发一个完整iOS直播app——GPUImage渲染底层实现-opengl

一、前言 本篇主要讲解GPUImage底层是如何渲染的,GPUImage底层使用的是OPENGL,操控GPU来实现屏幕展示 由于网上OpenGL实战资料特别少,官方文档对一些方法也是解释不清楚,避免广大同学再次爬坑,本篇讲解了不少OpenGL的知识,并且还讲解了花了大量时间解决bug的注意点,曾经因为对glDrawArrays这个方法不熟悉,遇上Bug,晚上熬到凌晨四点都没解决,还是第二天中午解决的。 如果喜欢我的文章,可以关注我微博:袁峥Seemygo,也可以来小码哥,了解下我们的iOS培训课程。后续还会更新更多内容,有任何问题,欢迎关注微,信号“小码哥订阅号”。 二、GPUImageVideoCamera 可以捕获采集的视频数据 关键是捕获到一帧一帧视频数据如何展示? 通过这个方法可以获取采集的视频数据 采集视频注意点:要设置采集竖屏,否则获取的数据是横屏 通过AVCaptureConnection就可以设置 三、自定义OpenGLView渲染视频 暴露一个接口,获取采集到的帧数据,然后把帧数据传递给渲染View,展示出来 四、利用OpenGL渲染帧数据并显示 导入头文件#import <GLKit/GLKit.h>,GLKit.h底层使用了OpenGLES,导入它,相当于自动导入了OpenGLES 步骤 01-自定义图层类型 02-初始化CAEAGLLayer图层属性 03-创建EAGLContext 04-创建渲染缓冲区 05-创建帧缓冲区 06-创建着色器 07-创建着色器程序 08-创建纹理对象 09-YUV转RGB绘制纹理 10-渲染缓冲区到屏幕 11-清理内存 01-自定义图层类型 为什么要自定义图层类型CAEAGLLayer? CAEAGLLayer是OpenGL专门用来渲染的图层,使用OpenGL必须使用这个图层 02-初始化CAEAGLLayer图层属性 1.不透明度(opaque)=YES,CALayer默认是透明的,透明性能不好,最好设置为不透明. 2.设置绘图属性 kEAGLDrawablePropertyRetainedBacking :NO (告诉CoreAnimation不要试图保留任何以前绘制的图像留作以后重用) kEAGLDrawablePropertyColorFormat :kEAGLColorFormatRGBA8 (告诉CoreAnimation用8位来保存RGBA的值) 其实设置不设置都无所谓,默认也是这个值,只不过GPUImage设置了 03-创建EAGLContext 需要将它设置为当前context,所有的OpenGL ES渲染默认渲染到当前上下文 EAGLContext管理所有使用OpenGL ES进行描绘的状态,命令以及资源信息,要绘制东西,必须要有上下文,跟图形上下文类似。 当你创建一个EAGLContext,你要声明你要用哪个version的API。这里,我们选择OpenGL ES 2.0 04-创建渲染缓冲区 有了上下文,openGL还需要在一块buffer进行描绘,这块buffer就是RenderBuffer OpenGLES 总共有三大不同用途的color buffer,depth buffer 和 stencil buffer. 最基本的是color buffer,创建它就好了 函数glGenRenderbuffers 它是为renderbuffer(渲染缓存)申请一个id(名字),创建渲染缓存 参数n表示申请生成renderbuffer的个数 参数renderbuffers返回分配给renderbuffer(渲染缓存)的id 。 注意:返回的id不会为0,id 0 是OpenGL ES保留的,我们也不能使用id 为0的renderbuffer(渲染缓存)。 函数glBindRenderbuffer 告诉OpenGL:我在后面引用GL_RENDERBUFFER的地方,其实是引用_colorRenderBuffer 参数target必须为GL_RENDERBUFFER 参数renderbuffer就是使用glGenRenderbuffers生成的id 。 当指定id的renderbuffer第一次被设置为当前renderbuffer时,会初始化该 renderbuffer对象,其初始值为: 函数renderbufferStorage 把渲染缓存(renderbuffer)绑定到渲染图层(CAEAGLLayer)上,并为它分配一个共享内存。 参数target,为哪个renderbuffer分配存储空间 参数drawable,绑定在哪个渲染图层,会根据渲染图层里的绘图属性生成共享内存。 实战代码 05-创建帧缓冲区 它相当于buffer(color, depth, stencil)的管理者,三大buffer可以附加到一个framebuffer上 本质是把framebuffer内容渲染到屏幕 函数glFramebufferRenderbuffer 该函数是将相关buffer()三大buffer之一)attach到framebuffer上,就会自动把渲染缓存的内容填充到帧缓存,在由帧缓存渲染到屏幕 参数target,哪个帧缓存 参数attachment是指定renderbuffer被装配到那个装配点上,其值是GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT中的一个,分别对应 color,depth和 stencil三大buffer。 renderbuffertarget:哪个渲染缓存 renderbuffer渲染缓存id 06-创建着色器 着色器 什么是着色器? 通常用来处理纹理对象,并且把处理好的纹理对象渲染到帧缓存上,从而显示到屏幕上。 提取纹理信息,可以处理顶点坐标空间转换,纹理色彩度调整(滤镜效果)等操作。 着色器分为顶点着色器,片段着色器 顶点着色器用来确定图形形状 片段着色器用来确定图形渲染颜色 步骤: 1.编辑着色器代码 2.创建着色器 3.编译着色器 只要创建一次,可以在一开始的时候创建 着色器代码 实战代码 07-创建着色器程序 步骤: 1.创建程序 2.贴上顶点和片段着色器 3.绑定attribute属性 4.连接程序 5.绑定uniform属性 6.运行程序 注意点:第3步和第5步,绑定属性,必须有顺序,否则绑定不成功,造成黑屏 08-创建纹理对象 纹理 采集的是一张一张的图片,可以把图片转换为OpenGL中的纹理, 然后再把纹理画到OpenGL的上下文中 什么是纹理?一个纹理其实就是一幅图像。 纹理映射,我们可以把这幅图像的整体或部分贴到我们先前用顶点勾画出的物体上去. 比如绘制一面砖墙,就可以用一幅真实的砖墙图像或照片作为纹理贴到一个矩形上,这样,一面逼真的砖墙就画好了。如果不用纹理映射的方法,则墙上的每一块砖都必须作为一个独立的多边形来画。另外,纹理映射能够保证在变换多边形时,多边形上的纹理图案也随之变化。 纹理映射是一个相当复杂的过程,基本步骤如下: 1)激活纹理单元、2)创建纹理 、3)绑定纹理 、4)设置滤波 注意:纹理映射只能在RGBA方式下执行 函数glTexParameter 控制滤波,滤波就是去除没用的信息,保留有用的信息 一般来说,纹理图像为正方形或长方形。但当它映射到一个多边形或曲面上并变换到屏幕坐标时,纹理的单个纹素很少对应于屏幕图像上的像素。根据所用变换和所用纹理映射,屏幕上单个象素可以对应于一个纹素的一小部分(即放大)或一大批纹素(即缩小) 固定写法 函数glPixelStorei 设置像素存储方式 pname:像素存储方式名 一种是GL_PACK_ALIGNMENT用于将像素数据打包,一般用于压缩。 另一种是GL_UNPACK_ALIGNMENT,用于将像素数据解包,一般生成纹理对象,就需要用到解包. param:用于指定存储器中每个像素行有多少个字节对齐。这个数值一般是1、2、4或8, 一般填1,一个像素对应一个字节; 函数CVOpenGLESTextureCacheCreateTextureFromImage 根据图片生成纹理 参数allocator kCFAllocatorDefault,默认分配内存 参数textureCache 纹理缓存 参数sourceImage 图片 参数textureAttributes NULL 参数target , GL_TEXTURE_2D(创建2维纹理对象) 参数internalFormat GL_LUMINANCE,亮度格式 参数width 图片宽 参数height 图片高 参数format GL_LUMINANCE 亮度格式 参数type 图片类型 GL_UNSIGNED_BYTE 参数planeIndex 0,切面角标,表示第0个切面 参数textureOut 输出的纹理对象 实战代码 09-YUV转RGB绘制纹理 纹理映射只能在RGBA方式下执行 而采集的是YUV,所以需要把YUV 转换 为 RGBA, 本质其实就是改下矩阵结构 注意点(熬夜凌晨的bug):glDrawArrays如果要绘制着色器上的点和片段,必须和着色器赋值代码放在一个代码块中,否则找不到绘制的信息,就绘制不上去,造成屏幕黑屏 之前是把glDrawArrays和YUV转RGB方法分开,就一直黑屏. 函数glUniform1i 指定着色器中亮度纹理对应哪一层纹理单元 参数location:着色器中纹理坐标 参数x:指定那一层纹理 函数glEnableVertexAttribArray 开启顶点属性数组,只有开启顶点属性,才能给顶点属性信息赋值 函数glVertexAttribPointer 设置顶点着色器属性,描述属性的基本信息 参数indx:属性ID,给哪个属性描述信息 参数size:顶点属性由几个值组成,这个值必须位1,2,3或4; 参数type:表示属性的数据类型 参数normalized:GL_FALSE表示不要将数据类型标准化 参数stride 表示数组中每个元素的长度; 参数ptr 表示数组的首地址 函数glBindAttribLocation 给属性绑定ID,通过ID获取属性,方便以后使用 参数program 程序 参数index 属性ID 参数name 属性名称 函数glDrawArrays 作用:使用当前激活的顶点着色器的顶点数据和片段着色器数据来绘制基本图形 mode:绘制方式 一般使用GL_TRIANGLE_STRIP,三角形绘制法 first:从数组中哪个顶点开始绘制,一般为0 count:数组中顶点数量,在定义顶点着色器的时候,就定义过了,比如vec4,表示4个顶点 注意点,如果要绘制着色器上的点和片段,必须和着色器赋值代码放在一个代码块中,否则找不到绘制的信息,就绘制不上去,造成屏幕黑屏。 实战代码 请点击此处输入图片描述请点击此处输入图片描述 10-渲染缓冲区到屏幕 注意点:必须设置窗口尺寸glViewport 注意点:渲染代码必须调用[EAGLContext setCurrentContext:_context] 原因:因为是多线程,每一个线程都有一个上下文,只要在一个上下文绘制就好,设置线程的上下文为我们自己的上下文,就能绘制在一起了,否则会黑屏. 注意点:每次创建纹理前,先把之前的纹理引用清空[self cleanUpTextures],否则卡顿 函数glViewport 设置OpenGL渲染窗口的尺寸大小,一般跟图层尺寸一样. 注意:在我们绘制之前还有一件重要的事情要做,我们必须告诉OpenGL渲染窗口的尺寸大小 方法presentRenderbuffer 是将指定renderbuffer呈现在屏幕上 实战代码 11-清理内存 注意:只要有Ref结尾的,都需要自己手动管理,清空 函数glClearColor 设置一个RGB颜色和透明度,接下来会用这个颜色涂满全屏. 函数glClear 用来指定要用清屏颜色来清除由mask指定的buffer,mask可以是 GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT的自由组合。 在这里我们只使用到 color buffer,所以清除的就是 clolor buffer。 GPUImage工作原理 GPUImage最关键在于GPUImageFramebuffer这个类,这个类会保存当前处理好的图片信息。 GPUImage是通过一个链条处理图片,每个链条通过target连接,每个target处理完图片后,会生成一个GPUImageFramebuffer对象,并且把图片信息保存到GPUImageFramebuffer。 这样比如targetA处理好,要处理targetB,就会先取出targetA的图片,然后targetB在targetA的图片基础上在进行处理. 本文转自ljianbing51CTO博客,原文链接:http://blog.51cto.com/ljianbing/1921654 ,如需转载请自行联系原作者

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

Android开发笔记——常见BUG类型之内存泄露与线程安全

一、内存泄露 1、很抱歉,”XXX”已停止运行。OOM? 怎样才能让app报OOM呢?最简单的办法如下: Bitmapbt1=BitmapFactory.decodeResource(this.getResources(),R.drawable.image); Bitmapbt2=BitmapFactory.decodeResource(this.getResources(),R.drawable.image); Bitmapbtn=... 2、查看内存占用 命令行:adb shell dumpsys meminfopackageName 通过Android Studio的Memory Monitor查看内存中Dalvik Heap的实时变化 3、发生内存泄露的条件 首先,每个app有最大内存限制。 ActivityManageractivityManager=(ActivityManager)context.getSystemServiceContext.ACTIVITY_SERVICE); activityManager.getMemoryClass(); getMemoryClass()取到的是最大内存资源。Android中的堆内存分为NativeHeap和DalvikHeap。C/C++申请的内存空间在NativeHeap中,Java申请的内存空间则在DalvikHeap中。对于head堆的大小限制,可以查看/system/build.prop文件: dalvik.vm.heapstartsize=8m dalvik.vm.heapgrowthlimit=96m dalvik.vm.heapsize=256m 注意: heapsize参数表示单个进程heap可用的最大内存,但如果存在以下参数”dalvik.vm.headgrowthlimit =96m”表示单个进程heap内存被限定在96m,即程序运行过程实际只能使用96m内存。 如果申请的内存资源超过上述限制,系统就会抛出OOM错误。 4、常见避免OOM的措施 以下主要从四个方面总结常见的措施:1)减小对象的内存占用;2)内存对象的重复利用;3)避免对象的内存泄露;4)内存使用策略优化。 4.1 减小对象的内存占用 使用ArrayMap/SparseArray而不是HashMap等传统数据结构。 参考链接:Android内存优化(使用SparseArray和ArrayMap代替HashMap) 在Android中避免使用枚举。 减小Bitmap对象的内存占用。inSampleSize和decode format。 4.2 内存对象的重复利用 ListView/GridView等出现大量重复子组件的视图里面对ConvertView的复用 使用LRU机制缓存Bitmap 避免在onDraw方法里面执行对象的创建 使用StringBuilder来替代频繁的”+” 4.3 避免对象的内存泄露 4.1和4.2都是比较常规的措施,4.3需要重点关注。 1)Activity泄露 导致Activity泄露的原因较多,下面列举一些比较常见的。从原理上主要分为两类:i)静态对象;ii)this$0。 Activity被static变量引用。这段代码来自于我们的Crash上传 privatestaticMap<ComponentName,ExceptionHandler>configMap= newHashMap<ComponentName,ExceptionHandler>();publicstaticvoidsetActivity(finalActivityactivity,booleansend2Server){ Log.d(TAG,"bindexceptionhandler:"+activity.getComponentName().getClassName());//上下文初始化SDKContext.init(activity.getApplication()); init(activity.getApplication()); ExceptionHandlerexceptionHandler=newExceptionHandler( activity,send2Server,Thread.getDefaultUncaughtExceptionHandler()); configMap.put(activity.getComponentName(),exceptionHandler); Thread.setDefaultUncaughtExceptionHandler(exceptionHandler); } 下面是通过MAT分析一个Activity泄露的截图: 内部类引用导致Activity的泄漏 最典型的场景是Handler导致的Activity泄漏,如果Handler中有延迟的任务或者是等待执行的任务队列过长,都有可能因为Handler继续执行而导致Activity发生泄漏。此时的引用关系链是Looper -> MessageQueue -> Message -> Handler -> Activity。为了解决这个问题,可以在UI退出之前,执行remove Handler消息队列中的消息与runnable对象。或者是使用Static + WeakReference的方式来达到断开Handler与Activity之间存在引用关系的目的。 可参考链接:线程通信 2)考虑使用Application Context而不是Activity Context 对于大部分非必须使用Activity Context的情况(Dialog的Context就必须是Activity Context),我们都可以考虑使用Application Context而不是Activity的Context,这样可以避免不经意的Activity泄露。 3)注意临时Bitmap对象的及时回收 虽然在大多数情况下,我们会对Bitmap增加缓存机制,但是在某些时候,部分Bitmap是需要及时回收的。例如临时创建的某个相对比较大的bitmap对象,在经过变换得到新的bitmap对象之后,应该尽快回收原始的bitmap,这样能够更快释放原始bitmap所占用的空间。 4)内存占用监控 通过Runtime获取maxMemory,而maxMemory-totalMemory即为剩余可使用的dalvik内存。定期检查这个值,达到80%就去释放各种cache资源(bitmap的cache)。 /** *Returnsthemaximumnumberofbytestheheapcanexpandto.See{@link#totalMemory}forthe *currentnumberofbytestakenbytheheap,and{@link#freeMemory}forthecurrentnumberof *thosebytesactuallyusedbyliveobjects.*/intmaxMemory=Runtime.getRuntime().maxMemory());//应用程序最大可用内存/** *Returnsthenumberofbytestakenbytheheapatitscurrentsize.Theheapmayexpandor *contractovertime,asthenumberofliveobjectsincreasesordecreases.See *{@link#maxMemory}forthemaximumheapsize,and{@link#freeMemory}foranideaofhowmuch *theheapcouldcurrentlycontract.*/longtotalMemory=Runtime.getRuntime().totalMemory());//应用程序已获得内存/** *Returnsthenumberofbytescurrentlyavailableontheheapwithoutexpandingtheheap.See *{@link#totalMemory}fortheheap'scurrentsize.Whenthesebytesareexhausted,theheap *mayexpand.See{@link#maxMemory}forthatlimit.*/longfreeMemory=Runtime.getRuntime().freeMemory());//应用程序已获得内存中未使用内存 5)注意Cursor对象是否及时关闭 在程序中我们经常会进行查询数据库的操作,但时常会存在不小心使用Cursor之后没有及时关闭的情况。这些Cursor的泄露,反复多次出现的话会对内存管理产生很大的负面影响,我们需要谨记对Cursor对象的及时关闭。 4.4 内存使用策略优化 谨慎使用large heap 综合考虑设备内存阈值与其他因素设计合适的缓存大小 onLowMemory()/onTrimMemory(int) 资源文件需要选择合适的文件夹进行存放 Try catch某些大内存分配的操作 谨慎使用static对象 优化布局层次,减少内存消耗 谨慎使用多进程 谨慎使用依赖注入框架 使用ProGuard来剔除不需要的代码 谨慎使用第三方libraries 考虑不同的实现方式来优化内存占用 二、线程安全 1、下面的方法是线程安全的吗? 怎样使上述方法线程安全? 2、Java中的线程安全 怎样保持在多线程环境下的数据一致性,Java提供了多种方法实现: synchronized java.util.concurrent.atomic java.util.concurrent.locks thread safe collection(ConcurrentHashMap) volatile 2.1 synchronized JVM保证被synchronized关键字修饰的代码段在同一时间只能被一个线程访问,内部通过对对象或类加锁来实现的。当方法被synchronized修饰时,锁加在对象上;当方法同时为static时,锁加在类上。从性能的角度来讲,一般不建议直接将锁加在类上,这样会使得类的所有对象的该方法均为synchronized的。 从之前扫描的问题来看,在编写synchronized程序时主要有两点需要注意: synchronized需要创建基于对象或者类的锁,所以不能在构造器或者变量上加锁。 synchronized造成死锁。 1) 锁加在哪里? List<ResultPoint>currentPossible=possibleResultPoints; List<ResultPoint>currentLast=lastPossibleResultPoints;intframeLeft=frame.left;intframeTop=frame.top;if(currentPossible.isEmpty()){ lastPossibleResultPoints=null; }else{ possibleResultPoints=newArrayList<>(5); lastPossibleResultPoints=currentPossible; paint.setAlpha(CURRENT_POINT_OPACITY); paint.setColor(resultPointColor);synchronized(currentPossible){for(ResultPointpoint:currentPossible){ canvas.drawCircle(frameLeft+(int)(point.getX()*scaleX),frameTop+(int)(point.getY()*scaleY),POINT_SIZE, paint); } } } 上述方法中,possibleResultPoints的创建没有采用同步措施,需要使用Collections.synchronizedXxx。 List<MyType>list=Collections.synchronizedList(newArrayList(<MyType>)); ...synchronized(list){for(MyTypem:list){ foo(m); m.doSomething(); } } 一般比较推荐创建一个虚拟的对象专门用于获取锁。 //dummyobjectvariableforsynchronizationprivateObjectmutex=newObject(); ...//usingsynchronizedblocktoread,incrementandupdatecountvaluesynchronouslysynchronized(mutex){ count++; } PS:直接在方法上加synchronized可能DoS攻击喔,举个栗子: publicclassMyObject{//Locksontheobject'smonitor publicsynchronizedvoiddoSomething(){ //...} }//黑客的代码MyObjectmyObject=newMyObject();synchronized(myObject){while(true){//IndefinitelydelaymyObjectThread.sleep(Integer.MAX_VALUE); } } 黑客的代码获取了MyObject对象的锁,导致doSomething死锁,从而引发Denial of Service。 publicclassMyObject{//locksontheclassobject'smonitor publicstaticsynchronizedvoiddoSomething(){ //...} }//黑客的代码synchronized(MyObject.class){while(true){ Thread.sleep(Integer.MAX_VALUE);//IndefinitelydelayMyObject} } 2) 死锁。 publicclassThreadDeadlock{publicstaticvoidmain(String[]args)throwsInterruptedException{ Objectobj1=newObject(); Objectobj2=newObject(); Objectobj3=newObject(); Threadt1=newThread(newSyncThread(obj1,obj2),"t1"); Threadt2=newThread(newSyncThread(obj2,obj3),"t2"); Threadt3=newThread(newSyncThread(obj3,obj1),"t3"); t1.start(); Thread.sleep(5000); t2.start(); Thread.sleep(5000); t3.start(); } }classSyncThreadimplementsRunnable{privateObjectobj1;privateObjectobj2;publicSyncThread(Objecto1,Objecto2){this.obj1=o1;this.obj2=o2; } @Overridepublicvoidrun(){ Stringname=Thread.currentThread().getName(); System.out.println(name+"acquiringlockon"+obj1);synchronized(obj1){ System.out.println(name+"acquiredlockon"+obj1); work(); System.out.println(name+"acquiringlockon"+obj2);synchronized(obj2){ System.out.println(name+"acquiredlockon"+obj2); work(); } System.out.println(name+"releasedlockon"+obj2); } System.out.println(name+"releasedlockon"+obj1); System.out.println(name+"finishedexecution."); }privatevoidwork(){try{ Thread.sleep(30000); }catch(InterruptedExceptione){ e.printStackTrace(); } } } 本文转自xsster51CTO博客,原文链接:http://blog.51cto.com/12945177/1929765 ,如需转载请自行联系原作者

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

Android GIS开发系列-- 入门季(6)GraphicsLayer添加文字与图片标签

一、GraphicsLayer添加图片 GraphicLayer添加图片Graphic,要用到PictureMarkerSymbol,也是样式的一种。添加代码如下: Drawable drawable = getResources().getDrawable(R.mipmap.ic_launcher); PictureMarkerSymbol pictureMarkerSymbol = new PictureMarkerSymbol(this, drawable); Graphic graphic = new Graphic(new Point(113,22), pictureMarkerSymbol); graphicsLayer.addGraphic(graphic); 添加图片效果如下: 二、GraphicsLayer添加文字 GraphicLayer添加文字,要利用TextSymbol。代码如下 TextSymbol textSymbol = new TextSymbol(20,"我是文字" , Color.RED); Graphic graphic = new Graphic(new Point(113,22), textSymbol ); graphicsLayer.addGraphic(graphic); 上面的代码没有设置使用哪种字体,Arcgis会默认的字体。如果 我们想添加中文,要设置textSymbol.setFontFamily("DroidSansFallback.ttf");, 为textSymbol设置字体,arcgis会到system\fonts下找到此字体。但有时我们设置此代码时中文并没有出现,可能会有乱码等,是因为系统的fon ts下面并没有此字体,解决的办法有,将手机root,将DroidSansFallback.ttf字体添加到system\fonts下。华为的手机是有此字体的,而三星5.0以 上则没有此字体,在华为手机上还有一款中文DroidSansChinese.ttf字体arcgis也是支持的。而三星5.0以上的中文字体好像不是ttf字体,而是otf字体 ,Arcgis好像不支持,本人也没有找到好的解决办法。 没有整理与归纳的知识,一文不值!高度概括与梳理的知识,才是自己真正的知识与技能。 永远不要让自己的自由、好奇、充满创造力的想法被现实的框架所束缚,让创造力自由成长吧! 多花时间,关心他(她)人,正如别人所关心你的。理想的腾飞与实现,没有别人的支持与帮助,是万万不能的。 本文转自wenglabs博客园博客,原文链接:http://www.cnblogs.com/arxive/p/7751888.html ,如需转载请自行联系原作者

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

Android Ap 开发 设计模式第一篇:迭代器模式

Iterator Pattern迭代器模式 场景猜想 经常编写代码都会碰到遍历一个数组,使用for循环得到数组下标之后去做进一步操作。例如下文代码: int []array = new int [ 5 ]; for ( int i = 0 ;i < array.length;i ++ ){ System.out.println( "" + i); } 或: int []array = new int [ 5 ]; for ( int i:array){ System.out.println( "" + i); } 当然,还有更多的作法,利用将 i 将下标推移的方式遍历数组元素。 使用设计模式来编写的时候,可以将 i 的行为,抽象化为迭代器,这种模式我们称之为迭代器模式 。迭代器模式可以用来作为遍历一个集合体。java 亦提供了一个工具类:java.util.Iterator<E> ,与其类似。 场景需求 书架上有5本技术书籍,要求将书籍名称一性遍历出来,将显示在Andriod 的 EditText 上面。以上面的需求为例,可以将具体需求具体到下面的示意图: Aggregate接口 Aggregate 接口是一个执行递增的“聚合”。实现此接口的类就变成类似数组、集合的“聚合”。表示己聚合的接口。 Iterator接口 Iterator 接口执行元素递增,具有类似循环变量的功能。表示执行递增、遍历的接口。 Book 类 Book 表示书籍,主要是为了获取书的详细。 BookShelf类 BookShelf 表示书架,此类实现Aggregate接口从而将其作为聚合处理。 BookShelfIterator类 BookShelfIterator表示扫描书架的类,实现了Iterator 的接口功能。 ----------------------------------------- IteratorPatternsActivity 类 IteratorPatternsActivity 为Android 的主界面,用来显示和测试上面编写的代码 ----------------------------------------- 代码实现: Aggregate接口 /** *声明一个Iterator方法,这是为了建立一个可对应聚合的Iterator *如欲递增、遍历或逐一检查某一个聚合时,利用iterator方法即可建立一个 *实现Iterator接口的类对象实例。 * @author terry * */ public interface Aggregate{ public abstract Iteratoriterator(); } Iterator 接口 public interface Iterator{ public abstract boolean hasNext(); // 存在一条则返回true public abstract Objectnext(); // 移到下一条记录 } next 方法是返回聚合当中的1个元素。不过,next 方法的功能并非仅仅如此。事实上它还会悄悄地先要在内部状态进入下一步,以便下次调用 next 方法时能确实返回下一个元素。而Iterator 只描述了有两个方法,具体这两个方法如何实现还要看BookShelfIterator内部的实现。 Book 类 /** *表示书籍的类 * @author terry * */ public class Book{ private Stringname = "" ; public Book(Stringname){ this .name = name; } public StringgetName(){ return this .name; } } BookShelf 类 /** *表示书架意图的类 *要实现Aggreagate接口才能把这个类当作聚合来处理。 * @author terry * */ public class BookShelf implements Aggregate{ private Book[]books; private int last = 0 ; public BookShelf( int maxSize){ this .books = new Book[maxSize]; } public BookgetBookAt( int index){ return books[index]; } public BookShelfappendBook(Bookbook){ this .books[last] = book; last ++ ; return this ; } public int getLength(){ return this .last; } @Override public Iteratoriterator(){ // TODOAuto-generatedmethodstub return new BookShelfIterator( this ); } } BookShelf实现了Aggregate接口 ,该方法产生并返回BookShelfIterator类的对象实例,即我们发一个命令说要对书架进行扫描时,就将得到的“聚合”返回出去。 BookShelfIterator 类 1 /** 2 *执行扫描书架用的“聚合”类 3 * @author Administrator 4 * 5 */ 6 public class BookShelfIterator implements Iterator{ 7 8 private BookShelfbookShelf; 9 private int index; 10 11 public BookShelfIterator(BookShelfbookShelf){ 12 this .bookShelf = bookShelf; 13 this .index = 0 ; 14 } 15 16 17 @Override 18 public boolean hasNext(){ 19 // TODOAuto-generatedmethodstub 20 if (index < bookShelf.getLength()){ 21 return true ; 22 } else { 23 24 return false ; 25 } 26 27 } 28 29 @Override 30 public Objectnext(){ 31 // TODOAuto-generatedmethodstub 32 Bookbook = bookShelf.getBookAt(index); 33 index ++ ; 34 35 return book; 36 } 37 38 } 做完上面一系列的代码编写之后,我们就可以在Android 的主界面上使用上面的Iterator 遍历出上面给出的需求。 IteratorPatternsActivity类 public class IteratorPatternsActivity extends Activity{ /** Calledwhentheactivityisfirstcreated. */ @Override public void onCreate(BundlesavedInstanceState){ super .onCreate(savedInstanceState); setContentView(R.layout.main); EditTextet = (EditText)findViewById(R.id.EditText01); BookShelfbookShelf = new BookShelf( 5 ); bookShelf.appendBook( new Book( " androiddeveloper " )) .appendBook( new Book( " WindowsPhone7developer " )) .appendBook( new Book( " IOSdeveloper " )) .appendBook( new Book( " BADAdeveloper " )) .appendBook( new Book( " Liondeveloper " )); Iteratoriterator = bookShelf.iterator(); while (iterator.hasNext()){ Bookbook = (Book)iterator.next(); et.setText(et.getText() + " \n " + book.getName()); } } } 上面执行的结果: 更多的功能扩展 上面只是讲述了两个功能,hasNext()和Next()方法,想想如果要在这个基础上面扩展将会是多么方便,例如: 由后往前的反向遍历 由前往后、由后往前的双向遍历(Next 、Previous......) 指定一个下标立即得到 等等 系列学习文章第一篇到此结束。下次学习Adapter Pattern (适配器模式) 本文转自 terry_龙 51CTO博客,原文链接:http://blog.51cto.com/terryblog/603456,如需转载请自行联系原作者

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

打开键盘遮住View的问题解决方法-IOS开发

5.0 以后键盘的高度貌似不是216了,不过不要紧,我们调整调整就是了: iPhone ipad 竖屏(portrait) 216 264 横屏(landScape) 140 352 我们采取的方法就是在textField(有可能是其他控件)接收到弹出键盘事件时把self.view整体上移216px了(我们就以iPhone竖屏为例了)。 有关View的frame,origin,size之类的知识点不懂的请参看我的另一篇博文: <<有关View的几个基础知识点>> 首先我们要设置textField的代理,我们就设为当前控制器了。 textField,delegate=self; 然后我们在当前控制器实现下面三个委托方法: [java]view plaincopy print? -(void)textFieldDidBeginEditing:(UITextField*)textField {//当点触textField内部,开始编辑都会调用这个方法。textField将成为firstresponder NSTimeIntervalanimationDuration=0.30f; CGRectframe=self.view.frame; frame.origin.y-=216; frame.size.height+=216; self.view.frame=frame; [UIViewbeginAnimations:@"ResizeView"context:nil]; [UIViewsetAnimationDuration:animationDuration]; self.view.frame=frame; [UIViewcommitAnimations]; } [java]view plaincopy print? -(BOOL)textFieldShouldReturn:(UITextField*)textField {//当用户按下ruturn,把焦点从textField移开那么键盘就会消失了 NSTimeIntervalanimationDuration=0.30f; CGRectframe=self.view.frame; frame.origin.y+=216; frame.size.height-=216; self.view.frame=frame; //self.view移回原位置 [UIViewbeginAnimations:@"ResizeView"context:nil]; [UIViewsetAnimationDuration:animationDuration]; self.view.frame=frame; [UIViewcommitAnimations]; [textFieldresignFirstResponder]; } 本文转自 卓行天下 51CTO博客,原文链接:http://blog.51cto.com/9951038/1772565,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

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

Nacos

Nacos

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

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册