锘??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲成色在线影院,亚洲理论片中文字幕电影,亚洲日韩中文字幕天堂不卡http://m.tkk7.com/paulwong/category/51708.htmlzh-cnSat, 15 Jan 2022 13:31:34 GMTSat, 15 Jan 2022 13:31:34 GMT60Android Application Architecture 瀹夊崜APP鏋舵瀯[璇慮http://m.tkk7.com/paulwong/archive/2015/12/18/428718.htmlpaulwongpaulwongFri, 18 Dec 2015 05:07:00 GMThttp://m.tkk7.com/paulwong/archive/2015/12/18/428718.htmlhttp://m.tkk7.com/paulwong/comments/428718.htmlhttp://m.tkk7.com/paulwong/archive/2015/12/18/428718.html#Feedback0http://m.tkk7.com/paulwong/comments/commentRss/428718.htmlhttp://m.tkk7.com/paulwong/services/trackbacks/428718.html搴?/h2>

鏈枃浠嬬粛浜嗘枃绔犱綔鑰呬粠浜嬩簡鍑犲勾android搴旂敤鐨勫紑鍙戯紝緇忓巻2嬈℃灦鏋勫彉闈╋紝絎竴嬈¢泦鎴愪簡RxJava絎簩嬈¢泦鎴愪簡MVP錛屽茍灝哛xJava涓嶮VP瀹岀編緇撳悎錛屽疄鐜頒簡浣庤﹀悎錛屼唬鐮佺畝鍗曪紝嫻嬭瘯鏂逛究鐨勬灦鏋勩?/p>

鍏跺疄鎴戜滑鍦ㄥ紑鍙戜腑涔熼亣鍒拌繃錛孉ndroid鍏ラ棬闂ㄦ杈冧綆錛屽鏋滃墠鏈熷APP瑙勫垝涓嶆竻鏅幫紝Coder浠鏈潵鍙樺寲鎶婃彙涓嶅噯錛屾妧鏈灦鏋勭粡楠屼笉澶熷己澶э紝鏈緇堝鑷村氨鏄竴涓狝ctivity鍑犲崈琛岋紝閲岄潰鍐欎簡澶ч噺鐨凱rivate鏂規硶錛屾媶鎴愬嚑涓狥ragment銆佸皝瑁呭嚭鏉ュ嚑涓被閮芥槸鏃犳硶瑙e喅錛岀粨鏋滃氨鏄湅Activity闅懼彈鐨勮姝伙紝綰犵粨錛岀湅浜嗕笉鐖芥敼涔熶笉鏄笉鏀逛篃涓嶆槸錛屼弗閲嶅獎鍝嶇湅鐨勪漢鐨勫績鎯呫傚茍涓旀ㄥぉ灝や漢榪欎釜鏄駭鍝佷漢鍛樿鍒扐pp涓嶅ソ錛屾病鏈夊墠鐬繪э紝鏀規潵鏀瑰幓銆傘傘?/p>

榪欑瘒鏂囩珷灝辨槸浣跨敤鏂扮殑緇撴瀯瑙e喅璇ラ棶棰樸?/p>

瀹夊崜APP鏋舵瀯

Android Application Architecture

Our journey from standard Activities and AsyncTasks to a modern MVP-based architecture powered by RxJava.

榪欑瘒鏂囩珷涓昏鐩殑鏄榪板浣曞皢浼犵粺鐨凙ctivities 涓?AsyncTasks 妯″紡鍚戠洰鍓嶄富嫻佺殑MVP鏋舵瀯鍩虹鐨勫搷搴斿紡緙栫▼妗嗘灦榪囧害銆?/p>

1*HrE2lljEfsCu1X_OUDfHYA

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
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.

2012騫撮偅涓椂鍊欙紝鎴戜滑鐨勪唬鐮侀兘鏄敤鐨勫師鐢烝ndroid錛屾病鏈変嬌鐢ㄤ換浣曠殑緗戠粶璇鋒眰妗嗘灦錛岃屾槸鍩轟簬AsyncTasks涔﹀啓銆?br style="padding: 0px; margin: 0px;" />1*TTtpcT4H80THBofnCtQ_L>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.

浠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
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:

涓昏闂鏄疺iew灞傛湁澶鐨勭瘡璧橈紝浠ヤ竴涓崥瀹㈠垪琛ㄤ負渚嬫潵璁茶堪錛屾瘮濡傚崥瀹㈤渶瑕佹樉紺轟竴涓狶istView錛屼粠SQLite璇誨彇鏁版嵁錛孉ctivity闇瑕佸仛鍒頒互涓嬪嚑鐐癸細

  1. Call a method loadPosts(callback) in the APIProvider
  2. Wait for the APIProvider success callback and then call savePosts(callback) in the CacheProvider.
  3. Wait for the CacheProvider success callback and then display the posts on the ListView.
  4. Separately handle the two potential errors callback from the APIProvider and CacheProvider.
  1. 鎵цAPIProvider閲岄潰鐨刲oadPosts鐨勬柟娉曪紝閲岄潰浼犲叆鍥炶皟鍙傛暟鍐呭銆?/li>
  2. 絳夊緟loadPosts鎵ц鎴愬姛鍚庯紝鎵ц鍥炶皟閲岄潰鐨凜acheProvider涓殑savePosts鏂規硶錛宻avePosts涔熻浼犲叆鍥炶皟鍙傛暟銆?/li>
  3. 絳夊緟savePosts鎵ц鎴愬姛鍚庯紝鎵ц鍥炶皟閲岄潰鐨勬柟娉曞埛鏂癓istView
  4. 鍒嗗埆涔﹀啓浠g爜澶勭悊2 3 涓ゆ鐨勯敊璇洖璋冨唴瀹廣?/li>

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:
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.

鎬諱箣錛屽洖璋冨浜嗕箣鍚庯紝Activity涓嶧ragment浼氫貢鐨勮姝伙紝騫朵笖涓鑸漢鏃犳硶鐩磋銆?/p>

鐗涢肩殑鏂版灦鏋勫嚭鏉ヤ簡

A new architecture driven by RxJava
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.

鎴戜滑鍦ㄨ泲鐤肩殑鏋舵瀯涓厧鐔簡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鐨勬灦鏋勫浘

1*kCynNIa5PscRl41V2scosA-200

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錛岃闂暟鎹簱絳夛紝铏界劧涓嶅悓鐨勫簲鐢ㄧ▼搴忛兘鏈変笉鍚岀殑綾誨簱錛屼絾鏄粬浠棤闈炲氨鏄互涓嬭繖浜涘唴瀹癸細

  • PreferencesHelper: reads and saves data in SharedPreferences.
  • DatabaseHelper: handles accessing SQLite databases.
  • Retrofit services: perform calls to REST APIs. We started using Retrofit instead of Volley because it provides support for RxJava. It’s also nicer to use.
  • 浠嶴haredPreferences涓鍙栨垨鑰呭啓鍏ユ暟鎹?/li>
  • 璇誨啓SQLite鏁版嵁搴?/li>
  • 綾諱技涓巗quare鐨凴etrofit鏈嶅姟錛屼篃灝辨槸Http Client,鎴戜滑鐢≧estrofit鏇夸唬浜哣olley鍥犱負浠栨敮鎸丷xjava,騫朵笖鏇村悐銆?/li>

Most of the public methods inside helper classes will return RxJava Observables.
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錛堣瑙傚療鑰咃紝浜嬩歡婧愶級鍜孲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鏄仛浠涔堢殑:

  1. Call the Retrofit service to load a list of blog posts from a REST API
  2. Save the posts in a local database for caching purposes using the DatabaseHelper.
  3. Filter the blog posts written today because those are the only ones the view layer wants to display.
  1. 璋冪敤Retrofit鐨勬湇鍔★紝鍘昏姹備竴涓崥瀹㈠垪琛ㄧ殑API
  2. 鐢―atabaseHelper淇濆瓨榪欎簺鏁版嵁鍒版暟鎹簱
  3. 榪囨護鍑鴻繖浜汢LOG鍝簺鏄粖澶╁啓鐨勶紝鐒跺悗鏄劇ず鍒癠I鐣岄潰涓娿?/li>

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?
RxJava Observables and operators remove the need for having nested callbacks.
1*BIsOCzJnc-SSU8fPXTiP1A

涓轟粈涔堣繖涓柟寮忚繖涔堢墰閫鹼紝鏄洜涓篛bservables涓巓perators鍙互鍘繪帀閭d竴鍫嗗繀欏葷殑鍥炶皟鏂規硶

The DataManager takes over responsibilities that were previously part of the view layer. Hence, it makes Activities and Fragments more lightweight.
Moving code from Activities and Fragments to the DataManager and helpers means that writing unit tests becomes easier.

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?
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妯″紡

Integrating Model View Presenter
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.

