[干貨](méi) Glow Android 優(yōu)化實(shí)踐 | wingjay

版權(quán)聲明:本文原創(chuàng)發(fā)布于公眾號(hào) wingjay,轉(zhuǎn)載請(qǐng)務(wù)必注明出處! http://m.itdecent.cn/p/a8b5278cdbcd

了解 Glow 的朋友應(yīng)該知道,我們主營(yíng)四款 App,分別是 Eve、Glow、Nuture和Baby。作為創(chuàng)業(yè)公司,我們的四款 App 都處于高速開發(fā)中,平均每個(gè) Android App 由兩人負(fù)責(zé)開發(fā),同時(shí)負(fù)責(zé) Android 和 Server 開發(fā),在滿足 PM 各種需求的同時(shí),我們的 session crash free 率保持不低于 99.8%,其中兩款 App 接近 100%。

本文將對(duì) Glow 當(dāng)前 Android App 中對(duì)現(xiàn)有工具的探索及優(yōu)化進(jìn)行講解,希望對(duì)讀者有所啟發(fā)。

整體結(jié)構(gòu)概覽

下面是 Glow Android 端的大體結(jié)構(gòu):

我們有四個(gè) Android App,它們共用同一個(gè) Community 社區(qū),最底層是 Base-Library,存放公用的模塊組件,如支付模塊,Logging模塊等等。

下面,我將依次從以下幾個(gè)方面進(jìn)行講解:

  • 網(wǎng)絡(luò)層優(yōu)化
  • 內(nèi)存優(yōu)化實(shí)踐
  • 在 App 和 Library 中集成依賴注入
  • etc.

網(wǎng)絡(luò)層優(yōu)化

1. Retrofit2 + OkHttp3 + RxJava

上面這套結(jié)構(gòu)是目前最為流行的網(wǎng)絡(luò)層架構(gòu),可以幫我們寫出簡(jiǎn)潔而穩(wěn)定的網(wǎng)絡(luò)請(qǐng)求代碼,比起以前復(fù)雜的異步回調(diào)、主次線程切換等代碼更為易用,而且能支持 https 請(qǐng)求。

基本用法如下:

UserApi userApi = retrofit.create(UserApi.class);  
@Get("/{id}")
Observable<User> getUser(@Path("id") long id);
userApi.getUser(1)
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Action1<User>() {
    @Override
    public void call(User user) {
        // handle user
    }
  }, new Action1<Throwable>() {
    @Override
    public void call(Throwable throwable) {
        // handle throwable
    }
  });

這只是通用做法。下面我們要根據(jù)實(shí)際情況進(jìn)行優(yōu)化。

2. 封裝線程切換代碼

上面的代碼中可以看到,為了執(zhí)行網(wǎng)絡(luò)請(qǐng)求,我們會(huì)利用RxJava提供的Schedulers工具來(lái)方便切換線程。

  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())

上面的代碼的作用是:讓網(wǎng)絡(luò)請(qǐng)求進(jìn)入 io線程 執(zhí)行,并將返回結(jié)果轉(zhuǎn)入 UI線程 去進(jìn)行渲染。

不過(guò),我們 app 有非常多的網(wǎng)絡(luò)請(qǐng)求,而且除了網(wǎng)絡(luò)請(qǐng)求,其他的數(shù)據(jù)庫(kù)操作 或者 文件讀寫操作 都需要一樣的線程切換。因此,為了代碼復(fù)用,我們利用 RxJava 提供的 Transformer 來(lái)進(jìn)行封裝。

// RxUtil.java  
public static <T> Observable.Transformer<T, T> normalSchedulers() {
  return new Observable.Transformer<T, T>() {
    @Override
    public Observable<T> call(Observable<T> source) {
      return source.subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());
    }
  };
}

然后,我們可以把網(wǎng)絡(luò)請(qǐng)求代碼轉(zhuǎn)化為

userApi.getUser(1)
  .compose(RxUtil.normalSchedulers())
  .subscribe(...)

