世界是普遍联系的,任何事物和个体都直接或间接相互依赖,在时空长河中共同发展。在面向对象的世界中,更是如此,类与类之间的依赖,关联关系,模块(亦或是分层架构中的层)之间的耦合关系,都是我们在软件开发实践中,时刻在处理着的联系。

在软件开发中,我们一直尝试弱化这种联系,以便让软件程序更健壮,更灵活,便于维护和升级。从面向过程遍程中提倡方法的分离(过程分离),到面向对象开发中的减少类依赖,层间低耦合,层内高类聚,无一不体现了这一点。正因为在面向对象的世界中,我们更容易处理类(对象),模块(或层)之间的关系,所以面向对象开发才成为了更受欢迎的开发方式。

为了处理代码中的依赖关系,依赖注入框架应运而生。在Java的世界中,开源的依赖注入框架更是不胜枚举,如PicoContainer ,google-guice,Butterfly Container 。。。。。而正如上一篇文章中提到的,Dagger2是第一个使用生成代码的方式实现依赖注入的框架,它完全依赖Java的注解处理和编译时检查来分析和验证依赖,在代码执行效率上拥有无可比拟的优势,而大部分的依赖注入框架都有依赖XML、运行期验证依赖或者影响应用启动速度的问题,所以它依然是我们今天要介绍的主角。

关于Dagger2,已在上一篇文章中详细介绍,在Android开发中,我们可以使用ButterKnife来进行控件注入(但是在子module中使用会有问题),使用Dagger2来进行依赖注入。下面我们来看看在Android开发中,如何使用Dagger2来处理依赖关系。

原理

使用依赖注入是为了降低类之间的依赖关系,从而达到被依赖的类(被依赖者)可以相对独立的变化而不影响其使用者(依赖者),同时在开发和测试过程中,也方便使用mocking object替代原有的被依赖类型,适应不同的开发环境和测试。如何处理好关系,关键在于划分二字,我们通常所说的责任、功能的划分,层、模块、类、方法的划分,都是为了更好的处理各种各样的关系。在Dagger2的使用中,我们依然需要良好划分它的各个组件以便更好的使用它。

下图是Android中推荐的组件级别划分,不熟悉Component,Provide和Module的小伙伴请看上一篇文章。

Application component/module

Application component/module一般提供和应用相同生命周期的依赖,所以通常和@Singleton关联,并暴露必要的依赖以便component dependency使用(通常application component会最为父组件或者component dependency中的dependency)。

 

Activity component/module

Activity component/module一般提供和Activity相同生命周期的依赖,通常和@PerActivity关联,并且一般是SubComponent或者component dependency。使用Activity component有以下几个好处:

可在Activity中注入字段(暴露inject方法在Activity中调用)。

在Activity基类中注入和使用单例对象

全局对象图不用涉及只在Activity中用到的依赖。

User component/module

User component/module用于提供User相关的依赖,一般也和@PerActivity关联,通常会扩展Activity Component,用于注入Fragment。

通常我们会建立一个包含很多接口的主Component,如果希望有多个不需要一直保留在内存中的components(例如绑定到Activity、Fragment甚至用户会话期的components),我们可以创建Dependent component。使用Dependent components需要注意这些方面:

1.两个Dependent components不能共享相同的Scope。

2.虽然Dagger2有创建Scope实例的能力,你依然有责任根据场景创建和删除(实例)应用。

3.创建Dependent components的时候,父component需要明确暴露下游组件需要的依赖对象。

Dependent components和SubComponents的差别在于:

  1. SubComponent需要在子组件声明工厂方法提供实例或Builder。Dependent components只需要传入父Component的实例即可正常初始化。
  2. SubComponent可以访问父Component的所有依赖,而Dependent components只能访问父Component声明提供的依赖。

注意

Dagger2区别于其他诸如框架的主要优势在于它严格依靠生成代码实现(而非反射),这意味着它可以适用于Android。当然,在Android应用中使用Dagger的时候仍然有一些需要考虑的地方。

因为Android应用采用Java语言来开发,从风格来说会非常不同。这种典型的区别是为了适应移动平台独特的表现考虑。

