android核心技术与最佳实践笔记(二)

   第四章  深入解析Android数据存储与管理
4.1 xml文件管理
        xml文件更多的是用来维护应用或系统的配置文件。SharedPreferences这个轻量级的类作为xml文件存储的上层接口。本质为
       根据配置信息是否对外开放,SharedPreferences提供了 MODE_PRIVATE,   MODE_WORLD_READABLE两种权限, SharedPreferences的操作分为获取配置信息和存储配置信息两种。下面是获取配置信息方法:
                SharedPreferences  settings = getSharedPreferences( PREFS_NAME,  MODE_PRIVATE );
               boolean  silent = settings.getBoolean( "silentMode" , false );
        存储配置信息方法:
               SharedPreferences  settings = getSharedPreferences( PREFS_NAME,  MODE_PRIVATE );
               SharedPreferences.Editor  editor = settings.edit();
               editor.putBoolean( "silentMode" , mSilentMode );
               editor.commit();
        若希望配置信息对其他应用程序开发,在设置权限时,可使用  MODE_WORLD_READABLE 。在其他应用获得相应的配置信息时,必须先获得相应的上下文,方法如下:
               context = createPackageContext( "com.miaozl.text" , Context.CONTENXT_IGNORE_SECURITY );
               if(context != null){
                        SharedPreferences  settings = context.getSharedPreferences(PREFS_NAME,  Context.MODE_WORLD_READABLE);
                        mTest = settings.getString( "test", null );
               }
        上述PreferenceActivity中内置了对SharedPreferences的支持。对于上层代码中的接入,实例如下;
                   
                           android:lkey="window_animations"
                           ......   />
         接入: 
                    private final static String WINDOW_ANIMATIONS_PREF = "window_animations";
                    mWindowAnimationsPref = (ListPreference)prefSet.findPreference( WINDOW_ANIMATIONS_PREF);    
                      mWindowAnimationsPref.setOnPreferenceChangeListener(this);
                    public boolean onPreferenceChange( Preference preference ,  Object objValue ){
                             if( preference == mWindowAnimationsPref ){
                                       writeAniamtionPreference( 0, objValue );
                             }
                    }
          若希望ListPreference保存或查看当前的选择,可调用ListPreference的方法:             
                    public  void  setValue(String  value);  //对应android:entries属性的值
                    public  void  setValueIndex(int index);  //对应android:entryValues属性的值