這雖然只是很簡(jiǎn)單的改進(jìn),但能讓我們的代碼更簡(jiǎn)潔,更不易出錯(cuò)。

3. 封裝響應(yīng)結(jié)果 JsonDataResponse

我們 server 的所有返回結(jié)果都符合如下格式:

{
  'rc': 0,
  'data': {...},
  'msg': "Successful Call"
}

其中 rc 是自定義的結(jié)果標(biāo)志,server 用來(lái)告訴我們?cè)撜?qǐng)求的邏輯處理是否成功(此時(shí) rc = 0)。data是這個(gè)請(qǐng)求需要的 json 數(shù)據(jù)。msg一般用來(lái)存放錯(cuò)誤提示信息。

于是我們創(chuàng)建了一個(gè)通用類來(lái)封裝所有的 Response

public class JsonDataResponse<T> {
  @SerializedName("rc")
  private int rc;

  @SerializedName("msg")
  private String msg;

  @SerializedName("data")
  T data;
  
  public int getRc() { return rc; }
  
  public T getData() { return data; }
} 

于是,我們的請(qǐng)求變成如下:

@Get("/{id}")
Observable<JsonDataResponse<User>> getUser(@Path("id") long id);
userApi.getUser(1)
  .compose(RxUtil.normalSchedulers())
  .subscribe(new Action1<JsonDataResponse<User>>() {
    @Override
    public void call(JsonDataResponse<User> response) {
        if (response.getRc() == 0) {
          User user = response.getData();
          // handle user
        } else {
          Toast.makeToast(context, response.getMsg())
        }
    }
  }, new Action1<Throwable>() {
    @Override
    public void call(Throwable throwable) {
        // handle throwable
    }
  });

4. 異常處理

上面已經(jīng)能完成正常的網(wǎng)絡(luò)請(qǐng)求了,但是,卻還沒(méi)有對(duì)錯(cuò)誤進(jìn)行處理。

一次網(wǎng)絡(luò)請(qǐng)求中,可能發(fā)生以下幾種錯(cuò)誤:

  • 沒(méi)有網(wǎng)絡(luò)
  • 網(wǎng)絡(luò)正常,但 http 請(qǐng)求失敗,即 http 狀態(tài)碼不在 [200, 300) 之間,如404、500
  • 網(wǎng)絡(luò)正常,http 請(qǐng)求成功,但是 server 在處理請(qǐng)求時(shí)出了問(wèn)題,使得返回結(jié)果的 rc != 0

不同的錯(cuò)誤,我們希望給用戶不同的提示,并且統(tǒng)計(jì)這些錯(cuò)誤。

目前我們的網(wǎng)絡(luò)請(qǐng)求里已經(jīng)能夠處理第三種情況,另外兩種都在 throwable 里面,我們可以通過(guò)判斷 throwableIOException 還是 retrofit2.HttpException 來(lái)區(qū)分這兩種情況。

因此,我們可得到如下異常處理代碼:

userApi.getUser(1)
  .compose(RxUtil.normalSchedulers())
  .subscribe(new Action1<JsonDataResponse<User>>() {
    @Override
    public void call(JsonDataResponse<User> response) {
        if (response.getRc() == 0) {
          User user = response.getData();
          // handle user
          handleUser();
        } else {
          // such as: customized errorMsg: "cannot find this user".
          Toast.makeToast(context, response.getMsg(), Toast.LENGTH_SHORT).show();
        }
    }
  }, new Action1<Throwable>() {
    @Override
    public void call(Throwable throwable) {
        String errorMsg = "";
        if (throwable instanceof IOException) {
          // io Exception
          errorMsg = "Please check your network status";
        } else if (throwable instanceof HttpException) {
          HttpException httpException = (HttpException) throwable;
          // http error.
          errorMsg = httpException.response(); 
        } else {
          errorMsg = "unknown error";
        }
        Toast.makeToast(...);
    }
  });

5. 封裝異常處理代碼