但是与其他的Java代码相反的是,很多模式在Android上都不适用,甚至《高效Java开发》中的大部分建议都被认为不适合Android。

为了同时达到代码惯用(符合语言习惯)和轻便的目标,Dagger依靠ProGuard来处理编译的字节码,这允许Dagger生成在Server和Android上看起来都很自然的代码(当使用不同的工具链来生成在这两个环境中都能高效执行的代码时)。更多的是,Dagger有明确的目标来确保生成的代码和ProGuard的优化兼容一致。

当然不是所有的问题都能以这种方式定位,但是它确实是提供Android特定兼容的主要机制。

Dagger假设Android开发者会使用ProGuard。

Talk is cheap,Show you the code

Setup

我们以AndroidStudio作为IDE,以下简称AS。AS 默认不会把Dagger2生成的代码视作合法的classes,我们可以通过添加android-apt插件将这些文件加入到IDE的class path。我们可以在Project的build.gradle中添加工程依赖:

  1. dependencies {
  2. // other classpath definitions here
  3. classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
  4. }

然后在用到Dagger2的Module中(如app)应用apt插件:

  1. // add after applying plugin: 'com.android.application'
  2. apply plugin: 'com.neenbedankt.android-apt'

同时添加如下Module依赖:

  1. dependencies {
  2. // apt command comes from the android-apt plugin
  3. apt 'com.google.dagger:dagger-compiler:2.2'
  4. compile 'com.google.dagger:dagger:2.2'
  5. provided 'javax.annotation:jsr250-api:1.0'
  6. }

Provided关键词引用的依赖只会在编译时需要,apt关键词是android-apt插件提供的,用于注解处理。

Coding

下面是一个简单的关于宠物的Demo,仅用于说明Dagger2的使用。

一般情况下,在Dagger2中,我们直接使用到的是Component,所以我们首先想到的是创建Component,按照上文的建议和一般的开发习惯,我们会先创建Application级别的Component,用于提供全局的服务或资源。

  1. public interface AppComponent {
  2.  
  3. /**
  4. * Provide your application level handler instance.
  5. *
  6. * @return
  7. */
  8. Handler getHandler();
  9.  
  10. /**
  11. * Provide your global http requester instance.
  12. *
  13. * @return
  14. */
  15. IHttpRequester getHttpRequester();
  16.  
  17. /**
  18. * Provide your global storage service.
  19. *
  20. * @return
  21. */
  22. IStorage getStorage();
  23.  
  24. }

我们声明了AppComponent,大家可以发现它是一个接口文件,我们在里面我们提供了全局的Handler、HttpRequester和Storage服务,当然在实际工程中你可以提供更多的服务。

既然声明了接口,那必然需要有它的实现,但是Dagger2会为我们做这件事,我们需要做的是使用Module来为Component接口声明的方法提供实际的依赖。

  1. /**
  2. * Created by Irwin on 2016/5/16.
  3. */
  4. @Module
  5. public class AppModule {
  6.  
  7. @Provides
  8. @Singleton
  9. public static Handler provideHandler() {
  10. return new Handler();
  11. }
  12.  
  13. @Provides
  14. @Singleton
  15. public static IHttpRequester provideHttpProvider() {
  16. return new DefaultHttpRequester();
  17. }
  18.  
  19. @Provides
  20. @Singleton
  21. public static IStorage provideStorage() {
  22. return new SqliteStorage();
  23. }
  24.  
  25. }

我们实现一个AppModule来为AppComponent提供依赖,其中提供了实际的Handler,HttpRequester和Storage。因为都是全局单例,所有都使用了Singleon关键字和static关键字。然后我们需要通过注解把AppComponent和AppModule关联起来,如下:

  1. /**
  2. * Created by Irwin on 2016/5/16.
  3. */
  4. @Component(modules = AppModule.class)
  5. @Singleton
  6. public interface AppComponent {
  7.  
  8. ......
  9.  
  10. }

完成后我们可以编译代码,如果没有出错,可以在Build目录下找到Dagger2为我们生成的Component接口的实现代码。