4.2  内部文件管理
       对于二进制数据,android提供内部存储的方式,可将数据存储在应用的私有空间中,避免其他程序访问,内部存储的数据会在应用卸载时删除。
       内部存储权限包括  MODE_PRIVATE,  MODE_APPEND,  MODE_WORLD_READABLE,   MODE_WORLD_WRITEABLE 等。
       内存存储所在目录为:  \data\data\com.company.packagename\files.
    4.2.1 写入数据
        写入字符串:
                     FileOutputStream  out = context.openFileOutput( file, Context.MODE_WORLD_WRITEABLE );
                     out.write( captureArray.toString().getBytes() );
                     out.close();
        写入结构体数据的方法:
                     DataOutputStream out = new DataOutputStream( context.openFileOutput( PREFERENCES, MODE_PRIVATE ) );
                     out.writeUTF( configuration.locale );
                     out.writeInt( configuration.mcc );
                      out.writeInt( configuration.mnc );
     4.2.2 读取数据
         使用函数  openFileInput()实现
         对于应用携带的静态数据,可放置在应用的 assets 目录或res   raw目录下
         对于assets目录下的静态数据,存在单个文件最大仅支持1MB的局限,读取方式如下:
                  InputStream  is = getAssets().open("read_asset.txt");
         对于res  raw目录下的静态数据,可通过如下方式读取:
                 InputStream  inputStream = resources.openRawResource( R.raw.definitions );
  4.3 外部文件管理
        即存储在外置SD卡上,可通过getExternalFilesDir()来获得具体路径,该具体路径依赖于应用的包名,其SD卡上的私有目录如下:
                     \mnt\sdcard\Android\data\com.miaozl.hello\files\
        获得图片路径的方法: 
                     File  path = getExternalFilesDir( Environment.DIRECTORY_PAICTURES );
        若希望存储在SD卡上的公共目录,可铜鼓    getExternalStoragePublicDirectory() 获得。注意,公共目录的具体路径视需要存储的文件类型而定。下面是获取公共目录的方法:
                    String  sdroot = Environment.getExternalStorageDirectory().getAbsolutePath();
                    String  dwndir = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS ).gerAbsolutePath() ;
         注意使用SD卡目录前,先通过 Environment.getExternalStorageState()方法判断SD卡是否已挂载。
 4.4 数据库管理
       android提供两种SQLite的实现方法:  基于android封装接口来实现的;  基于原生 SQLite 的方法来实现。
    4.4.1  android封装接口
         google提供的封装类 SQLiteOpenHelper, 通过继承 SQLiteOpenHelper来设计操作数据库。注意封装会使android的性能降低。
         在继承SQLiteOpenHelper时,必须实现 onCreate() 和 onUpgrade()方法。下面是通过封装android接口设计数据库的步骤:
       (1)定义数据库名,数据库版本号,数据表名
       (2)实现 onCreate() 和 onUpgrade() 方法
       (3)实现插入,更新,删除和查询方法
         实现过程:
              定义数据库名,数据库版本号,数据表名:
                    private  static final  String  DATABASE_NAME = "notepad.db";
                    private  static  final  int  DATABASE_VERSION = 2;
                    private  static  final  String  NOTES_TABLE_NAME = "notes";
              实现 onCreate() 和 onUpgrade() 方法 :
                    public  void  onCreate( SQLiteDatabase db ){
                             db.execSQL( "CREATE TABLE" + NOTES_TABLE_NAME + "(" 
                              + NoteColumns._ID + "  INTEGER PRIMARY KEY,"
                              + NoteColumns.TITLE +  " TEXT, "
                              + NoteColumns.NOTE + " TEXT, "
                              + NoteColumns..CREATED_DATE + " INTERGER,"
                              + NoteColumns.MODIFIED_DATA + " INTEGER"
                              + ");");
                     }
                     public  void  onUpgrade( SQLiteDatabase db,  int oldVersion, int newVersion ){
                               db.execSQL("DROP TABLE IF EXISTS notes");
                               onCreate(db);
                     }
                当一次性需要修改多个数据时,建议通过SQLite的书屋处理进行批量处理,有利于提高执行效率,事务处理相关的方法: 
                     public  void  beginTransaction();
                     public  void  beginTransactionWithListener(SQLiteTransactionListener  transactionListener);
                     public  void  endTransaction();
                     public  boolean  inTransaction();
                     public  void  setTransactionSuccessful();
    4.4.3 原生方法处理
          采用 raw 方法来操作SQLite数据库,但必须做好SQL语句异常处理:
                    SQLiteDatabase  db;
                    String args[] = { id };
                    ContentValues  cv = new ContentValues();
                    cv.put("miaozl", id);
                    Cursor c = db.rawQuery( "SELECT * FROM table  WHERE miaozl = ?", args );
                    if( c.getCount() != 0 ){
                              ContentValues cv = new ContentValues();
                              cv.put(" miaozl ", "cwj");
                              db.insert("table", " miaozl ", cv);
                              String args[] = {id};
                              ContentValues cv2 = new ContentValues();
                              cv2.put("miaozl", id);
                              db.delete("table",  "miaozl = ?", args);
                    }
   4.5  数据处理
   

  第五章 android通信机制
        android,通信技术涉及多个层面,在UI层涉及多种事件(触控事件,按键事件,轨迹球事件等);框架层涉及intent,message等。
 5.1 intent 通信
      通常情况下,intent仅能传递基本的数据类型,对于复杂的数据类型,则要借助Serializable,  Parcelable 两个接口进行。
   5.1.1 intent的常见用法
      针对网络,地图,电话,消息,电子邮件,多媒体,系统等几个方面介绍Intent用法:
    1.  网络相关
       网络功能实现,需要拥有 android.permission.INTERNET 权限,与网络相关的intent通信包括显示网页,google地图,搜索等。
       在由HTTP定义的与服务器交互的方法中,发起请求的方式有两种,即GET 和 POST, 其中GET方式通过URL提交数据,数据在URL中可以看到,无法保证私密性,且提交的数据最多只能为1024字节; 而 POST方式将数据放置在HTML  HEADER中提交,且在数据长度上没有限制,通常用于传输敏感数据。
       在Intent中,目前只支持GET方式,为了打开网站,其采用的ACTION 为 ACTION_VIEW,方法如下:
               Uri  uri = Uri.parse("http://www.163.com");
               Intent  it = new Intent(Intent.ACTION_VIEW, uri);
               startActivitity(it);
       如果传递敏感数据,在WebView中,采用的方法如下:
               public  void  postUrl( String url,  byte[] postData )
       在网络中搜索相关的信息,实现方法如下:
               intent.setAction( Intent.ACTION_WEB_SEARCH );
               intent.putExtra(SearchManager.QUERY,  "android123");
               startActivity(intent);
    2. 消息相关
        为了查看消息,需有android.permission.READ_SMS权限,方法如下: 
                Intent  it  = new intent( Intent.ACTION_VIEW );
                it.setType("vnd.android-dir/mms-sms");
                startActivity(it);
        为了发送短信,需有android.permission.WRITE_SMS权限,方法如下:
                Uri  uri = Uri.parse( "smsto:10010" );
                Intent  it = new  Intent( Intent.ACTION_SENDTO,  uri );
                it.putExtra( "sms_body", "hello" );
                startActivity(it);
         发送彩信,还要涉及附件,下面是一个发送实例:
                Uri  uri  = Uri.parse("content://media/external/images/media/10");
                Intent  it  = new Intent( Intent.ACTION_SEND );
                it.putExtra( "sms_body", "hello" ); 
                it.putExtra(Intent.EXTRA_STREAM,  uri);
                it.setType("image/png");
                startActivity(it);
    3. 电子邮件相关
            直接发送文字的邮件:
                   Intent  it =  new  Intent( Intent.ACTION_SEND );
                   it.putExtra( Intent.EXTRA_EMAIL,  "[email protected]" );
                   it.putExtra( Intent.EXTRA_TEXT,  "EMAIL——内容" );
                   it.setType( "text/plain" );
                   startActivity( Intent.createChooser( it , "选择一个Email客户端" ) ) ;
          发送的时候有抄送人,方法如下;
                   it.putExtra( Intent.EXTRA_CC,  "[email protected]" );//抄送人
                   it.putExtra( Intent.EXTRA_SUBJECT,  "标题" );
                   it.setType( "message/rfc822" ); //编码类型
                   startActivity( Intent..createChooser( it, "选择一个Email客户端" ) );
          带附件:
                   it.putExtra( Intent.EXTRA_STREAM, "file:///sdcard/miaozj.mp3" );
                   sendIntent.setType( "audio/mp3" );
   4. 系统相关
          卸载应用:
                  Uri  uri  =  Uri.fromParts( "package", strPackageName, null );
                  Intent  it  = new  Intent( Intent.ACTION_DELETE,  uri );
                  startActivity( it );
          安装应用:
                 Uri  installUri  =  Uri.fromParts( "package" ,  "xxx",  null );
                 return  it = new Intent( Intent.ACTION_PACKAGE_ADDED,  installUri );
          创建快捷方式:
                 Intent  shortcut = new Intent( "com.android.launcher.action.INSTALL_SHORTCUT" );
                 shortcut.putExtra( Intent.EXTRA_SHORTCUT_NAME, "shortcutname" );
                 shortcut.putExtra( "duplicate",  false ); //不允许重复创建
                 ShortcutIconResource  iconRes = Intent.ShortcutIconResource.fromContext( this, R.drawable.background );
                 shortcut.putExtra( Intent.EXTRA_SHORTCUT_ICON_RESOURCE,  iconRes ); //资源图标
                 String  action = "com.android.action.START";
                 Intent  respondIntent = new intent( this, this.getClass() );
                 respondIntent.setAction( action );
                 shortcut.putExtra( Intent.EXTRA_SHORTCUT_INTENT,  respondIntent );
                 sendBroadcast( shortcut );
          删除快捷方式:
                 Intent  shortcut = new Intent( "com.android.launcher.action.UNINSTALL_SHORTCUT" );
                 shortcut.putExtra( Intent.EXTRA_SHORTCUT_NAME,  "shortcutname" );    
                 String action = "com.android.action.START";
                 String appClass = this.getPackageName() + " . " + this.getLocalClassName();
                  ComponentName comp = new ComponentName( this.getPackageName(),  appClass );//应用标识符
                  shortcut.putExtra( Intent.EXTRA_SHORTCUT_INTENT,  new Intent(action).setComponent(comp) );
                  sendBroadcast( shortcut );
         创建和删除快捷键的权限:   com.android.launcher.permission.INSTALL_SHORTCUT            ......UNINSTALL_SHORTCUT
   5.1.2  Serializable 接口
       Serializable 接口。要实现某个类支持序列化,可是该类继承Serializable接口。Serializable接口通常在Intent中使用。
        Serializable 接口本质上是基于 ObjectOutStream 和 ObjectInputStream 对象进行的。具备Serializable序列化的类的实现非常简单。实例如下:      
               public  final  class  CodeSigner  implements Serializable{
                        private  static  final  long serialVersionUID = 4648464646846846L;
               }
        serialVersionUID用于运行时,判断类版本的一致性,其可由JDK中的 serialver 工具生成,也可以采用默认的方式来定义。
       在Intent中,可通过Bundle来设置序列化数据:
                   putSerializable( String key, Serializable value );
  5.1.3  Parcelable接口
           Parcelable接口是android特定的序列化接口,比Serializable接口有效,常用于Binder 和 AIDL场景中。
           Parcelable接口序列化的数据可存储在Parcel中,继承Parcelable接口的类必须具有一个CREATOR的静态变量。下面是一个Parcelable接口序列化的示例:
                    public  class  CatCmdMessage  implements  Parcelable{
                            public  CatCmdMessage(Parcel  in){
                                     mCmdDet = in.readParcelable(null);
                                     mTextMsg = in.readParcelable(null); 
                                     mMenu = in.readParcelable(null); 
                                     mInput = in.readParcelable(null);
                            }
                            public  void  writeToParcel( Parcel dest,  int flags ){
                                     dest.writeParcelable(mCmdDet, 0);
                                     dest.writeParcelable(mTextMsg, 0);
                                     dest.writeParcelable(mMenu, 0);
                                     dest.writeParcelable(mInput, 0);
                            }
                             public  int  describeContents(){
                                      return 0;
                             }
                             //CREATOR的静态变量
                             public  static final  Parcelable.Creator CREATOR = new
                             Parcelable.Creator(){
                                      public  CatCmdMessage createFromParcel(Parcel in){
                                                 return new CatCmdMessage(in);
                                      }
                                      public  CatCmdMessage[] newArray(int size){
                                                 return new CatCmdMessage[size];
                                      }
                             };
                    }
          通过Intent传递Parcelable接口序列化数据的方法如下:
                     public  Intent  putExtra(String name ,  Parcelable value );
          另外,Parcelable也可以通过Bundle在Intent 中传递。在Bundle中设置Parcelable的方法如下:
                     public  void  putParcelable( String key,  Parcelable value );  //Parcelable 方法
                     public  Intent  putExtra( String name, Bundle value );  //Intent方法
 5.2 UI事件处理
        主要包括事件监听器相关事件,事件句柄相关事件,焦点相关事件触控事件,按键事件,轨迹球时间等。UI事件均由View来实现的。
       
 第六章 android多线程编程
        在android中,UI主线程并非线程安全的,所有UI相关的操作均需在UI主线程中完成。在默认情况下,开发者创建的Service, Activity,  Broadcast均运行在UI主线程中,一些耗时的操作,如网络下载,大文件读取,加解密计算,数据库操作等,也放在UI线程中执行,往往会阻塞线程,造成ANR异常。
       android中,实现多线程的方式有,通过原生java线程,或android对java线程的封装及AsyncTask来实现多线程的效果。