當(dāng)然,我們并不想在每一個(gè)網(wǎng)絡(luò)請(qǐng)求里都寫上面一大段代碼來(lái)處理 error,那樣太傻了。比如上面 getUser() 請(qǐng)求,我希望只要寫 handleUser() 這個(gè)方法,至于是網(wǎng)絡(luò)問(wèn)題還是 server 自己?jiǎn)栴}我都不想每次去 handle。

接下來(lái)我們來(lái)封裝上面兩個(gè) Action 。我們可以自定義兩個(gè) Action:

WebSuccessAction<T extends JsonDataResponse> implements Action1<T> 
WebFailureAction implements Action1<Throwable>

其中,WebSuccessAction 用來(lái)處理一切正常(網(wǎng)絡(luò)正常,請(qǐng)求正常,rc=0)后的處理,WebFailureAction 用來(lái)統(tǒng)一處理上面三種 error。

實(shí)現(xiàn)如下:

class WebSuccessAction<T extends JsonDataResponse> implements Action1<T> {
  @Override
  public void call(T response) {
    int rc = response.getRc();
    if (rc != 0) {
      throw new ResponseCodeError(extendedResponse.getMessage());
    }
    onSuccess(extendedResponse);
  }

  public abstract void onSuccess(T extendedResponse);
}
// (rc != 0) Error
class ResponseCodeError extends RuntimeException {
  public ResponseCodeError(String detailMessage) {
    super(detailMessage);
  }
}

WebSuccessAction 里,我們把 rc != 0 這種情況轉(zhuǎn)化成 ResponseCodeError 并拋出給 WebFailureAction 去統(tǒng)一處理。

class WebFailAction implements Action1<Throwable> {
  @Override
  public void call(Throwable throwable) {
    String errorMsg = "";
    if (throwable instanceof IOException) {
      errorMsg = "Please check your network status";
    } else if (throwable instanceof HttpException) {
      HttpException httpException = (HttpException) throwable;
      // such as: "server internal error".
      errorMsg = httpException.response(); 
    } else {
      errorMsg = "unknown error";
    }
    Toast.makeToast(...);
  }
}

有了上面兩個(gè)自定義 Action 后,我們就可以把前面 getUser() 請(qǐng)求轉(zhuǎn)化如下:

userApi.getUser(1)
  .compose(RxUtil.normalSchedulers())
  .subscribe(new WebSuccessAction<JsonDataResponse<User>>() {
      @Override
      public void onSuccess(JsonDataResponse<User> response) {
        handleUser(response.getUser());
      }
    }, new WebFailAction())

Bingo! 至此我們能夠用非常簡(jiǎn)潔的方式來(lái)執(zhí)行網(wǎng)絡(luò)操作,而且完全不用擔(dān)心異常處理。

內(nèi)存優(yōu)化實(shí)踐

在內(nèi)存優(yōu)化方面,Google 官方文檔里能找到非常多的學(xué)習(xí)資料,例如常見的內(nèi)存泄漏、bitmap官方最佳實(shí)踐。而且 Android studio 里也集成了很多有效的工具如 Heap Viewer, Memory MonitorHierarchy Viewer 等等。

下面,本文將從其它角度出發(fā),來(lái)對(duì)內(nèi)存作進(jìn)一步優(yōu)化。

1. 當(dāng)Activity關(guān)閉時(shí),立即取消掉網(wǎng)絡(luò)請(qǐng)求結(jié)果處理。

這一點(diǎn)很容易被忽略掉。大家最常用的做法是在 Activity 執(zhí)行網(wǎng)絡(luò)操作,當(dāng) Http Response 回來(lái)后直接進(jìn)行UI渲染,卻并不會(huì)去判斷此時(shí) Activity 是否仍然存在,即用戶是否已經(jīng)離開了當(dāng)時(shí)的頁(yè)面。

