2015年3月6日星期五

android service 详解

转载

有了 Service 类我们如何启动他呢,有两种方法:
      • Context.startService() 
      • Context.bindService()

     1.  在同一个应用任何地方调用 startService() 方法就能启动 Service 了,然后系统会回调 Service 类的 onCreate() 以及 onStart() 方法。这样启动的 Service 会一直运行在后台,直到 Context.stopService() 或者 selfStop() 方法被调用。另外如果一个 Service 已经被启动,其他代码再试图调用 startService() 方法,是不会执行 onCreate() 的,但会重新执行一次 onStart() 。
      2. 另外一种 bindService() 方法的意思是,把这个 Service 和调用 Service 的客户类绑起来,如果调用这个客户类被销毁,Service 也会被销毁。用这个方法的一个好处是,bindService() 方法执行后 Service 会回调上边提到的 onBind() 方发,你可以从这里返回一个实现了 IBind 接口的类,在客户端操作这个类就能和这个服务通信了,比如得到 Service 运行的状态或其他操作。如果 Service 还没有运行,使用这个方法启动 Service 就会 onCreate() 方法而不会调用 onStart()。
     
      总结:
      1. startService()的目的是回调onStart()方法,onCreate() 方法是在Service不存在的时候调用的,如果Service存在(例如之前调用了bindService,那么Service的onCreate方法已经调用了)那么startService()将跳过onCreate() 方法。
      2.  bindService()目的是回调onBind()方法,它的作用是在Service和调用者之间建立一个桥梁,并不负责更多的工作(例如一个Service需要连接服务器的操作),一般使用bindService来绑定到一个现有的Service(即通过StartService启动的服务)。
      由于Service 的onStart()方法只有在startService()启动Service的情况下才调用,故使用onStart()的时候要注意这点。

与 Service 通信并且让它持续运行

      如果我们想保持和 Service 的通信,又不想让 Service 随着 Activity 退出而退出呢?你可以先 startService() 然后再 bindService() 。当你不需要绑定的时候就执行 unbindService() 方法,执行这个方法只会触发 Service 的 onUnbind() 而不会把这个 Service 销毁。这样就可以既保持和 Service 的通信,也不会随着 Activity 销毁而销毁了。

提高 Service 优先级

      Android 系统对于内存管理有自己的一套方法,为了保障系统有序稳定的运信,系统内部会自动分配,控制程序的内存使用。当系统觉得当前的资源非常有限的时候,为了保 证一些优先级高的程序能运行,就会杀掉一些他认为不重要的程序或者服务来释放内存。这样就能保证真正对用户有用的程序仍然再运行。如果你的 Service 碰上了这种情况,多半会先被杀掉。但如果你增加 Service 的优先级就能让他多留一会,我们可以用 setForeground(true) 来设置 Service 的优先级。
      为什么是 foreground ? 默认启动的 Service 是被标记为 background,当前运行的 Activity 一般被标记为 foreground,也就是说你给 Service 设置了 foreground 那么他就和正在运行的 Activity 类似优先级得到了一定的提高。当让这并不能保证你得 Service 永远不被杀掉,只是提高了他的优先级。

摘自网络其他资料:关于Service生命周
Android Service生命周期与Activity生命周期是相似的,但是也存在一些细节上也存在着重要的不同:
onCreate和onStart是不同的
通过从客户端调用Context.startService(Intent)方法我们可以启动一个服务。如果这个服务还没有运行,Android将启动它并且在onCreate方法之后调用它的onStart方法。如果这个服务已经在运行,那么它的onStart方法将被新的Intent再次调用。所以对于单个运行的Service它的onStart方法被反复调用是完全可能的并且是很正常的。
onResume、onPause以及onStop是不需要的
回调一个服务通常是没有用户界面的,所以我们也就不需要onPause、onResume或者onStop方法了。无论何时一个运行中的Service它总是在后台运行。
onBind
如果一个客户端需要持久的连接到一个服务,那么他可以调用Context.bindService方法。如果这个服务没有运行方法将通过调用onCreate方法去创建这个服务但并不调用onStart方法来启动它。相反,onBind方法将被客户端的Intent调用,并且它返回一个IBind对象以便客户端稍后可以调用这个服务。同一服务被客户端同时启动和绑定是很正常的。
onDestroy
与Activity一样,当一个服务被结束是onDestroy方法将会被调用。当没有客户端启动或绑定到一个服务时Android将终结这个服务。与很多Activity时的情况一样,当内存很低的时候Android也可能会终结一个服务。如果这种情况发生,Android也可能在内存够用的时候尝试启动被终止的服务,所以你的服务必须为重启持久保存信息,并且最好在onStart方法内来做。