接下来,我们在Application中初始化Component:

  1. public class MyApplication extends Application {
  2. private AppComponent mAppComponent;
  3. private String TAG = "ApplicationInfo";
  4.  
  5. @Override
  6. public void onCreate() {
  7. super.onCreate();
  8. mAppComponent = DaggerAppComponent.create();
  9. }
  10.  
  11. public AppComponent getAppComponent() {
  12. return mAppComponent;
  13. }
  14.  
  15. }

为了增加趣味性,我们这个Demo是简单展示宠物的,所以接下来我们添加一个宠物类。

  1. /**
  2. * Created by Irwin on 2016/5/16.
  3. */
  4. public interface IPet {
  5.  
  6. public int getPortrait();
  7.  
  8. public String getType();
  9.  
  10. public String getName();
  11.  
  12. public String getStatus();
  13.  
  14. public void enjoy(TextView view);
  15. }
  1. /**
  2. * Created by Irwin on 2016/5/16.
  3. */
  4. public abstract class AbsPet implements IPet {
  5.  
  6. private int mIndex = -1;
  7.  
  8. private String mStatus;
  9.  
  10. private int mPortrait;
  11.  
  12. @Inject
  13. Handler mHandler;
  14.  
  15. @Inject
  16. List<String> mStatusList;
  17.  
  18. @Inject
  19. protected String mName;
  20.  
  21. @Inject
  22. @Descript("Yell")
  23. protected String mYell;
  24.  
  25. public AbsPet(int portrait) {
  26. mPortrait = portrait;
  27. }
  28.  
  29. @Override
  30. public String getName() {
  31. return mName;
  32. }
  33.  
  34. @Override
  35. public int getPortrait() {
  36. return mPortrait;
  37. }
  38.  
  39. public Handler getHandler() {
  40. return mHandler;
  41. }
  42.  
  43. public List<String> getStatusList() {
  44. return mStatusList;
  45. }
  46.  
  47. @Override
  48. public String getStatus() {
  49. if (mStatus == null) {
  50. mStatus = mYell;
  51. }
  52. return mStatus;
  53. }
  54.  
  55. @Override
  56. public void enjoy(final TextView view) {
  57. if (mIndex == -1) {
  58. view.setText(mYell);
  59. }
  60. Random random = new Random(System.currentTimeMillis());
  61. int seconds = random.nextInt(5);
  62. if (seconds < 1) {
  63. seconds = 1;
  64. }
  65. getHandler().postDelayed(new Runnable() {
  66. @Override
  67. public void run() {
  68. boolean hasNext = nextStatus();
  69. if (hasNext) {
  70. enjoy(view);
  71. }
  72. view.setText(getStatus() + (hasNext ? " Ing" : " ..."));
  73. }
  74. }, seconds * 1000);
  75. }
  76.  
  77. public boolean nextStatus() {
  78. if (++mIndex < getStatusList().size()) {
  79. mStatus = getStatusList().get(mIndex);
  80. return true;
  81. }
  82. mIndex = -1;
  83. return false;
  84. }
  85.  
  86. }

然后分别实现两只宠物,Dog和Cat,这是我们具体要依赖的对象,下面是Dog的代码。

  1. public class Dog extends AbsPet {
  2. private String mType;
  3.  
  4. @Inject
  5. public Dog(int portrait) {
  6. super(portrait);
  7. this.mType = "汪星人";
  8. }
  9.  
  10. @Override
  11. public String getType() {
  12. return mType;
  13. }
  14.  
  15. }

注意构造函数上的@Inject注解,它表明Dagger2可以自动new 这个类的实例。

接下来,我们添加一个Activity来显示宠物,

  1. public class MainActivity extends AppCompatActivity {
  2.  
  3. private static final String TAG = "Info";
  4. private GifImageView mPetView;
  5. private TextView mTV_Info;
  6. private TextView mTV_Status;
  7.  
  8. @Inject
  9. IPet mPet;
  10.  
  11. ......
  12.  
  13. }

如代码中所示,我们将mPet字段添加@Inject注解,通过依赖注入的方式提供值。

