亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

深入理解Android組件間通信機制對面向對象特性的影響詳解

2020-04-11 12:23:01
字體:
來源:轉載
供稿:網友

組件的特點
對于Android的四大組件Activity, Service, ContentProvider和Service,不能有Setter和Getter,也不能給組件添加接口。原因是組件都是給系統框架調用的,開發者只能實現其規定的回調接口,組件的創建與銷毀都是由系統框架控制的,開發者不能強行干預,更沒有辦法獲取組件的對象。比如Activity,Service,BroadcastReceiver,你沒有辦法去創建一個Activity,Service或BroadcastReceiver,然后像使用其他類那樣的調用其上的接口與其通信,用Setters和Getters改變屬性等等。這也決定了,組件之間通信只能用系統支持的Intent。而Intent只能傳遞基本數據類型和Uri等一些常見的數據類型。Intent只支持傳遞內置類型和一些限制類型,這就導致了組件之間的數據傳遞必須都是基本類型,所以枚舉類型無法使用。
多態無法實現
比如你有一個Service用于在后臺執行UI中發來的請求,這些請求有些是做數據請求,有些是做數據分析,等等。這里可以用多態,定義一個統一的Transaction類,然后再為每種特定的Transaction類型,Transaction中統一接口process()用于實際的處理,理想的情況是,Service接收一個Transaction對象,然后調用其process(),沒有必要知道具體的類型,UI創建具體的一個類型對象然后交由Service來處理。但是這在Android當中是無法實現的,因為Intent通信機制所限,因為它不能直接傳遞Transaction對象。所以,Service必須要知道具體的類型。原生應用Mms中就有如此的現象,在transaction包中TransactionService是處理服務,UI發送到Service的只是區別不同Transaction的Id(一個整數),Service查看不同的Id創建不同的Transaction對象,然后調用process()對其處理。
建議:自己實現一個類似Service的服務類,在其內用Handler,Thread和Looper讓其長時間運行。這樣就沒有組件間通信的限制,你可以像正常使用Java對象那樣來使用這個服務類,向其傳遞自定義的處理請求:

復制代碼 代碼如下:

public class TransactionServer extends HandlerThread {
public TransactionServer() {
start();
}
public void onLooperPrepared() {
mHandler = new Handler(getLooper(), new Handler.Callback() {
 @Override
  public void handleMessage(Message msg) {
Transaction request = (Transaction) msg.obj;
 request.process();
   }
 }
}

   public void execute(Transaction request) {
   if (mHandler == null) {
return;
}
   Message msg = Message.obtain();
   msg.obj = request;
   mHandler.sendMessage(msg);
   }
}


在Activity中就可以創建此Server的對象,然后使用它:
復制代碼 代碼如下:

TransactionServer server = new TransactionServer();
Transaction updateRequest = new UpdateTransaction();
server.execute(updateRequest);

另外,用AIDL與Service通信,雖可以獲取Service的對象引用,可以直接調用Service的方法,但這個也有限制,對于AIDL的接口,所有的參數和返回類型都必須是基本數據
據類型,不能有對象。原因也好理解,因為AIDL也是要通過IPC的,即便Service與Activity在同一個進程內,所以本質上它與Intent通信機制無區別。
封裝性被破壞
組件間的通信機制決定了Android的封裝性,先來看一些實例:
復制代碼 代碼如下:

Intent i = new Intent(Intent.ACTION_VIEW);
i.setDataAndType(uri, "text/html");
startActivity(i);

這在Android當中是再常見不過的了。
Intent和IntentFilter的使用讓封裝性受到大大的破壞,因為你必須把字串,參數等直接寫入到Intent或IntentFilter當中。例如:
復制代碼 代碼如下:

Intent i = new Intent("android.contacts.action.MULTIPLECONTACTSLISTS");
i.setExtra("request_type", 3);
<intent-filter>
 <action android:name="android.contacts.action.MULTIPLECONTACTSLISTS" />
</intent-filter>

當然,可以再好一點,就是:
Intent i = new Intent(Contacts.ACTION_GET_CONTACTS);
但是在AndroidManifest中的IntentFilter還是要寫字串常量(Literal Strings),這樣就有一個問題,就是即使你寫錯了,編譯器不會提醒你,直到你運行的時候才會發現程序不正常工作,你調試啊,調試,最終發現是字串寫錯了?;蛘撸琣ctivity的name寫錯了,編譯器同樣不會提醒你,但你運行時卻因找不到類而報出RuntimeException。
建議:盡可能在所有作用域內定義常量,以讓組件間接口保持一致性,特別是對于字串常量,一定要在二個組件都可見的作用域內定義常量,否則將會有維護的麻煩。
例子:intent.putExtra("request_type", 3); --> intent.putExtra(TargetActivity.REQUEST_TYPE, TargetActivity.NO_BACKGROUND);否則,特別是當目標組件不在同一個包內,或距離很遠時,如果另一方改了,編譯時不會有錯,但程序不會正常工作,從而引發難以發覺的Bug。
無法在組件間傳遞自定義的數據結構
如前面所述,因為無法獲取組件的對象的引用,所以你無法向其設置數據,當然,可以用靜態方法,但是不優雅且難以維護(對于Service倒是可以通過AIDL方式獲取Service對象的引用,然后調用其方法來添加數據)。又因為Intent只能攜帶基本的數據類型,所以對于自定義的數據結構想要在組件間傳遞就特別的麻煩。當然你可以以讓數據結構實現Parcleable接口,但是用起來也相當的麻煩。
建議:
1. 盡可能的避免使用自定義數據結構,特別是除了Setters和Getters以外不具有其他行為的數據結構
對于結構化的數據,為其定義ContentProvider,把數據寫入SQLite數據庫,這樣數據庫表中的每行數據都相當于是一個數據對象,每一列都是其屬性。因為Android的組件與SQLite數據庫的粘性很大,每個組件都可以很方便的從數據庫中獲取數據,再通過Cursor等工具來操作數據。最最重要的是這很方便在組件間傳遞數據,因數ContentProvider的訪問都是通過Uri來實現的,而Uri又可以與Intent無縫接合,Uri可以方便的放入和從Intent中取出,每個組件又都可以直接訪問ContentProvider用Uri讀取數據,從而就可以實現組件間的無縫數據傳遞。
2. 盡可能的不要在組件之間傳遞數據
不要用太多的Activity,Service也能免則免,Activity+線程可能解決大部分問題,當然了,線程也不是那么好用的,特別是在Android里面。
3. 避免在組件之間傳遞自定義數據結構
如前所述,組件之間最好直接傳遞基本數據和Intent支持的數據類型。對于自定義的數據結構,要么不定義數據結構,要么不要在組件間傳遞,否則會很麻煩,雖然可能以實現Parcelable接口,但是效率和操作的方便性上都會大打折扣。
關于枚舉和整數集
先前一篇<Android開發筆記之:用Enum(枚舉類型)取代整數集的應用詳解>曾說要盡量使用枚舉(enum)代替整數集(ints),而且很多編程書籍(Effective Java)也建議用枚舉代替整數集,這其中的好處就是降低出錯率,把運行時的檢查可以放到編譯時,因為整數的范圍較大,你可傳遞任意的整數,直到運行時才會檢測或產生問題,但是枚舉會在編譯時檢查類型,如果不是合法的枚舉,編譯器會報怨。
但是我們可以看到,在Android中的情況卻很差,Android中大量的使用了整數集,系統定義了大量的整數集,很多參數也都是整數,雖然正確的方法都是向這些API傳遞其所定義的整數常量,但是你如果傳個Integer.MAX_VALUE或Integer.MIN_VALUE,起碼在編譯時不會出問題。
既然這不是一個好的編程規范,為什么Android中還要大量的使用整數集呢?原因就在于組件間通信,組件之間要傳遞參數,但是Intent又只能放入基本數據類型,也就是說如果使用枚舉,那么將無法用Intent傳遞給其他的組件,因為枚舉轉為整數很容易,但反過來整數轉成枚舉就不是那么容易了。
所以,如果你的常量不需要在組件間來回的傳遞,那么最好定義成為枚舉,否則,只能用整數集了。
關于組件一般的設計原則
1. 不要用組件實現某些接口,比如點擊接口,等等
因為組件是一個開銷非常巨大的對象,組件的繼承層次也非常的深,用組件實現接口,傳遞給調用者,就相當于用一個火車去運送一個小老鼠一樣,給了別人一個相當大的對象,但是僅有一個或二個方法是別人需要的。特別是對于Activity,不要去實現一些公共的接口比如View.OnClickListener,除了前面的原因以外,另外一個就是你的onClick必須用條件來區分點擊的是哪個UI元素,這很難維護,還有一個原因就是Activity的對象不是很穩定,因為系統的某些事件如轉屏,語言切換等等會把Activity殺死并重新創建一個實例,所以有可能會引發問題,雖然看起來Activity還在,但是并不同一個實例,如果某些東西與具體實例相關,就會引發問題,要么程序不正常工作,要么有RuntimeException。還有可能引發內存泄漏,因為送給使用者的接口對象都是Activity的實例引用,一旦某個引用超過Activity的生命周期,就會造成內存泄漏。
推薦的做法是用匿名內部類來實現接口,如果其他地方需要對此接口對象的操作,可以聲明一個成員變量或者一個內部類,這樣也方便Activity來控制,以保證所有東西都生存在Activity的生存周期之內。
2. 少用Service
組件Service并沒有傳說中的好用,而且它還會讓你的程序退出頁面后仍然在后臺跑,占系統資源不說,還會被罵(看看這些文章吧),因為Service的生命周期是由系統來控制,我們無法干預,即使你確切的知道某些時候你已經完全不用它了。用Activity和線程就可以完成絕大多數操作,而且你還能做到讓所有線程都在Activity的控制之內,讓它們都活在Activity的生命周期之內。另外的原因就是,因為線程都屬于自建的類,或者普通的Java類,可以應用面向對象,因為沒有了組件通信的限制。
3. 利用ContentProvider來做復雜數據結構的通信工具
ContentProvider和SQLiteDatabase存儲的就是結構化數據,相當于一個數據結構,它的引用就是它的Uri,任何組件通過Uri就可獲得此數據結構。它有如下優點:
1. 可以方便的在組件間傳遞
因為數據實際是在數據庫中,你在組件間僅傳遞其地址Uri即可,任何組件或任何持有Context的類都可以方便的獲取它,無論從實用性還是從效率上講,這比用Intent傳,或者實際傳送數據結構對象來得快。
2. ContentProvider組件有自己的進程和線程,不會有線程同步問題
外部都是通過ContentResolver來訪問ContentProvider,因此ContentProvider對外界來講是一樣的,訪問方式相同,自然就不會有線程同步之類的問題。
3. ContentProvider可以進行封裝,從而使數據操作更加方便
因為ContentProvier提供統一的接口,你可以利用數據自身的特點,在實現這些接口時進行一些封裝,比如添加默認值等等。
4. ContentProvider可以用作隊列或堆棧
因為每一行都是一個結構化數據,每一行的數據插入的順序又是按先后順序,所以這完全可以當做一個隊列,或一個堆棧。

可以參考原生Mms中信息的發送流程,信息從用戶點擊發送就寫入數據庫,然后一路把其Uri在各個組件間中傳遞,每個組件更新信息的狀態,直到最后發送。還有DownloadProvider,Android中默認的下載,應用程序通過DownloadManager提交一個Request,但實際做下載的是DownloadService,而DownloadServer是在packages/provider/DownloadProvider中,是一個完全獨立的進程。DownloadManager僅是把一個Request寫入DownloadProvider中,這個Request包含下載一個東西的相關信息如URL等。DownloadService僅是監聽DownloadProvider的變化,一旦有新數據插入,就創建線程讀出此Request,然后開始下載。下載的同時,也是把數據直接更新到DownloadProvider中,這樣UI就可以顯示進度等信息。這一過程涉及二個進程,至少三個組件:提交Request的用戶進程和DownloadProvider進程,DownloadManager(是一個公共API),DownloadService(單獨進程,私有的package)和DownloadList(在DownloadProvider包內部,用于顯示下載進度的UI),這些組件之間沒有直接的通信,它們都是圍繞著ContentProvider。同時這里的ContentProvider也被用做下載請求的隊列,DownloadManager可以不斷的向其中加入請求,DownloadService會監聽其變化從其中取出數據然后做下載。
別說Android開發很簡單
雖然Android上手很容易,但是要想寫出優質的代碼并不簡單,分裂現象,碎片化,系統架構等等都給很多事情加大了難度??梢钥匆幌略鷳弥械闹饕腁ctivity代碼量都在5000行以上,它們的界面比較復雜,是主要核心業務邏輯所在,這些Activity控制的東西比較多,所以很臃腫。當然這里的主要原因,還是未能進行良好的設計和重構。比如ICS中的Browser就做的好一些,它的BrowserActivity只有幾百行的代碼,但以前的代碼卻是6000多行,現在它把各種業務邏輯分別拆開,Activity只負責接收Frameworks層的回調,所有的業務邏輯控制交由Controller來完成,而Controller只負責Tab的管理,菜單等的管理。具體的菜單和布局分辨率相關的東西又交由PhoneUi來處理。下載的處理由DownloadHandler來處理,等等。原來這些所有的事情都放在了BrowserActivity中的,可以想像原來它里面的邏輯會是多么的亂,維護起來會是多么的痛苦。當然,現在的設計也還有待提高,因為類之間的耦合依然很大,比如Controller中持有PhoneUi對象,但是PhoneUi對象又持有Controller,等等現象。很多時候會出現相互調用的情況,這是相當難以維護的,也破壞了相當多的設計原則。

總之,凡是程序,如果要想寫的好,都需要投稿額外的精力,平臺雖然有優劣但更重要的是對代碼投入的精力。但現在可悲的是,Android平臺贏利不理想,加之碎片化和浮躁的心理,使得很多應用都在一二個月內做出來,所以整個Android生態系統中的應用質量都不高,更為嚴重的是反編譯和克隆,很多人都是把應用抓下來,反編譯然后改了改就是一個新的應用,越是如此不關注質量,用戶就越不買帳,開發者無法贏利,就越難投入精力做好應用,如此進入了一個惡性循環。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲已满18点击进入在线看片| 91精品国产色综合| 国产成人精品免高潮费视频| 日韩美女福利视频| 中文字幕久热精品在线视频| 久久综合伊人77777| 亚洲精品av在线播放| 午夜欧美不卡精品aaaaa| 国内精品视频久久| 国产日韩精品在线| 久久99精品久久久久久噜噜| 国产日韩欧美在线| 中文字幕视频一区二区在线有码| 欧美性20hd另类| 亲爱的老师9免费观看全集电视剧| 午夜欧美不卡精品aaaaa| 欧美成人性色生活仑片| 国产视频丨精品|在线观看| 亚洲欧美日韩精品久久奇米色影视| 亚洲va码欧洲m码| 欧美国产日韩一区二区| 国内外成人免费激情在线视频网站| 蜜月aⅴ免费一区二区三区| 91免费版网站入口| 亚洲精品欧美极品| 狠狠久久五月精品中文字幕| 俺去了亚洲欧美日韩| 日韩福利伦理影院免费| 久久久久女教师免费一区| 中文字幕亚洲欧美一区二区三区| 欧美性猛交xxxx免费看漫画| 欧美午夜精品久久久久久人妖| 91色视频在线观看| 亚洲精品第一国产综合精品| 欧美人与物videos| 色中色综合影院手机版在线观看| 成人免费黄色网| 国产精品视频免费观看www| 国产精品久久久久久婷婷天堂| 亚洲天堂网在线观看| 国产精品视频最多的网站| 毛片精品免费在线观看| 欧美视频免费在线观看| 久热精品视频在线观看一区| 亚洲精品二三区| 在线观看日韩专区| 亚洲精品一区二区三区婷婷月| 69视频在线免费观看| 日韩高清有码在线| 日韩激情av在线播放| 最好看的2019年中文视频| 亚洲国产欧美久久| 欧美xxxx14xxxxx性爽| 91丝袜美腿美女视频网站| 色噜噜狠狠色综合网图区| 伊人久久男人天堂| 国产精品日韩在线| 久久视频在线播放| 国产在线日韩在线| 免费av在线一区| 欧美视频不卡中文| 国产69久久精品成人看| 欧美高清第一页| 久久视频免费在线播放| 久久这里有精品视频| 欧美疯狂做受xxxx高潮| 欧美性做爰毛片| 色婷婷**av毛片一区| 久久精品色欧美aⅴ一区二区| 国产精品免费久久久久久| 精品国产鲁一鲁一区二区张丽| 亚洲成人久久久久| 国产精品视频xxxx| 九九九久久久久久| 午夜精品久久久99热福利| 亚洲夜晚福利在线观看| yw.139尤物在线精品视频| 91精品国产高清久久久久久91| 欧美性猛交xxxx乱大交蜜桃| 亚洲iv一区二区三区| 亚洲第一av网站| 欧美日韩激情小视频| 亚洲视频在线观看视频| 操人视频在线观看欧美| 国产精品久久久久免费a∨大胸| 91人成网站www| 国产欧美中文字幕| 国产在线观看91精品一区| 亚洲午夜精品久久久久久性色| 精品久久久久久久久久| 亚洲视频在线免费观看| 国产欧美在线视频| 国产大片精品免费永久看nba| 亚洲欧洲日韩国产| 日韩免费在线观看视频| 国产精品大片wwwwww| 亚洲精品网站在线播放gif| 亚洲最大福利视频网站| 欧美精品一区二区三区国产精品| 欧美亚洲另类激情另类| 久久久久久久久久亚洲| 亚洲最大的av网站| 久久久久久九九九| 精品久久久久久| 欧美视频中文在线看| 久久久久久999| 日韩在线精品一区| 在线精品国产成人综合| 亚洲精品美女久久久| 欧美做爰性生交视频| 欧美韩日一区二区| 亚洲国产三级网| 欧美一区二区影院| 久久成人精品一区二区三区| 影音先锋欧美精品| 欧美午夜激情小视频| 中国china体内裑精亚洲片| 欧美黑人一级爽快片淫片高清| 国产免费成人av| 欧美一区二区大胆人体摄影专业网站| 亚洲欧美日韩国产精品| 97香蕉久久夜色精品国产| 有码中文亚洲精品| 久青草国产97香蕉在线视频| 九九久久综合网站| 久久久久99精品久久久久| 欧美日韩精品在线视频| 日韩一区av在线| 欧美成人免费在线视频| 亚洲第一精品久久忘忧草社区| 亚洲丁香久久久| 久久久精品网站| 国产免费一区视频观看免费| 亚洲国产成人av在线| 色多多国产成人永久免费网站| 欧美亚洲另类制服自拍| 国产亚洲一区二区精品| 欧美黑人又粗大| 法国裸体一区二区| 好吊成人免视频| 国产黑人绿帽在线第一区| 91国自产精品中文字幕亚洲| 亚洲成人性视频| 亚洲色在线视频| 国产v综合ⅴ日韩v欧美大片| 亚洲字幕一区二区| 国产美女久久精品香蕉69| 欧美精品久久一区二区| 久久99精品久久久久久琪琪| 精品国产鲁一鲁一区二区张丽| 亚洲无av在线中文字幕| 日韩欧美中文免费| 日韩高清有码在线| 日韩av在线电影网| 97涩涩爰在线观看亚洲| 日日狠狠久久偷偷四色综合免费| 久久久久成人网| 最近中文字幕mv在线一区二区三区四区| 欧美在线视频网站| 国产精品视频网站| 国产啪精品视频| 亚洲已满18点击进入在线看片| 精品国产欧美一区二区三区成人| 欧美大奶子在线|