鍓嶅嚑騫村紑濮嬶紝寰堝綾諱技MVP涓嶮VVM鍦ˋndroid鐨勪竴浜涚ぞ鍖烘瘮杈冩祦琛岋紝緇忚繃鐮旂┒涔嬪悗錛屾垜浠彂鐜癕VP妯″紡鏄鎴戜滑鐩墠鐨勬柟妗堟渶鏈変環鍊肩殑鏀瑰姩銆傛垜浠殑涓ゅ眰鏋舵瀯View-Data涓嶮VP鐨?Model-View鏋舵瀯澶╃劧铻嶅悎錛岀悊蹇典竴鑷淬傛垜浠彧闇瑕佸鍔犱竴涓猵resenters灞傦紝鐒跺悗鎶婁箣鍓嶅湪view瀹炵幇鐨勪唬鐮佺Щ鍒頒笂闈㈠氨鍙互浜嗐?br style="padding: 0px; margin: 0px;" />1*NonRJ0uzzN9o1ygT6J421g

The data layer remains as it was but it’s now called model to be more consistent with the name of the pattern.
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.

涔嬪墠鐨凞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>

// Activity onCreate 涓殑浠g爜孌? if (presenter == null)         presenter = new Presenter1();         presenter.onTakeView(this);  

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鐨勬枃绔犲彲浠ヨ嚜琛岀櫨搴︿竴涓嬶紝MVP Android 鍏抽敭璇?/p>

Why is this approach better?

涓轟粈涔堣繖涓張鏈鍚?/strong>

  • Activities and Fragments become very lightweight. Their only responsibilities are to set up/update the UI and handle user events. Therefore, they become easier to maintain.
  • We can now easily write unit tests for the presenters by mocking the view layer. Before, this code was part of the view layer so we couldn’t unit test it. The whole architecture becomes very test-friendly.
  • If the data manager is becoming bloated, we can mitigate this problem by moving some code to the presenters.
  • Activity涓嶧ragment浠g爜閲忓ぇ澶ч檷浣庯紝閫昏緫浠g爜鍏ㄩ儴閮戒涪緇欎簡Presenter,緇撴灉灝辨槸Activity鍙渶瑕佽礋璐I浜や簰鐨勬寜閽瓑浠g爜銆?/li>
  • 瀵逛簬Presenter鍙互鍐欏崟鐙殑鍗曞厓嫻嬭瘯浠g爜錛屽彧闇瑕佸Presenter鎻愪緵鐨勬柟娉曟祴璇曞嵆鍙?/li>
  • 濡傛灉DataManager鍙樺緱鑷冭偪搴炲ぇ浜嗭紝鎴戜滑鍙互鍒嗙榪欎簺浠g爜鍒板悇鑷殑Presenter涓幓銆?/li>

What problems do we still have?
鐜板湪榪樻湁閬楃暀浠涔堥棶棰?/strong>

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
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