需要注入字段的类也需要有一个Component来实现注入,所以我们为此Activity添加一个Component,在此之前我们先添加一个Module来为Component提供依赖,下面是提供Dog依赖的Module

  1.  
  1. @Module(includes = DogStatusModule.class)
  1. public class DogModule {
  2.  
  3. @Provides
  4. @Activity
  5. public IPet provideDog(Dog dog) {
  6. return dog;
  7. }
  8.  
  9. @Provides
  10. public List<String> provideStatus(Set<String> set) {
  11. return new ArrayList<>(set);
  12. }
  13.  
  14. @Provides
  15. public String provideName() {
  16. return "二汪";
  17. }
  18.  
  19. @Provides
  20. public int providePortrait() {
  21. return R.drawable.dog2;
  22. }
  23.  
  24. @Provides
  25. @Descript("Yell")
  26. public String provideYell()
  27. {
  28. return "汪汪";
  29. }
  30.  
  31. }

其中我们为DogModule提供了它所需要的状态Module  DogStatusModule。

接下来添加Activity的Component并关联DogModule,

  1. @Component(dependencies = AppComponent.class, modules = DogModule.class)
    @Activity
    public interface PetActivityComponent {
  2.  
  3. public void inject(MainActivity activity);
  4.  
  5. public IPet getPet();
  6.  
  7. }

完成并编译后,可以在Build目录下找到Dagger2为我们生成的Component接口的实现代码。接下来我们就可以在Activity中简单的使用:

  1. public class MainActivity extends AppCompatActivity {
  2.  
  3. private static final String TAG = "Info";
  4. private GifImageView mPetView;
  5. private TextView mTV_Info;
  6. private TextView mTV_Status;
  7.  
  8. @Inject
  9. IPet mPet;
  10.  
  11. @Override
  12. protected void onCreate(Bundle savedInstanceState) {
  13. super.onCreate(savedInstanceState);
  14. setContentView(R.layout.activity_main);
  15. Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
  16. setSupportActionBar(toolbar);
  17. mPetView = (GifImageView) findViewById(R.id.IMV_Pet);
  18. mTV_Info = (TextView) findViewById(R.id.TV_Info);
  19. mTV_Status = (TextView) findViewById(R.id.TV_Status);
  20.  
  21. PetActivityComponent component = DaggerPetActivityComponent.builder()
  22. .appComponent(((MyApplication) getApplicationContext()).getAppComponent())
  23. .build();
  24. //You must call inject first so that Dagger2 will inject fields for you, otherwise you'll get NullPointer exceptions.
  25. component.inject(this);
  26.  
  27. FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
  28. fab.setOnClickListener(new View.OnClickListener() {
  29. @Override
  30. public void onClick(View view) {
  31. playWith(mPet);
  32. getHttpRequester().request(0, "http://www.baidu.com", "search=dagger2", new IResultHandler<String>() {
  33. @Override
  34. public void onSuccess(String o) {
  35. Log.i(TAG, "Response: " + o);
  36. }
  37.  
  38. @Override
  39. public void onFail(Throwable error) {
  40.  
  41. }
  42. });
  43. }
  44. });
  45. bindPet(mPet);
  46. }
  47.  
  48. public void bindPet(IPet pet) {
  49. mPetView.setImageResource(pet.getPortrait());
  50. mTV_Info.setText(getString(R.string.PetInfo, mPet.getType(), mPet.getName()));
  51. mTV_Status.setText(pet.getStatus());
  52. }
  53.  
  54. public void playWith(IPet pet) {
  55. pet.enjoy(mTV_Status);
  56. }
  57.  
  58. public IHttpRequester getHttpRequester() {
  59. return ((MyApplication) getApplicationContext()).getAppComponent().getHttpRequester();
  60. }
  61.  
  62. public IStorage getStorage() {
  63. return ((MyApplication) getApplicationContext()).getAppComponent().getStorage();
  64. }
  65.  
  66. }

在调用Component的Inject后,Dagger2就为mPet字段注入了值,然后我们就可以放心的使用mPet字段了,同时,我们还可以使用AppComponent中提供的所有服务或资源,如

  1. ((MyApplication) getApplicationContext()).getAppComponent().getHttpRequester().request(...);