6.1 java线程实现
    1.  基于Thread的实现
          基于Thread实现新线程和在Runnable构造器中实现新线程是传统java实现线程的两种方式,基于Thread的实现如下:
                      Static  class  AThread  extends  Thread{
                            ActivityManagerSerivce  mService;
                            boolean  mReady = false;
                            public  AThread(){
                                      super( "ActivityManager" );
                            }
                             public  void  run(){
                                      Looper.prepare();  //初始化事件循环之后应调用loop()
                                    android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND ); //设置线程优先级
                                      android.os.Process.setCanSelfBackground( false );
                                      ActivityManagerService m = new ActivityManagerService();
                                      synchronized( this ){
                                               mService = m;
                                               notifyAll();
                                      }
                                      synchronized( this ){
                                               while( !mReady ){
                                                          try{        wait();
                                                          }catch (InterruptedException e){  ........  } 
                                               }
                                      }
                                      Looper.loop();
                            }
                    }
         在Thread声明周期中,存在NEW,  RUNNABLE,  BLOCKED,  WAITING,  TIMED_WAITING,  TERMINATED等多个状态。线程的生命周期和状态迁移过程如下:
            
    2. 基于Runnable的实现
                   private  Runnable  mForceCheckBoxRunnable = new Runnable(){
                          public  void  run(){
                                   if( mCheckBoxPreference != null ){
                                              mCheckBoxPreference.setChecked( !mCheckBoxPreference.isChecked() );
                                   }
                                   mHandler.postDelayed( this, 1000 );
                          }
                  }
             通过Handler的 removeCallbacks()方法可以移除待运行的线程,通过postAtTime()方法可以在特定的时间将线程放入消息队列。
  3. 线程优先级
       基于linux设置,用数字表示,范围-20~~19,其中-20为最高优先级,19为最低优先级。优先级的设置通常用于处理并发线程产生的阻塞。设置优先级的方法:
                 Process.setThreadPriority( priority );