那么,有什么方法能夠讓每個(gè)網(wǎng)絡(luò)請(qǐng)求都自動(dòng)監(jiān)聽 Activity(Fragment) 的 lifecycle 事件并且當(dāng)特定 lifecycle 事件發(fā)生時(shí),自動(dòng)中斷掉網(wǎng)絡(luò)請(qǐng)求的繼續(xù)執(zhí)行呢?

首先來(lái)看下我們的網(wǎng)絡(luò)請(qǐng)求代碼:

userApi.getUser(1)
  .compose(RxUtil.normalSchedulers())
  .subscribe(new WebSuccessAction<JsonDataResponse<User>>() {
      @Override
      public void onSuccess(JsonDataResponse<User> response) {
        handleUser(response.getUser());
      }
    }, new WebFailAction())

我們希望達(dá)到的是,當(dāng) Activity 進(jìn)入 onStop 時(shí)立即停掉網(wǎng)絡(luò)請(qǐng)求的后續(xù)處理。

這里我們參考了 RxLifecycle 的實(shí)現(xiàn)方式,之所以沒(méi)有直接使用 RxLifecycle 是因?yàn)楫?dāng)時(shí)集成時(shí)它必須我們的 BaseActivity 繼承其提供的 RxActivity ,而 RxActivity 并未繼承我們需要的 AppCompatActivity(不過(guò)現(xiàn)在已經(jīng)提供了)。因此本人只能在學(xué)習(xí)其源碼后,自己重新實(shí)現(xiàn)一套,并做了一些改動(dòng)以更符合我們自己的應(yīng)用場(chǎng)景。

具體實(shí)現(xiàn)如下:

  • 首先,我們?cè)?BaseActivity 里,利用 RxJava 提供的 PublishSubject 把所有 lifecycle event 發(fā)送出來(lái)。
  class BaseActivity extends AppCompatActivity {
    protected final PublishSubject<ActivityLifeCycleEvent> lifecycleSubject = PublishSubject.create();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      lifecycleSubject.onNext(ActivityLifeCycleEvent.CREATE);
    }
    
    @Override
    protected void onDestroy() {
      lifecycleSubject.onNext(ActivityLifeCycleEvent.DESTROY);
      
      super.onDestroy();
    }
    
    @Override
    protected void onStop() {
      lifecycleSubject.onNext(ActivityLifeCycleEvent.STOP);

      super.onStop();
    }
  }
  • 然后,在 BaseActivity 里,提供 bindUntilEvent(LifeCycleEvent) 方法
  class BaseActivity extends AppCompatActivity {
    
    @NonNull
    @Override
    public <T> Observable.Transformer<T, T> bindUntilEvent(@NonNull final ActivityLifeCycleEvent event) {
      return new Observable.Transformer<T, T>() {
        @Override
        public Observable<T> call(Observable<T> sourceObservable) {
          Observable<ActivityLifeCycleEvent> o =
              lifecycleSubject.takeFirst(activityLifeCycleEvent -> {
                return activityLifeCycleEvent.equals(event);
              });
          return sourceObservable.takeUntil(o);
        }
      };
    }
  }

這個(gè)方法可以用于每一個(gè)網(wǎng)絡(luò)請(qǐng)求 Observable 中,當(dāng)它監(jiān)聽到特定的 lifecycle event 時(shí),就會(huì)自動(dòng)讓網(wǎng)絡(luò)請(qǐng)求 Observable 終止掉,不會(huì)再去監(jiān)聽網(wǎng)絡(luò)請(qǐng)求結(jié)果。

  • 具體使用如下:
  userApi.getUser(1)
    .compose(bindUntilEvent(ActivityLifeCycleEvent.PAUSE))
    .compose(RxUtil.normalSchedulers())
    .subscribe(new WebSuccessAction<JsonDataResponse<User>>() {
      @Override
        public void onSuccess(JsonDataResponse<User> response) {
          handleUser(response.getUser());
        }
    }, new WebFailAction())