至此,代码编写完成。当然,我们还添加了一些额外的代码,如HttpRequester,不同的Storage实现,CatModule,全局都是通过Dagger2使用这些实现,所以你可以随意改变自己的实现或者替换依赖,而不影响上层代码的使用,可以使你的开发和测试变得更简单哦!

运行工程,就可以看到效果了,

此时,如果我们希望使用依赖对象的不同实现,简单的替换Component依赖的Module,或者直接修改Module中相应提供依赖的地方即可。下面我们将Activity中的关联的DogModule替换为CatModule

  1. @Component(dependencies = AppComponent.class, modules = CatModule.class)
  2. @Activity
  3. public interface PetActivityComponent {
  4.  
  5. ...
  6. }

替换后效果:

在实际的开发环境中,你可以随意替换不同的存储,网络请求实现来满足不同的场景和需求,是不是很方便:D

总结

一、      使用Dagger需要引入Dagger和Dagger compiler,在IDEA和Eclipse中可能需要打开Annotation processor。在AndroidStudio中需要引入android-apt plugin(see https://bitbucket.org/hvisser/android-apt,用于将Dagger2生成的代码加入classpath)

Android-apt插件帮你把annotation processors和AndroidStudio结合起来使用,它主要有两个功能:

1.允许配置编译时的annotation作为依赖,但是不会把这些东西包括到最终的Apk或Library中。
2.设置源码路径以便注解处理生成的代码可以被AndroidStudio识别。

二、      类构造函数加上@Inject生成实例,类字段加上@Inject可注入字段,注入字段依靠@Inject的构造函数或者@Provides方法。

注:

  1. Dagger2不能注入final字段和static字段,以及不可访问的字段(Private)。
  2. Dagger2无法注入多个类型相同的字段,但是可以通过加上qualifier识别依赖。
  3. Dagger2创建实例是在生成的T_Factory中进行的,而注入字段是通过T_MembersInjector中完成的,所以直接调用new T()不会注入字段。对于已有的非Dagger注入的对象,可以在Component中添加inject(T)(方法名可以随意)方法,调用这个方法就可以注入需要的字段。

三、      @Provides方法可以有自己的依赖,返回不同的类(也可以是基本类型)。

四、      用一个接口(或抽象类)来提供Component,这个接口里面应该声明没有参数但返回需要类型的方法,接口上需要加上@Component,并通过module参数引入需要的module。Dagger会按照约定生成一个Component实现。

五、      Component的使用方式有两种:一种是使用builder并传入需要的module;另一种是直接调用它的create()方法(适用于它的module中全是@Provides的静态方法时或有无参构造函数时)。

Component像连接@Module和@Inject的桥梁,将所有的东西联系起来。

Component实现主要是通过生成的builder来实例化。使用它的builder()方法来获取一个builder实例。如果一个嵌套的@Component.Builder类型已经存在,builder()方法会返回它的一个生成实现;如果嵌套的@Component.Builder不存在,返回的builder会提供设置每一个需要的modules和component依赖的方法(方法名是以小驼峰命名法命名的module和component类型名)。没有可见默认构造函数的module和component依赖必须明确设置,但是有默认或无参构造函数的可被component访问的module可以省略。下面是使用component builder的例子:(From:http://google.github.io/dagger/api/latest/dagger/Component.html)

  1. public static void main(String[] args) {
  2.  
  3. OtherComponent otherComponent = ...;
  4.  
  5. MyComponent component = DaggerMyComponent.builder()
  6.  
  7. // required because component dependencies must be set
  8.  
  9. .otherComponent(otherComponent)
  10.  
  11. // required because FlagsModule has constructor parameters
  12.  
  13. .flagsModule(new FlagsModule(args))
  14.  
  15. // may be elided because a no-args constructor is visible
  16.  
  17. .myApplicationModule(new MyApplicationModule())
  18.  
  19. .build();
  20.  
  21. }

在component只有无参(构造函数)modules,没有component依赖的情况下,component实现也会有一个工厂方法create(),SomeComponent.create()和SomeComponent.builder().build()是等同的。

六、      Module是一个用来满足稍复杂依赖(在@Inject无法满足的时候)的类,以@Module声明,在它里面可以声明@Provides的方法(Provides方法优先于@Inject构造函数),@Provides方法提供Component里面的绑定需要的依赖(通过返回类型指定,所以无法注入多个类型相同的字段),这些依赖可以类实例,也可以是基本类型。Module还可以通过include引入其他的module。

七、      Scope

@Singleton

@Singleton用来标记单例,但是是在component的生命周期而非应用的生命周期中的单例。

如果T被注解为单例(在T的类声明或在provideT上加@Singleton,同时在Component上加@Singleton),引入T的component中的TProvider会使用如下方式生成(其他的Scope也应如此):

TProvider= ScopedProvider.create(CoffeeMaker_Factory.create(…));

如果T未被标记为单例,TProvider会这样生成:

  1. TProvider=T_Factory.create(…);
@Scope

用@Scope注解组件就可以加上范围限定。在这种情况下,组件实现会保存对所有加上范围限定的类实例的引用以便重用。有Scope的带有@Provides方法的模块只能被拥有相同范围限定的组件引用。

每一个Component都可以用Scope注解来添加Scope。Component实现确保每个实例中每个Scope绑定只有一个规定。如果Component声明了一个Scope,它就只能包括Unscope或者该Scope的绑定。例如:

  1. @Singleton @Component
  1.    interface MyApplicationComponent {
  1.      // this component can only inject types using unscoped or @Singleton bindings
  1.    }

为了让Scope表现正确,何时初始化component实例是调用者的责任。比如说,一个单例组件,在一个应用中只应该被实例化一次,而一个RequestScoped的组件应该请求一次,实例化一次。因为组件是自维护(Self-contained)的实现,退出一个scope跟丢掉对component实例的所有引用一样简单。

父子组件的Scope应该是一种包含关系,父组件的Scope包括的子组件Scope的范围。

八、      Lazy injection

Lazy injection会等到第一次调用Lazy<T>.get()的时候创建T的实例并缓存。

九、      Provider

Provider<T>提供可以返回不同实例的功能,每一次调用Provider<T>.get()都会调用绑定逻辑,生成新的实例。

十、      Qualifier

Qualifier用于单靠类型无法识别依赖的时候(即多个依赖(可注入字段)类型相同时),使用Qualifier注解,Dagger会同时通过类型和qualifier来识别依赖。

十一、        MultiBindings

多重绑定可以把多个对象绑定到一个Set或Map,通过@Provides(type=SET|MAP)方法提供对象到集合中,对于Map 的Key,分为两种方式:一种是在运行时知道Key的情况,需要通过注解来指定Key(支持复杂的Mapkey);一种是在运行时才知道Key的情况,这时可以通过提供Set<Map.Entry>,然后组装成Map。

十二、        子组件

使用子组件可以把应用的对象图划分成子图,可以压缩应用的各个部分,或者在一个组件上使用多个范围限定。组件提供子组件有两种方式,一种是在组件中提供工厂方法来返回子组件或子组件的Builder;第二种是注入子组件的builder。

父子组件会共享相同的Module实例,父组件已经引用的Module类,子组件不需要再传入(包括工厂方法和Builder方法)。

十三、        Producer实现异步注入

Producers引入了新的注解@ProducerModule, @Produces, 和@ProductionComponent,分别对应@Module,@Provides, 和 @Component。我们把@ProducerModule注解的类视为producer modules,@Produces注解的方法视为producer methods,@ProductionComponent注解的接口视为producer graphs(类似于modules, provider methods, 和object graphs)。

和原始Dagger最关键的不同在于producer方法会返回ListenableFuture<T>,并且后面的producer会依赖于这个未封装的T,当future可用的时候,框架会计划执行后续的方法。

通过绑定@Production Executor来指定一个线程池,每一个Producer 的方法都会在这个线程池中被计划执行。

Dagger2在Android开发中的应用的更多相关文章

  1. Android学习探索之Java 8 在Android 开发中的应用

    前言: Java 8推出已经将近2年多了,引入很多革命性变化,加入了函数式编程的特征,使基于行为的编程成为可能,同时减化了各种设计模式的实现方式,是Java有史以来最重要的更新.但是Android上, ...

  2. android开发中fragment获取context

    在用到fragment时无法使用.this来指定当前context内容,android开发中fragment获取context,可以使用getActivity().getApplicationCont ...

  3. java中的反射机制在Android开发中的用处

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反 ...

  4. Android开发中的输入合法性检验

    Why ? 合法性检查对于程序的健壮性具有重要作用.在Android开发中,良好的合法性检查设计机制可以使程序更加清晰,产生bug更少,交互更加友好. What ? 合法性检查的目的在于确定边界.对于 ...

  5. 在android开发中使用multdex的方法-IT蓝豹为你整理

    Android系统在安装应用时,往往需要优化Dex,而由于处理工具DexOpt对id数目的限制,导致其处理的数目不能超过65536个,因此在Android开发中,需要使用到MultiDex来解决这个问 ...

  6. 怎样实现了捕获应用中的日志在android开发中

    怎样实现了捕获应用中的日志在android开发中,大家可研究一下. Process mLogcatProc = null; BufferedReader reader = null; try { mL ...

  7. Android开发中Eclispe相关问题及相应解决(持续更新)

    1.Eclipse项目中的Android Private Libraries没有自动生成. 一般而言,在Android开发中,项目中引用到的jar包会放到项目目录中的libs中,引入库会放到Andro ...

  8. Android开发中的问题及相应解决(持续更新)

    最近博客写的少了,以后还得经常更新才行. ------------------------------------------------------------ 1.特定业务需求下try cath ...

  9. 关于Android开发中的证书和密钥等问题

    关于Android开发中的证书和密钥等问题 引言 除了Android发布应用签名时需要用到证书外,在进行google Map Api开发和Facebook SDK API开发等时都需要申请API Ke ...

随机推荐

  1. Unicode字符集和编码方式

    通常将一个标准中能够表示的所有字符的集合称为字符集,比如ISO/Unicode所定义的字符集为Unicode.在Unicode中,每个字符占据一个码位/Unicode 编号(用4位十六进制数表示,Co ...

  2. SmartCoder每日站立会议02

    1.站立会议内容 经过昨天一天的学习,大家了解到了很多新的知识,在各自任务实现方面也遇到了问题,比如首页的css样式实现.API接口的连接和地图接入的样式,经过今日站立会议,大家在一起讨论了各自出现的 ...

  3. SSH实现无密码验证登录

    http://blog.csdn.net/houqd2012/article/details/8544517

  4. linux JDK或JRE安装或配置

    1. 使用命令“java –version”如果显示如下内容则jdk已安装成功则无需后续操作. 2. 将解压后的文件“jdk-7u79-linux-x64.rpm ”上传到linux系统目录:/usr ...

  5. openfire muc 移除成员

    muc添加成员到数据库可参考 将Openfire中的MUC改造成类似QQ群一样的永久群 插件 插件是一位大神参考第一篇文章改进后编写的插件,进测试可以直接使用. ------------------- ...

  6. 移动端web解决方案

    范畴 移动端web浏览器.至少需要适配的,UC,QQ,各手机内置浏览器,chrome,safari. 是不是觉得和PC端差不多?错了!每款同一版本ios的内置浏览器一样.但每款同一版本android的 ...

  7. Git基础-打标签

    打标签 同大多数 VCS 一样,Git 也可以对某一时间点上的版本打上标签.人们在发布某个软件版本(比如 v1.0 等等)的时候,经常这么做.本节我们一起来学习如何列出所有可用的标签,如何新建标签,以 ...

  8. ubuntu16.04 英文环境安装google chrome

    1.下载google wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 2.安装缺少的依赖 ...

  9. 关于bootstrap中cropper的截图上传问题

    之前做一个关于截图的东东,搞了好久终于弄好了,其主要关键是把前端截图的数据(x坐标,y坐标,宽度,高度和旋转角度)传到后台,然后在后台对图片做相关处理,记录一下方便以后查看. 后台配置为ssm. Ja ...

  10. TreeMap集合特点、排序原理

    TreeMap特点(类似于TreeSet): 1.无序,不允许重复(无序指元素顺序与添加顺序不一致) 2.TreeMap集合默认会对键进行排序,所以键必须实现自然排序和定制排序中的一种 3..底层使用 ...