6.2  android线程封装
     对java线程进行了封装,最主要的工作包括 AsyncTask封装 和UI线程。
   1.  AsyncTask封装
        AsyncTask通常用于后台线程和UI线程的交互。虽然AsyncTask本质上还是Thread加 Handler的一个封装,一个实例如下:
               private  class  ReceivePushTask   extends  AsyncTask  {
                          protected  void  doInBackground( Intent ... intents ){
                                    Intent  intent = intents[ 0 ];
                         }
                          public  void  onReceive(){ ... }
               }
         AsyncTask中最基本的两个方法doInBackground() 和 onReceive(), 前者执行真正的后台计算,后者向UI主线程反馈计算结果。为了使用AsyncTask,必须继承它。
         其他重要方法: 
                 public  final AsyncTask  execute( Params......  params ); //执行AsyncTask
                 protected  void   onPostExecute( Result  result ); //在doInBackground()方法执行后执行,在UI线程中执行
                 protected  void   onPreExecute(); //在执行doInBackground()方法前执行
                 protected  final void  publishProgress( Progress...   values ); //向UI线程反馈计算进度 
         AsyncTask可以支持多个输入参数甚至可变参数,其参数在execute()方法中传入。实际开发中,通常存在中断线程执行的情况,方法如下:
                 public  final  boolean   cancel  ( boolean  mayInterruptIfRunning );
         此方法可能因为进程已经执行结束,已经被中断或其他原因而执行失败,判断AsyncTask是否已经被中断方法:
                 public  final  boolean   isCancelled();
         在中断执行时,系统会调用AsyncTask的onCancelled()方法,开发者可在该方法中进行一些收尾工作,但要注意,必须显式判断线程是否中断。
    2. 接入UI线程
        在多线程编程中,执行后台计算的线程通常需要和UI线程进行交互,将计算结果反馈给UI线程,呈现给用户,传统的方法一般需要借助于Handler。  android,从activity,view和UI主线程3 个层次,提供了4中方式接入Ui线程。
   (1)Activity的runOnUiThread(Runnable)方法
            这个方法本质上是基于Handler的 post(Runnable r) 方法实现的。实例如下:
                    mActivity.runOnUiThread( new Runnable(){
                             public  void  run(){    ......   }
                    } );
   (2)View的 post(Runable)方法
            View的 post(Runnable) 方法从控件层面对多线程提供了支持,实现原理与(1)类似,实现实例:
                     public  void  onClick( View v ){
                              new Thread( new Runnable(){
                                        public  void  run(){
                                                final  Bitmap  bitmap = loadImageFromNetwork( "http://example.com/image.png" );
                                                mImageView.post( new Runnable() {
                                                         public  void  run(){
                                                                  mImageView.setImageBitmap( bitmap );
                                                         }
                                                } );
                                       }
                              } ).start();
                     }
     (3)View 的 postDelayed(Runnable, long) 方法
              View 的 postDelayed(Runnable, long)方法提供了延时处理的能力,实例如下:
                     final  static  int  PAUSE_DELAY = 100;
                     Runnable uRunnable = new Runnable(){
                            public  void  run(){
                                    if( mToggle ){
                                            mCount++;
                                            mView.onResume();
                                    }else{
                                           mView.onPause();
                                    }
                                    mToggle = !mToggle;
                                    mView.postDelayed( mRunnable,  PAUSE_DELAY );
                            }
                     }
     (4)Handler 方法
            利用Message消息向UI主线程发送后台计算的信息,及时将计算结果反馈给用户。当UI线程接收到Message消息后,会调用其Handler进行消息处理。Handler处理消息的过程如下:
                   Message message = mHandler.obtainMessage( ORIENTATION_CHANGED );
                   mHandler.sendMessage( message );
                   ......
                   Handler mHandler = new Handler(){
                             public  void  handleMessage(Message msg){
                                        switch( msg.what ){
                                                   case  ORIENTATION_CHANGED:
                                                   ......
                                                   break;
                                        }
                             }
                   };
  6.3 线程间的消息通信
         相比Thread加Handler的封装,AsyncTask更为可靠,更易于维护。AsyncTask的缺点是,一旦线程开启即dobackground方法执行后无法向线程发送消息,仅能通过预先设置好的标记来控制逻辑,当然可以通过挂起线程并等待标志位的改变来进行通信。
        