利用 .compose(bindUntilEvent(ActivityLifeCycleEvent.STOP)) 來(lái)監(jiān)聽 Activity 的 Stop 事件并終止 userApi.getUser(1)subscription,從而防止內(nèi)存泄漏。

2. 圖片優(yōu)化實(shí)踐

Android開發(fā)者都知道,每個(gè)app的可用內(nèi)存時(shí)有限的,一旦內(nèi)存占用太多或者在主線程突然請(qǐng)求較大內(nèi)存,很有可能發(fā)生 OOM 問(wèn)題。而其中,圖片又是占用內(nèi)存的大頭,因此我們必須采取多種方法來(lái)進(jìn)行優(yōu)化。

多數(shù)情況下我們是從 server 獲取一張高清圖片下來(lái),然后在內(nèi)存里進(jìn)行裁剪成需要的大小來(lái)進(jìn)行顯示。這里面存在兩個(gè)問(wèn)題,

1:假設(shè)我們只需要一張小圖,而server取回來(lái)的圖如果比較大,那就會(huì)浪費(fèi)帶寬和內(nèi)存。

2:如果直接在主線程去為圖片請(qǐng)求大塊空間,很容易由于系統(tǒng)難于快速分配而 OOM;

比較理想的情況是:需要顯示多大的圖片,就向server請(qǐng)求多大的圖片,既節(jié)省用戶帶寬流量,更減少內(nèi)存的占用,減小 OOM 的機(jī)率。

為了實(shí)現(xiàn) server 端的圖片Resize,我們采用了 Thumbor 來(lái)提供圖片 Resize 的功能。android端只需要提供一個(gè)原圖片 URL 和需要的 size 信息,就可以得到一張 Resize 好的圖片資源文件。具體server端實(shí)現(xiàn)這里就不細(xì)講了,感興趣的讀者可以閱讀官方文檔。

這里介紹下我們?cè)?Android 端的實(shí)現(xiàn),以 Picasso 為栗子。

  • 首先要引入 Square 提供的 pollexor 工具,它可以讓我們更簡(jiǎn)便的創(chuàng)建 thumbor 的規(guī)范 URI,參考如下:
  thumbor.buildImage("http://example.com/image.png")
      .resize(48, 48)
      .toUrl()
  • 然后,利用 Picasso 提供的 requestTransformer 來(lái)實(shí)時(shí)獲取當(dāng)前需要顯示的圖片的真實(shí)尺寸,同時(shí)設(shè)置圖片格式為 WebP,這種格式的圖片可以保持圖片質(zhì)量的同時(shí)具有更小的體積:
  Picasso picasso = new Picasso.Builder(context).requestTransformer(new Picasso.RequestTransformer() {
        @Override
        public Request transformRequest(Request request) {
          String modifiedUrl = URLEncoder.encode(originUrl);
          ThumborUrlBuilder thumborUrlBuilder = thumbor.buildImage(modifiedUrl);
          String url = thumborUrlBuilder.resize(request.targetWidth, request.targetHeight)
              .filter(ThumborUrlBuilder.format(ThumborUrlBuilder.ImageFormat.WEBP))
              .toUrl();
          Timber.i("SponsorAd Image Resize url to " + url);
          return request.buildUpon().setUri(Uri.parse(url)).build();
        }
      }).build();
  • 利用修改后的 picasso 對(duì)象來(lái)請(qǐng)求圖片
  picasso.load(originUrl).fit().centerCrop().into(imageView);

利用上面這種方法,我們可以為不同的 ImageView 計(jì)算顯示需要的真實(shí)尺寸,然后去請(qǐng)求一張尺寸匹配的圖片下來(lái),節(jié)約帶寬,減小內(nèi)存開銷。

當(dāng)然,在應(yīng)用這種方法的時(shí)候,不要忘記考慮服務(wù)器的負(fù)載情況,畢竟這種方案意味著每張圖片會(huì)被生成各種尺寸的小圖緩存起來(lái),而且Android設(shè)備分辨率不同,即使是同一個(gè) ImageView,真實(shí)的寬高 Pixel 值也會(huì)不同,從而生成不同的小圖。

