锘??xml version="1.0" encoding="utf-8" standalone="yes"?> 鏈枃浠嬬粛浜嗘枃绔犱綔鑰呬粠浜嬩簡鍑犲勾android搴旂敤鐨勫紑鍙戯紝緇忓巻2嬈℃灦鏋勫彉闈╋紝絎竴嬈¢泦鎴愪簡RxJava絎簩嬈¢泦鎴愪簡MVP錛屽茍灝哛xJava涓嶮VP瀹岀編緇撳悎錛屽疄鐜頒簡浣庤﹀悎錛屼唬鐮佺畝鍗曪紝嫻嬭瘯鏂逛究鐨勬灦鏋勩?/p> 鍏跺疄鎴戜滑鍦ㄥ紑鍙戜腑涔熼亣鍒拌繃錛孉ndroid鍏ラ棬闂ㄦ杈冧綆錛屽鏋滃墠鏈熷APP瑙勫垝涓嶆竻鏅幫紝Coder浠鏈潵鍙樺寲鎶婃彙涓嶅噯錛屾妧鏈灦鏋勭粡楠屼笉澶熷己澶э紝鏈緇堝鑷村氨鏄竴涓狝ctivity鍑犲崈琛岋紝閲岄潰鍐欎簡澶ч噺鐨凱rivate鏂規硶錛屾媶鎴愬嚑涓狥ragment銆佸皝瑁呭嚭鏉ュ嚑涓被閮芥槸鏃犳硶瑙e喅錛岀粨鏋滃氨鏄湅Activity闅懼彈鐨勮姝伙紝綰犵粨錛岀湅浜嗕笉鐖芥敼涔熶笉鏄笉鏀逛篃涓嶆槸錛屼弗閲嶅獎鍝嶇湅鐨勪漢鐨勫績鎯呫傚茍涓旀ㄥぉ灝や漢榪欎釜鏄駭鍝佷漢鍛樿鍒扐pp涓嶅ソ錛屾病鏈夊墠鐬繪э紝鏀規潵鏀瑰幓銆傘傘?/p> 榪欑瘒鏂囩珷灝辨槸浣跨敤鏂扮殑緇撴瀯瑙e喅璇ラ棶棰樸?/p> Android Application Architecture Our journey from standard Activities and AsyncTasks to a modern MVP-based architecture powered by RxJava. 榪欑瘒鏂囩珷涓昏鐩殑鏄榪板浣曞皢浼犵粺鐨凙ctivities 涓?AsyncTasks 妯″紡鍚戠洰鍓嶄富嫻佺殑MVP鏋舵瀯鍩虹鐨勫搷搴斿紡緙栫▼妗嗘灦榪囧害銆?/p> Different parts of a software codebase should be independent, yet perfectly work together like a well-oiled machine — photo by Chester Alvarez. 鍏堢晠浜竴涓嬶細~~~濡傛灉鏉捐﹀悎鏋舵瀯錛屽垎宸ユ槑紜紝鐒跺悗瀹岀編鐨勭粍鍚堝湪涓璧峰伐浣滄槸涓涓緢鍚婄殑浜嬫儏銆?br style="padding: 0px; margin: 0px;" />錛堣漿涓浘鐗囪繕瑕佸啓鏄庣櫧璋佹媿鐨勶紝鐗堟潈鎰忚瘑鐪熷己錛?/p> The Android dev ecosystem moves very quickly. Every week new tools are created, libraries are updated, blog posts are written and talks are given. If you go on holiday for a month, by the time you come back there will be a new version of the support library and/or Play Services. 鏈榪戝嚑騫碅ndroid鐨勭敓鎬侀摼鍙樺寲闈炲父榪呴燂紝浠庡簳灞傜殑Android Api鍒板簲鐢ㄥ眰鐨勫悇縐嶅紑婧愮殑綾誨簱銆佸伐鍏鋒洿鏂伴潪甯歌繀閫熴備竴涓嶇暀紲炲氨钀藉悗浜嗐?/p> I’ve been making Android apps with the ribot team for over three years. During this time, the architecture and technologies we’ve used to build Android apps have been continuously evolving. This article will take you through this journey by explaining our learnings, mistakes and the reasoning behind these architectural changes. 鎴戝湪Ribot鍥㈤槦浠庝簨Android搴旂敤寮鍙戝伐浣滀笁騫村錛屼即闅忕潃鍏徃鎶鏈殑涓嶆柇鍒涙柊錛岀Н绱簡寰堝緇忛獙銆侀敊璇互鍙婂湪鎶鏈夊瀷鑳屽悗鐨勬晠浜嬨?/p> The old times 2012騫撮偅涓椂鍊欙紝鎴戜滑鐨勪唬鐮侀兘鏄敤鐨勫師鐢烝ndroid錛屾病鏈変嬌鐢ㄤ換浣曠殑緗戠粶璇鋒眰妗嗘灦錛岃屾槸鍩轟簬AsyncTasks涔﹀啓銆?br style="padding: 0px; margin: 0px;" /> 浠g爜鍒嗕負涓ゅ眰錛孌ata涓嶸iew,Data灞備富瑕佹槸鐢ㄦ潵浠嶢PI鑾峰彇鏁版嵁錛屼繚瀛樺埌鎸佷箙鍖栫殑db褰撲腑銆俈iew灞備富瑕佸氨鏄妸Data鐨勬暟鎹樉紺哄埌UI涓娿侫PIProvider鎻愪緵鏂規硶鍑烘潵錛岀敤浜庡湪Activity鎴栬匜ragment涓柟渚跨殑榪涜鎺у埗涓庝氦浜掋傛妧鏈笂灝嗭紝浣跨敤URLConnection涓嶢syncTasks瀹炵幇浜嗕竴涓紓姝ョ殑緗戠粶璇鋒眰騫跺皢緇撴灉榪斿洖鍒拌皟鐢ㄧ殑鍥炶皟鏂規硶閲岄潰銆?/p> In a similar way, the CacheProvider contains methods that retrieve and store data from SharedPreferences or a SQLite database. It also uses callbacks to pass the result back to the Activities. 鐩稿悓鐨勫師鐞咰acheProvider鎻愪緵涓緋誨垪鏂規硶錛屽皢SharedPreferences鎴栬匰QLite鐨勬暟鎹彇鍑烘潵錛屽茍涓旇繑鍥炵粰鍒癆ctivity 闂 The problems 涓昏闂鏄疺iew灞傛湁澶鐨勭瘡璧橈紝浠ヤ竴涓崥瀹㈠垪琛ㄤ負渚嬫潵璁茶堪錛屾瘮濡傚崥瀹㈤渶瑕佹樉紺轟竴涓狶istView錛屼粠SQLite璇誨彇鏁版嵁錛孉ctivity闇瑕佸仛鍒頒互涓嬪嚑鐐癸細 This is a very simple example. In a real case scenario the REST API will probably not return the data like the view needs it. Therefore, the Activity will have to somehow transform or filter the data before showing it. Another common case is when the loadPosts() method takes a parameter that needs to be fetched from somewhere else, for example an email address provided by the Play Services SDK. It’s likely that the SDK will return the email asynchronously using a callback, meaning that we now have three levels of nested callbacks. If we keep adding complexity, this approach will result into what is known as callback hell. 榪欒繕鏄竴涓瘮杈冪畝鍗曠殑渚嬪瓙錛屽湪涓浜涚湡瀹炵殑鍦烘櫙涓紝榪滅▼鐨凙PI鍙兘娌℃湁榪斿洖紼嬪簭鐨勫繀欏誨鹼紝浣嗘槸activity蹇呴』鎶婃暟鎹鐞嗗畬鎴愪箣鍚庢墠鑳芥樉紺虹粨鏋溿傚啀涓涓緥瀛愬氨鏄鏋渓oadPosts鏂規硶闇瑕佸熷姪涓浜涘叾浠栧湴鏂圭殑榪斿洖鍙傛暟鏃訛紝綾諱技鐢ㄥ綰跨▼鍘誨疄鐜板悓姝ヨ姹傦紝涓轟繚璇佹暟鎹甯歌姹傦紝鎰忓懗鐫蹇呴』鍋氫竴涓笁灞傜殑鍥炶皟錛屽鏋滃啀澶嶆潅涓浜涳紝鎯崇悊娓呮榪欎簺鍥炶皟灝辨槸寰堣泲鐤肩殑浜嬫儏銆?/p> In summary: 鎬諱箣錛屽洖璋冨浜嗕箣鍚庯紝Activity涓嶧ragment浼氫貢鐨勮姝伙紝騫朵笖涓鑸漢鏃犳硶鐩磋銆?/p> A new architecture driven by RxJava 鎴戜滑鍦ㄨ泲鐤肩殑鏋舵瀯涓厧鐔簡2騫達紝褰撶劧涔熷皾璇曡繃寰堝鏂瑰紡錛屾渶緇堜篃鍙兘鏄紦鍜屼竴涓嬩貢鐨勯棶棰樸傛垜浠湪APIProvider浣跨敤浜哣olley錛屼唬鏇夸簡AsyncHttpClient錛屼絾鏄叾瀹炴槸涓涓悐鏍楓?/p> It wasn’t until 2014 when we started reading about RxJava. After trying it on a few sample projects, we realised that this could finally be the solution to the nested callback problem. If you are not familiar with reactive programming you can read this introduction. In short, RxJava allows you to manage data via asynchronous streams and gives you many operators that you can apply to the stream in order to transform, filter or combine the data. 涓嶅埌2014騫存垜浠氨寮濮嬭繘琛孯xJava鐨勯鐮旓紝鐒跺悗灝濊瘯浜嗕竴鎵圭畝鍗曠殑欏圭洰錛屾劅瑙塕xJava鐨勬柟寮忔槸瑙e喅鎴戜滑宓屽鍥炶皟鐨勭粓鏋佽В鍐沖姙娉曘傜畝鍗曠殑璇達紝RxJava鍏佽浣犻氳繃寮傛嫻佺殑鏂瑰紡綆$悊浣犵殑鏁版嵁錛屽茍涓旇繕鍙互閫氳繃鎿嶄綔絎︼紙Operators錛夊Observable瀵硅薄鐨勫彉鎹?/p> Taking into account the pains we experienced in previous years, we started to think about how the architecture of a new app would look. So we came up with this. 鎴戜滑鐢ㄤ簡鍑犲勾鐨勭粡楠岀棝瀹氭濈棝錛屾悶浜嗕笅闈㈣繖涔堜釜涓滆タ錛屾柊鐨凙PP鐨勬灦鏋勫浘 Similar to the first approach, this architecture can be separated into a data and view layer. The data layer contains the DataManager and a set of helpers. The view layer is formed by Android framework components like Fragments, Activities, ViewGroups, etc. 涓庣涓縐嶆柟娉曠浉浼鹼紝榪欎釜鏋舵瀯涔熸槸鍒嗕負Data灞備笌View灞傦紝Data灞傚寘鍚獶ataManager涓庝竴鍫咹elper錛沄iew灞傛槸鍖呭惈Fragments, Activities, ViewGroups絳夈?/p> Helper classes (third column on diagram) have very specific responsibilities and implement them in a concise manner. For example, most projects have helpers for accessing REST APIs, reading data from databases or interacting with third party SDKs. Different applications will have a different number of helpers but the most common ones are: Helper涓昏鏄泦鎴愮涓夋柟鐨勭被搴擄紝浠ヤ究浜庡湪浠g爜涓嚑琛屼唬鐮佸氨鍙互娓呮櫚鐨勫疄鐜版煇涓姛鑳斤紝姣斿璇鋒眰API錛岃闂暟鎹簱絳夛紝铏界劧涓嶅悓鐨勫簲鐢ㄧ▼搴忛兘鏈変笉鍚岀殑綾誨簱錛屼絾鏄粬浠棤闈炲氨鏄互涓嬭繖浜涘唴瀹癸細 Most of the public methods inside helper classes will return RxJava Observables. RxJava鏈鏍稿績鐨勪袱涓笢瑗挎槸Observables錛堣瑙傚療鑰咃紝浜嬩歡婧愶級鍜孲ubscribers錛堣瀵熻咃級,鍦℉elper綾諱腑鐨凱ublic鏂規硶錛屼竴鑸兘浼氳繑鍥炰竴涓猂xJava鐨凮bservables;DataManager鏄暣涓灦鏋勭殑澶ц剳錛屼粬澶ч噺鐨勪嬌鐢≧xjava鐨刼perators瀵笻elper榪斿洖鏉ョ殑鏁版嵁榪涜鐨勬暣鍚堣繃婊ゃ佷簩嬈″鐞嗐?/p> The code below shows what a DataManager method would look like. This sample method works as follows: 涓嬮潰鐢ㄤ竴涓緥瀛愭潵璇存槑DataManager鏄仛浠涔堢殑: Components in the view layer such as Activities or Fragments would simply call this method and subscribe to the returned Observable. Once the subscription finishes, the different Posts emitted by the Observable can be directly added to an Adapter in order to be displayed on a RecyclerView or similar. Observables鍙戝嚭涓緋誨垪浜嬩歡錛孲ubscribers錛堜緥濡?Activities or Fragments錛夊鐞嗚繖浜涗簨浠?鍙互鐩存帴灝嗘暟鎹樉紺哄埌涓浜涘彲浠ュ洖鏀躲侀噸鐢ㄧ殑View涓婇潰銆?br style="padding: 0px; margin: 0px;" />銆怋TW:濡傛灉涓涓狾bserverble娌℃湁浠諱綍鐨勭殑Subscriber錛岄偅涔堣繖涓狾bservable鏄笉浼氬彂鍑轟換浣曚簨浠剁殑銆?/p> The last element of this architecture is the event bus. The event bus allows us to broadcast events that happen in the data layer, so that multiple components in the view layer can subscribe to these events. For example, a signOut() method in the DataManager can post an event when the Observable completes so that multiple Activities that are subscribed to this event can change their UI to show a signed out state. 榪欎釜鏋舵瀯鐨勫彟澶栦竴涓ā鍧楁槸event bus錛宔vent bus鍙互璁╂垜浠湪Data灞傚彂鍑哄箍鎾紙涓嶆槸Android鐨凚roadcast錛夌劧鍚庝笉鍚岀殑妯″潡鍘繪敞鍐屽茍鎺ユ敹涓嶅悓鐨勫箍鎾簨浠?/p> Why was this approach better? 涓轟粈涔堣繖涓柟寮忚繖涔堢墰閫鹼紝鏄洜涓篛bservables涓巓perators鍙互鍘繪帀閭d竴鍫嗗繀欏葷殑鍥炶皟鏂規硶 The DataManager takes over responsibilities that were previously part of the view layer. Hence, it makes Activities and Fragments more lightweight. DataManager鏇夸唬浜嗕紶緇熸灦鏋勪腑寰堝浠g爜錛屼粠鑰屼嬌寰桝ctivity涓嶧ragment鍙樺緱鏇村姞杞婚噺綰с傚茍涓斾嬌寰楀崟鍏冩祴璇曞彉寰楁洿鍔犵畝鍗曘?/p> Clear separation of responsibilities and having the DataManager as the only point of interaction with the data layer, makes this architecture test-friendly. Helper classes or the DataManager can be easily mocked. DataManager鎴愪負浜嗗敮涓鐨勬暟鎹氦浜掗儴鍒嗭紝榪欐牱娓呮櫚鐨勬灦鏋勪嬌寰楁洿鏂逛究榪涜浠g爜鑷祴銆?/p> What problems did we still have? 鎴戜滑榪樻湁浠涔堥棶棰橈紵 Integrating Model View Presenter 鍓嶅嚑騫村紑濮嬶紝寰堝綾諱技MVP涓嶮VVM鍦ˋndroid鐨勪竴浜涚ぞ鍖烘瘮杈冩祦琛岋紝緇忚繃鐮旂┒涔嬪悗錛屾垜浠彂鐜癕VP妯″紡鏄鎴戜滑鐩墠鐨勬柟妗堟渶鏈変環鍊肩殑鏀瑰姩銆傛垜浠殑涓ゅ眰鏋舵瀯View-Data涓嶮VP鐨?Model-View鏋舵瀯澶╃劧铻嶅悎錛岀悊蹇典竴鑷淬傛垜浠彧闇瑕佸鍔犱竴涓猵resenters灞傦紝鐒跺悗鎶婁箣鍓嶅湪view瀹炵幇鐨勪唬鐮佺Щ鍒頒笂闈㈠氨鍙互浜嗐?br style="padding: 0px; margin: 0px;" /> The data layer remains as it was but it’s now called model to be more consistent with the name of the pattern. 涔嬪墠鐨凞ata灞傚氨鏄幇鍦ㄧ殑MVP涓殑Model錛孭resenter鐜板湪璐熻矗浠嶮odel涓姞杞芥暟鎹紝鍔犺澆瀹屾垚鍚庡悗鍐嶅幓璋冪敤宸﹁竟鐨勫湪Activity銆乂iewGroup涓殑鏂規硶銆侾resenters鐨剆ubscribe鍘繪帴鏀禿ata manager涓殑Observables騫挎挱鍑烘潵鐨勬暟鎹?br style="padding: 0px; margin: 0px;" />涓句緥璇存槑錛屽鏋滄垜浠渶瑕佸鍔犳暟鎹殑榪囨護鎿嶄綔浣嗘槸騫朵笉鏄墍鏈夊湴鏂歸兘闇瑕佺殑閭g錛岄偅灝卞彲浠ュ湪presenter閲岄潰鍐欒繖浜涗唬鐮侊紝鑰屼笉鐢ㄥ啓鍦ㄥ叕鍏辯殑datamanager閲岄潰銆?/p> Below you can see what a public method in the presenter would look like. This code subscribes to the Observable returned by the dataManager.loadTodayPosts() method we defined in the previous section. 鎴戜滑瀹氫箟鐨刣ataManager.loadTodayPosts()浼氬箍鎾嚭鏁版嵁緇欏埌瀵瑰簲鐨剆ubscribes The mMvpView is the view component that this presenter is assisting. Usually the MVP view is an instance of an Activity, Fragment or ViewGroup. MVP鐨刅iew騫朵笉鏄寚鐨凙ndroid鐨刅iew錛岃屾槸涓涓晫闈㈢粍浠剁殑鐨勫疄渚嬶紝渚嬪Activity, Fragment 錛?ViewGroup 鍦ㄦ敞鍐宲resenter鐨勬椂鍊欙紝闇瑕佹妸鑷繁褰撳墠鐨勫疄渚嬩紶閫掕繘鍘匯?/p> Like the previous architecture, the view layer contains standard framework components like ViewGroups, Fragments or Activities. The main difference is that these components don’t subscribe directly to Observables. They instead implement an MvpView interface and provide a list of concise methods such as showError() or showProgressIndicator(). The view components are also in charge of handling user interactions such as click events and act accordingly by calling the right method in the presenter. For example, if we have a button that loads the list of posts, our Activity would call presenter.loadTodayPosts() from the onClick listener. 榪欎釜鏋舵瀯涓庝笂涓涓灦鏋勪笉鍚岀殑鏄紝ViewLayer 涔熷氨鏄疉ctivity榪欎簺錛屼笉浼氱洿鎺ュ幓璁㈤槄鎺ユ敹Observables鍙戝嚭鐨勮繖浜涗簨浠躲傝屾槸鍙湪Activity瀹炵幇鍑犱釜綆鍗曠殑鏄劇ず閿欒銆佹樉紺鴻繘搴︾殑鏂規硶錛堢敤鎺ュ彛interface鏉ヨ鑼冪粺涓錛夛紝鐒跺悗鎶婂綋鍓嶅疄渚嬩互鍙傛暟褰㈠紡浼犻掔粰鍒板搴斾簨浠剁殑Presenter錛岀敱Presenter鍘繪墽琛岃繖浜涙樉紺洪敊璇佹樉紺鴻繘搴︾殑鏂規硶銆?br style="padding: 0px; margin: 0px;" />褰撶劧瀵逛簬鐢ㄦ埛浜や簰閮ㄥ垎鐨勬寜閽偣鍑諱簨浠惰繕鏄鍦ˋctivity涓繘琛屽鐞嗐?/p> If you want to see a full working sample of this MVP-based architecture, you can check out our Android Boilerplate project on GitHub. You can also read more about it in the ribot’s architecture guidelines. 鍏充簬MVP鐨勬枃绔犲彲浠ヨ嚜琛岀櫨搴︿竴涓嬶紝 Why is this approach better? 涓轟粈涔堣繖涓張鏈鍚?/strong> What problems do we still have? Having a single data manager can still be an issue when the codebase becomes very large and complex. We haven’t reached the point where this is a real problem but we are aware that it could happen. 鍙湁涓涓狣ataManager浠嶆棫鏄竴涓棶棰橈紝灝ゅ叾鏄綋浠g爜欏圭洰姣旇緝搴炲ぇ鐨勬椂鍊欙紝褰撶劧鎴戜滑榪樻病鏈夊埌杈捐繖涓簽澶х殑鍦版錛屽敖綆℃垜浠煡閬撹繖涓皢鏉ユ煇澶╀細鍙戠敓銆?/p> It’s important to mention that this is not the perfect architecture. In fact, it’d be naive to think there is a unique and perfect one that will solve all your problems forever. The Android ecosystem will keep evolving at a fast pace and we have to keep up by exploring, reading and experimenting so that we can find better ways to continue building excellent Android apps. 濡傛灉鎯蟲湁涓畬緹庣殑鏋舵瀯瑙e喅浣犳墍鏈夐棶棰樻槸涓嶅彲鑳界殑銆俆MD Android鐨勬暣涓敓鎬佸湀鍙樺寲澶揩錛屽張TM鐨勪笉鏍囧噯錛屽氨瀵艱嚧鎴戜滑涓嶆柇鐨勫幓鎺㈢儲鎺㈢儲銆傘傘備互鑷翠簬鍘繪壘鍒版洿鍚婄殑鏂規硶鍘誨仛Android apps銆?/p> I hope you enjoyed this article and you found it useful. If so, don’t forget to click the recommend button. Also, I’d love to hear your thoughts about our latest approach. 甯屾湜璇諱簡涔嬪悗瀵規垜浠殑鏈鏂拌В鍐蟲柟妗堣兘鏈変簺寤鴻鎯蟲硶銆?/p> 銆愭湰鏂囩炕璇戠殑鐩殑鏄湪闂叉殗鏃墮棿錛岀爺絀舵柊鎶鏈紝鐢ㄩ氫織鎶鏈璦鍐欑粰鑷繁鐪嬶紝渚夸簬鏃ュ悗鏂逛究鏌ラ槄涓虹洰銆?br style="padding: 0px; margin: 0px;" />鍘熸枃錛?a style="padding: 0px; margin: 0px; color: #ff8373; outline: 0px; font-size: 12px;">https://medium.com/ribot-labs/android-application-architecture-8b6e34acda65瀹夊崜APP鏋舵瀯
鏃х殑搴旂敤鏋舵瀯
Back in 2012 our codebases used to follow a basic structure. We didn’t use any networking library and AsyncTasks were still our friends. The diagram below shows approximately how the architecture was.>The code was structured in two layers: the data layer that was in charge of retrieving/saving data from REST APIs and persistent data stores; and the view layer, whose responsibility was handling and displaying the data on the UI.
The APIProvider provides methods to enable Activities and Fragments to easily interact with the REST API. These methods use URLConnection and AsyncTasks to perform network calls in a separate thread and return the result to the Activities via callbacks.
The main issue with this approach was that the View layer had too many responsibilities. Imagine a simple common scenario where the application has to load a list of blog posts, cache them in a SQLite database and finally display them on a ListView. The Activity would have to do the following:
Activities and Fragments become very large and difficult to maintain
Too many nested callbacks means the code is ugly and difficult to understand so painful to make changes or add new features.
Unit testing becomes challenging, if not impossible, because a lot of the logic lives within the Activities or Fragments that are arduous to unit test.鐗涢肩殑鏂版灦鏋勫嚭鏉ヤ簡
We followed the previous approach for about two years. During that time, we made several improvements that slightly mitigated the problems described above. For example, we added several helper classes to reduce the code in Activities and Fragments and we started using Volley in the APIProvider. Despite these changes, our application code wasn’t yet test-friendly and the callback hell issue was still happening too often.
The DataManager is the brain of the architecture. It extensively uses RxJava operators to combine, filter and transform data retrieved from helper classes. The aim of the DataManager is to reduce the amount of work that Activities and Fragments have to do by providing data that is ready to display and won’t usually need any transformation.
RxJava Observables and operators remove the need for having nested callbacks.
Moving code from Activities and Fragments to the DataManager and helpers means that writing unit tests becomes easier.
For large and very complex projects the DataManager can become too bloated and difficult to maintain.
Although view layer components such as Activities and Fragments became more lightweight, they still have to handle a considerable amount of logic around managing RxJava subscriptions, analysing errors, etc.
- 濡傛灉瀵逛簬闈炲父搴炲ぇ騫朵笖澶嶆潅鐨勯」鐩潵璇達紝DataManger涔熶細鍙樺緱闈炲父鑷冭偪騫朵笖闅句互緇存姢銆?br style="padding: 0px; margin: 0px;" />- 灝界Activity涓嶧ragment宸茬粡鍙樺緱鏇村姞杞婚噺綰э紝浣嗘槸瀵逛簬閿欒寮傚父鐨勫鐞嗚繕鏄鍦╯ubscriptions鐨勫湴鏂瑰幓涔﹀啓銆?/p>涓浣撳寲鐨凪VP妯″紡
In the past year, several architectural patterns such as MVP or MVVM have been gaining popularity within the Android community. After exploring these patterns on a sample project and article, we found that MVP could bring very valuable improvements to our existing approach. Because our current architecture was divided in two layers (view and data), adding MVP felt natural. We simply had to add a new layer of presenters and move part of the code from the view to presenters.
Presenters are in charge of loading data from the model and calling the right method in the view when the result is ready. They subscribe to Observables returned by the data manager. Therefore, they have to handle things like schedulers and subscriptions. Moreover, they can analyse error codes or apply extra operations to the data stream if needed. For example, if we need to filter some data and this same filter is not likely to be reused anywhere else, it may make more sense to implement it in the presenter rather than in the data manager.// Activity onCreate 涓殑浠g爜孌? if (presenter == null) presenter = new Presenter1(); presenter.onTakeView(this);
MVP Android
鍏抽敭璇?/p>
鐜板湪榪樻湁閬楃暀浠涔堥棶棰?/strong>
MVP浠嬬粛錛?a style="padding: 0px; margin: 0px; color: #ff8373; outline: 0px; font-size: 12px;">http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0425/2782.html
RxAndroid:https://github.com/ReactiveX/RxAndroid
Eventbus:https://github.com/greenrobot/EventBus
]]>
欏圭洰鍦板潃 https://github.com/smanikandan14/Volley-demo
(1) JSON錛屽浘鍍忕瓑鐨勫紓姝ヤ笅杞斤紱
(2) 緗戠粶璇鋒眰鐨勬帓搴忥紙scheduling錛?br /> (3) 緗戠粶璇鋒眰鐨勪紭鍏堢駭澶勭悊
(4) 緙撳瓨
(5) 澶氱駭鍒彇娑堣姹?br /> (6) 鍜孉ctivity鍜岀敓鍛藉懆鏈熺殑鑱斿姩錛圓ctivity緇撴潫鏃跺悓鏃跺彇娑堟墍鏈夌綉緇滆姹傦級
2銆乤ndroid-async-http
欏圭洰鍦板潃錛歨ttps://github.com/loopj/android-async-http
鏂囨。浠嬬粛錛歨ttp://loopj.com/android-async-http/
(1) 鍦ㄥ尶鍚嶅洖璋冧腑澶勭悊璇鋒眰緇撴灉
(2) 鍦║I綰跨▼澶栬繘琛宧ttp璇鋒眰
(3) 鏂囦歡鏂偣涓婁紶
(4) 鏅鴻兘閲嶈瘯
(5) 榛樿gzip鍘嬬緝
(6) 鏀寔瑙f瀽鎴怞son鏍煎紡
(7) 鍙皢Cookies鎸佷箙鍖栧埌SharedPreferences
3銆丄final妗嗘灦
欏圭洰鍦板潃錛歨ttps://github.com/yangfuhai/afinal
涓昏鏈夊洓澶фā鍧楋細
(1) 鏁版嵁搴撴ā鍧楋細android涓殑orm妗嗘灦錛屼嬌鐢ㄤ簡綰跨▼姹犲sqlite榪涜鎿嶄綔銆?br /> (2) 娉ㄨВ妯″潡錛歛ndroid涓殑ioc妗嗘灦錛屽畬鍏ㄦ敞瑙f柟寮忓氨鍙互榪涜UI緇戝畾鍜屼簨浠剁粦瀹氥傛棤闇findViewById鍜宻etClickListener絳夈?br /> (3) 緗戠粶妯″潡錛氶氳繃httpclient榪涜灝佽http鏁版嵁璇鋒眰錛屾敮鎸乤jax鏂瑰紡鍔犺澆錛屾敮鎸佷笅杞姐佷笂浼犳枃浠跺姛鑳姐?br /> (4) 鍥劇墖緙撳瓨妯″潡錛氶氳繃FinalBitmap錛宨mageview鍔犺澆bitmap鐨勬椂鍊欐棤闇鑰冭檻bitmap鍔犺澆榪囩▼涓嚭鐜扮殑oom鍜宎ndroid瀹瑰櫒蹇熸粦鍔ㄦ椂鍊欏嚭鐜扮殑鍥劇墖閿欎綅絳夌幇璞°?br /> FinalBitmap鍙互閰嶇疆綰跨▼鍔犺澆綰跨▼鏁伴噺錛岀紦瀛樺ぇ灝忥紝緙撳瓨璺緞錛屽姞杞芥樉紺哄姩鐢葷瓑銆侳inalBitmap鐨勫唴瀛樼鐞嗕嬌鐢╨ru綆楁硶錛?br /> 娌℃湁浣跨敤寮卞紩鐢紙android2.3浠ュ悗google宸茬粡涓嶅緩璁嬌鐢ㄥ急寮曠敤錛宎ndroid2.3鍚庡己琛屽洖鏀惰蔣寮曠敤鍜屽急寮曠敤錛岃鎯呮煡鐪媋ndroid瀹樻柟鏂囨。錛夛紝
鏇村ソ鐨勭鐞哹itmap鍐呭瓨銆侳inalBitmap鍙互鑷畾涔変笅杞藉櫒錛岀敤鏉ユ墿灞曞叾浠栧崗璁樉紺虹綉緇滃浘鐗囷紝姣斿ftp絳夈傚悓鏃跺彲浠ヨ嚜瀹氫箟bitmap鏄劇ず鍣紝
鍦╥mageview鏄劇ず鍥劇墖鐨勬椂鍊欐挱鏀懼姩鐢葷瓑錛堥粯璁ゆ槸娓愬彉鍔ㄧ敾鏄劇ず錛夈?br />
4銆亁Utils妗嗘灦
欏圭洰鍦板潃錛歨ttps://github.com/wyouflf/xUtils
涓昏鏈夊洓澶фā鍧楋細
(1) 鏁版嵁搴撴ā鍧楋細android涓殑orm妗嗘灦錛屼竴琛屼唬鐮佸氨鍙互榪涜澧炲垹鏀規煡錛?br /> 鏀寔浜嬪姟錛岄粯璁ゅ叧闂紱
鍙氳繃娉ㄨВ鑷畾涔夎〃鍚嶏紝鍒楀悕錛屽閿紝鍞竴鎬х害鏉燂紝NOT NULL綰︽潫錛孋HECK綰︽潫絳夛紙闇瑕佹販娣嗙殑鏃跺欒娉ㄨВ琛ㄥ悕鍜屽垪鍚嶏級錛?br /> 鏀寔緇戝畾澶栭敭錛屼繚瀛樺疄浣撴椂澶栭敭鍏寵仈瀹炰綋鑷姩淇濆瓨鎴栨洿鏂幫紱
鑷姩鍔犺澆澶栭敭鍏寵仈瀹炰綋錛屾敮鎸佸歡鏃跺姞杞斤紱
鏀寔閾懼紡琛ㄨ揪鏌ヨ錛屾洿鐩磋鐨勬煡璇㈣涔夛紝鍙傝冧笅闈㈢殑浠嬬粛鎴杝ample涓殑渚嬪瓙銆?
(2) 娉ㄨВ妯″潡錛歛ndroid涓殑ioc妗嗘灦錛屽畬鍏ㄦ敞瑙f柟寮忓氨鍙互榪涜UI錛岃祫婧愬拰浜嬩歡緇戝畾錛?br /> 鏂扮殑浜嬩歡緇戝畾鏂瑰紡錛屼嬌鐢ㄦ販娣嗗伐鍏鋒販娣嗗悗浠嶅彲姝e父宸ヤ綔錛?br /> 鐩墠鏀寔甯哥敤鐨?0縐嶄簨浠剁粦瀹氾紝鍙傝ViewCommonEventListener綾誨拰鍖卌om.lidroid.xutils.view.annotation.event銆?br /> (3) 緗戠粶妯″潡錛氭敮鎸佸悓姝ワ紝寮傛鏂瑰紡鐨勮姹傦紱
鏀寔澶ф枃浠朵笂浼狅紝涓婁紶澶ф枃浠朵笉浼歰om錛?br /> 鏀寔GET錛孭OST錛孭UT錛孧OVE錛孋OPY錛孌ELETE錛孒EAD錛孫PTIONS錛孴RACE錛孋ONNECT璇鋒眰錛?br /> 涓嬭澆鏀寔301/302閲嶅畾鍚戯紝鏀寔璁劇疆鏄惁鏍規嵁Content-Disposition閲嶅懡鍚嶄笅杞界殑鏂囦歡錛?br /> 榪斿洖鏂囨湰鍐呭鐨勮姹?榛樿鍙惎鐢ㄤ簡GET璇鋒眰)鏀寔緙撳瓨錛屽彲璁劇疆榛樿榪囨湡鏃墮棿鍜岄拡瀵瑰綋鍓嶈姹傜殑榪囨湡鏃墮棿銆?
(4) 鍥劇墖緙撳瓨妯″潡錛氬姞杞絙itmap鐨勬椂鍊欐棤闇鑰冭檻bitmap鍔犺澆榪囩▼涓嚭鐜扮殑oom鍜宎ndroid瀹瑰櫒蹇熸粦鍔ㄦ椂鍊欏嚭鐜扮殑鍥劇墖閿欎綅絳夌幇璞★紱
鏀寔鍔犺澆緗戠粶鍥劇墖鍜屾湰鍦板浘鐗囷紱
鍐呭瓨綆$悊浣跨敤lru綆楁硶錛屾洿濂界殑綆$悊bitmap鍐呭瓨錛?br /> 鍙厤緗嚎紼嬪姞杞界嚎紼嬫暟閲忥紝緙撳瓨澶у皬錛岀紦瀛樿礬寰勶紝鍔犺澆鏄劇ず鍔ㄧ敾絳?..
5銆乀hinkAndroid
欏圭洰鍦板潃錛歨ttps://github.com/white-cat/ThinkAndroid
涓昏鏈変互涓嬫ā鍧楋細
(1) MVC妯″潡錛氬疄鐜拌鍥句笌妯″瀷鐨勫垎紱匯?br /> (2) ioc妯″潡錛歛ndroid涓殑ioc妯″潡錛屽畬鍏ㄦ敞瑙f柟寮忓氨鍙互榪涜UI緇戝畾銆乺es涓殑璧勬簮鐨勮鍙栥佷互鍙婂璞$殑鍒濆鍖栥?
(3) 鏁版嵁搴撴ā鍧楋細android涓殑orm妗嗘灦錛屼嬌鐢ㄤ簡綰跨▼姹犲sqlite榪涜鎿嶄綔銆?
(4) http妯″潡錛氶氳繃httpclient榪涜灝佽http鏁版嵁璇鋒眰錛屾敮鎸佸紓姝ュ強鍚屾鏂瑰紡鍔犺澆銆?br /> (5) 緙撳瓨妯″潡錛氶氳繃綆鍗曠殑閰嶇疆鍙婅璁″彲浠ュ緢濂界殑瀹炵幇緙撳瓨錛屽緙撳瓨鍙互闅忔剰鐨勯厤緗?br /> (6) 鍥劇墖緙撳瓨妯″潡錛歩mageview鍔犺澆鍥劇墖鐨勬椂鍊欐棤闇鑰冭檻鍥劇墖鍔犺澆榪囩▼涓嚭鐜扮殑oom鍜宎ndroid瀹瑰櫒蹇熸粦鍔ㄦ椂鍊欏嚭鐜扮殑鍥劇墖閿欎綅絳夌幇璞°?br /> (7) 閰嶇疆鍣ㄦā鍧楋細鍙互瀵圭畝鏄撶殑瀹炵幇閰嶅閰嶇疆鐨勬搷浣滐紝鐩墠閰嶇疆鏂囦歡鍙互鏀寔Preference銆丳roperties瀵歸厤緗繘琛屽瓨鍙栥?br /> (8) 鏃ュ織鎵撳嵃妯″潡錛氬彲浠ヨ緝蹇殑杞繪槗鐨勬槸瀹炵幇鏃ュ織鎵撳嵃錛屾敮鎸佹棩蹇楁墦鍗扮殑鎵╁睍錛岀洰鍓嶆敮鎸佸sdcard鍐欏叆鏈湴鎵撳嵃銆佷互鍙婃帶鍒跺彴鎵撳嵃
(9) 涓嬭澆鍣ㄦā鍧?鍙互綆鍗曠殑瀹炵幇澶氱嚎紼嬩笅杞姐佸悗鍙頒笅杞姐佹柇鐐圭畫浼犮佸涓嬭澆榪涜鎺у埗銆佸寮濮嬨佹殏鍋溿佸垹闄ょ瓑絳夈?br /> (10) 緗戠粶鐘舵佹嫻嬫ā鍧楋細褰撶綉緇滅姸鎬佹敼鍙樻椂錛屽鍏惰繘琛屾
6銆丩oonAndroid
欏圭洰鍦板潃錛歨ttps://github.com/gdpancheng/LoonAndroid
涓昏鏈変互涓嬫ā鍧楋細
(1) 鑷姩娉ㄥ叆妗嗘灦錛堝彧闇瑕佺戶鎵挎鏋跺唴鐨刟pplication鏃㈠彲錛?br /> (2) 鍥劇墖鍔犺澆妗嗘灦錛堝閲嶇紦瀛橈紝鑷姩鍥炴敹錛屾渶澶ч檺搴︿繚璇佸唴瀛樼殑瀹夊叏鎬э級
(3) 緗戠粶璇鋒眰妯″潡錛堢戶鎵夸簡鍩烘湰涓婄幇鍦ㄦ墍鏈夌殑http璇鋒眰錛?br /> (4) eventbus錛堥泦鎴愪竴涓紑婧愮殑妗嗘灦錛?br /> (5) 楠岃瘉妗嗘灦錛堥泦鎴愬紑婧愭鏋訛級
(6) json瑙f瀽錛堟敮鎸佽В鏋愭垚闆嗗悎鎴栬呭璞★級
(7) 鏁版嵁搴擄紙涓嶇煡閬撴槸鍝綅鍐欑殑 蹇樿浜嗭級
(8) 澶氱嚎紼嬫柇鐐逛笅杞斤紙鑷姩鍒ゆ柇鏄惁鏀寔澶氱嚎紼嬶紝鍒ゆ柇鏄惁鏄噸瀹氬悜錛?br /> (9) 鑷姩鏇存柊妯″潡
(10) 涓緋誨垪宸ュ叿綾?br />
鍏朵腑鐨?volley 錛?3 騫存湁鐮旂┒榪囷紝鎵╁睍鎬ч潪甯稿ソ錛屼釜浜烘瘮杈冨枩嬈㈢殑椋庢牸銆傚叾浠栧 android-async-http銆丄final 涔熺浉褰撲笉閿欍?img src ="http://m.tkk7.com/paulwong/aggbug/415632.html" width = "1" height = "1" />
]]>
鐩墠鍖呮嫭錛?/span>
Android寮婧愰」鐩涓綃?#8212;—涓у寲鎺т歡(View)綃?/a>
鍖呮嫭ListView銆丄ctionBar銆丮enu銆乂iewPager銆丟allery銆丟ridView銆両mageView銆丳rogressBar絳夌瓑
Android寮婧愰」鐩浜岀瘒——宸ュ叿搴撶瘒
鍖呮嫭渚濊禆娉ㄥ叆妗嗘灦銆佸浘鐗囩紦瀛樸佺綉緇滅浉鍏熾佹暟鎹簱ORM寤烘ā銆丄ndroid鍏叡搴撱侀珮鐗堟湰鍚戜綆鐗堟湰鍏煎搴撱佸濯掍綋絳夌瓑
Android寮婧愰」鐩涓夌瘒——浼樼欏圭洰綃?/a>
姣旇緝鏈夋剰鎬濈殑瀹屾暣鐨凙ndroid欏圭洰
Android寮婧愰」鐩鍥涚瘒——寮鍙戝強嫻嬭瘯宸ュ叿綃?/a>
鍖呮嫭寮鍙戣嚜嫻嬨佽嚜鍔ㄥ寲嫻嬭瘯銆佺紪璇戞墦鍖呯浉鍏沖伐鍏?/em>
Android寮婧愰」鐩浜旂瘒——浼樼涓漢鍜屽洟浣撶瘒
涔愪簬鍒嗕韓騫朵笖鏈変竴浜涘緢涓嶉敊鐨勫紑婧愰」鐩殑涓漢鍜岀粍緇囷紝鍖呮嫭JakeWharton銆丆hris Banes銆並oushik Dutta絳夊ぇ鐗?/em>