【网学网提醒】:网学会员为广大网友收集整理了,android学习资料大全,希望对大家有所帮助!
1.android单实例运行方法我们都知道Android平台没有任务管理器,而内部App维护者一个Activityhistorystack来实现窗口显示和销毁,对于常规从快捷方式运行来看都是startActivity可能会使用FLAG_ACTIVITY_NEW_TASK标记来打开一个新窗口,比如Launcher,所以考虑单任务的实现方法比较简单,首先Android123纠正下大家一种错误的方法就是直接在androidmanifest.xml的application节点中加入android:launchMode="singleInstance"这句,其实这样将不会起到任何作用,Apps内部维护的历史栈作用于Activity,我们必须在activity节点中加入android:launchMode="singleInstance"这句才能保证单实例,当然一般均加在主程序启动窗口的Activity。2.px像素如何转为dip设备独立像素最近有网友问如何将px像素转为dip独立设备像素,由于Android的设备分辨率众多,目前主流的为wvga,而很多老的设备为hvga甚至低端的qvga,对于兼容性来说使用dip无非是比较方便的,由于他和分辨率无关和屏幕的密度大小有关,所以推荐使用。px=(int)(dip*density+0.5f)//这里android开发网提示大家很多网友获取density(密度)的方法存在问题,从资源中获取的是静态定义的,一般为1.0对于HVGA是正好的,而对于wvga这样的应该从WindowsManager中获取,WVGA为1.5这里可以再补充一下dip,sip的知识3.Android中动态改变ImageView大小很多网友可能发现在layout.xml文件中定义了ImageView的绝对大小后,无法动态修改以后的大小显示,其实Android平台在设计UI控件时考虑到这个
问题,为了适应不同的Drawable可以通过在xml的相关ImageView中加入android:scaleType="fitXY"这行即可,但因为使用了缩放可能会造成当前UI有所变形。使用的前提是限制ImageView所在的层,可以使用一个内嵌的方法限制显示。4.如何判断Android手机当前是否联网?如果拟开发一个网络应用的
程序,首先考虑是否接入网络,在Android手机中判断是否联网可以通过ConnectivityManager类的isAvailable()方法判断,首先获取网络通讯类的实例ConnectivityManagercwjManager=(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);,使用cwjManager.getActiveNetworkInfo().isAvailable();来返回是否有效,如果为True则表示当前Android手机已经联网,可能是WiFi或GPRS、HSDPA等等,具体的可以通过ConnectivityManager类的getActiveNetworkInfo()方法判断详细的接入方式,需要注意的是有关调用需要加入
这个权限,android开发网提醒大家在真机上Market和Browser程序都使用了这个方法,来判断是否继续,同时在一些网络
超时的时候也可以检查下网络连接是否存在,以免浪费手机上的电力资源。5.Drawable、Bitmap、Canvas和Paint的关系
很多网友刚刚开始学习Android平台,对于Drawable、Bitmap、Canvas和Paint它们之间的概念不是很清楚,其实它们除了Drawable外早在Sun的J2ME中就已经出现了,但是在Android平台中,Bitmap、Canvas相关的都有所变化。首先让我们理解下Android平台中的显示类是View,但是还提供了底层图形类android.graphics,今天所说的这些均为graphics底层图形接口。Bitmap-称作位图,一般位图的文件格式后缀为bmp,当然编码器也有很多如RGB565、RGB888。作为一种逐像素的显示对象执行效率高,但是缺点也很明显存储效率低。我们理解为一种存储对象比较好。Drawable-作为Android平下通用的图形对象,它可以装载常用格式的图像,比如GIF、PNG、JPG,当然也支持BMP,当然还提供一些高级的可视化对象,比如渐变、图形等。Canvas-名为画布,我们可以看作是一种处理过程,使用各种方法来管理Bitmap、GL或者Path路径,同时它可以配合Matrix矩阵类给图像做旋转、缩放等操作,同时Canvas类还提供了裁剪、选取等操作。Paint-我们可以把它看做一个画图工具,比如画笔、画刷。他管理了每个画图工具的字体、颜色、样式。如果涉及一些Android游戏开发、显示特效可以通过这些底层图形类来高效实现自己的应用。6.Activity切换导致的onCreate重复执行部分网友会发现Activity在切换到后台或布局从横屏LANDSCAPE切换到PORTRAIT,会重新切换Activity会触发一次onCreate方法,我们可以在androidmanifest.xml中的activit元素加入这个属性android:configChanges="orientation|keyboardHidden"即可,比如同时在Activity的Java文件中重载onConfigurationChanged(ConfigurationnewConfig)这个方法,这样就不会在布局切换或窗口切换时重载onCreate等方法。代码如下:@OverridepublicvoidonConfigurationChanged(ConfigurationnewConfig){super.onConfigurationChanged(newConfig);if(this.getResources().getConfiguration().orientation==Configuration.ORIENTATION_LANDSCAPE){//land}elseif(this.getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT)
{//port}}7.Android的ImageButton问题很多网友对Android提供的ImageButton有个疑问,当显示Drawable图片时就不会再显示文字了,其实解决的方法有两种,第一种就是图片中就写入文字,但是这样解决会增加程序体积,同时硬编码方式会影响多国语言的发布。第二种解决方法很简单,通过分析可以看
到ImageButton的layout,我们可以直接直接继承,添加一个TextView,对齐方式为右侧即可实现ImageButton支持文字右侧显示。8.Android代码优化技术1.Java内存控制对于字符串操作而言如果需要连加这样的操作建议使用StringBuilder,经过调试不难发现如果你的字符串每次连加,使用String需要的内存开销会远大于StringBuilder,然后Android手机常规的运行内存大约在128MB左右,对于运行多任务就需要考虑了,Android开发网提示因为Java有GC不需要手动释放那么分配的时候就要格外的小心,频繁的GC操作仍然是很影响性能的,在调试时我们可以通过logcat查看内存释放情况。2.循环使用平时在访问一个属性的时候效率远比一个固定变量低,如果你的循环估计次数常常大于5,假设xxx.GetLength()方法的值一般大于5,推荐这样写,比如for(inti=0;i 对于Android的网络通讯性能的提高,我们可以使用Java上高性能的NIO(NewI/O)技术进行处理,NIO是从JDK1.4开始引入的,NIO的N我们可以理解为Noblocking即非阻塞的意思,相对应传统的I/O,比如Socket的accpet()、read()这些方法而言都是阻塞的。NIO主要使用了Channel和Selector来实现,Java的Selector类似Winsock的Select模式,是一种基于事件驱动的,整个处理方法使用了轮训的状态机,如果你过去开发过Symbian应用的话这种方式有点像活动对象,好处就是单线程更节省系统开销,NIO的好处可以很好的处理并发,对于Android网游开发来说比较关键,对于多点Socket连接而言使用NIO可以大大减少线程使用,降低了线程死锁的概率,毕竟手机游戏有UI线程,音乐线程,网络线程,管理的难度可想而知,同时I/O这种低速设备将影响游戏的体验。NIO作为一种中高负载的I/O模型,相对于传统的BIO(BlockingI/O)来说有了很大的提高,处理并发不用太多的线程,省去了创建销毁的时间,如果线程过
多调度是问题,同时很多线程可能处于空闲状态,大大浪费了CPU时间,同时过多的线程可能是性能大幅下降,一般的解决方案中可能使用线程池来管理调度但这种方法治标不治本。使用NIO可以使并发的效率大大提高。当然NIO和JDK7中的AIO还存在一些区别,AIO作为一种更新的当然这是对于Java而言,如果你开发过Winsock服务器,那么IOCP这样的I/O完成端口可以解决更高级的负载,当然了今天Android123主要给大家讲解下为什么使用NIO在Android中有哪些用处。NIO我们分为几个类型分别描述,作为Java的特性之一,我们需要了解一些新的概念,比如ByteBuffer类,Channel,SocketChannel,ServerSocketChannel,Selector和SelectionKey。有关具体的使用,Android开发网将在明天详细讲解。网友可以在AndroidSDK文档中看下java.nio和java.nio.channels两个包了解。android123/androidkaifa/695.html了解下这种技术,看看在马上要做的项目中是否用得到10.AndroidTheme和Styles内部定义解析昨天我们讲到的有关在AndroidManifest.xml中定义Activity的theme方法来实现无标题的方法,在使用xml让你的Activity无标题方法一文中讲到的,很多网友不明白为什么这样做,其实在Android123以前的文章中多次提到了styles样式定义方法,今天Android开发网再次把一些网友回顾了解下android样式的内部定义。在一个工程的res/values/theme.xml中我们可以方便的定义自己的风格主题,比如下面的cwjTheme中我们使用了基于android内部的白色调的背景Theme.Light,设置windowsNoTitle为true代表没有标题,背景颜色我们使用了android内部定义的透明,同时设置listView控件的样式为cwjListView,xml样式代码如下:true@android:color/transparent@style/cwjListView
有关ListView控件我们自定义的风格就是修改下系统listview这个控件的每行分隔符样式,这里我们在工程下res/drawable文件夹下放一个图片名为list_selector图片,这样我们的cwjListView的代码可以这样写@drawable/list_selector通过定义style可以设置更多,比如让cwjListView的字体颜色就加入textAppearance属性,比如@android:style/TextAppearance等等。11.AndroidJSON解析示例代码来自Google官方的有关Android平台的JSON解析示例,如果远程服务器
使用了json而不是xml的数据提供,在Android平台上已经内置的org.json包可以很方便的实现手机客户端的解析处理。下面Android123一起分析下这个例子,帮助Android开发者需要有关HTTP通讯、正则表达式、JSON解析、appWidget开发的一些知识。publicclassWordWidgetextendsAppWidgetProvider{//appWidget@OverridepublicvoidonUpdate(Contextcontext,AppWidgetManagerappWidgetManager,int[]appWidgetIds){context.startService(newIntent(context,UpdateService.class));//避免ANR,所以Widget中开了个服务}publicstaticclassUpdateServiceextendsService{@OverridepublicvoidonStart(Intentintent,intstartId){//BuildthewidgetupdatefortodayRemoteViewsupdateViews=buildUpdate(this);ComponentNamethisWidget=newComponentName(this,WordWidget.class);AppWidgetManagermanager=AppWidgetManager.getInstance(this);manager.updateAppWidget(thisWidget,updateViews);}publicRemoteViewsbuildUpdate(Contextcontext){//PickoutmonthnamesfromresourcesResourcesres=context.getResources();String[]monthNames=res.getStringArray(R.array.month_names);Timetoday=newTime();today.setToNow();
StringpageName=res.getString(R.string.template_wotd_title,monthNames[today.month],today.monthDay);RemoteViewsupdateViews=null;StringpageContent="";try{SimpleWikiHelper.prepareUserAgent(context);pageContent=SimpleWikiHelper.getPageContent(pageName,false);}catch(ApiExceptione){Log.e("WordWidget","Couldn'tcontactAPI",e);}catch(ParseExceptione){Log.e("WordWidget","Couldn'tparseAPIresponse",e);}Patternpattern=Patternpile(SimpleWikiHelper.WORD_OF_DAY_REGEX);//正则表达式处理,有关定义见下面的SimpleWikiHelper类Matchermatcher=pattern.matcher(pageContent);if(matcher.find()){updateViews=newRemoteViews(context.getPackageName(),R.layout.widget_word);StringwordTitle=matcher.group(1);updateViews.setTextViewText(R.id.word_title,wordTitle);updateViews.setTextViewText(R.id.word_type,matcher.group(2));updateViews.setTextViewText(R.id.definition,matcher.group(3).trim());StringdefinePage=res.getString(R.string.template_define_url,Uri.encode(wordTitle));IntentdefineIntent=newIntent(Intent.ACTION_VIEW,Uri.parse(definePage));//这里是打开相应的网页,所以Uri是http的url,action是view即打开web浏览器PendingIntentpendingIntent=PendingIntent.getActivity(context,0/*norequestCode*/,defineIntent,0/*noflags*/);updateViews.setOnClickPendingIntent(R.id.widget,pendingIntent);//单击Widget打开Activity}else{updateViews=newRemoteViews(context.getPackageName(),R.layout.widget_message);CharSequenceerrorMessage=context.getText(R.string.widget_error);updateViews.setTextViewText(R.id.message,errorMessage);}returnupdateViews;}
@OverridepublicIBinderonBind(Intentintent){//Wedon'tneedtobindtothisservicere
turnnull;}}}有关网络通讯的实体类,以及一些常量定义如下:publicclassSimpleWikiHelper{privatestaticfinalStringTAG="SimpleWikiHelper";publicstaticfinalStringWORD_OF_DAY_REGEX="(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}";privatestaticfinalStringWIKTIONARY_PAGE="en.wiktionary.org/w/api.php?action=query&;prop=revisions&;titles=%s&;"+"rvprop=content&;format=json%s";privatestaticfinalStringWIKTIONARY_EXPAND_TEMPLATES="&;rvexpandtemplates=true";privatestaticfinalintHTTP_STATUS_OK=200;privatestaticbyte[]sBuffer=newbyte[512];privatestaticStringsUserAgent=null;publicstaticclassApiExceptionextendsException{publicApiException(StringdetailMessage,Throwablethrowable){super(detailMessage,throwable);}publicApiException(StringdetailMessage){super(detailMessage);}}publicstaticclassParseExceptionextendsException{publicParseException(StringdetailMessage,Throwablethrowable){super(detailMessage,throwable);}}
publicstaticvoidprepareUserAgent(Contextcontext){try{//ReadpackagenameandversionnumberfrommanifestPackageManagermanager=context.getPackageManager();PackageInfoinfo=manager.getPackageInfo(context.getPackageName(),0);sUserAgent=String.format(context.getString(R.string.template_user_agent),info.packageName,info.versionName);}catch(NameNotFoundExceptione){Log.e(TAG,"Couldn'tfindpackageinformationinPackageManager",e);}}publicstaticStringgetPageContent(Stringtitle,booleanexpandTemplates)throwsApiException,ParseException{StringencodedTitle=Uri.encode(title);StringexpandClause=expandTemplates?WIKTIONARY_EXPAND_TEMPLATES:"";Stringcontent=getUrlContent(String.format(WIKTIONARY_PAGE,encodedTitle,expandClause));try{JSONObjectresponse=newJSONObject(content);JSONObjectquery=response.getJSONObject("query");JSONObjectpages=query.getJSONObject("pages");JSONObjectpage=pages.getJSONObject((String)pages.keys().next());JSONArrayrevisions=page.getJSONArray("revisions");JSONObjectrevision=revisions.getJSONObject(0);returnrevision.getString("*");}catch(JSONExceptione){thrownewParseException("ProblemparsingAPIresponse",e);}}protectedstaticsynchronizedStringgetUrlContent(Stringurl)throwsApiException{if(sUserAgent==null){thrownewApiException("User-Agentstringmustbeprepared");}HttpClientclient=newDefaultHttpClient();HttpGetrequest=newHttpGet(url);request.setHeader("User-Agent",sUserAgent);//设置客户端标识try{HttpResponseresponse=client.execute(request);
StatusLinestatus=response.getStatusLine();if(status.getStatusCode()!=HTTP_STATUS_OK){thrownewApiException("Invalidresponsefromserver:"+status.toString());}HttpEntityentity=response.getEntity();InputStreaminputStream=entity.getContent();//获取HTTP返回的数据流ByteArrayOutputStreamcontent=newByteArrayOutputStrea
m();intreadBytes=0;while((readBytes=inputStream.read(sBuffer))!=-1){content.write(sBuffer,0,readBytes);//转化为字节数组流}returnnewString(content.toByteArray());//从字节数组构建String}catch(IOExceptione){thrownewApiException("ProblemcommunicatingwithAPI",e);}}}有关整个每日维基的widget例子比较简单,主要是帮助大家积累常用代码,了解Android平台JSON的处理方式,毕竟很多Server还是Java的。12.Android中使用定时器TimerTask类介绍在Android平台中需要反复按周期执行方法可以使用Java上自带的TimerTask类,TimerTask相对于Thread来说对于资源消耗的更低,除了使用Android自带的AlarmManager使用Timer定时器是一种更好的解决方法。我们需要引入importjava.util.Timer;和importjava.util.TimerTask;privateTimermTimer=newTimer(true);privateTimerTaskmTimerTask;mTimerTask=newTimerTask(){publicvoidrun(){Log.v("android123","cwj");}};mTimer.schedule(mTimerTask,5000,1000);//在1秒后每5秒执行一次定时器中的方法,比如本文为调用log.v打印输出。
如果想取消可以调用下面方法,取消定时器的执行while(!mTimerTask.cancel());mTimer.cancel();最后Android123提示大家,如果处理的东西比较耗时还是开个线程比较好,Timer还是会阻塞主线程的执行,更像是一种消息的执行方式。当然比Handler的postDelay等方法更适合处理计划任务。13.Android应用Icon大小在不同分辨率下定义对于Android平台来说,不同分辨率下Icon的大小设计有着不同的要求,对于目前主流的HDPI即WVGA级别来说,通常hdpi的应用icon大小为72x72,而标准的mdpi即hvga为48x48,对于目前HTC和Motorola推出的一些QVGA的使用了ldpi,图标为32x32,常见的Android图标大小设计规范如下表所示:Launcher36x36px48x48px72x72pxMenu36x36px48x48px72x72pxStatusBar24x24px32x32px48x48pxTab24x24px32x32px48x48pxDialog24x24px32x32px48x48pxListView24x24px32x32px48x48px
对于android界面设计的安全色,如下表
而对于系统自带默认程序的图标,下面为png的透明格式,直接鼠标右键另存为即可
看看sdk文档上的关于界面图标的详细说明。14.Android控件美化Shape你会用吗?
如果你对Android系统自带的UI控件感觉不够满意,可以尝试下自定义控件,我们就以Button为例,很早以前Android123就写到过AndroidButton按钮控件美化方法里面提到了xml的selector构造。当然除了使用drawable这样的图片外今天Android开发网谈下自定义图形shape的方法,对于Button控件Android上支持以下几种属性shape、gradient、stroke、corners等。我们就以目前系统的Button
的selector为例说下:对于上面,这条shape的定义,分别为渐变,gradient中startColor属性为开始的颜色,在endColor为渐变结束的颜色,下面的angle是角度。接下来是stroke可以理解为边缘,corners为拐角这里radius属性为半径,最后是相对位置属性padding。对于一个Button完整的定义可以为 android:left="10dp"android:top="10dp"android:right="10dp"android:bottom="10dp"/>
注意Android123提示大家,以上几个item的区别主要是体现在state_pressed按下或state_focused获得焦点时,当当来判断显示什么类型,而没有state_xxx属性的item可以看作是常规状态下。15.Android开发者应该保持以下特质Android123推荐新手应该遵循1.深读SDK文档2.深读SDK的APIDemo和Samples3.掌握GIT开源代码4.多了解Android开源项目,学习别人的手法写程序。16.Android数组排序常见方法Android的数组排序方式基本上使用了Sun原生的JavaAPI实现,常用的有Comparator接口实现compare方法和Comparable接口的compareTo方法,我们对于一个数组列表比如ArrayList可以通过这两个接口进行排序和比较,这里Android123给大家一个例子privatefinalComparatorcwjComparator=newComparator(){privatefinalCollatorcollator=Collator.getInstance();
publicfinalintcompare(Objecta,Objectb){CharSequencea=((Item)a).sName;CharSequenceb=((Item)b).sID;returncollatorpare(a,b);}};我们的ArrayList对象名为mList,则执行排序可以调用方法Collections.sort(mList,cwjComparator);17.Android控件
TextProgressBar进度条上显文字Android系统的进度条控件默认的设计的不是很周全,比如没有包含文字的显示,那么如何在Android进度条控件上显示文字呢?来自Google内部的代码来了解下,主要使用的addView这样的方法通过覆盖一层Chronometer秒表控件来实现,整个代码如下publicclassTextProgressBarextendsRelativeLayoutimplementsOnChronometerTickListener{publicstaticfinalStringTAG="TextProgressBar";staticfinalintCHRONOMETER_ID=android.R.id.text1;staticfinalintPROGRESSBAR_ID=android.R.id.progress;
ChronometermChronometer=null;ProgressBarmProgressBar=null;longmDurationBase=-1;intmDuration=-1;booleanmChronometerFollow=false;intmChronometerGravity=Gravity.NO_GRAVITY;publicTextProgressBar(Contextcontext,AttributeSetattrs,intdefStyle){super(context,attrs,defStyle);}publicTextProgressBar(Contextcontext,AttributeSetattrs){super(context,attrs);}publicTextProgressBar(Contextcontext){super(context);}//Android开发网提示关键部分在这里@OverridepublicvoidaddView(Viewchild,intindex,ViewGroup.LayoutParamsparams){super.addView(child,index,params);intchildId=child.getId();if(childId==CHRONOMETER_ID&;&;childinstanceofChronometer){mChronometer=(Chronometer)child;mChronometer.setOnChronometerTickListener(this);//CheckifChronometershouldmovewithwithProgressBarmChronometerFollow=(params.width==ViewGroup.LayoutParams.WRAP_CONTENT);mChronometerGravity=(mChronometer.getGravity()&;Gravity.HORIZONTAL_GRAVITY_MASK);}elseif(childId==PROGRESSBAR_ID&;&;childinstanceofProgressBar){mProgressBar=(ProgressBar)child;}}@android.view.RemotableViewMethodpublicvoidsetDurationBase(longdurationBase){mDurationBase=durationBase;if(mProgressBar==null||mChronometer==null){thrownewRuntimeException("ExpectingchildProgressBarwithid"+"'android.R.id.progress'andChronometerid'android.R.id.text1'");}
//UpdatetheProgressBarmaximumrelativetoChronometerbasemDuration=(int)(durationBase-mChronometer.getBase());if(mDuration<=0){mDuration=1;}mProgressBar.setMax(mDuration);}publicvoidonChronometerTick(Chronometerchronometer){if(mProgressBar==null){thrownewRuntimeException("ExpectingchildProgressBarwithid'android.R.id.progress'");}//StopChronometerifwe'repastdurationlongnow=SystemClock.elapsedRealtime();if(now>=mDurationBase){mChronometer.stop();}intremaining=(int)(mDurationBase-now);mProgressBar.setProgress(mDuration-remaining);if(mChronometerFollow){RelativeLayout.LayoutParamsparams;params=(RelativeLayout.LayoutParams)mProgressBar.getLayoutParams();intcontentWidth=mProgressBar.getWidth()-(params.leftMargin+params.rightMargin);intleadingEdge=((contentWidth*mProgressBar.getProgress())/mProgressBar.getMax())+params.leftMargin;intadjustLeft
=0;inttextWidth=mChronometer.getWidth();if(mChronometerGravity==Gravity.RIGHT){adjustLeft=-textWidth;}elseif(mChronometerGravity==Gravity.CENTER_HORIZONTAL){adjustLeft=-(textWidth/2);}leadingEdge+=adjustLeft;intrightLimit=contentWidth-params.rightMargin-textWidth;if(leadingEdgerightLimit){leadingEdge=rightLimit;}params=(RelativeLayout.LayoutParams)mChronometer.getLayoutParams();params.leftMargin=leadingEdge;mChronometer.requestLayout();
}}}18.Android内存管理-SoftReference的使用很多时候我们需要考虑Android平台上的内存管理问题,DalvikVM给每个进程都分配了一定量的可用堆内存,当我们处理一些耗费资源的操作时可能会产生OOM错误(OutOfMemoryError)这样的异常,Android123观察了下国内的类似Market客户端设计,基本上都没有采用很好的内存管理机制和缓存处理。如果细心的网友可能发现AndroidMarket客户端载入时,每个列表项的图标是异步刷新显示的,但当我们快速的往下滚动到一定数量比如50个,再往回滚动时可能我们看到了部分App的图标又重新开始加载,当然这一过程可能是从SQLite数据库中缓存的,但是在内存中已经通过类似SoftReference的方式管理内存。在Java中内存管理,引用分为四大类,强引用HardReference、弱引用WeakReference、软引用SoftReference和虚引用PhantomReference。它们的区别也很明显,HardReference对象是即使虚拟机内存吃紧抛出OOM也不会导致这一引用的对象被回收,而WeakReference等更适合于一些数量不多,但体积稍微庞大的对象,在这四个引用中,它是最容易被垃圾回收的,而我们对于显示类似AndroidMarket中每个应用的AppIcon时可以考虑使用SoftReference来解决内存不至于快速回收,同时当内存短缺面临JavaVM崩溃抛出OOM前时,软引用将会强制回收内存,最后的虚引用一般没有实际意义,仅仅观察GC的活动状态,对于测试比较实用同时必须和ReferenceQueue一起使用。对于一组数据,我们可以通过HashMap的方式来添加一组SoftReference对象来临时保留一些数据,同时对于需要反复通过网络获取的不经常改变的内容,可以通过本地的文件系统或数据库来存储缓存,希望给国内做AppStore这样的客户端一些改进建议。19.反射在Android开发中的利弊由于Android2.2的推出,很多新的API加入导致很多项目移植需要考虑使用Java的反射机制Reflection来动态调用,动态调用的好处就是不需要使用引用文件,直接通过JDK中声明好的方法直接调用,本身原理基于JVM的,从Java1.5开始支持,原理上就是根据类名而不实例化对
象的情况下,获得对象的方法或属性而直接调用。Android开发时反射能帮助我们多少?1.有些网友可能发现Android的SDK比较封闭,很多敏感的方法常规的用户无法编译,我们如果翻看了代码直接在反射中声明动态调用即可。比如很多internal或I开头的AIDL接口均可以通过反射轻松调用。2.反射对于Android123来说更重要的是考虑到应用的兼容性,我们目前主要兼容从Android1.5到2.2的项目,APILevel从3到8可以方便的扩充,调用前我们预留一个标志位声明该API的最低以及最高的APILevel为多少可以调用。
3.对于调试Java的反射是功臣了,在Logcat中我们可以看到出错的地方肯定有类似java.lang.reflect.XXX的字样,这种自检机制可以帮助我们方便的调试Android应用程序。反射的缺点有哪些?1.因为是动态执行的,效率自然没有预编译时引用现有的库效率高,就像平时我们Win32开发时,可以不用h文件,直接通过GetProcAddress一样去动态获取方法的地址。当然效率要根据复杂程度而决定,一般稍微复杂的处理性能损失可能超过20%,对于一些复杂的涉及Java自动类型转换判断,执行时间可能是直接引用的上千倍,所以最终我们调试时必须考虑性能问题。2.因为反射是动态的,所以需要处理很多异常,不然Dalvik崩溃出ForceClose的概率会大很多,很简单的一个反射就需要至少3个异常捕获,本身try-catch效率就不是很高,自然进一步影响运行效率,对于Android开发我们必须考虑这些问题。3.反射因为导致代码臃肿,自然稍微复杂的几个方法实用反射将会导致代码可读性和维护性降低,如果很抽象的调用Android开发网强烈不推荐这种方法。最后要说的是Reflection并不是Java的专利,微软的.Net也同样支持,同时更多的动态语言如Ruby等均支持这一特性。20.AsyncTask对比Thread加Handler很多网友可能发现Android平台很多应用使用的都是AsyncTask,而并非Thread和Handler去更新UI,这里Android123给大家说下他们到底有什么区别,我们平时应该使用哪种解决方案。Android1.5从开始系统将AsyncTask引入到android.os包中,过去在很早1.1和1.0SDK时其实官方将其命名为UserTask,其内部是JDK1.5开始新增的concurrent库,做过J2EE的网友可能明白并发库效率和强大性,比Java原始的Thread更灵活和强大,但对于轻量级的使用更为占用系统资源。Thread是Java早期为实现多线程而设计的,比较简单不支持concurrent中很多特性在同步和线程池类中需要自己去实现很多的东西,对于分布式应用来说更需要自己写调度代码,而为了AndroidUI的刷新Google引入了
Handler和Looper机制,它们均基于消息实现,有事可能消息队列阻塞或其他原因无法准确的使用。Android开发网推荐大家使用AsyncTask代替Thread+Handler的方式,不仅调用上更为简单,经过实测更可靠一些,Google在Browser中大量使用了异步任务作为处理耗时的I/O操作,比如下载文件、读写数据库等等,它们在本质上都离不开消息,但是AsyncTask相比Thread加Handler更为可靠,更易于维护,AsyncTask缺点也是有的比如一旦线程开启即dobackground方法执行后无法给线程发送但消息,仅能通过预先设置好的标记来控制逻辑,当然可以通过线程的挂起等待标志位的改变来通讯,对于某些应用Thread和Handler以及Looper可能更灵活。21.AndroidDrawable叠加处理方法大家可能知道Bitmap的叠加处理在Android平台中可以通过Canvas一层一层的画就行了,而Drawable中如何处理呢?除了使用BitmapDrawable的getBitmap方法将Drawable转换为Bitmap外,今天Android123给大家说下好用简单的LayerDrawable类,LayerDrawable顾名思义就是层图形对象。下面直接用一个简单的代码表示:
Bitmapbm=BitmapFactory.decodeResource(getResources(),R.drawable.cwj);Drawable[]array=newDrawable[3];array[0]=newPaintDrawable(Color.BLACK);//黑色array[1]=newPaintDrawable(Color.WHITE);//白色array[2]=newBitmapDrawable(bm);//位图资源LayerDrawableld=newLayerDrawable(array);//参数为上面的Drawable数组ld.setLayerInset(1,1,1,1,1);//第一个参数1代表数组的第二个元素,为白色ld.setLayerInset(2,2,2,2,2);//第一个参数2代表数组的第三个元素,为位图资源mImageView.setImageDrawable(ld);上面的方法中LayerDrawable是关键,Android开发网提示setLayerInset方法原型为publicvoidsetLayerInset(intindex,intl,intt,intr,intb)其中第一个参数为层的索引号,后面的四个参数分别为left、top、right和bottom。对于简单的图片合成我们可以将第一和第二层的PaintDrawable换成BitmapDrawable即可实现简单的图片合成。22.onRetainNonConfigurationInstance和getLastNonConfigurationInstance很多网友可能知道Android横竖屏切换时会触发onSaveInstanceState,而还原时会产生onRestoreInstanceState,但是Android的Activity类还有一个方法名为onRetainNonConfigurationInstance和getLastNonConfigurationInstance这两个方法。我们可以通过onRetainNonConfigurationInstance代替onSaveInstanceState,比如距离2@OverridepublicObjectonRetainNonConfigurationInstance(){//这里需要保存的内容,在切换时不是bundle了,我们可以直接通过Object来代替returnobj;}在恢复窗口时,我们可以不使用onRestoreInstanceState,而代替的是getLastNonConfigurationInstance方法。
我们可以直接在onCreate中使用,比如Objectobj=getLastNonConfigurationInstance();最终obj的内容就是上次切换时的内容。
这里Android123提醒大家,每次Activity横竖屏切换时onCreate方法都会被触发。
23.Android中String资源文件的format方法很多时候我们感性Google在设计Android时遵守了大量MVC架构方式,可以让写公共代码、美工和具体逻辑开发人员独立出来。有关Android的资源文件values/strings.xml中如何实现格式化字符串呢?这里Android123举个简单的例子,以及最终可能会用到哪些地方。
cwj_Demoandroid开发网上面是一段简单的字符串资源文件,没有用到格式化,因为比较简单直接描述了意思,当我们设计一个类似DeletexxxFile?的时候,我们可能需要在Java中动态获取xxx的名称,所以定义资源时使用格式化可以轻松解决,不需要一堆String去拼接或StringBuffer一个一个append这样的愚蠢方法,看例子Delete%1$sFile这里%1$s代表这是一个字符串型的,如
果是整数型可以写为%1$d,类似printf这样的格式化字符串函数,当然如果包含了多个需要格式化的内容,则第二个可以写为%2$s或%2$d了,那么最终在Java中如何调用呢?看下面的例子:例一:整数型的Iam%1$dyearsold定义的是这样的当然,我们杜绝意外情况,比如冒出个secret这样的string类型的,注意上面是%1$d不是%1$s,所以默认标准的合并成为intnAge=23;StringsAgeFormat=getResources().getString(R.string.alert);StringsFinalAge=String.format(sAgeFormat,nAge);这样执行完后,就组成了Iam23yearsold,是不是很方便啊.当然了,下面看下String字符串时的情况.例二:字符串型的StringsName="cwj"StringsCity="Shanghai"资源定义为Mynameis%1$s,Iamform%2$s
则Java中只需要StringsInfoFormat=getResources().getString(R.string.alert2);StringsFinalInfo=String.format(sInfoFormat,sName,sCity);
我们看到了整个,整个定义类似MFC的CString::Format或MacOS中的NSLog,但是需要显示类似C#中那样显示的标出参数的数字,比如%1或%n,这里数字代表参数的第n个。本行最终sFinalInfo显示的内容为Mynameiscwj,IamformShanghai。当然了你有什么不懂的地方可以来函至android123@16324.Android工程内嵌资源文件的两种方法Android软件一般处理大的资源通过sdcard比如在线下载资源到sdcard,而apk中内嵌资源或二进制文件时一般使用下面的两种方法:方法一res/raw目录下存放,比如cwj.dat一个二进
制文件,我们可以读取可以直接InputStreamis=context.getResources().openRawResource(R.raw.cwj);方法二工程根目录下的assets文件夹中存放,比如assets/cwj.dat这样我们使用下面的代码AssetManageram=context.getAssets();InputStreamis=am.open(cwj.dat);这里Android123提示大家Google的Android系统处理Assert有个bug,在AssertManager中不能处理单个超过1MB的文件,不然会报异常具体数值大家可以测试下传个稍大的文件,我们在两年前的文章中有提到,而第一种raw没这个限制可以放个4MB的Mp3文件没问题。25.Android自定义View以及layout属性全攻略对于Android系统的自定义View可能大家都熟悉了,对于自定义View的属性添加,以及Android的Layout的命名空间问题,很多网友还不是很清楚,今天Android123一起再带大家温习一下CwjViewmyView=newCwjView(context);如果用于游戏或整个窗体的界面,我们可能直接在onCreate中setContentView(myView);当然如果是控件,我们可能会需要从Layout的xml中声明,比如当然,我们也可以直接从父类声明比如
上面我们仅用了父类View的两个属性,均来自android命名空间,而名称为layout_width或layout_height,我们自定义的控件可能有更多的功能,比如我们可以看到上面的三个属性,是我们自定义的。作为标准xml规范,可能还包含了类似xmlns:android="schemas.android/apk/res/android"这样的语句,对于定义完整的View,我们的命名空间为cwj,这里可以写为xmlns:cwj=schemas.android/apk/res/cn.android123.cwjView或xmlns:cwj=schemas.android/apk/res/android都可以。。对于定义的cwj命名空间和age、university以及city的三个属性我们如何定义呢?在工程的res/values目录中我们新建一个cwj_attr.xml文件,编码方式为utf-8是一个好习惯,内容如下这里我们可能对format不是很熟悉,目前Android系统内置的格式类型有integer比如ProgressBar的进度值,float比如RatingBar的值可能是3.5颗星,boolean比如ToggleButton的是否勾选,string比如TextView的text属性,当然除了我们常见的基础类型外,Android的
属性还有特殊的比如color是用于颜色属性的,可以识别为#FF0000等类型,当然还有dimension的尺寸类型,比如23dip,15px,18sp的长度单位,还有一种特殊的为reference,一般用于引用@+id/cwj@drawable/xxx这样的类型。当然什么时候用reference呢?我们就以定义一个颜色为例子,这里我们用了逻辑或的运算符,定义的红色是颜色类型的,同时可以被引用
当然,对于我们自定义的类中,我们需要使用一个名为obtainStyledAttributes的方法来获取我们的定义。在我们自定义View的构造方法(Contextcontext,AttributeSetattrs)的重载类型中可以用publicCwjView(Contextcontext,AttributeSetattrs){super(context,attrs);TypedArraya=context.obtainStyledAttributes(attrs,R.styleable.cwj_attr);mAge=a.getInteger(R.styleable.CwjView_age,22);mCity=a.getString(R.styleable.CwjView_city,"shanghai");mUniversity=a.getString(R.styleable.CwjView_university,"sjtu");a.recycle();//Android123提示大家不要忘了回收资源}这样类的全局成员变量mAge、mCity就获取了我们需要的内容,当然根据layout中的数值我们自定义的CwjView需要动态的处理一些数据的情况,可以使用AttributeSet类的getAttributeResourceValue方法获取。publicCwjView(Contextcontext,AttributeSetattrs){super(context,attrs);resId=attrs.getAttributeResourceValue("cn.android123.CwjView","age",100);resId=attrs.getAttributeResourceValue("cn.android123.CwjView","city","shanghai");//resID就可以任意使用了}以上两种方法中,参数的最后一个数值为默认的,如果您有不明白的地方可以来函到android123@163我们会在第一时间回复。26.自定义Android主题风格theme.xml方法在Android中可以通过自定义主题风格方式来实现个性化以及复用,首先我们创建theme.xml主题文件,保存位置为工程的res/values/theme.xml,这里我们可以可以为主题起一个名称,比如CWJ,这里去除了xml的文件头这行,我们在工程中只需在androidmanifest.xml文件的Activity节点中加入android:theme="@style/Theme.CWJ"属性,则这个Activity就使用了这种主题风格,整个xml的关键代码如下:@drawable/android123
其中上面的代码中,我们定义设置全局android:windowBackground即背景值为/res/drawable中的android123图片为背景,更多的属性定义可以参考view的layoutxml属性设置,比如我们设置所有字体颜色、大体大小和样式,可以在style节点中加入#fff14sp "android:textStyle">bold当然我们可以将上面的android123的图片改进下,使用一个xml文件替代,比如使用bitmap对象,则/res/drawable/android123.xml的完整代码变为这里我们使用了一个bitmap对象来解析cwj_image图片,当然这里可以识别各种类型的图片,其中android:tileMode是bitmap的内部属性,其中tileMode设置为repeat代表重复,这样可以节省bitmap资源,比如我们的背景是一层楼,那么全屏可以显示同样的为5层效果,而图片仅是一层大小,对于资源利用相对更高。当然bitmap的属性tileMode的值为repeat外还有其他的值比如clamp、mirror,这些值并没有在SDK中并没有找到定义,通过上次Android开发网的Android自定义View以及layout属性全攻略一文,我们可以联想到bitmap属于android.graphics.Bitmap包,由于是android框架,所以下载git的base包,找到该类,类的实例化时android123已经在Android自定义View以及layout属性全攻略说的很清楚,所以我们定位到res\values中找到attr.xml有关bitmap的定义即可,有关bitmap的更多属性如antialias、filter和dither都可以找到使用。27.android调试工具monkey压力测试实战很多Android开发者可能因为没有充分测试自己的软件造成很容易出现FC(ForceClose)的问题,这里我们可以通过使用Android固件中自带的monkey工具来做软件的压力测试,monkey工具可以模拟各种按键,触屏,轨迹球、activity等事件,这里Android123提示大家说白了monkey就是一个小猴子随机狂玩你的android软件,看看会不会产生异常。具体的使用我们通过AndroidSDK给我们的adb调试桥链接设备或模拟器,进入LinuxShell状态,当然我们可以输入adbshell获取设备的shell,也可以直接通过adb命令执行,比如说adbshellmonkey来查看monkey工具中的参数说明,如图:
我们要测试的apk文件要在android设备中已经安装,当然模拟器中也可以测试的。执行adbshellmonkey-pcn.android123.cwj-v100我们执行这句的中包含了p参数,这里代表已安装软件的packageName,而v代表查看monkey生成的详细随机事件名,最后的数字100为我们测试的随机事件数量为100.有关更多的测试方法,请查看上图中的参数,整个测试比较简单单很有效,不妨试试。28.自定义View有关Android的自定义View的框架今天我们一起讨论下,对于常规的游戏,我们在View中需要处理以下几种问题:1.控制事件2.刷新View3.绘制View1.对于控制事件今天我们只处理
按键事件onKeyDown,以后的文章中将会讲到屏幕触控的具体处理onTouchEvent以及Sensor重力感应等方法。2.刷新view的方法这里主要有invalidate(intl,intt,intr,intb)刷新局部,四个参数分别为左、上、右、下。整个view刷新invalidate(),刷新一个矩形区域invalidate(Rectdirty),刷新一个特性Drawable,invalidateDrawable(Drawabledrawable),执行invalidate类的方法将会设置view为无效,最终导致onDraw方法被重新调用。由于今天的view比较简单,Android123提示大家如果在线程中刷新,除了使用handler方式外,可以在Thread中直接使用postInvalidate方法来实现。3.绘制View主要是onDraw()中通过形参canvas来处理,相关的绘制主要有drawRect、drawLine、drawPath等等。view方法内部还重写了很多接口,其回调方法可以帮助我们判断出view的位置和大小,比如onMeasure(int,int)Calledtodeterminethesizerequirementsforthisviewandallofitschildren.、onLayout(boolean,int,int,int,int)Calledwhenthisviewshouldassignasizeandpositiontoallofitschildren和onSizeChanged(int,int,int,int)Calledwhenthesizeofthisviewhaschanged.具体的作用,大家可以用Logcat获取当view变化时每个形参的变动。下面cwjView是我们为今后游戏设计的一个简单自定义View框架,我们可以看到在Android平台自定义view还是很简单的,同时Java支持多继承可以帮助我们不断的完善复杂的问题。
publicclasscwjViewextendsView{publiccwjView(Contextcontext){super(context);setFocusable(true);//允许获得焦点setFocusableInTouchMode(true);//获取焦点时允许触控}@OverrideprotectedParcelableonSaveInstanceState(){//处理窗口保存事件ParcelablepSaved=super.onSaveInstanceState();Bundlebundle=newBundle();//dosomethingreturnbundle;}@OverrideprotectedvoidonRestoreInstanceState(Parcelablestate){//处理窗口还原事件Bundlebundle=(Bundle)state;//dosomethingsuper.onRestoreInstanceState(bundle.getParcelable("cwj"));return;}@OverrideprotectedvoidonSizeChanged(intw,inth,intoldw,intoldh)//处理窗口大小变化事件{super.onSizeChanged(w,h,oldw,oldh);}@OverrideprotectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){super.onMeasure(widthMeasureSpec,heightMeasureSpec);//如果不让父类处理记住调用setMeasuredDimension}@OverrideprotectedvoidonLayout(booleanchanged,intleft,inttop,intright,intbottom){super.onLayout(changed,left,top,ight,bottom);}@OverrideprotectedvoidonDraw(Canvascanvas){Paintbg=newPaint();bg.setColor(Color.Red);
canvas.drawRect(0,0,getWidth()/2,getHeight()/2,bg);//将view的左上角四分之一填充为红色}@OverridepublicbooleanonTouchEvent(MotionEventevent){returnsuper.onTouchEvent(ev
ent);//让父类处理屏幕触控事件}@OverridepublicbooleanonKeyDown(intkeyCode,KeyEventevent){//处理按键事件,响应的轨迹球事件为publicbooleanonTrackballEvent(MotionEventevent)switch(keyCode){caseKeyEvent.KEYCODE_DPAD_UP:break;caseKeyEvent.KEYCODE_DPAD_DOWN:break;caseKeyEvent.KEYCODE_DPAD_LEFT:break;caseKeyEvent.KEYCODE_DPAD_RIGHT:break;caseKeyEvent.KEYCODE_DPAD_CENTER://处理中键按下break;default:returnsuper.onKeyDown(keyCode,event);}returntrue;}}上面我们可以看到onMeasure使用的是父类的处理方法,如果我们需要解决自定义View的大小,可以尝试下面的方法@OverrideprotectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){height=View.MeasureSpec.getSize(heightMeasureSpec);width=View.MeasureSpec.getSize(widthMeasureSpec);