在App和Library中集成依賴注入

依賴注入框架 Dagger 我們很早就開始用了,從早期的 Dagger1 到現(xiàn)在的 Dagger2。雖然 Dagger 本身較為陡峭的學(xué)習(xí)曲線使得不少人止步,不過(guò)一旦用過(guò),根本停不下來(lái)。

如果只是在 App 里使用 Dagger 相對(duì)比較簡(jiǎn)單,不過(guò),我們還需要在 CommunityBase-Android 兩個(gè)公用 Library 里也集成 Dagger,這就需要費(fèi)點(diǎn)功夫了。

下面我來(lái)逐步講解下我們是如何將 Dagger 同時(shí)集成進(jìn) App 和 Library 中。

1. 在App里集成Dagger

首先需要在 GlowApplication 里生成一個(gè)全局的 AppComponent

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
  void inject(MainActivity mainActivity);
}

創(chuàng)建 AppModule

@Module
public class AppModule {
  private final LexieApplication lexieApplication;

  public AppModule(LexieApplication lexieApplication) {
    this.lexieApplication = lexieApplication;
  }
  
  @Provides Context applicationContext() {
    return lexieApplication;
  }
  
  // mock tool object
  @Provides Tool provideTool() {
    return new Tool();
  }
}

集成進(jìn) Application

class GlowApplication extends Application {
  private AppComponent appComponent;
  
  @Override
  public void onCreate() {
    appComponent = DaggerAppComponent.builder()
        .appModule(new AppModule(this))
        .build();
  }
  
  public static AppComponent getAppComponent() {
    return appComponent;
  }
}

MainActivity中使用inject 一個(gè) tool 對(duì)象

class MainActivity extends Activity {
  @Inject Tool tool;
  
  @Override
  public void onCreate() {
    GlowApplication.getAppComponent().inject(this);
  }
}

2. 在 Library 中集成 Dagger

(下面以公用Library:Community為例子)

逆向思維下,先設(shè)想應(yīng)用場(chǎng)景:即 Dagger 已經(jīng)集成好了,那么我們應(yīng)該可以按如下方式在 CommunityActivityinject 一個(gè) tool 對(duì)象。

class CommunityActivity extends Activity {
  @Inject Tool tool;
  
  @Override
  public void onCreate() {
    GlowApplication.getAppComponent().inject(this);
  }
}

關(guān)鍵在于: GlowApplication.getAppComponent().inject(this); 這一句。

那么問(wèn)題來(lái)了:

對(duì)于一個(gè) Library 而言,它是無(wú)法拿到 GlowApplication 對(duì)象的,因?yàn)樽鳛橐粋€(gè)被別人調(diào)用的 Library,它甚至不知道這個(gè)上層 class 的存在

為了解決這個(gè)問(wèn)題,我們?cè)?code>community里定義一個(gè)公用接口作為中間橋梁,讓GlowApplication實(shí)現(xiàn)這個(gè)公共接口即可。

// 在Community定義接口CommunityComponentProvider
public interface CommunityComponentProvider {
  AppComponent getAppComponent();
}
// 每個(gè)app的Application類都實(shí)現(xiàn)這個(gè)接口來(lái)提供AppComponent
class GlowApplication implements CommunityComponentProvider {
  AppComponent getAppComponent() {
    return appComponent;
  }
}

然后 CommunityActivity就可以實(shí)現(xiàn)如下:

class CommunityActivity extends Activity {
  @Inject Tool tool;
  
  @Override
  public void onCreate() {
    Context applicationContext = getApplicationContext();
    CommunityComponentProvider provider = (CommunityComponentProvider) applicationContext;
    provider.getAppComponent().inject(this);
  }
}

3. 從 AppComponent 抽離 CommunityComponent

provider.getAppComponent().inject(this);

這一句里我們已經(jīng)實(shí)現(xiàn)前半句 provider.getAppComponent() 了,但后半句的實(shí)現(xiàn)呢?

