4.1 xml文件管理
xml文件更多的是用來維護應用或系統的配置文件。SharedPreferences這個輕量級的類作爲xml文件存儲的上層接口。本質爲<key, value>
根據配置信息是否對外開放,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的支持。對於上層代碼中的接入,實例如下;
<ListPreference
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_TEXT, "EMAIL——內容" );
it.setType( "text/plain" );
startActivity( Intent.createChooser( it , "選擇一個Email客戶端" ) );
發送的時候有抄送人,方法如下;
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<CatCmdMessage> CREATOR = new
Parcelable.Creator<CatCmdMessage>(){
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<Intent , Void, Void> {
protected void doInBackground( Intent ... intents ){
Intent intent = intents[ 0 ];
}
public void onReceive(){ ... }
}
AsyncTask中最基本的兩個方法doInBackground() 和 onReceive(), 前者執行真正的後臺計算,後者向UI主線程反饋計算結果。爲了使用AsyncTask,必須繼承它。
其他重要方法:
public final AsyncTask<Params, Progress, Result> 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主線程的方法處理操作執行的結果。