paulwong 2015-12-18 13:07 鍙戣〃璇勮
]]>
鍏鍊煎緱鎺ㄨ崘鐨刟ndroid錛堝畨鍗擄級寮婧愭鏋剁畝浠嬨愯漿銆?/title><link>http://m.tkk7.com/paulwong/archive/2014/07/09/415632.html</link><dc:creator>paulwong</dc:creator><author>paulwong</author><pubDate>Wed, 09 Jul 2014 08:56:00 GMT</pubDate><guid>http://m.tkk7.com/paulwong/archive/2014/07/09/415632.html</guid><wfw:comment>http://m.tkk7.com/paulwong/comments/415632.html</wfw:comment><comments>http://m.tkk7.com/paulwong/archive/2014/07/09/415632.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.tkk7.com/paulwong/comments/commentRss/415632.html</wfw:commentRss><trackback:ping>http://m.tkk7.com/paulwong/services/trackbacks/415632.html</trackback:ping><description><![CDATA[<span style="color: #0000ff;">1銆乿olley </span><br /><br />欏圭洰鍦板潃 https://github.com/smanikandan14/Volley-demo<br /> (1) JSON錛屽浘鍍忕瓑鐨勫紓姝ヤ笅杞斤紱<br /> (2) 緗戠粶璇鋒眰鐨勬帓搴忥紙scheduling錛?br /> (3) 緗戠粶璇鋒眰鐨勪紭鍏堢駭澶勭悊<br /> (4) 緙撳瓨<br /> (5) 澶氱駭鍒彇娑堣姹?br /> (6) 鍜孉ctivity鍜岀敓鍛藉懆鏈熺殑鑱斿姩錛圓ctivity緇撴潫鏃跺悓鏃跺彇娑堟墍鏈夌綉緇滆姹傦級<br /><br /><br /><span style="color: #0000ff;">2銆乤ndroid-async-http </span><br /><br />欏圭洰鍦板潃錛歨ttps://github.com/loopj/android-async-http<br />鏂囨。浠嬬粛錛歨ttp://loopj.com/android-async-http/ <br /> (1) 鍦ㄥ尶鍚嶅洖璋冧腑澶勭悊璇鋒眰緇撴灉<br /> (2) 鍦║I綰跨▼澶栬繘琛宧ttp璇鋒眰<br /> (3) 鏂囦歡鏂偣涓婁紶<br /> (4) 鏅鴻兘閲嶈瘯<br /> (5) 榛樿gzip鍘嬬緝<br /> (6) 鏀寔瑙f瀽鎴怞son鏍煎紡<br /> (7) 鍙皢Cookies鎸佷箙鍖栧埌SharedPreferences<br /><br /><br /><span style="color: #0000ff;">3銆丄final妗嗘灦</span><br /><br />欏圭洰鍦板潃錛歨ttps://github.com/yangfuhai/afinal<br />涓昏鏈夊洓澶фā鍧楋細<br /> (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瀹樻柟鏂囨。錛夛紝<br /> 鏇村ソ鐨勭鐞哹itmap鍐呭瓨銆侳inalBitmap鍙互鑷畾涔変笅杞藉櫒錛岀敤鏉ユ墿灞曞叾浠栧崗璁樉紺虹綉緇滃浘鐗囷紝姣斿ftp絳夈傚悓鏃跺彲浠ヨ嚜瀹氫箟bitmap鏄劇ず鍣紝<br /> 鍦╥mageview鏄劇ず鍥劇墖鐨勬椂鍊欐挱鏀懼姩鐢葷瓑錛堥粯璁ゆ槸娓愬彉鍔ㄧ敾鏄劇ず錛夈?br /><br /><br /><span style="color: #0000ff;">4銆亁Utils妗嗘灦</span><br /><br />欏圭洰鍦板潃錛歨ttps://github.com/wyouflf/xUtils<br />涓昏鏈夊洓澶фā鍧楋細<br /> (1) 鏁版嵁搴撴ā鍧楋細android涓殑orm妗嗘灦錛屼竴琛屼唬鐮佸氨鍙互榪涜澧炲垹鏀規煡錛?br /> 鏀寔浜嬪姟錛岄粯璁ゅ叧闂紱<br /> 鍙氳繃娉ㄨВ鑷畾涔夎〃鍚嶏紝鍒楀悕錛屽閿紝鍞竴鎬х害鏉燂紝NOT NULL綰︽潫錛孋HECK綰︽潫絳夛紙闇瑕佹販娣嗙殑鏃跺欒娉ㄨВ琛ㄥ悕鍜屽垪鍚嶏級錛?br /> 鏀寔緇戝畾澶栭敭錛屼繚瀛樺疄浣撴椂澶栭敭鍏寵仈瀹炰綋鑷姩淇濆瓨鎴栨洿鏂幫紱<br /> 鑷姩鍔犺澆澶栭敭鍏寵仈瀹炰綋錛屾敮鎸佸歡鏃跺姞杞斤紱<br /> 鏀寔閾懼紡琛ㄨ揪鏌ヨ錛屾洿鐩磋鐨勬煡璇㈣涔夛紝鍙傝冧笅闈㈢殑浠嬬粛鎴杝ample涓殑渚嬪瓙銆? <br /> (2) 娉ㄨВ妯″潡錛歛ndroid涓殑ioc妗嗘灦錛屽畬鍏ㄦ敞瑙f柟寮忓氨鍙互榪涜UI錛岃祫婧愬拰浜嬩歡緇戝畾錛?br /> 鏂扮殑浜嬩歡緇戝畾鏂瑰紡錛屼嬌鐢ㄦ販娣嗗伐鍏鋒販娣嗗悗浠嶅彲姝e父宸ヤ綔錛?br /> 鐩墠鏀寔甯哥敤鐨?0縐嶄簨浠剁粦瀹氾紝鍙傝ViewCommonEventListener綾誨拰鍖卌om.lidroid.xutils.view.annotation.event銆?br /> (3) 緗戠粶妯″潡錛氭敮鎸佸悓姝ワ紝寮傛鏂瑰紡鐨勮姹傦紱<br /> 鏀寔澶ф枃浠朵笂浼狅紝涓婁紶澶ф枃浠朵笉浼歰om錛?br /> 鏀寔GET錛孭OST錛孭UT錛孧OVE錛孋OPY錛孌ELETE錛孒EAD錛孫PTIONS錛孴RACE錛孋ONNECT璇鋒眰錛?br /> 涓嬭澆鏀寔301/302閲嶅畾鍚戯紝鏀寔璁劇疆鏄惁鏍規嵁Content-Disposition閲嶅懡鍚嶄笅杞界殑鏂囦歡錛?br /> 榪斿洖鏂囨湰鍐呭鐨勮姹?榛樿鍙惎鐢ㄤ簡GET璇鋒眰)鏀寔緙撳瓨錛屽彲璁劇疆榛樿榪囨湡鏃墮棿鍜岄拡瀵瑰綋鍓嶈姹傜殑榪囨湡鏃墮棿銆? <br /> (4) 鍥劇墖緙撳瓨妯″潡錛氬姞杞絙itmap鐨勬椂鍊欐棤闇鑰冭檻bitmap鍔犺澆榪囩▼涓嚭鐜扮殑oom鍜宎ndroid瀹瑰櫒蹇熸粦鍔ㄦ椂鍊欏嚭鐜扮殑鍥劇墖閿欎綅絳夌幇璞★紱<br /> 鏀寔鍔犺澆緗戠粶鍥劇墖鍜屾湰鍦板浘鐗囷紱<br /> 鍐呭瓨綆$悊浣跨敤lru綆楁硶錛屾洿濂界殑綆$悊bitmap鍐呭瓨錛?br /> 鍙厤緗嚎紼嬪姞杞界嚎紼嬫暟閲忥紝緙撳瓨澶у皬錛岀紦瀛樿礬寰勶紝鍔犺澆鏄劇ず鍔ㄧ敾絳?..<br /><br /><br /><span style="color: #0000ff;">5銆乀hinkAndroid</span><br /><br />欏圭洰鍦板潃錛歨ttps://github.com/white-cat/ThinkAndroid<br />涓昏鏈変互涓嬫ā鍧楋細<br /> (1) MVC妯″潡錛氬疄鐜拌鍥句笌妯″瀷鐨勫垎紱匯?br /> (2) ioc妯″潡錛歛ndroid涓殑ioc妯″潡錛屽畬鍏ㄦ敞瑙f柟寮忓氨鍙互榪涜UI緇戝畾銆乺es涓殑璧勬簮鐨勮鍙栥佷互鍙婂璞$殑鍒濆鍖栥?<br /> (3) 鏁版嵁搴撴ā鍧楋細android涓殑orm妗嗘灦錛屼嬌鐢ㄤ簡綰跨▼姹犲sqlite榪涜鎿嶄綔銆? <br /> (4) http妯″潡錛氶氳繃httpclient榪涜灝佽http鏁版嵁璇鋒眰錛屾敮鎸佸紓姝ュ強鍚屾鏂瑰紡鍔犺澆銆?br /> (5) 緙撳瓨妯″潡錛氶氳繃綆鍗曠殑閰嶇疆鍙婅璁″彲浠ュ緢濂界殑瀹炵幇緙撳瓨錛屽緙撳瓨鍙互闅忔剰鐨勯厤緗?br /> (6) 鍥劇墖緙撳瓨妯″潡錛歩mageview鍔犺澆鍥劇墖鐨勬椂鍊欐棤闇鑰冭檻鍥劇墖鍔犺澆榪囩▼涓嚭鐜扮殑oom鍜宎ndroid瀹瑰櫒蹇熸粦鍔ㄦ椂鍊欏嚭鐜扮殑鍥劇墖閿欎綅絳夌幇璞°?br /> (7) 閰嶇疆鍣ㄦā鍧楋細鍙互瀵圭畝鏄撶殑瀹炵幇閰嶅閰嶇疆鐨勬搷浣滐紝鐩墠閰嶇疆鏂囦歡鍙互鏀寔Preference銆丳roperties瀵歸厤緗繘琛屽瓨鍙栥?br /> (8) 鏃ュ織鎵撳嵃妯″潡錛氬彲浠ヨ緝蹇殑杞繪槗鐨勬槸瀹炵幇鏃ュ織鎵撳嵃錛屾敮鎸佹棩蹇楁墦鍗扮殑鎵╁睍錛岀洰鍓嶆敮鎸佸sdcard鍐欏叆鏈湴鎵撳嵃銆佷互鍙婃帶鍒跺彴鎵撳嵃<br /> (9) 涓嬭澆鍣ㄦā鍧?鍙互綆鍗曠殑瀹炵幇澶氱嚎紼嬩笅杞姐佸悗鍙頒笅杞姐佹柇鐐圭畫浼犮佸涓嬭澆榪涜鎺у埗銆佸寮濮嬨佹殏鍋溿佸垹闄ょ瓑絳夈?br /> (10) 緗戠粶鐘舵佹嫻嬫ā鍧楋細褰撶綉緇滅姸鎬佹敼鍙樻椂錛屽鍏惰繘琛屾<br /><br /><br /><span style="color: #0000ff;">6銆丩oonAndroid </span><br /><br />欏圭洰鍦板潃錛歨ttps://github.com/gdpancheng/LoonAndroid<br />涓昏鏈変互涓嬫ā鍧楋細<br /> (1) 鑷姩娉ㄥ叆妗嗘灦錛堝彧闇瑕佺戶鎵挎鏋跺唴鐨刟pplication鏃㈠彲錛?br /> (2) 鍥劇墖鍔犺澆妗嗘灦錛堝閲嶇紦瀛橈紝鑷姩鍥炴敹錛屾渶澶ч檺搴︿繚璇佸唴瀛樼殑瀹夊叏鎬э級<br /> (3) 緗戠粶璇鋒眰妯″潡錛堢戶鎵夸簡鍩烘湰涓婄幇鍦ㄦ墍鏈夌殑http璇鋒眰錛?br /> (4) eventbus錛堥泦鎴愪竴涓紑婧愮殑妗嗘灦錛?br /> (5) 楠岃瘉妗嗘灦錛堥泦鎴愬紑婧愭鏋訛級<br /> (6) json瑙f瀽錛堟敮鎸佽В鏋愭垚闆嗗悎鎴栬呭璞★級<br /> (7) 鏁版嵁搴擄紙涓嶇煡閬撴槸鍝綅鍐欑殑 蹇樿浜嗭級<br /> (8) 澶氱嚎紼嬫柇鐐逛笅杞斤紙鑷姩鍒ゆ柇鏄惁鏀寔澶氱嚎紼嬶紝鍒ゆ柇鏄惁鏄噸瀹氬悜錛?br /> (9) 鑷姩鏇存柊妯″潡<br /> (10) 涓緋誨垪宸ュ叿綾?br /><br />鍏朵腑鐨?volley 錛?3 騫存湁鐮旂┒榪囷紝鎵╁睍鎬ч潪甯稿ソ錛屼釜浜烘瘮杈冨枩嬈㈢殑椋庢牸銆傚叾浠栧 android-async-http銆丄final 涔熺浉褰撲笉閿欍?img src ="http://m.tkk7.com/paulwong/aggbug/415632.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.tkk7.com/paulwong/" target="_blank">paulwong</a> 2014-07-09 16:56 <a href="http://m.tkk7.com/paulwong/archive/2014/07/09/415632.html#Feedback" target="_blank" style="text-decoration:none;">鍙戣〃璇勮</a></div>]]></description></item><item><title>Android寮婧愰」鐩垎綾繪眹鎬?/title><link>http://m.tkk7.com/paulwong/archive/2014/01/09/408723.html</link><dc:creator>paulwong</dc:creator><author>paulwong</author><pubDate>Thu, 09 Jan 2014 03:03:00 GMT</pubDate><guid>http://m.tkk7.com/paulwong/archive/2014/01/09/408723.html</guid><wfw:comment>http://m.tkk7.com/paulwong/comments/408723.html</wfw:comment><comments>http://m.tkk7.com/paulwong/archive/2014/01/09/408723.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.tkk7.com/paulwong/comments/commentRss/408723.html</wfw:commentRss><trackback:ping>http://m.tkk7.com/paulwong/services/trackbacks/408723.html</trackback:ping><description><![CDATA[<p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #efefef;"><span style="font-size: 18px;">鍒嗙被鏁寸悊浜?50涓瘮杈冨ソ鐨凙ndroid寮婧愰」鐩紝宸叉眹鎬誨埌<a target="_blank" style="color: #006699;">AndroidOpenProject@Github</a>錛屾榪嶴tar鍜孎ork^_*</span></p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #efefef;"> </p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #efefef;"><span style="font-size: 18px;">鐩墠鍖呮嫭錛?/span></p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #efefef;"><a style="color: #4183c4; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">Android寮婧愰」鐩涓綃?#8212;—涓у寲鎺т歡(View)綃?/a><br style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;" /><em style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">  鍖呮嫭ListView銆丄ctionBar銆丮enu銆乂iewPager銆丟allery銆丟ridView銆両mageView銆丳rogressBar絳夌瓑</em><br style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;" /><a style="color: #4183c4; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">Android寮婧愰」鐩浜岀瘒——宸ュ叿搴撶瘒</a><br style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;" /><em style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">  鍖呮嫭渚濊禆娉ㄥ叆妗嗘灦銆佸浘鐗囩紦瀛樸佺綉緇滅浉鍏熾佹暟鎹簱ORM寤烘ā銆丄ndroid鍏叡搴撱侀珮鐗堟湰鍚戜綆鐗堟湰鍏煎搴撱佸濯掍綋絳夌瓑</em><br style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;" /><a style="color: #4183c4; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">Android寮婧愰」鐩涓夌瘒——浼樼欏圭洰綃?/a><br style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;" /><em style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">  姣旇緝鏈夋剰鎬濈殑瀹屾暣鐨凙ndroid欏圭洰</em><br style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;" /><a style="color: #4183c4; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">Android寮婧愰」鐩鍥涚瘒——寮鍙戝強嫻嬭瘯宸ュ叿綃?/a><br style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;" /><em style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">  鍖呮嫭寮鍙戣嚜嫻嬨佽嚜鍔ㄥ寲嫻嬭瘯銆佺紪璇戞墦鍖呯浉鍏沖伐鍏?/em><br style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;" /><a style="color: #4183c4; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">Android寮婧愰」鐩浜旂瘒——浼樼涓漢鍜屽洟浣撶瘒</a><br style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;" /><em style="color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15px; line-height: 25px;">  涔愪簬鍒嗕韓騫朵笖鏈変竴浜涘緢涓嶉敊鐨勫紑婧愰」鐩殑涓漢鍜岀粍緇囷紝鍖呮嫭JakeWharton銆丆hris Banes銆並oushik Dutta絳夊ぇ鐗?/em></p><img src ="http://m.tkk7.com/paulwong/aggbug/408723.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.tkk7.com/paulwong/" target="_blank">paulwong</a> 2014-01-09 11:03 <a href="http://m.tkk7.com/paulwong/archive/2014/01/09/408723.html#Feedback" target="_blank" style="text-decoration:none;">鍙戣〃璇勮</a></div>]]></description></item><item><title>縐誨姩璁懼鎶撳寘鏂規硶http://m.tkk7.com/paulwong/archive/2013/12/30/408195.htmlpaulwongpaulwongMon, 30 Dec 2013 03:23:00 GMThttp://m.tkk7.com/paulwong/archive/2013/12/30/408195.htmlhttp://m.tkk7.com/paulwong/comments/408195.htmlhttp://m.tkk7.com/paulwong/archive/2013/12/30/408195.html#Feedback0http://m.tkk7.com/paulwong/comments/commentRss/408195.htmlhttp://m.tkk7.com/paulwong/services/trackbacks/408195.htmlios鎶撳寘鏂規硶錛?a target="_blank">http://www.99css.com/archives/1272