下面主要介绍消息队列以及消息的分发和接收
6.3.1 消息队列
       消息队列的维护是在线程中进行的,但默认除UI主线程外,其他线程并不拥有消息线程。为了维护一个消息队列,需要在线程中调用 Looper 的prepare() 方法进行初始化。为了使线程可以接收发送过来的消息,通常需要借助Handler来接收消息。一个维护消息队列的实例如下:
                 private  class  QueryRunner  extends  Thread{
                          public  Handler  mHandle;
                          public  void  run(){
                                   Looper.prepare();//loop()方法应随后调用,实现维护消息队列的初始化
                                   mHandler = new Handler(){
                                             public  void  handleMessage( Message msg ){
                                               //处理得到的消息
                                             }
                                   };
                                   Looper.loop();//提供对消息的监听
                          }
                 }
          通过Looper还可以判断当前线程是否为UI主线程,方法如下:
                 private  void  assertUiThread(){
                          if( !Looper.getMainLooper().equals( Looper.myLooper() ) ){
                                    throw new RuntimeException( "not  on the UI thread" );
                          }
                 }
           通过查看Message的实现即可发现, Message继承了Parcelable,在消息的传递过程中,实际上传递的是序列化数据。Message的实现如下:
                  public  final  class Message  implements  Parcelable{
                           public  int  whatl;     //消息标识符
                           public  int  arg1;      //传递参数
                           public  int  arg2;      //传递对象
                           public  Object  obj; //传递的对象
                           public  Messenger  replyTo;
                           long  when;
                           Bundle  data;
                           Handler  target;  //目标Handler
                           Runnable  callback;
                           Message  next;
                           private  static   final  int  MAX_POOL_SIZE = 10;  .......
                  }
         源码显示,一个线程会自动维护一个消息池,该消息池的大小为10 。 在需要生成消息时,首先从消息池中获取信息,只有当消息池中的消息均被使用时,才会重新创建新消息,当消息使用结束时,消息池会回收消息,实现过程如下:
                  private  static  final  Object  sPoolSync = new Object();
                     private  static  Message  sPool;
                     private  static  int sPoolSize = 0;  // 消息池当前的大小
                     private  static  final  int  MAX_POOL_SIZE = 10;  //消息池极限
        从消息池中获取消息:
                   public  static  Message  obtain(){
                            Synchronized( sPoolSync ){
                                    if( sPool != null ){
                                             Message m = sPool;
                                             sPool = m.next;
                                             m.next = null;
                                             sPoolSize--;
                                             return m;
                                    }
                            }
                            return new Message
                   }
        从上可见,在发送消息时,通过obtain()方法获得Message比创建Message的效率更高,发送消息的实例如下:
                   pubic  void progress( boolean  progress ){
                            android.os.Message   msg =  android.os.Message.obtain();
                            msg.what = MSG_PROGRESS;
                            msg.arg1 = progress ? 1 : 0;
                            sendMessage( msg );
                   }
         向消息池中回收消息的实现过程:
                   public   void  recycle(){
                            synchronized( sPoolSync ){
                                      if( sPoolSize < MAX_POOL_SIZE ){
                                               clearForRecycle();
                                               next = sPool;
                                               sPool = this;
                                               sPoolSize++;
                                      } 
                            }
                   }
   6.3.2 消息分发
           消息的分发和接收均与Handler密切相关,对于消息的分发,android目前提供了两种消息类型,一种是 post 消息,一种是 send 消息。 其中 post 消息会在未来某一时间加入消息队列,而 send 消息则会立即加入到消息队列。
      1.  send  消息
           分发一个简单的 send 消息的方法如下:
                  Message  msg = mHandler.obtainMessage( KILL_APPLICATION_MSG );
                  msg.arg1 = uid;
                  msg.arg2 = 0;
                  msg.obj = pkg;
                  mHandler.sendMessage( msg );
          如果没有消息需要传递,那么可以发送仅携带消息标示符的空消息,方法: 
                    mHandler.sendEmptyMessage( CANCEL_HEAVY_NOTIFICATION_MSG );
     2.  post 消息
           通过 post 消息可以实现类似循环计算的功能,方法如下:
                   mTicker = new Runnable(){
                            public  void  run(){
                                      if( mTickerStopped ) return;
                                      mCalender.setTimeInMillis( System.currentTimeMillis() );
                                      setText( DateFormat.format( mFormat,  mCalendar ) );
                                      invalidate();
                                      long  now = SystemClock.uptimeMillis();
                                      long  next = now + (1000 - now%1000);
                                      mHandler.postAtTime(mTicker,  next);  //间隔一段时间后,再调用线程
                            } 
                   };
                   mTicker.run();
    6.3.3  消息接收
           消息接收相对简单,如果存在重要问题,应注意数据的同步,Handler中消息接收的过程:
                  public  void  dispatchMessage( Message msg ){
                           if( msg.callback != null ){
                                     handleCallback( msg ); //通过Message自身回调进行处理
                           }else{
                                     if( mCallback != null ){ //通过当前线程回调进行处理
                                              if( mCallback.handleMessage(msg) ){
                                                         return;
                                              }
                                     }
                                     handleMessage(msg);//Handler自己处理
                           }
                  }
        对于消息队列而言,在Looper 的loop()方法中执行对消息的监听。 loop()方法的实现如下:
                  public  static  final  void  loop(){
                           Looper  me = myLooper();
                           MessageQueue  queue = me.mQueue;
                           Binder.clearCallingIdentity();
                           final  long  ident = Bindler.clearCallingIdentity();
                           if( msg != null ){
                                  if(msg.target == null){   return;   }
                           msg.target.dispatchMessage(msg);//调用Handler进行消息处理
                           msg.recycle();//消息回收
                           }
                  }