Android中Parcel的分析以及使用

转载

简单点来说:Parcel就是一个存放读取数据的容器, Android系统中的binder进程间通信(IPC)就使用了Parcel类来进行客户端与服务端数据的交互,而且AIDL的数据也是通过Parcel来交互的。在Java空间和C++都实现了Parcel,由于它在C/C++中,直接使用了内存来读取数据,因此,它更有效率。
分析Binder机制中的客户端与服务器端进行实际操作ontransact()函数 :
[java]
  1. //参数说明:   
  2. // code :是请求的ID号    
  3. // data :客户端请求发送的参数   
  4. // reply:服务器端返回的结果   
  5. // flags:一些额外的标识,如FLAG_ONEWAY等,通常为0.   
  6. virtual status_t    onTransact( uint32_t code,  
  7.                                 const Parcel& data,  
  8.                                 Parcel* reply,  
  9.                                 uint32_t flags = 0);  
从中我们可以看到Parcel的重要性以及窥探它的使用情况,接下来,我主要分析它的存储机制。 
     
    常用方法介绍:
            obtain()                          获得一个新的parcel ,相当于new一个对象
            dataSize()                      得到当前parcel对象的实际存储空间
            dataCapacity()               得到当前parcel对象的已分配的存储空间, >=dataSize()值  (以空间换时间)
            dataPostion()                 获得当前parcel对象的偏移量(类似于文件流指针的偏移量)
            setDataPosition()           设置偏移量
            recyle()                           清空、回收parcel对象的内存
            writeInt(int)                     写入一个整数
            writeFloat(float)              写入一个浮点数
            writeDouble(double)       写入一个双精度数
            writeString(string)           写入一个字符串
         当然,还有更多的writeXXX()方法,与之对应的就是readXXX(),具体方法请参阅SDK。
          其中几个值得注意的方法为:
             writeException()        在Parcel队头写入一个异常
             writeException()        Parcel队头写入“无异常“
             readException()        在Parcel队头读取,若读取值为异常,则抛出该异常;否则,程序正常运行。

一、Parcel的分析


       相信看了前面的值,对Parcel的使用该有了初步印象。那么,Parcel的内部存储机制是怎么样的?偏移量又是
  什么情况?让我们回忆一下基本数据类型的取值范围:
                   boolean     1bit          1字节
                   char          16bit         2字节
                   int             32bit        4字节
                   long          64bit        8字节
                   float          32bit        4字节
                  double       64bit         8字节

        如果大家对C语言熟悉的话,C语言中结构体的内存对齐和Parcel采用的内存存放机制一样,即读取最小字节
为32bit,也即4个字节。高于4个字节的,以实际数据类型进行存放,但得为4byte的倍数。基本公式如下:
             实际存放字节:
                       判别一:  32bit      (<=32bit)             例如:boolean,char,int
                       判别二:  实际占用字节(>32bit)     例如:long,float,String,数组等
        当我们使用readXXX()方法时,读取方法也如上述:
              实际读取字节:
                        判别一:  32bit      (<=32bit)            例如:boolean,char,int
                        判别二:  实际字节大小(>32bit)     例如:long,float,String,数值等
      由上可以知道,当我们写入/读取一个数据时,偏移量至少为4byte(32bit),于是,偏移量的公式如下:
                 f(x)= 4x  (x=0,1,…n)
        事实上,我们可以显示的通过setDataPostion(int postion) 来直接操作我们欲读取数据时的偏移量。毫无疑问,
你可以设置任何偏移量,但所读取的值是类型可能有误。因此显示设置偏移量读取值的时候,需要小心。
      
      另外一个注意点就是我们在writeXXX()和readXXX()时,导致的偏移量是共用的,例如,我们在writeInt(23)后,
此时的datapostion=4,如果我们想读取5,简单的通过readInt()是不行的,只能得到0。这时我们只能通过
setDataPosition(0)设置为起始偏移量,从起始位置读取四个字节,即23。因此,在读取某个值时,可能需要使用
setDataPostion(int postion)使偏移量装换到我们的值处。

      巧用setDataPosition()方法,当我们的parcel对象中只存在某一类型时,我们就可以通过这个方法来快速的读取
所有值。具体方法如下:

[html]
  1. /**  
  2.      * 前提条件,Parcel存在多个类型相同的对象,本例子以10个float对象说明:  
  3.      */  
  4.     public void readSameType() {  
  5.         Parcel parcel =Parcel.obtain() ;  
  6.         for (int i = 0; i < 10; i++) {  
  7.             parcel.writeDouble(i);  
  8.             Log.i(TAG, "write double ----> " + getParcelInfo());  
  9.         }  
  10.         //方法一 ,显示设置偏移量   
  11.         int i = 0;  
  12.         int datasize = parcel.dataSize();  
  13.         while (i < datasize) {  
  14.             parcel.setDataPosition(i);  
  15.             double fvalue = parcel.readDouble();  
  16.             Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  
  17.             i += 8; // double占用字节为 8byte   
  18.         }  
  19. //      方法二,由于对象的类型一致,我们可以直接利用readXXX()读取值会产生偏移量  
  20. //      parcel.setDataPosition(0)  ;  //  
  21. //      while(parcel.dataPosition()<parcel.dataSize()){  
  22. //          double fvalue = parcel.readDouble();  
  23. //          Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  
  24. //      }  
  25.     }  

    由于可能存在读取值的偏差,一个默认的取值规范为:
             1、  读取复杂对象时: 对象匹配时,返回当前偏移位置的该对象;
                               对象不匹配时,返回null对象 ;
             2、  读取简单对象时: 对象匹配时,返回当前偏移位置的该对象 ;

                               对象不匹配时,返回0;

2015年3月5日星期四

设置Storyboard的页面大小


真正让他变成手机尺寸的是 要把 Use Size Classes 去掉。

AndroidStudio快捷键

1.快速添加override或者implements方法
 右键--->Generate--->选择Implements methods 或者Override methods
    或者使用快捷键Alt+Insert

2015年3月4日星期三

Stack(栈)的使用

public static void main(String[] args) { 
        Stack<String> stack = new Stack<String>(); 
        System.out.println("now the stack is " + isEmpty(stack)); 
        stack.push("1"); 
        stack.push("2"); 
        stack.push("3"); 
        stack.push("4"); 
        stack.push("5"); 
        System.out.println("now the stack is " + isEmpty(stack)); 
        System.out.println(stack.peek()); 
        System.out.println(stack.pop()); 
        System.out.println(stack.pop()); 
        System.out.println(stack.search("2"));
        System.out.println(stack.pop());
        System.out.println(stack.pop()); 
        System.out.println(stack.pop());
        System.out.println(stack.pop()); 
       
    } 
    public static String isEmpty(Stack<String> stack) { 
        return stack.empty() ? "empty" : "not empty"

    }

now the stack is empty
now the stack is not empty
5
5
4
2
3
2
1
Exception in thread "main" java.util.EmptyStackException
         at java.util.Stack.peek(Unknown Source)
         at java.util.Stack.pop(Unknown Source)
         at com.main.StackClass.main(StackClass.java:22)

Android Git上传

前提条件:假设你已经把项目上传到了GITHUB服务器。

之后修改代码或者添加文件时使用GIT来上传更新

1.如果你要添加新文件的话首先需要再 VCS->GIT->ADD来添加文件
2.添加完以后 VCS->GIT->COMMIT FILE来完成上传。这里需要注意的是上传方式
   你如果只COMMIT的话是无法上传到GITHUB服务器上的,必须是COMMIT+PUSH才能上传。PUSH才是重点。

2015年3月3日星期二

泛型的使用

网上关于泛型的有很多.我在这里记录了 我能想到的使用方法。

第一种:泛型类里的T无继承

public class ClassComponenetType<T> {
        
         public String keyname;
        
         /** 断传的参数是不是String */
         public <T> boolean cls(Class<T> cls){
                  if (cls==String.class) {
                           return true;
                  }
                  return false;
         }
        
         /**
          * 使用泛型类的属性
          * @param t
          */
         public void put(T t){
                  if (t.getClass()==String.class) {
                           keyname=(String) t;
                  }
                 
                  if (t=="你好") {
                           //会进入到这里
                           keyname=(String) t;
                  }
         }
        
         public String get(){
                  return keyname;
         }
        
         public static void main(String[] args) {
                  ClassComponenetType<String> mCom=new ClassComponenetType<String>();
                  mCom.put("你好");
                  System.out.println("类泛型的使用-->"+mCom.get());
                  System.out.println("泛型变量的使用->"+mCom.cls(String.class));
         }

}

第二种:泛型类里的T有继承

public class ClassComponenetType<T extends GenericClass> {
        
         public String keyname;
         /**
          * 使用泛型类的属性
          * @param t
          */
         public void put(T t){
                  keyname=t.name;
         }
        
         public String get(){
                  return keyname;
         }
        
         public static void main(String[] args) {
                  ClassComponenetType<GenericClass> mCom=new ClassComponenetType<GenericClass>();
                  GenericClass gc=new GenericClass();
                  gc.name="你好";
                  mCom.put(gc);
                  System.out.println("类泛型的使用-->"+mCom.get());
         }

}

public class GenericClass {
public String name;
}