android鎶撳寘鏂規硶錛?a target="_blank">http://www.cnblogs.com/xyzlmn/p/3168065.html

paulwong 2013-12-30 11:23 鍙戣〃璇勮
]]>
ANDROID APPS DATA璧勬簮http://m.tkk7.com/paulwong/archive/2013/06/25/400952.htmlpaulwongpaulwongTue, 25 Jun 2013 14:11:00 GMThttp://m.tkk7.com/paulwong/archive/2013/06/25/400952.htmlhttp://m.tkk7.com/paulwong/comments/400952.htmlhttp://m.tkk7.com/paulwong/archive/2013/06/25/400952.html#Feedback0http://m.tkk7.com/paulwong/comments/commentRss/400952.htmlhttp://m.tkk7.com/paulwong/services/trackbacks/400952.htmlhttp://stackoverflow.com/questions/10272155/getting-data-from-android-play-store


https://developer.android.com/distribute/googleplay/promote/linking.html


http://www.scriptrr.com/data-scrapping-get-android-google-play-app-info/

paulwong 2013-06-25 22:11 鍙戣〃璇勮
]]>
ANDROID APP 涓?WEB APP鐨勬瘮杈?/title><link>http://m.tkk7.com/paulwong/archive/2013/05/26/399764.html</link><dc:creator>paulwong</dc:creator><author>paulwong</author><pubDate>Sat, 25 May 2013 16:59:00 GMT</pubDate><guid>http://m.tkk7.com/paulwong/archive/2013/05/26/399764.html</guid><wfw:comment>http://m.tkk7.com/paulwong/comments/399764.html</wfw:comment><comments>http://m.tkk7.com/paulwong/archive/2013/05/26/399764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.tkk7.com/paulwong/comments/commentRss/399764.html</wfw:commentRss><trackback:ping>http://m.tkk7.com/paulwong/services/trackbacks/399764.html</trackback:ping><description><![CDATA[ANDROID APP 涓?WEB APP鏈夌浉浼間箣澶勶紝閮芥槸闇瑕佸湪瀹瑰櫒鍐呰繍琛岋紝閮藉彲浠ュ浜嬩歡榪涜鍝嶅簲銆? <div><br /> </div> <div>WEB APP鐨勬牳蹇冪粍浠舵槸SERVLET錛岃繖鏄娓歌鍣ㄤ簨浠惰繘琛屽搷搴旂殑涓涓被錛屼簨浠舵湁GET/POST絳変簨浠訛紝涓嶅悓鐨刄RL瀵瑰簲涓嶅悓鐨凷ERVLET銆?/div> <div>濡傛灉瑕佽緭鍑轟笉鍚岀殑鍐呭錛屽垯杈撳嚭涓嶅悓鐨凥TML鍗沖彲銆?br />WEB APP鐨勯厤緗枃浠舵槸WEB.XML錛屽綋瀹瑰櫒鍚姩鏃訛紝浼氫粠榪欒鍙栨湁澶氬皯涓猄ERVLET銆傚綋嫻忚鍣ㄥ彂榪囨潵涓嶅悓鐨刄RL璇鋒眰鏃訛紝浼氫粠涓夋嫨鐩稿簲鐨凷ERVLET鎵ц銆?/div> <div><br /> </div> <div>ANDROID APP鐨勬牳蹇冪粍浠舵槸ACTIVITY錛岃繖鏄竴涓緋葷粺浜嬩歡榪涜鍝嶅簲鐨勪竴涓被錛屼簨浠舵湁鍚姩浜嬩歡錛岃彍鍗曠偣鍑諱簨浠剁瓑錛岀偣鍑諱笉鍚岀殑鍦版柟瀵瑰簲涓嶅悓鐨凙CTIVITY銆?/div> <div>濡傛灉瑕佽緭鍑轟笉鍚岀殑鍐呭錛屽垯杈撳嚭涓嶅悓鐨刅IEW鍗沖彲銆?br />ANDROID APP鐨勯厤緗枃浠舵槸MAINFRE.XML錛屽綋搴旂敤琚儴緗插埌緋葷粺鏃訛紝緋葷粺浼氫粠榪欒鍙栨湁澶氬皯涓狝CTIVITY銆傚綋涓嶅悓鐨勪簨浠跺彂鐢熸椂錛岀郴緇熶細浼氫粠涓夋嫨鐩稿簲鐨凙CTIVITY鎵ц銆?/div><img src ="http://m.tkk7.com/paulwong/aggbug/399764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.tkk7.com/paulwong/" target="_blank">paulwong</a> 2013-05-26 00:59 <a href="http://m.tkk7.com/paulwong/archive/2013/05/26/399764.html#Feedback" target="_blank" style="text-decoration:none;">鍙戣〃璇勮</a></div>]]></description></item><item><title>婕斿寲鐞嗚В Android 寮傛鍔犺澆鍥劇墖http://m.tkk7.com/paulwong/archive/2012/05/15/378229.htmlpaulwongpaulwongTue, 15 May 2012 14:27:00 GMThttp://m.tkk7.com/paulwong/archive/2012/05/15/378229.htmlhttp://m.tkk7.com/paulwong/comments/378229.htmlhttp://m.tkk7.com/paulwong/archive/2012/05/15/378229.html#Feedback1http://m.tkk7.com/paulwong/comments/commentRss/378229.htmlhttp://m.tkk7.com/paulwong/services/trackbacks/378229.html闃呰鍏ㄦ枃

