編輯:關於Android編程
依賴注入就是將調用者需要的另一個對象實例不在調用者內部實現,而是通過一定的方式從外部傳入實例,解決了各個類之間的耦合。
那麼這個外部,到底指的是哪裡,如果指的是另一個類,那麼,另一個類內部不就耦合了。能不能有一種方式,將這些構造的對象放到一個容器中,具體需要哪個實例時,就從這個容器中取就行了。那麼,類的實例和使用就不在有聯系了,而是通過一個容器將他們聯系起來。實現了解耦。這個容器,便是Dagger2
。
Dagger2
是Google出的依賴注入框架。肯定有小伙伴疑問,為什麼會有個 2 呢。該框架是基於square
開發的dagger
基礎上開發的。
Dagger2
的原理是在編譯期生成相應的依賴注入代碼。這也是和其他依賴注入框架不同的地方,其他框架是在運行時期反射獲取注解內容,影響了運行效率。
使用Dagger2
之前需要一些配置,該配置是在Android Studio
中進行操作。
在工程的build.gradle
文件中添加android-apt
插件(該插件後面介紹)
buildscript {
....
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
// 添加android-apt 插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
在app的中的build.gradle
文件中添加配置
apply plugin: 'com.android.application'
// 應用插件
apply plugin: 'com.neenbedankt.android-apt'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.mahao.alex.architecture"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
// dagger 2 的配置
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
compile 'org.glassfish:javax.annotation:10.0-b28'// 添加java 注解庫
}
以上兩個配置就可以了。
android-apt
是Gradle
編譯器的插件,根據其官方文檔,主要兩個目的:
編譯時使用該工具,最終打包時不會將該插件打入到apk中。
能夠根據設置的源路徑,在編譯時期生成相應代碼。
在導入類庫時,
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
dagger
是主要的工具類庫。dagger-compiler
為編譯時期生成代碼等相關的類庫。
在android-apt
的文檔中,也推薦使用這種方式。因為,編譯時期生成代碼的類庫在運行期並不需要,那麼將其分為兩個庫,(運行類庫dagger
)和(編譯器生成代碼類庫(dagger-compiler
)),那麼在打包時,就不需要將dagger-compiler
打入其中(用不到),減小APK 的大小。
Dagger2
的使用,需要大量的學習成本,不是很能夠容易的上手並使用。該博客將從簡單入手,盡可能的使用簡單的例子演示Dagger2
的功能。
一個東西需要先會用,然後才更好的學習原理。該篇博客的目的主要是講解如何使用。後面會有專門的分析源碼的博客。
在之前的分析中,通過Dagger2
的目的是將程序分為三個部分。
- 實例化部分:對象的實例化。類似於容器,將類的實例放在容器裡。
- 調用者:需要實例化對象的類。
- 溝通橋梁:利用Dagger2
中的一些API 將兩者聯系。
先看實例化部分(容器),在此處是Module
。
@Module //提供依賴對象的實例
public class MainModule {
@Provides // 關鍵字,標明該方法提供依賴對象
Person providerPerson(){
//提供Person對象
return new Person();
}
}
溝通部分Component
@Component(modules = MainModule.class) // 作為橋梁,溝通調用者和依賴對象庫
public interface MainComponent {
//定義注入的方法
void inject(MainActivity activity);
}
使用者Actvity
中調用。
public class MainActivity extends AppCompatActivity{
@Inject //標明需要注入的對象
Person person;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 構造橋梁對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();
//注入
component.inject(this);
}
}
看一下Person
類
public class Person {
public Person(){
Log.i("dagger","person create!!!");
}
}
最後結果不在演示。其過程如下:
創建Component
(橋梁),並調用注入方法。
// 構造橋梁對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();
//注入
component.inject(this);
查找當前類中帶有@Inject
的成員變量。
@Inject //標明需要注入的對象
Person person;
根據成員變量的類型從Module
中查找哪個有@Provides
注解的方法返回值為當前類型。
@Provides // 關鍵字,標明該方法提供依賴對象
Person providerPerson(){
//提供Person對象
return new Person();
}
在使用過程出現了很多注解:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxjb2RlPkBNb2R1bGU8L2NvZGU+Otf3zqrKtcD9ttTP87XEyN3G96GjIDxjb2RlPkBQcm92aWRlczwvY29kZT46serXosTcubvM4bmpyrXA/buvttTP87XEt723qKGjIDxjb2RlPkBDb21wb25lbnQ8L2NvZGU+Otf3zqrHxcG6o6zXosjrttTP87XEzai1wKGjIDxjb2RlPkBJbmplY3Q8L2NvZGU+o7rQ6NKq16LI67XEt723qA0KPHA+yOfJz8q508PT0NK71tax5M2oo6zQ3rjEPGNvZGU+TWFpbk1vZHVsZTwvY29kZT66zTxjb2RlPlBlcnNvbjwvY29kZT7A4KGjPC9wPg0KPHByZSBjbGFzcz0="brush:java;">
@Module //提供依賴對象的實例
public class MainModule {
/*
@Provides // 關鍵字,標明該方法提供依賴對象
Person providerPerson(){
//提供Person對象
Log.i("dagger"," from Module");
return new Person();
}
*/
}
public class Person {
@Inject // 添加注解關鍵字
public Person(){
Log.i("dagger","person create!!!");
}
}
將Module
中的providePerson()
方法注釋,在Person
中添加@Inject
注解,依然能夠實現。
邏輯如下:
- 先判斷Module
中是否有提供該對象實例化的方法。
- 如果有則返回。結束。
- 如果沒有,則查找該類的構造方法,是否有帶有@Inject
的方法。如過存在,則返回。
假如,對於同一個對象,我們需要注入兩次,如下方式
public class MainActivity extends AppCompatActivity{
@Inject
Person person;
@Inject
Person person2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 構造橋梁對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();
//注入
component.inject(this);
// 打印兩個對象的地址
Log.i("dagger","person = "+ person.toString()+"; person2 = "+ person2.toString());
}
}
看一下結果:
person = com.mahao.alex.architecture.dagger2.Person@430d1620; person2 = com.mahao.alex.architecture.dagger2.Person@430d17c8
可見兩個對象不一致。也就是說創建了兩個對象。
可以在提供實例化對象的方法上添加@Singleton
注解
@Provides // 關鍵字,標明該方法提供依賴對象
@Singleton
Person providerPerson(){
return new Person();
}
同時,對於MainComponent
也需要添加注解,不添加會無法編譯
@Singleton
@Component(modules = MainModule.class) // 作為橋梁,溝通調用者和依賴對象庫
public interface MainComponent {
//定義注入的方法
void inject(MainActivity activity);
}
此時在Log,會發現兩個對象的地址一樣,可見是同一個對象。
person = com.mahao.alex.architecture.dagger2.Person@4310f898; person2 = com.mahao.alex.architecture.dagger2.Person@4310f898
那麼不同的Activity
之間,能否保持單例呢?
創建一個新的Activity
,代碼如下:
public class Main2Actvity extends AppCompatActivity {
@Inject
Person person;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 構造橋梁對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();
//注入
component.inject(this);
Log.i("dagger","person = "+ person.toString());
}
}
結果如下:
person create!!!
person = com.mahao.alex.architecture.dagger2.Person@4310f898; person2 = com.mahao.alex.architecture.dagger2.Person@4310f898
person create!!!
person = com.mahao.alex.architecture.dagger2.Person@43130058
可見,@Singleton
只對一個Component
有效,即其單例所依賴Component
對象。
Person
的構造方法發生了變化,需要傳入一個Context
,代碼如下:
public class Person {
private Context mContext;
public Person(Context context){
mContext = context;
Log.i("dagger","create");
}
}
這樣的話,我們需要修改MainModule
@Module //提供依賴對象的實例
public class MainModule {
private Context mContext;
public MainModule(Context context){
mContext = context;
}
@Provides
Context providesContext(){
// 提供上下文對象
return mContext;
}
@Provides // 關鍵字,標明該方法提供依賴對象
@Singleton
Person providerPerson(Context context){
return new Person(context);
}
}
修改providerPerson
方法,傳入Context
對象。 添加providesContext()
,用以提供Context
對象。
看一下使用
// 構造橋梁對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule(this)).build();
//注入
component.inject(this);
邏輯:
根據@Inject
注解,查找需要依賴注入的對象。 從MainModule
中根據返回值,找到providerPerson(Context context)
對象。 發現其需要傳入參數Context
,找到moudule
中具有返回值為Context
的方法providesContext()
。 最後就成功的構建了實例化對象。
可能會有疑問,我既然module
中已經保存了Context
對象,那麼為什麼不直接使用Context
對象呢,因為解耦,如果使用了保存的對象,會導致下次Context
獲取發生變化時,需要修改providerPerson(Context context)
中的代碼。
在編寫Module
中,不能出現傳入參數和返回參數一致的情況,會導致死循環。
很容易理解,需要的和獲取的是同一個方法,循環調用。
在使用中,往往會有依賴另一個組件的情況。比如,在AppMoudle
中能夠提供Context
對象,如下:
@Module
public class AppModule {
private Context mContext;
public AppModule(Context context){
mContext = context;
}
@Provides
Context providesContext(){
// 提供Context對象
return mContext;
}
}
而在另一個Module
中需要依賴Context
對象,那麼怎麼寫呢?
首先編寫當前AppModule
的Component
類
/**
*
* 全局的Component 組件
* Created by MH on 2016/7/18.
*/
@Component(modules = AppModule.class)
public interface AppComponent {
// 向其下層提供Context 對象
Context proContext();
}
在此種,因為Module
中需要向下層提供Context
對象,而其與下層的聯系時通過Component
,所以需要在這裡聲明一個其所提供對象的方法。以便下層Module
獲取。
/**
*
* 下層Module類
* Created by MH on 2016/7/18.
*/
@Module
public class ActivityMoudule {
@Provides
Person providePerson(Context context){
// 此方法需要Context 對象
return new Person(context);
}
}
/**
* 子的Component
* Created by MH on 2016/7/18.
*/
@Component(dependencies = AppComponent.class,modules = ActivityMoudule.class)
public interface ActivityComponent {
// 注入
void inject(MainActivity activity);
}
在子Component
中,有一句關鍵的注解dependencies = AppComponent.class
,添加了上層依賴。
看一下使用
// 依賴對象 Component
AppComponent appCom = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
// 子類依賴對象 ,並注入
DaggerActivityComponent.builder()
.appComponent(appCom)
.activityMoudule(new ActivityMoudule())
.build()
.inject(this);
在其中使用過程中,有很重的兩點。
父依賴的Component
中需要添加提供對象的接口。 子依賴的Component
中的注解中添加dependencies = AppComponent.class
。
在使用中,會出現兩個方法返回對象相同時的情況,那麼如何區分呢。
Person
對象具有兩個構造方法,根據不同的參數值構造不同的方法。
public class Person {
private Context mContext;
public Person(Context context){
mContext = context;
Log.i("dagger","create");
}
public Person(String name){
Log.i("dagger",name);
}
}
ActivityModule
中添加@Named
標記
@Module
public class ActivityMoudule {
@Named("Context") // 通過context創建Person 對象
@Provides
Person providePersonContext(Context context){
// 此方法需要Context 對象
return new Person(context);
}
@Named("name") // 通過name創建Person 對象
@Provides
Person providePersonName(){
// 此方法需要name
return new Person("1234");
}
}
使用時,也需要添加此標記
public class MainActivity extends AppCompatActivity{
@Named("context") // 標記
@Inject
Person person;
@Named("name") // 標記
@Inject
Person person2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注入
component.inject(this);*/
// 依賴對象 Component
AppComponent appCom = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
// 子類依賴對象 ,並注入
DaggerActivityComponent.builder()
.appComponent(appCom)
.activityMoudule(new ActivityMoudule())
.build()
.inject(this);
}
}
使用時,使用者的@Inject
上,必須要加入注解@Named("xxx")
,不然編譯期會報錯。
這樣使用過程中,雖然解決了問題,但是通過字符串標記一個對象,容易導致前後不匹配,可以通過自定義注解的方式解決。
添加兩個注解,分別對應Context
和name
。
@Qualifier // 關鍵詞
@Retention(RetentionPolicy.RUNTIME) // 運行時仍可用
public @interface PersonForContext {
// Context 對象的注解
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonForName {
// name 對象的注解
}
在使用@Named("")
的地方替換為上面的注解
@PersonForContext // 通過context創建Person 對象
@Provides
Person providePersonContext(Context context){
// 此方法需要Context 對象
return new Person(context);
}
@PersonForName // 通過name創建Person 對象
@Provides
Person providePersonName(){
// 此方法需要Context 對象
return new Person("123");
}
注入時:
@PersonForContext // 標記
@Inject
Person person;
@PersonForName // 標記
@Inject
Person person2;
在前面中提到@Singleton
注解,該注解能夠使同一個Component
中的對象保持唯一,即單例。
回憶一下,如下方式:
@Provides // 關鍵字,標明該方法提供依賴對象
@Singleton
Person providerPerson(Context context){
return new Person(context);
}
Module
中,對應方法中添加@Singleton
注解,同時其所在的Component
中,類生命上也需要添加注解
@Singleton
@Component(modules = MainModule.class) // 作為橋梁,溝通調用者和依賴對象庫
public interface MainComponent {
}
如果我們看這個意思,感覺其內部應該做了很多的實現,用以達到單例。其實,沒我們想的那麼復雜。
看一下@Singleton
的實現
@Scope //注明是Scope
@Documented //標記在文檔
@Retention(RUNTIME) // 運行時級別
public @interface Singleton {}
通過@Scope
定義的一個新的注解。
在之前的,我們知道該單例是依托於他所在的Component
組件。那麼我們是否可以這樣理解,因為方法上添加的@Scope
標記的注解和Component
上添加的@Scope
標記的注解相同(確實相同,同為@Singleton
),就表明了該方法提供的實例對象在Component
保持唯一。保持唯一的條件是通過@Scope
標記的注解相同。
通過在上面的依賴層級上,Android
中通常定義兩個生命周期。
全局的生命周期PerApp
/**
* 全局的生命周期單例
*/
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PerApp {
}
在使用中完全和@Singleton
相同。
@Module
public class AppModule {
private Context mContext;
public AppModule(Context context){
mContext = context;
}
@Provides
@PerApp // 添加該標記表明該方法只產生一個實例
Context providesContext(){
// 提供上下文對象
return mContext;
}
}
@PerApp // 因為Module 中使用了該標記,所以需要在此添加
@Component(modules = AppModule.class)
public interface AppComponent {
// 向其下層提供Context 對象
Context proContext();
}
因為單例的依托於他所在的Component
中,所以需要在Application
中進行實例化。
public class App extends Application {
// 為什麼可以使用靜態
public static AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
// 實例化
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
}
為什麼可以使用靜態的,因為該AppComponent
對象的生命周期是整個App。那麼在使用中,其所在Module
中的實例化對象,可以保持全局單例。
一個Activity的生命周期PerActivity
有全局的單例,而對於一個Activity
,他也有些對象需要保持單例。我們需要定義該注解。
/**
* Activity 單例生命周期
*/
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {
}
會發現,除了定義名不一樣,其余都和PerApp
一樣。在前面,說過這樣一句話:保持唯一的條件是通過@Scope
標記的注解相同。
@Module
public class ActivityMoudule {
@PersonForContext
@Provides
@PerActivity // 添加標記,生命其所構造的對象單例
Person providePersonContext(Context context){
// 此方法需要Context 對象
return new Person(context);
}
.....
}
@PerActivity // ActivityMoudule 中使用了該標記
@Component(dependencies = AppComponent.class,modules = ActivityMoudule.class)
public interface ActivityComponent {
// 注入
void inject(MainActivity activity);
}
使用方式,因為其所保持的單例是在Activity
中,具體使用如下。
public class MainActivity extends AppCompatActivity{
@PersonForContext // 標記
@Inject
Person person;
@PersonForName // 標記
@Inject
Person person2;
/**
* 不使用靜態的,因為該Component只是針對於該Activity,而不是全局的
*/
ActivityComponent activityComponent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activityComponent = DaggerActivityComponent.builder()
.appComponent(App.appComponent) // 添加了全局的AppComponent組件,可以使用全局的實例化對象
.activityMoudule(new ActivityMoudule())
.build();
activityComponent.inject(this);
對於具有依賴關系的Component,不能使用相同的Scope,如果使用相同的會帶來語意不明
public class MainActivity extends AppCompatActivity{
@PersonForContext // 標記
@Inject
Lazy lazyPerson; // 注入Lazy元素
@PersonForName // 標記
@Inject
Provider providerPerson; // 注入Provider
/**
* 不使用靜態的,因為該Component只是針對於該Activity,而不是全局的
*/
ActivityComponent activityComponent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activityComponent = DaggerActivityComponent.builder()
.appComponent(App.appComponent) // 添加了全局的AppComponent組件
.activityMoudule(new ActivityMoudule())
.build();
activityComponent.inject(this);
Person person = lazyPerson.get();// 調用該方法時才會去創建Person,以後每次調用獲取的是同一個對象
// 調用該方法時才回去創建Person1,以後每次調用都會重新加載Module中的具體方法,根據Module中的實現,可能相同,可能不相同。
Person person1 = providerPerson.get();
}
}
該博客中使用的代碼已經上傳到github,有需要者請移步。https://github.com/AlexSmille/alex_mahao_sample/tree/master/architecture
概述:一般情況下,我們知道View類有個View.OnTouchListener內部接口,通過重寫他的onTouch(View v, MotionEvent event)
Android項目總結之社會化分享隨著現在社交網絡的日益繁多,眾多的社交客戶端已占據了人們的大量時間,所以在我們的應用中具有一鍵分享的功能對提高我們產品的知名度有很大的幫
本文實例講述了Android基於socket實現的簡單C/S聊天通信功能。分享給大家供大家參考,具體如下:主要想法:在客戶端上發送一條信息,在後台開辟一個線程充當服務端,
在移動應用滿天飛的時代,隨著移動支付的盛行,很多應用中都集成了支付功能。之前的支付一直不是我負責,近期這個項目我負責訂單模塊少不了要做支付,每每提起支付就覺得怕怕,覺得很