正常情況下,我們要把

void inject(CommunityActivity communityActivity);

放入 AppComponent 中,如下:

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
  void inject(MainActivity mainActivity);
  
  // 加在這里
  void inject(CommunityActivity communityActivity);
}

其實(shí)這樣我們就已經(jīng)幾乎完成了整個(gè) Library 和 App 的依賴注入了。

但細(xì)心的朋友應(yīng)該發(fā)現(xiàn)里面存在一個(gè)小問(wèn)題,那就是

void inject(CommunityActivity communityActivity);

這句代碼如果放入了 App 里的 AppComponent 里,那就意味著我們也需要在另外三個(gè) App 里的 AppComponent 都加上一句相同的代碼?這樣可以嗎?

理論上當(dāng)然是可行的。但是,從單一職責(zé)的角度來(lái)考慮,AppComponent 只需要負(fù)責(zé) App 層的 inject 就行,我們不應(yīng)該把屬于 Communityinject 放到App 里,這樣的代碼太ugly,而且更重要的是,隨著 Community 越來(lái)越多 Activity 需要 inject ,每個(gè) inject 都要在各個(gè) App 里重復(fù)加,這太煩了,也太笨了。

因此,我們采用了一個(gè)簡(jiǎn)潔有效的方法來(lái)改進(jìn)。

Community 里創(chuàng)建一個(gè) CommunityComponent,所有屬于 Communityinject 直接寫在 CommunityComponent 里,不需要 App 再去關(guān)心。與此同時(shí),為了保持前面 provider.getAppComponent() 仍然有效,我們讓 AppComponent 繼承 CommunityComponent。

實(shí)現(xiàn)代碼如下:

class AppComponent extends CommunityComponent {...}

Community

class CommunityComponent {
  void inject(CommunityActivity communityActivity);
}
class CommunityActivity extends Activity {
  @Inject Tool tool;
  
  @Override
  public void onCreate() {
    Context applicationContext = getApplicationContext();
    CommunityComponentProvider provider = (CommunityComponentProvider) applicationContext;
    provider.getAppComponent().inject(this);
  }
}
dagger

Bingo! 至此我們已經(jīng)能夠優(yōu)雅簡(jiǎn)潔地在 App 和 Library 里同時(shí)應(yīng)用依賴注入了。

關(guān)于demo

很多讀者提到想要demo,有需要的小伙伴可以先關(guān)注我的Github:https://github.com/wingjay 之后會(huì)抽空把demo上傳到Github上的。

小結(jié)

由于篇幅有限,本文暫時(shí)先從網(wǎng)絡(luò)層、內(nèi)存優(yōu)化和依賴注入方面進(jìn)行講解,之后會(huì)再考慮從 Logging模塊、數(shù)據(jù)同步模塊、Deep Linking模塊、多Library的Gradle發(fā)布管理、持續(xù)集成和崩潰監(jiān)測(cè)模塊等進(jìn)行講解。

謝謝!

wingjay

https://github.com/wingjay

wingjay

版權(quán)聲明:轉(zhuǎn)載必須得到本人授權(quán)。謝謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,324評(píng)論 25 708
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 14,110評(píng)論 2 59
  • afinalAfinal是一個(gè)android的ioc,orm框架 https://github.com/yangf...
    passiontim閱讀 15,898評(píng)論 2 45
  • 有沒(méi)有這樣一種感情,經(jīng)歷人情世故的染指,依然原滋原味。 ――題記 破曉前的清冽與隱落后的...
    蒙蠻閱讀 337評(píng)論 0 0
  • 人格修習(xí)的目標(biāo)是什么?每個(gè)人都有不同的答案。 今天羅胖在60秒語(yǔ)音分享中說(shuō),人格修習(xí)的目標(biāo)是成為一...
    潘多拉簡(jiǎn)書閱讀 868評(píng)論 0 1

友情鏈接更多精彩內(nèi)容