paulwong 2012-05-15 22:27 鍙戣〃璇勮
]]>
主站蜘蛛池模板: 亚洲国产精品SSS在线观看AV| 免费国产小视频在线观看| 日韩va亚洲va欧洲va国产| 一区二区三区免费视频网站| 无码专区一va亚洲v专区在线| 欧美色欧美亚洲另类二区| 国产片免费福利片永久| 特级毛片全部免费播放| 亚洲第一页日韩专区| 三级网站在线免费观看| 久久精品国产亚洲AV麻豆王友容| 久久综合九色综合97免费下载| 亚洲人成网址在线观看| 亚洲香蕉免费有线视频| 亚洲中文字幕无码久久2020| 国产免费黄色大片| 亚洲免费无码在线| 久久亚洲中文字幕精品有坂深雪| 18女人毛片水真多免费| 亚洲熟女www一区二区三区| 国产免费黄色大片| 三年片在线观看免费西瓜视频| 亚洲国产女人aaa毛片在线| 国产成人无码免费看视频软件| 亚洲成a∧人片在线观看无码| 亚洲av日韩av欧v在线天堂| 久久九九全国免费| 亚洲香蕉在线观看| 亚洲精品无码久久毛片| 日日麻批免费40分钟无码| 在线亚洲高清揄拍自拍一品区| 国产青草视频免费观看97| a毛片免费全部播放完整成| 亚洲国产精品综合福利专区| 国产中文字幕免费| 日韩视频免费在线观看| 日韩欧美亚洲中文乱码| 亚洲欧洲免费视频| 国产一区二区三区在线免费 | 精品亚洲aⅴ在线观看| 嫩草影院免费观看|