zoukankan      html  css  js  c++  java
  • Android开发网上的一些重要知识点【2】

    31. View和SurfaceView

    在Android游戏当中充当主要的除了控制类外就是显示类,在J2ME中我们用Display和Canvas来实现这些,而Google Android中涉及到显示的为view类,Android游戏开发中比较重要和复杂的就是显示和游戏逻辑的处理。这里我们说下android.view.View和android.view.SurfaceView。SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使用SurfaceView,到底有哪些优势呢? SurfaceView可以控制表面的格式,比如大小,显示在屏幕中的位置,最关键是的提供了SurfaceHolder类,使用getHolder方法获取,相关的有Canvas  lockCanvas()
    Canvas  lockCanvas(Rect dirty)  、void  removeCallback(SurfaceHolder.Callback callback)、void  unlockCanvasAndPost(Canvas canvas) 控制图形以及绘制,而在SurfaceHolder.Callback 接口回调中可以通过下面三个抽象类可以自己定义具体的实现,比如第一个更改格式和显示画面。

    abstract void  surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    abstract void  surfaceCreated(SurfaceHolder holder)
    abstract void  surfaceDestroyed(SurfaceHolder holder)
      对于Surface相关的,Android底层还提供了GPU加速功能,所以一般实时性很强的应用中主要使用SurfaceView而不是直接从View构建,同时Android123未来后面说到的OpenGL中的GLSurfaceView也是从该类实现。

    32. Android程序内存管理必读

    很多开发者都是从J2ME或J2EE上过来的,对于内存的使用和理解并不是很到位,Android开发网本次给大家一些架构上的指导,防止出现豆腐渣工程的出现。Android作为以Java语言为主的智能平台对于我们开发一些高性能和质量的软件来说了解Android程序内存管理机制是必须的。 Android的Dalvik VM在基础方面和Sun JVM没有什么大的区别仅仅是字节码的优化,我们要知道什么时候用gc什么时候用recycle以及到底用不用finalization,因为Java对内存的分配只需要new开发者不需要显示的释放内存,但是这样造成的内存泄露问题的几率反而更高。

      1.对于常规开发者而言需要了解 Java的四种引用方式,比如强引用,软引用,弱引用以及虚引用。一些复杂些的程序在长期运行很可能出现类似OutOfMemoryError的异常。

    2.并不要过多的指望gc,不用的对象可以显示的设置为空,比如obj=null,这里Android123提示大家,java的gc使用的是一个有向图,判断一个对象是否有效看的是其他的对象能到达这个对象的顶点,有向图的相对于链表、二叉树来说开销是可想而知。

    3.Android为每个程序分配的对内存可以通过Runtime类的totalMemory() freeMemory() 两个方法获取VM的一些内存信息,对于系统heap内存获取,可以通过Dalvik.VMRuntime类的getMinimumHeapSize() 方法获取最小可用堆内存,同时显示释放软引用可以调用该类的gcSoftReferences() 方法,获取更多的运行内存。

    4.对于多线程的处理,如果并发的线程很多,同时有频繁的创建和释放,可以通过concurrent类的线程池解决线程创建的效率瓶颈。

    5. 不要在循环中创建过多的本地变量。

    有关Android和Java的系统性能分析,Android123将在以后的文章中详细讲述如何调试Java分析内存泄露以及Android上的gdb调试器分析得出内存性能改进。

    33. Android中内嵌字体实现个性化

    在Android中我们的应用可以灵活的内嵌自己的字体文件,实现各个手机上可以正常的显示个性化文字,我们都知道TextView的setTypeface方法可以设置目标文字的显示特性,比如字体、颜色、粗体、斜体等。我们直接找一个TrueTypeFont的字体文件即.ttf,对于Win32系统的用户可以直接在Windows/fonts文件夹中能找到很多。比如微软雅黑就不错,可是体积太大,由于Android的Assets类有单个文件1MB体积的限制,我们先找个英文字体做测试。这里我们将字体文件android123.ttf放到工程的assets文件夹的fonts目录中。

         Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/android123.ttf");  
         TextView tv = (TextView)findViewById(R.id.text);   

            tv.setTypeface(tf);    //设置TextView的风格
            tv.setText("CWJ Test"); 
            tv.setTextSize(12);
            tv.setTextColor(Color.RED);

    34. 获取和设置ListView的选择项

    获取当前选中项  int curPos = listView.getFirstVisiblePosition(); 当然是用getItemAtPosition(int nPos)方法也可以 ,设置当前选择位置 listView.setSelectedPosition(lastPos);  对于基于AbsListView为基类的ListView等控件均可以使用这种方法。

    35. android.text.format文件大小和日期解析类

    很多网友可能直接将自己的J2ME项目生硬的移植到Android平台,其实Google为我们提供好了文件大小和时间日期解析类,它位于android.text.format这个包中,它提供了强大的标准化解析方法:

      1. IP地址解析类 在android.text.format.Formatter中提供了String formatIpAddress(int addr) 这个方法可以轻松方便的将socket中的int型转成类似127.0.0.1的IP格式,需要注意的是Linux平台的字节顺序,即小字节序、低字节序little-endian。

      2. 文件大小解析类 细心的网友可能还看到了android.text.format.Formatter中的formatFileSize方法,该方法String formatFileSize (Context context, long number) ,第二个参数是long型,一般为File对象的最后修改时间或创建时间的方法,最终返回类似 12KB、5Bytes的值,20MB的字符串。

      3. 日期时间解析类 ,该类位于android.text.format.DateFormat这个package中,该类提供了Java中的三种时间对象,Android123提示大家下面三种方法为静态可以直接调用,如下:

      final static CharSequence  format(CharSequence inFormat, Date inDate)  //传入Date对象
      Given a format string and a Date object, returns a CharSequence containing the requested date.

    final static CharSequence  format(CharSequence inFormat, Calendar inDate)  //Calendar对象
    Given a format string and a Calendar object, returns a CharSequence containing the requested date.

    final static CharSequence  format(CharSequence inFormat, long inTimeInMillis)  //long对象
    Given a format string and a time in milliseconds since Jan 1, 1970 GMT, returns a CharSequence containing the requested date.

      我们可能看到了第一个参数均为inFormat这是一个CharSequence接口的String类型,它提供了灵活的时间格式解析字符串描述,Android开发网提示大家注意大小写要区分,如

       April 6, 1970 at 3:23am 例子,那么inFormat参数的写法和最终执行的结果如下对照,下面就以Android123的CWJ生日为例子如下

    "MM/dd/yy h:mmaa" -> "11/03/87 11:23am"
    "MMM dd, yyyy h:mmaa" -> "Nov 3, 1987 11:23am"
    "MMMM dd, yyyy h:mmaa" -> "November  3, 1987 11:23am"
    "E, MMMM dd, yyyy h:mmaa" -> "Tues, November 3, 1987 11:23am"
    "EEEE, MMMM dd, yyyy h:mmaa" -> "Tuesday, Nov 3, 1987 11:23am"

      对于判断一个时间是否为24小时制式可以通过android.text.format.DateFormat类的static boolean  is24HourFormat(Context context)方法来判断。

    36. Android代码性能优化技巧

    目前来说Android 2.2的JIT性能有了本质的提高,不过对于老版本的程序提高Java执行效率还有很多语言特点来说,今天Android123提到的不是语法糖,而是基础的问题,对于Java 1.5之后将会有明显的改进。下面的例子来自SDK:

    static class Foo {
            int mSplat;
        }
        Foo[] mArray = ...

    上面的静态类Foo的执行效果和性能,我们分三个方法zero、one和two来做对比。
        public void zero() {  //大多数人可能简单直接这样写
            int sum = 0;
            for (int i = 0; i < mArray.length; ++i) {
                sum += mArray[i].mSplat;
            }
        }
        public void one() { //通过本地对象改进性能
            int sum = 0;
            Foo[] localArray = mArray;
            int len = localArray.length;
            for (int i = 0; i < len; ++i) {
                sum += localArray[i].mSplat;
            }
        }
        public void two() { //推荐的方法,通过Java 1.5的新语法特性可以大幅改进性能
            int sum = 0;
            for (Foo a : mArray) {
                sum += a.mSplat;
            }
        }

    zero() is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the loop.

    one() is faster. It pulls everything out into local variables, avoiding the lookups. Only the array length offers a performance benefit.

    two() is fastest for devices without a JIT, and indistinguishable fromone() for devices with a JIT. It uses the enhanced for loop syntax introduced in version 1.5 of the Java programming language.

    37. Android开发注意点 Part One

    Android已经的很多细节问题我们通过平台开发总结不断完善这个列表,如果你有相关的内容可以联系 android123@163.com .

       一、AssetManager - 已知单个文件处理不能大于1MB,所以如果资源很大,建议使用Zip格式压缩存放。

       二、ScrollView中嵌入ListView - 这个作法可能会出现你的ListView仅仅显示1行半。

       三、Android自带的Zip处理类对文件名编码无法识别,也没有提供显示的设置方法,在zlib中写死了。

       四、使用一些资源对象记住关闭,比如对于文件流对象最后

              FileOutputStream os = xxx;

               try {
                    //dosomething
                } finally {
                    os.close();  //显示的使用finally关闭文件对象。
                }

              对于Cursor而言,在移动位置时首先判断Cursor是否为空,最终使用完仍然需要 close方法,如果重用,可以使用deactivate方法释放当前资源,通过requery方法再次查询。

      五、SDK中标记为 deprecated 字样的,常规情况下是有更好的方法可以替代,短期内可以放心使用。这些方法一般高版本的SDK都可以向上兼容,目前尚未发现Android放弃某些API的支持。

      六、Notification的Intent无法传递到目标的Activity,Service和Broardcast没有测试过,中途需要通过PendingIntent,可能这里出现了问题。

    38. Android上HTTP协议通讯状态获取

    通常情况下轻量级的Http传输Android平台可以直接使用Sun Java的HttpURLConnection类方法处理,比如果自己定义一次请求header可以通过setRequestProperty设置,而我们需要获取的Http Web Server状态可以通过HttpURLConnection.getResponseCode() 的方法获取。

      当然Http协议返回值常见的有 200 为成功,400为请求错误,404为未找到,500为服务器内部错误,403无权查看,302为重定向等等。

      对于Android平台提供更完善的Apache类有HttpClient 、HttpPost、HttpResponse、HttpGet和HttpEntity,其中对于数据报头header构造通过HttpEntity,而返回状态值可以通过HttpResponse获取。

      有关Android客户端和Server通讯类相关的开发我们将会在以后文章中做大量实例介绍。

    39. Android布局Java代码构造法

    一般情况下对于Android程序布局我们往往使用XML文件来编写,这样可以提高开发效率,但是考虑到代码的安全性以及执行效率,可以通过Java代码执行创建,虽然Android编译过的xml是二进制的,但是加载xml解析器的效率对于资源占用还是比较大的,一般一个简单的TextView,比如

        <TextView
        android:id="@+id/textControl "
        android:layout_width="100px"
        android:layout_height="wrap_content" />

       可以等价于下面的Java代码:

       LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(100, LayoutParams.WRAP_CONTENT); //宽度为100px,高为自适应最小的高度

       // setOrientation(VERTICAL); 设置布局为垂直

       TextView textControl = new TextView(this);//如果从一个XXXLayout.,比如LinearLayout为View的基类时这里this应该换成为创建改类的Context
       textControl.setText("Android开发网欢迎您");
       addView( textControl, textParams );

       当然Java处理效率比XML快得多,但是对于一个复杂界面的编写,可能需要一些套嵌考虑,如果你思维灵活的话,使用Java代码来布局你的Android应用程序是一个更好的方法。

    40. 测试Android软件性能主要方法

    对于Android平台上软件的性能测试可以通过以下几种方法来分析效率瓶颈,目前Google在Android软件开发过程中已经引入了多种测试工具包,比如Unit测试工程,调试类,还有模拟器的Dev Tools都可以直接反应执行性能。

       1. 在模拟器上的Dev Tools可以激活屏幕显示当前的FPS,CPU使用率,可以帮助我们测试一些3D图形界面的性能。

       2.  一般涉及到网络应用的程序,在效率上和网速有很多关系,这里需要多次的调试才能实际了解。

       3. 对于逻辑算法的效率执行,我们使用Android上最普遍的,计算执行时间来查看:

          long start = System.currentTimeMillis();
          //android开发网提示这里做实际的处理do something
          long duration = System.currentTimeMillis() - start;

          最终duration保存着实际处理该方法需要的毫秒数。这里类似Win32上的GetTickCount,在Win 32和Symbian上都提供了高精度的性能计数器和低阶计时器,这里在Dalvik VM上的Java层这种方法对于一般的应用足以。

       4. GC效率跟踪,如果你执行的应用比较简单,可以在DDMS中查看下Logcat的VM释放内存情况,大概模拟下那些地方可以缓存数据或改进算法的。

       5. 线程的使用和同步,Android平台上给我们提供了丰富的多任务同步方法,但在深层上并没有过多的比如自旋锁等高级应用,不过对于Service和appWidget而言,他们实际的产品中都应该以多线程的方式处理,以释放CPU时间,对于线程和堆内存的查看这些都可以在DDMS中看到。

       更多的调试和性能测试方法Android123将在以后的内容中出现。

    41. Splash Screen开场屏在Android中的实现

    很多网友可能发现近期Tencent推出的手机QQ Android版包含了一个开场屏Splash Screen载入效果,通常游戏或大型软件打开时可能需要一个释放解析资源的过程,需要一个前台的动画播放和后台的逻辑处理线程配合,当然对于简单的软件也可以加一个Splash Screen作为美化。在Android平台上如何实现呢?

      首先创建一个Activirty,在SetContentView时直接通过ImageView创建一个全屏的图片,Android123提示大家还要考虑好分辨率和当前设备一致,onCreate添加代码如下:

      new Handler().postDelayed(new Runnable(){   // 为了减少代码使用匿名Handler创建一个延时的调用
                public void run() { 
                    Intent i = new Intent(SplashScreen.this, Main.class);    //通过Intent打开最终真正的主界面Main这个Activity
                    SplashScreen.this.startActivity(i);    //启动Main界面
                    SplashScreen.this.finish();    //关闭自己这个开场屏
                } 
            }, 5000);   //5秒,够用了吧

    42. Android的Activity你知多少呢?

    看到这个标题很多网友肯定回答,我知道Activity是Android上的窗口基类,了解Activity的生命周期比如onCreate onStop等,呵呵,按照这样说Android123还知道Activity的实现其实是从ApplicationContext,而ApplicationContext是从Context这个抽象类派生而来的,当然我们看到显示的是View或者ViewGroup,当然今天说的不是这些东西,而是很多网友来问的Android为什么不设计一个任务管理器,当然从Android 1.5开始ActivityManager类提供了restartPackage可以关闭一个程序,需要加上<uses-permission android:name="android.permission.RESTART_PACKAGES"/>这个权限,不过我们注意到,长按Home键可以看到以前程序的运行,同时可以快速的切换回来。这就是Android独有的程序生命周期管理机制 Activity历史栈。

      我们在一个普通的程序主窗口A中打开了一个窗口B,而窗口B打开了窗口C,但是按下Back键后结果出乎了预期,是的这就是Activity的history stack的原因,在数据结构中栈是FIFO的,阻止我们不愿意看的情况的发生则可以在打开新Activity时加上标记FLAG_ACTIVITY_NO_HISTORY,代码如下:

        Intent i= new Intent(this, cwj.class); 
        i.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);   //Android开发网提示大家相关的还有Intent.FLAG_ACTIVITY_CLEAR_TOP,都试试
        startActivity(i); 

      当然更多的程序Activity控制可以再androidmanifest.xml中定义。

    43. JSONObject在Android上的应用

    如果你过去开发过AJAX应用,相信对JSONObject不会陌生吧,作为基于JavaScript的数据交换格式,可以直接代替Xml,这里Android从1.0开始就完全支持JSONObject。在平时应用中直接引入import org.json.JSONObject;即可方便使用。当然同类的还有SOAP。

      在常规使用方便JSONObject对象可以实现类似Bundle或Parcel可以封装数据,代替一个XML的ITEM,但最大的优势是可以执行一些简单的方法,比如说getString、has、put、getBoolean、getInt等数据类型的存取操作。Android123提示大家对于常规的项目开发,今天本文不考虑Server端的布局,在Android平台上处理这些比较简单,主要是一些http的请求处理。可以直接引入import org.apache.http.xxx来实现web server层的数据交换,如果你没有专业的Server开发技术,可以通过简单的Web配合JSON方式快速实现自己的交互式应用。

    44. Android高性能文件类MemoryFile

    很多网友抱怨Android处理底层I/O性能不是很理想,如果不想使用NDK则可以通过MemoryFile类实现高性能的文件读写操作。MemoryFile顾名思义就是内存文件的意思,如果你过去从事过Win32开发,那么它的原理就是MapViewOfFile(),当然开发过Linux的网友可能很快就联想到了mmap(),是的该类就是他们的托管代码层封装,位于android.os.MemoryFile这个位置,从Android 1.0开始就被支持。

      MemoryFile适用于哪些地方呢?

      对于I/O需要频繁操作的,主要是和外部存储相关的I/O操作,MemoryFile通过将 NAND或SD卡上的文件,分段映射到内存中进行修改处理,这样就用高速的RAM代替了ROM或SD卡,性能自然提高不少,对于Android手机而言同时还减少了电量消耗。Android123提示网友该类实现的功能不是很多,直接从Object上继承,通过JNI的方式直接在C底层执行。

    主要的构造方法 MemoryFile(String name, int length) ,这里第二个参数为文件大小,需要说明的是Android的MemoryFile和传统的mmap还有一点点区别,毕竟是手机,它内部的内存管理方式ashmem会从内核中回收资源。毕竟目前部分低端机型的RAM也比较吃紧。 

    synchronized boolean  allowPurging(boolean allowPurging)  //允许ashmem清理内存,线程安全同步的方式。
    void  close() //关闭,因为在Linux内部mmap占用一个句柄,不用时一定要释放了
    InputStream  getInputStream()  返回读取的内容用Java层的InputStream保存
    OutputStream  getOutputStream()  把一个OutputSream写入到MemoryFile中
    boolean  isPurgingAllowed() //判断是否允许清理
    int  length()  //返回内存映射文件大小

    下面就是我们熟悉的,读写细节,主要是对字符数组的操作,这里大家要计算好每个文件类型的占用,同时考虑到效率对于自己分配的大小考虑粒度对齐。
    int  readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
    void  writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)  

      具体的实际应用,Android开发网将在下次和大家讲到。

    45. TextUtils类-Android字符串处理类

    对于字符串处理Android为我们提供了一个简单实用的TextUtils类,如果处理比较简单的内容不用去思考正则表达式不妨试试这个在android.text.TextUtils的类,主要的功能如下:

      是否为空字符 static boolean  isEmpty(CharSequence str) 

    拆分字符串  public static String[] split (String text, String expression) ,Android开发网提示大家仔细看例子如下 String.split() returns [''] when the string to be split is empty. This returns []. This does not remove any empty strings from the result. For example split("a,", "," ) returns {"a", ""}.

    拆分字符串使用正则 public static String[] split (String text, Pattern pattern)

      确定大小写是否有效在当前位置的文本TextUtils.getCapsMode(CharSequence cs, int off, int reqModes)

      使用HTML编码这个字符串  static String  TextUtils.htmlEncode(String s)  

    46. InputSream输入流转String字符串,Android开发工具类

    在Android平台上使用Java层处理I/O时主要使用流,这里Android开发网给大家一个方便的类,可以处理InputStream输入流转为String字符串,在效率上,我们使用了字符串拼接StringBuilder类减少内存碎片以及BefferedReader类实现一个缓存。

        private String Stream2String(InputStream is) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(is), 16*1024); //强制缓存大小为16KB,一般Java类默认为8KB
                StringBuilder sb = new StringBuilder();
                String line = null;
                try {
                    while ((line = reader.readLine()) != null) {  //处理换行符
                        sb.append(line + " ");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                return sb.toString();
            }

        }

    47. layout资源包含,android开发必读

    有时候我们在一个Android程序中可能会复用布局文件,这时可以在一个xml文件中复用过去的布局文件,但是和常规的使用不同的是,需要加上类似包含头文件一样的include关键字,比如下面我们需要包含layout文件夹下的view.xml布局文件,需要<include layout="@layout/view" />  这样下,完整的如下,大家可以试一试。

    <?xml version="1.0" encoding="utf-8"?> 
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        > 
    <TextView   
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="@string/cwj"
        />
    <include layout="@layout/view" />
    <include android:id="@+id/block" layout="@layout/item" />  

    <TextView   
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="@string/android123"
        /> 
    </LinearLayout>

    48.Android控件开发之ToggleButton原理

    在Android平台上比较有特色的就是ToggleButton控件,虽然它的功能和CheckBox有些类似,但是他们的用处还是有一定的区别比如ToggleButton原本有图片装饰,通过ToggleButton可以很清楚的显示某些状态。它们均从Button为基类的CompoundButton中实现,其真假事件从Checkable来实现。

      public abstract class CompoundButton extends Button implements Checkable {
        private boolean mChecked; //状态是否选中
        private int mButtonResource;
        private boolean mBroadcasting;
        private Drawable mButtonDrawable; //按钮的图标
        private OnCheckedChangeListener mOnCheckedChangeListener; //选中状态改变监听
        private OnCheckedChangeListener mOnCheckedChangeWidgetListener;

        private static final int[] CHECKED_STATE_SET = {
            R.attr.state_checked
        };

        public CompoundButton(Context context) {
            this(context, null);
        }

        public CompoundButton(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }

        public CompoundButton(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);

            TypedArray a =
                    context.obtainStyledAttributes(
                            attrs, com.android.internal.R.styleable.CompoundButton, defStyle, 0);

            Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);
            if (d != null) {
                setButtonDrawable(d);
            }

            boolean checked = a
                    .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false);
            setChecked(checked);

            a.recycle(); //显式的GC
        }

        public void toggle() {
            setChecked(!mChecked);
        }

        @Override
        public boolean performClick() {
                  toggle();
            return super.performClick();
        }

        public boolean isChecked() {
            return mChecked;
        }

        public void setChecked(boolean checked) {
            if (mChecked != checked) {
                mChecked = checked;
                refreshDrawableState(); //更新当前状态的按钮图标

                if (mBroadcasting) {
                    return;
                }

                mBroadcasting = true;
                if (mOnCheckedChangeListener != null) {
                    mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
                }
                if (mOnCheckedChangeWidgetListener != null) {
                    mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
                }

                mBroadcasting = false;          
            }
        }

        public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
            mOnCheckedChangeListener = listener;
        }

        void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) {
            mOnCheckedChangeWidgetListener = listener;
        }

        public static interface OnCheckedChangeListener {
            void onCheckedChanged(CompoundButton buttonView, boolean isChecked);
        }

         public void setButtonDrawable(int resid) {
            if (resid != 0 && resid == mButtonResource) {
                return;
            }

            mButtonResource = resid;

            Drawable d = null;
            if (mButtonResource != 0) {
                d = getResources().getDrawable(mButtonResource);
            }
            setButtonDrawable(d);
        }

        public void setButtonDrawable(Drawable d) {
            if (d != null) {
                if (mButtonDrawable != null) {
                    mButtonDrawable.setCallback(null);
                    unscheduleDrawable(mButtonDrawable);
                }
                d.setCallback(this);
                d.setState(getDrawableState());
                d.setVisible(getVisibility() == VISIBLE, false);
                mButtonDrawable = d;
                mButtonDrawable.setState(null);
                setMinHeight(mButtonDrawable.getIntrinsicHeight());
            }

            refreshDrawableState();
        }

        @Override
        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
            boolean populated = super.dispatchPopulateAccessibilityEvent(event);

            if (!populated) {
                int resourceId = 0;
                if (mChecked) {
                    resourceId = R.string.accessibility_compound_button_selected;
                } else {
                    resourceId = R.string.accessibility_compound_button_unselected;
                }
                String state = getResources().getString(resourceId);
                event.getText().add(state);
                event.setChecked(mChecked);
            }

            return populated;
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);

            final Drawable buttonDrawable = mButtonDrawable;
            if (buttonDrawable != null) {
                final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
                final int height = buttonDrawable.getIntrinsicHeight();

                int y = 0;

                switch (verticalGravity) {
                    case Gravity.BOTTOM:
                        y = getHeight() - height;
                        break;
                    case Gravity.CENTER_VERTICAL:
                        y = (getHeight() - height) / 2;
                        break;
                }

                buttonDrawable.setBounds(0, y, buttonDrawable.getIntrinsicWidth(), y + height);
                buttonDrawable.draw(canvas);
            }
        }

        @Override
        protected int[] onCreateDrawableState(int extraSpace) {
            final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
            if (isChecked()) {
                mergeDrawableStates(drawableState, CHECKED_STATE_SET);
            }
            return drawableState;
        }

        @Override
        protected void drawableStateChanged() { //android123提示状态改变时需要更换按钮的图标
            super.drawableStateChanged();
            if (mButtonDrawable != null) {
                int[] myDrawableState = getDrawableState();
                mButtonDrawable.setState(myDrawableState);
                invalidate();
            }
        }

        @Override
        protected boolean verifyDrawable(Drawable who) {
            return super.verifyDrawable(who) || who == mButtonDrawable;
        }

        static class SavedState extends BaseSavedState {
            boolean checked;

                 SavedState(Parcelable superState) {
                super(superState);
            }
            private SavedState(Parcel in) {
                super(in);
                checked = (Boolean)in.readValue(null);
            }

            @Override
            public void writeToParcel(Parcel out, int flags) {
                super.writeToParcel(out, flags);
                out.writeValue(checked);
            }

            @Override
            public String toString() {
                return "CompoundButton.SavedState{"
                        + Integer.toHexString(System.identityHashCode(this))
                        + " checked=" + checked + "}";
            }

            public static final Parcelable.Creator<SavedState> CREATOR
                    = new Parcelable.Creator<SavedState>() {
                public SavedState createFromParcel(Parcel in) {
                    return new SavedState(in);
                }

                public SavedState[] newArray(int size) {
                    return new SavedState[size];
                }
            };
        }

        @Override
        public Parcelable onSaveInstanceState() {
            // Force our ancestor class to save its state
            setFreezesText(true);
            Parcelable superState = super.onSaveInstanceState();

            SavedState ss = new SavedState(superState);

            ss.checked = isChecked();
            return ss;
        }

        @Override
        public void onRestoreInstanceState(Parcelable state) {
            SavedState ss = (SavedState) state;
            super.onRestoreInstanceState(ss.getSuperState());
            setChecked(ss.checked);
            requestLayout();
        }
    }

    从上面来看我们知道CompuundButton的实现相对繁琐了些,主要是考虑状态是否已经选中等情况的消息通知,Android开发网提醒大家而ToggleButton相对CompuundButton增加的给用户而言主要是开关的文字显示。

    public class ToggleButton extends CompoundButton {
        private CharSequence mTextOn;
        private CharSequence mTextOff;
        private Drawable mIndicatorDrawable;

        private static final int NO_ALPHA = 0xFF;
        private float mDisabledAlpha;
        public ToggleButton(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            TypedArray a =
                context.obtainStyledAttributes(
                        attrs, com.android.internal.R.styleable.ToggleButton, defStyle, 0);
            mTextOn = a.getText(com.android.internal.R.styleable.ToggleButton_textOn);
            mTextOff = a.getText(com.android.internal.R.styleable.ToggleButton_textOff);
            mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.ToggleButton_disabledAlpha, 0.5f);
            syncTextState();
            a.recycle();
        }

        public ToggleButton(Context context, AttributeSet attrs) {
            this(context, attrs, com.android.internal.R.attr.buttonStyleToggle);
        }

        public ToggleButton(Context context) {
            this(context, null);
        }

        @Override
        public void setChecked(boolean checked) {
            super.setChecked(checked);
            syncTextState();
        }

        private void syncTextState() {
            boolean checked = isChecked();
            if (checked && mTextOn != null) {
                setText(mTextOn);
            } else if (!checked && mTextOff != null) {
                setText(mTextOff);
            }
        }

        public CharSequence getTextOn() {
            return mTextOn;
        }

        public void setTextOn(CharSequence textOn) {
            mTextOn = textOn;
        }

        public CharSequence getTextOff() {
            return mTextOff;
        }

        protected void onFinishInflate() {
            super.onFinishInflate();
            updateReferenceToIndicatorDrawable(getBackground());
        }

        @Override
        public void setBackgroundDrawable(Drawable d) {
            super.setBackgroundDrawable(d);
            updateReferenceToIndicatorDrawable(d);
        }

        private void updateReferenceToIndicatorDrawable(Drawable backgroundDrawable) {
            if (backgroundDrawable instanceof LayerDrawable) {
                LayerDrawable layerDrawable = (LayerDrawable) backgroundDrawable;
                mIndicatorDrawable =
                        layerDrawable.findDrawableByLayerId(com.android.internal.R.id.toggle);
            }
        }
        @Override
        protected void drawableStateChanged() {
            super.drawableStateChanged();
            if (mIndicatorDrawable != null) {
                mIndicatorDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
            }
        }
    }

    49. AsyncTask实例代码演示Android异步任务

    上次我们讲到了Android提供了一个较线程更简单的处理多任务的方法AsyncTask异步任务类,相对于线程来说AsyncTask对于简单的任务处理更安全,其内部的实现方法使用了Android的Handler机制,对于常见的文件下载可以使用AsyncTask类来处理,在Browser浏览器中就是用了该类下载Web服务器URL的Favicon图标。

      首先Android123以简单的下载例子演示该类的大致结构,如下

    private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalSize = 0;
            for (int i = 0; i < count; i++) {
                totalSize += Downloader.downloadFile(urls[i]);
                publishProgress((int) ((i / (float) count)100));
            }
            return totalSize;
        }
        protected void onProgressUpdate(Integer... progress) {
            setProgressPercent(progress[0]);
        }
        protected void onPostExecute(Long result) {
            showDialog("Downloaded " + result + " bytes");
        }
    }

      最终我们执行 DownloadFilesTask().execute(url1, url2, url3); 即可。

      在Android浏览器中下载Favicon图标的实现如下:

    class DownloadTouchIcon extends AsyncTask<String, Void, Bitmap> {
        private final ContentResolver mContentResolver;
        private final Cursor mCursor;
        private final String mOriginalUrl;
        private final String mUrl;
        private final String mUserAgent;
        /* package */ BrowserActivity mActivity;

        public DownloadTouchIcon(BrowserActivity activity, ContentResolver cr,
                Cursor c, WebView view) { //构造方法
            mActivity = activity;
            mContentResolver = cr;
            mCursor = c;
            mOriginalUrl = view.getOriginalUrl();
            mUrl = view.getUrl();
            mUserAgent = view.getSettings().getUserAgentString();
        }

        public DownloadTouchIcon(ContentResolver cr, Cursor c, String url) { //实现本类的构造
            mActivity = null;
            mContentResolver = cr;
            mCursor = c;
            mOriginalUrl = null;
            mUrl = url;
            mUserAgent = null;
        }

        @Override
        public Bitmap doInBackground(String... values) {   //返回Bitmap类型
            String url = values[0];

            AndroidHttpClient client = AndroidHttpClient.newInstance(mUserAgent);
            HttpGet request = new HttpGet(url);

           HttpClientParams.setRedirecting(client.getParams(), true); //处理302等重定向问题

            try {
                HttpResponse response = client.execute(request);

                if (response.getStatusLine().getStatusCode() == 200) { //如果OK
                    HttpEntity entity = response.getEntity();
                    if (entity != null) {
                        InputStream content = entity.getContent(); //将图标保存到InputStream中,因为是二进制内容
                        if (content != null) {
                            Bitmap icon = BitmapFactory.decodeStream( //从流中取出Bitmap,这里使用了BitmapFactory类的静态方法decodeStream
                                    content, null, null);
                            return icon;
                        }
                    }
                }
            } catch (IllegalArgumentException ex) {
                request.abort();
            } catch (IOException ex) {
                request.abort();
            } finally {
                client.close();
            }
            return null;
        }

        @Override
        protected void onCancelled() {
            if (mCursor != null) {
                mCursor.close();
            }
        }

        @Override
        public void onPostExecute(Bitmap icon) {
              if (mActivity != null) {
                 mActivity.mTouchIconLoader = null;
            }

            if (icon == null || mCursor == null || isCancelled()) {
                return;
            }

        最终图标要保存到浏览器的内部数据库中,系统程序均保存为SQLite格式,Browser也不例外,因为图片是二进制的所以使用字节数组存储数据库的BLOB类型

            final ByteArrayOutputStream os = new ByteArrayOutputStream();
            icon.compress(Bitmap.CompressFormat.PNG, 100, os); //将Bitmap压缩成PNG编码,质量为100%存储
            ContentValues values = new ContentValues(); //构造SQLite的Content对象,这里也可以使用raw sql代替
            values.put(Browser.BookmarkColumns.TOUCH_ICON,os.toByteArray()); //写入数据库的Browser.BookmarkColumns.TOUCH_ICON字段

            if (mCursor.moveToFirst()) {
                do {
                    mContentResolver.update(ContentUris.withAppendedId(Browser.BOOKMARKS_URI, mCursor.getInt(0)),values, null, null);
                } while (mCursor.moveToNext());
            }
            mCursor.close();
        }
    }

      本次Android开发网通过两个AsyncTask类演示了多种类型的任务构造,这里大家注意返回类型,本节演示了Android平台上Content Provider、AsyncTask、Bitmap、HTTP以及Stream的相关操作,大家如何想很快提高开发水平其实只要理解Google如何去实现Android系统常规构架就可以轻松入门谷歌移动平台。

    50. Android自定义View实例AnalogClock源码

    针对Android底层View的直接构造很多网友没有实战经验,本次Android开发网结合目前平台开源代码一起通过AnalogClock类来理解View的直接继承。AnalogClock就是Home Screen上的那个带有两根指针的表盘类。它的实现我们直接从开源代码可以了解到:

      public class AnalogClock extends View {
        private Time mCalendar;

        private Drawable mHourHand; //时针
        private Drawable mMinuteHand; //分针
        private Drawable mDial; //表盘背景

        private int mDialWidth; //表盘宽度
        private int mDialHeight; //表盘高度

        private boolean mAttached; //附着状态

        private final Handler mHandler = new Handler(); //定一个Handler类实现更新时间
        private float mMinutes;
        private float mHour;
        private boolean mChanged; //时间是否改变

        public AnalogClock(Context context) {
            this(context, null);
        }

        public AnalogClock(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }

        public AnalogClock(Context context, AttributeSet attrs,
                           int defStyle) {
            super(context, attrs, defStyle);
            Resources r = mContext.getResources();
            TypedArray a =
                    context.obtainStyledAttributes(
                            attrs, com.android.internal.R.styleable.AnalogClock, defStyle, 0);

            mDial = a.getDrawable(com.android.internal.R.styleable.AnalogClock_dial); //加载表盘资源
            if (mDial == null) {
                mDial = r.getDrawable(com.android.internal.R.drawable.clock_dial);
            }

            mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour); //加载时针图片资源
            if (mHourHand == null) {
                mHourHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_hour);
            }

            mMinuteHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_minute); //加载分针图片
            if (mMinuteHand == null) {
                mMinuteHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_minute);
            }

            mCalendar = new Time(); //获取当前系统时间

            mDialWidth = mDial.getIntrinsicWidth(); //获取表盘图片的宽度
            mDialHeight = mDial.getIntrinsicHeight(); //高度,同上
        }

        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();

            if (!mAttached) {
                mAttached = true;
                IntentFilter filter = new IntentFilter(); //注册一个消息过滤器,获取时间改变、时区改变的action

                filter.addAction(Intent.ACTION_TIME_TICK);
                filter.addAction(Intent.ACTION_TIME_CHANGED);
                filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);

                getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
            }

              mCalendar = new Time();

            onTimeChanged();
        }

        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            if (mAttached) {
                getContext().unregisterReceiver(mIntentReceiver); //反注册消息过滤器
                mAttached = false;
            }
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSize =  MeasureSpec.getSize(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSize =  MeasureSpec.getSize(heightMeasureSpec);

            float hScale = 1.0f;
            float vScale = 1.0f;

            if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {
                hScale = (float) widthSize / (float) mDialWidth;
            }

            if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) {
                vScale = (float )heightSize / (float) mDialHeight;
            }

            float scale = Math.min(hScale, vScale);

            setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec),
                    resolveSize((int) (mDialHeight * scale), heightMeasureSpec));
        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mChanged = true;
        }

        主要的绘图重写View的onDraw方法,我们可以看到通过canvas实例直接屏幕

    @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);

            boolean changed = mChanged;
            if (changed) {
                mChanged = false;
            }

            int availableWidth = mRight - mLeft;
            int availableHeight = mBottom - mTop;

            int x = availableWidth / 2;
            int y = availableHeight / 2;

            final Drawable dial = mDial;
            int w = dial.getIntrinsicWidth();
            int h = dial.getIntrinsicHeight();

            boolean scaled = false;

            if (availableWidth < w || availableHeight < h) {
                scaled = true;
                float scale = Math.min((float) availableWidth / (float) w,
                                       (float) availableHeight / (float) h);
                canvas.save();
                canvas.scale(scale, scale, x, y);
            }

            if (changed) {
                dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
            }
            dial.draw(canvas);

            canvas.save();
            canvas.rotate(mHour / 12.0f * 360.0f, x, y); //计算时针旋转的角度,android123提示就是那个时针图片的旋转角度,直接反应的就是表盘上那个针的时间
            final Drawable hourHand = mHourHand;
            if (changed) {
                w = hourHand.getIntrinsicWidth();
                h = hourHand.getIntrinsicHeight();
                hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
            }
            hourHand.draw(canvas);
            canvas.restore();

            canvas.save();
            canvas.rotate(mMinutes / 60.0f * 360.0f, x, y); //同理,分针旋转的角度

            final Drawable minuteHand = mMinuteHand;
            if (changed) {
                w = minuteHand.getIntrinsicWidth();
                h = minuteHand.getIntrinsicHeight();
                minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
            }
            minuteHand.draw(canvas);
            canvas.restore();

            if (scaled) {
                canvas.restore();
            }
        }

        private void onTimeChanged() {  //获取时间改变,计算当前的时分秒
            mCalendar.setToNow();

            int hour = mCalendar.hour;
            int minute = mCalendar.minute;
            int second = mCalendar.second;

            mMinutes = minute + second / 60.0f;
            mHour = hour + mMinutes / 60.0f;
            mChanged = true;
        }

        private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //监听获取时间改变action
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
                    String tz = intent.getStringExtra("time-zone");
                    mCalendar = new Time(TimeZone.getTimeZone(tz).getID());
                }

                onTimeChanged(); //获取新的时间
                invalidate(); //刷新屏幕,强制类调用onDraw方法实现分针时针的走动
            }
        }; 

      看了本例根据,Android开发很简单吧,感兴趣的网友可以为本程序加入一个秒针,不过Android123提醒网友的是可能对于电池,以及系统运行效率产生一定的影响,不过作为练习大家可以试一试。

    51. ArrayList LinkedList Set HashMap介绍

      在Android开发中我们经常需要对数据进行分类和操作,对于轻量级的数据存储我们可能不需要动用SQLite或效率以及类库不完善的XML,由于SharedPreferences不具备数据枚举方法,如果仅仅是一个String或Int数组可以通过一个标记分割设计外,我们还是主要来看看Android或者说Java提供的基础数据类型辅助类ArrayList LinkedList Set HashMap的介绍,如果你熟悉C++的STL或Boost库可以略过本文。

       在Java中提供了Collection和Map接口。其中List和Set继承了Collection接口;同时用Vector、ArrayList、LinkedList三个类实现List接口,HashSet、TreeSet实现Set接口。直接有HashTable、HashMap、TreeMap实现Map接口。

        Vector基于Array的List,性能也就不可能超越Array,并且Vector是“sychronized”的,这个也是Vector和ArrayList的唯一的区别。

        ArrayList:同Vector一样是一个基于Array的,但是不同的是ArrayList不是同步的。所以在性能上要比Vector优越一些。Android123提示大家适用于顺序性的查找

        LinkedList:不同于前面两种List,它不是基于Array的,作为链表数据结构方式,所以不受Array性能的限制。当对LinkedList做添加,删除动作的时候只要更改nextNode的相关信息就可以实现了所以它适合于进行频繁进行插入和删除操作。这就是LinkedList的优势,当然对于元素的位置获取等方面就逊色很多。

        List:

            1. 所有的List中只能容纳单个不同类型的对象组成的表,而不是Key-Value键值对。例如:[ tom,1,c ];

            2. 所有的List中可以有相同的元素,例如Vector中可以有 [ tom,koo,too,koo ];

            3. 所有的List中可以有null元素,例如[ tom,null,1 ];

            4. 基于Array的List(Vector,ArrayList)适合查询,而LinkedList(链表)适合添加,删除操作。

    虽然Set同List都实现了Collection接口,但是他们的实现方式却大不一样。List基本上都是以Array为基础。但是Set则是在HashMap的基础上来实现的,这个就是Set和List的根本区别。

         HashSet:HashSet的存储方式是把HashMap中的Key作为Set的对应存储项,HashMap的key是不能有重复的。HashSet能快速定位一个元素,但是放到HashSet中的对象需要实现hashCode()方法0。

        TreeSet:将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的。TreeSet不同于HashSet的根本是TreeSet是有序的。它是通过SortedMap来实现的。

        Set总结: 1. Set实现的基础是Map(HashMap); 2. Set中的元素是不能重复的,如果使用add(Object obj)方法添加已经存在的对象,则会覆盖前面的对象,不能包含两个元素e1、e2(e1.equals(e2))。

        Map是一种把键对象和值对象进行关联的容器,Map有两种比较常用的实现: HashTable、HashMap和TreeMap。

        HashMap也用到了哈希码的算法,以便快速查找一个键,

        TreeMap则是对键按序存放,因此它有一些扩展的方法,比如firstKey(),lastKey()等。

        HashMap和Hashtable的区别。 HashMap允许空(null)键(key)或值(value),由于非线程安全,效率上可能高于Hashtable。 Hashtable不允许空(null)键(key)或值(value)。

       有关更多实用的Android开发技巧我们将在后面的文章中着重介绍。

    52. ConditionVariable Android线程同步

    ConditionVariable类位于android.os.ConditionVariable,它可以帮助Android线程同步。在SDK上的介绍ConditionVariable不同于标准Java位于java.lang.Object wait() 和 notify() ,这个类可以等待自己,这就意味着 open(), close() 和 block() 可能会假死 ,如果使用ConditionVariable类的open()在调用 block() 之前, block() 将不会阻塞,相反将会返回立即。

       该类一共有4个方法

       boolean  block(long timeout)
      阻止当前线程知道条件是open,或直到超时,这里参数long timeout为超时设置,Android123提示大家如果你们从事过Win32开发,这个方法类似DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds); 函数。

      void  block()
      阻止当前线程知道条件 open ,是上面的无超时等待重载版本。

      void  close()
    重置条件为 close状态。

    void  open()
    Open条件,释放所有线程的阻塞.

      ConditionVariable在创建时还有一种构造方法是 public ConditionVariable (boolean state) ,如果为true,默认时为opened,如果为false则是closed. ,默认public ConditionVariable () 为close().

    53.Android开发之Eclipse调试技巧

    使用Google提供的ADT插件可以在Eclipse上很轻松的调试Android程序,我们切换到DDMS标签,选择“Devices”标签,我们可以看到会出现类似下面的Debug Process(调试进程)、Update Threads(更新线程)、Update Heap(更新堆)、Cause GC(引起垃圾回收)、Stop Process(停止进程)、Screen Capture(屏幕截图)、Reset adb(重启Android Debug Bridge)

      这里我们就可以很好的观察Android程序运行时的各种状态,比如进程信息、线程分析、堆内存的占用,结束一个进程,当然这些操作都是在DDMS框架下进行的,日程开发的程序是无法执行调用的。如果遇到adb调试桥运行不稳定时可以选择reset adb来重新启动adb.exe进程,整个界面如图:

    android ddms debug

    很多网友对于一些常的Android规程序性能测试、文件管理或屏幕截图均使用Eclipse中的DDMS插件来查看,其实通过SDK中提供的Dalvik Debug Monitor可以很好的调试Android程序,这里可以更直观的现实设备的各种信息,除了Logcat、VM Heap堆查看、Thread线程状态外,在菜单的Device中可以找到Screen capture来截图、File Explorer进行文件同步操作,使用Show process status可以显示设备当前的进程状态,以及 快速的过滤Logcat信息,可以分析无线状态radio state、程序状态app state等等。这里支持模拟器和真机的显示,该工具可以再android-sdk-windows-1.5_r1 oolsddms.bat找到,目前我们测试环境为Windows平台,下次讲述下CentOS中的操作,如图:

    Dalvik-Debug-Monitor

    Android性能与调试很重要

      用于手持的移动设备,Android软件性能上需要多加考虑。首先Java VM在资源占用上开销是很大的,很多垃圾GC处理机制直接影响到内存释放和整个平台运行的流畅度。

      1.节省电量

      手机软件必须考虑的问题是省电,如果需要大型处理尽量由服务器处理,直接把结果返回到手持设备上。多线程也是一种奢侈的使用,但是I/O存储必需这样才能保证流畅度,线程的阻塞将会降低用户体验,但是线程间切换调度的开销一直是重点。Android在DDMS中加入了Thread查看。

      2.内存占用

      在Eclipse+ADT插件的开发方式中,我们在DDMS中可以看到Heap堆内存的显示,Android开发网提示的是Java内存分配方式的问题,尽量产生少的对象,比如字符串操作如果连加比较多,可以使用StringBuilder代替String类。在游戏开发中经常用到的图片可以通过切片的方式从一个大的png图片上截取,或者放在gif文件作为逐帧保存,这样可以共用文件头减小体积。

      3.调试工具

      Android调试工具主要是模拟器中的Dev Tools和DDMS中的Logcat查看。当然模拟器自带的Dev Tools在功能上还是很详细的,可以显示CPU占用率,内存使用量,在单元测试时需要多加分析。

    Android开发工具Dev Tools介绍

    ndroid提供了很多开发调试工具除了ADB、TraceView、Logcat外,今天这个名为Dev Tools的Android开发调试工具隐藏在Android模拟器中,为我们提供了强大的调试支持。我们在功能表中找到Dev Tools,运行后可以看到有很多条目,比如Development Settings,用来开发设置,进入后我们看到了比如Show CPU Usage这样的实用功能显示CPU占用率,帮助Android开发人员分析当前软件性能情况,今天就分析下Development Settings中的选项:

    Wait for debugger 等待调试器
    Enable ADB 启用ADB(android调试桥)
    Show running processs (显示运行中的进程)
    Show screen updates (显示屏幕更新)

      下面是一些常规的调试选项,Android开发网友情提示开启这些选项后可能会影响运行效率,这些探测选项也是CPU敏感的。

    Immediately destroy activites (立即销毁activities)
    Show CPU usage (显示CPU占用率)
    Show background (显示北京)
    Show Sleep state on LED (在休眠状态下LED开启)
    Keep screen on while plugged in (保持屏幕开启当插入后)
    Show GTalk service connection status (显示GTalk服务连接状态)

  • 相关阅读:
    郁闷的Alexa破10万。
    回到杭州,2009开始了。
    美剧。
    英来网招聘:兼职js开发。(补充)
    村姑的Beta 2.0,TT。
    爆牙齿的Web标准面试考题II(iPhone SMS/iChat UI的Web标准实现)
    天使飞来。
    Web标准的未来,浏览器的未来,应用的未来。
    咖啡厅与产品形态。
    学习使用笔和纸。
  • 原文地址:https://www.cnblogs.com/xieyuan/p/3787495.html
Copyright © 2011-2022 走看看