6.4 线程安全处理
       线程安全多是由多线程对共享资源的访问引起的。在线程安全层面,android更多采用java的实现,除了java的join(), wait(), sleep(),  notify()等方法和synchronized关键字外,还有java的并发库。
6.4.1 synchronized同步
     在android应用层,线程的并发很多是靠 synchronized 关键字来实现的,通过 synchronized 关键字,可以实现方法和语句块的同步。同步的本质是对特定对象的加锁,他们可以是来自调用方法的对象,也可以是开发这自己的对象。
     另外,synchronized 关键字并不能继承,对于父类的同步方法,在子类中必须再次显式声明才能成为同步方法。
     synchronized 关键字的局限在与 在试图获得锁时,无法设定超时和中断,每个所只有一个条件,在某些场景下可能不够用。
     synchronized 关键字可以是实现方法同步和语句块同步。
1. 方法同步
     方法同步分一般方法同步和静态方法同步。一般方法同步的本质在于将synchronized关键字过用于对象引用,作用域仅限类的单个对象。实例:
             public  synchronized  void  setMax(int max){
                      super.setMax(max);   ......
             }
             等同于:
             public  void  setMax(int max){
                       synchronized(this){
                      super.setMax(max);   ......
                       }
             }
     静态方法同步的本质是将类本身作为锁,其作用域是该类的所有对象。下面是BitmaoManager中利用synchronized实现单子模式的过程:
             private  static  BitmapManager  sManager = null;
             public  static  synchronized  BitmapManager  instance(){
                      if( sManager == null ){
                              sManager = new BitmapManager();
                      }
                      return sManager;
             }
  2. 语句块的同步
        当需要同步的范围不是很大时,可以采用语句块同步,下面是一个实例:
              private  void  setOrientation( int  orientation ){
                      synchronized(this){   ......   }
              }
        将对象本身作为锁,显然影响并发效率,更灵巧的是设计自定义锁。 自定义锁可以是任何类型的对象,但通常将其类型设计为Object。实例如下:
              public  abstract  class  AbstractPreferences  extends  Preferences
              {  
                        protected  final  Object  lock;
                        protected    AbstractPreferences getChild(String  name) throws  BackingStoreException{
                                 synchronized(lock){  ......  }
                        }
              }
6.4.2  RPC通信
6.4.3 SQLite调用
      对于SQLite的调用,可能会存在多处同时执行读写操作的场景,这种场景也需要考虑线程的安全性。android提供了类似AysncTask的 AsyncQueryHandler 方法来解决这一问题。将耗时的查询等操作放置非UI主线程中,操作结束后,通过Handler 调用响应的UI主线程的方法处理操作执行的结果。

你可能感兴趣的:(android核心技术与最佳实践笔记(二))