从Eclipse到Intellij

由于本人最近项目做过一次从eclipse到intellij切换的分享会,本来打算把相关的技术文档发给组员就可以完事。但是发现很多同学在切换的过程中仍然感觉到很难受,所以我打算把这次分享会的内容以文章的形式呈现出来。给更多的同学使用。

此文章是个人工作中的一次总结,当看到网上很多ctrl+c,ctrl+v的文章的时候。个人觉得还不如自己总结一下整个流程来得真实。这也是写本文章的一个动机。

前提

在我们公司将软件工程师简称RD(Research Development)。所以我们从事着一份富有创造力和探究精神的职业。我们秉承软件工程师 ≠ 码农的职业路线,在科学技术的道路上不断探索、创造和前行着。我相信跟我同样职业的大部分人都有着自己“高傲”的一面,遇到问题解决问题以及不服的劲头经常出现。我是一个有着代码洁癖的人,以前遇到一个很好的架构师对方法和{}之间的空隙都有着严格的要求。当时的我是那么的不理解,但是当代码量越来越大的时候,好的编码格式就体现的是那么的明显。工作中好的东西需要我们有敢于尝试的勇气,这样对于我们提升自己我驱动的效率会高很多。所以很希望同行的同学们都保持一种高质量的创造水准,一起同行吧。。。

这里扯的有点多,不过也正是因为有问题的出现,新的探究就会萌芽。最后发展推广到成熟与运用。

文章主要侧重点是Android studio,不过个人更愿意大家能接触到intellij IDEA这个编译器上。

为何要做迁移?

不得不说的Intellij

intellij号称是最专业的IDE工具。下面这些总结都是本人在实际工作中深有体会的一些值得说明的优点:

  1. 自动完成的特性(由于非常快速的索引,这种支持速度要快上几个数量级)
  2. 强大的插件化工具集成(例如像版本控制git,svn)
  3. 代码的检测功能(自动检测unused/null,代码可精简写法,多个try catch的合并提示,String的拼接自动提示,toString的warp包装提示)
  4. 可以根据选择的地方来判断新建文件类型(同时还提供不同模板,这一条eclipse绝对是做不到的)
  5. IntelliJ的调试器(“Smart step into”,如果一行中存在多个方法调用,我就可以选择进入到哪个方法中了)
  6. 重构功能(Android特定重构和快速修复)
  7. 工程目录与maven更相符合
  8. 更加强大的快捷键(alt+enter)
  9. show history功能对每一个本地操作都可以追溯到
  10. terminal 集成
  11. 个人非常喜欢黑色经典主题Darcula(语法高亮)
  12. 学习成本,其实IntelliJ是要比Eclipse低的,至少省去了很多配置插件、理清依赖、处理问题的功夫,同时设置也比Eclipse要简单不少

Android studio独有特性

  1. 采用最流行的Gradle构建工程(支持ProGuard和应用签名功能)
  2. 提示(比如左侧的图片、颜色和标签,右侧的警告)
  3. UI设计(自带布局编辑器,text与实时的设计界面效果review,还能针对不同屏幕尺寸做效果切换,自带布局编辑器,可以让你拖放UI组件,并在多个屏幕配置上预览布局,等等。)
  4. 提示工具更好地对程序性能、可用性、版本兼容性和其问题进行版本捕捉;
  5. analyze inspecting code (lint 去除无用资源)

如何做迁移?

下面侧重讲一下Android studio如何做迁移这个事情

  • 导入工程,直接import
  • 新建工程,直接New Project,或者 New Library Project
  • 如果说工程中存在*iml文件(这样的文件类似于 .classpath文件),后续直接open就ok

自动化工具的演进

  1. make
  2. ant Ant是第一个“现代”构建工具,主要的不足是用XML作为脚本编写格式。 XML,本质上是层次化的,并不能很好地贴合Ant过程化编程的初衷。Ant的另外一个问题是,除非是很小的项目,否则它的XML文件很快就大得无法管理。Ant的主要优点在于对构建过程的控制上。
  3. maven Maven具备从网络上自动下载依赖的能力(Ant后来通过Ivy也具备了这个功能),这一点革命性地改变了我们开发软件的方式。但是,依赖管理不能很好地处理相同库文件不同版本之间的冲突
  4. gradle 它具有Ant的强大和灵活,又有Maven的生命周期管理且易于使用。Gradle样板文件的代码很少,这是因为它的DSL被设计用于解决特定的问题:贯穿软件的生命周期,从编译,到静态检查,到测试,直到打包和部署。

Gradle
Gradle是以Groovy为基础,面向java应用,基于DSL(Domain Specific Language)语法的自动化构建工具。是Google引入,替换ant和maven的新工具,其依赖兼容maven(mavenCentral())和ivy(jcenter())。
使用gradle的目的:

  • 更容易重用资源和代码
  • 可以更容易创建不同的版本的程序,多个类型的apk包
  • 更容易配置,扩展
  • 更好的IDE集成

Android Studio
从工程的整体结构上来讲,Android studio采用了gradle工具来构建整个工程。脚本主要涵盖了如下五个方面:

  • Properties
  • Signing
  • Flavors
  • Build Types
  • Dependencies

项目构成
在最外层构建一个公共脚本,包括一些基本的构建工具版本信息和整个项目的编码方式等等。下边这段脚本是本人在项目中的运用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ext {
compileSdkVersion = 21
buildToolsVersion = System.env.CUSTOM_BUILDTOOLS != null ? System.env.CUSTOM_BUILDTOOLS : '21.1.2'
}
buildscript {
def gradleVersion = System.env.CUSTOM_GRADLE != null ? System.env.CUSTOM_GRADLE : '1.0.1'
repositories {
if (System.env.CUSTOM_REPO != null) {
maven { url System.env.CUSTOM_REPO }
} else {
mavenCentral()
}
}
dependencies {
classpath "com.android.tools.build:gradle:$gradleVersion"
}
}
allprojects {
repositories {
if (System.env.CUSTOM_REPO != null) {
maven { url System.env.CUSTOM_REPO }
} else {
mavenCentral()
}
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
}
apply plugin: 'android-reporting'

ext主要是定义工程非正式属性,像编译工程使用的sdk版本,构建工具gradle版本。
buildscript主要是定义项目使用的库地址,这里一般有两种选择mavenCentral()和jcenter()。

Libray工程构建
Library工程只需要简单配置:

  1. 插件
  2. 构建工具
  3. 依赖(可以添加本地jar、也可以添加远程依赖jar、还可以添加library工程)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apply plugin: 'com.android.library'

android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion

defaultConfig {
minSdkVersion 8
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.code.gson:gson:2.3.1'
compile project(":tuancorelibrary")
}

App工程
在app工程中比较重要的是对android属性的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
defaultConfig {
applicationId "com.baidu.nuomi.sale"
minSdkVersion 8
targetSdkVersion 21
versionCode 15
versionName "2.0.5"
multiDexEnabled false

ndk {
moduleName "MyJNI"
ldLibs "log", "z", "m"
abiFilters "armeabi", "mips", "x86"
}
}

ndk是很多同学容易搞不懂的地方。其实这个地方主要是配置我们工程下src->main->jni下的c代码打包成so并随着apk发布。
另外,第三方的so包应该放在src->main->jniLibs 下面的armeabi、x86对应文件夹中,这样就可以随着apk发布了。

签名与混淆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
signingConfigs {
debug {
}
release {
storeFile file("androidkey.keystore")
storePassword "Showmethemoney"
keyAlias "bnsale"
keyPassword "Showmethemoney"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true //实现了自动清除无用资源的功能,还能够移除项目中引用的libraries的无用资源
zipAlignEnabled true
shrinkResources true
buildConfigField "boolean", "ISDEBUG", "false"
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’ //混淆
}
debug {
shrinkResources false
signingConfig signingConfigs.debug
buildConfigField "boolean", "ISDEBUG", "true"
}
}

多渠道打包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
productFlavors { //就是可定义的产品特性
wandoujia {
}
91 {
}
360 {
}
}
productFlavors.all { flavor -> //此处name 就是flavor中定义的sale_rd。此处是一个遍历所有特性替换渠道号
flavor.manifestPlaceholders = [BaiduMobAd_CHANNEL_VALUE: name]
//百度统计中采用这样的方式处理 android:value="${BaiduMobAd_CHANNEL_VALUE}"
}
applicationVariants.all { variant ->
def Date currentTime = new Date();
def java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyyMMddHHmmss");
def String dateString = formatter.format(currentTime);
variant.outputs.each { output -> //为每个不同的输出包重新打包,修改名称,可以采用java的方式写
def String tmpName
def String fileName
if (variant.name.contains("Release")) {
tmpName = variant.name.replace("Release", "");
fileName = "saleapp_${tmpName}_${defaultConfig.versionName}_${dateString}_release";
} else {
tmpName = variant.name.replace("Debug", "");
fileName = "saleapp_${tmpName}_${defaultConfig.versionName}_${dateString}_debug";
}
output.outputFile = file("$project.buildDir/${fileName}.apk")
}
}

由于脚本采用了groovy,所以内部可以使用java的代码。在上面这段代码中本人重点用了Date来对apk重新定义,这一点在多渠道打包中是非常有用的。

Gradle自定义Task

由于我们在开发中经常遇到一些不常见的需求,那么我们需要自定义一些Task来达到我们的需求

例如项目还无法完全迁移的时候,我们可以采用ant引用原始打包方式来执行打包。

1
ant.importBuild ‘build.xml’ //使用ant原始打包方式

自定义任务

1
2
3
4
5
6
task releaseJar(type: Copy) {  //使用task重命名包
from( 'build/bundles/release')
into( 'build/libs')
include('classes.jar')
rename('calsses.jar', 'superlog' + VERSION_NAME + '.jar')
}

调取系统命令行做更多事情

1
2
3
4
5
task testJavaExec(type: JavaExec){ //使用java 命令执行main方法
main = 'Hello'
args '-d',"build"
args '-p', "nice"
}

键位映射

下面列出了一些个人认为比较使用的一些快捷键。很多同学说自己去改这个key-map,不过个人不是很认同,当你改了过后你会发现你越改越乱。

  1. 高亮所有相同变量ctrl+shift+F7
  2. 在方法和内部类之间跳转
    alt+↑/↓
  3. 类文件结构弹窗
    ctrl+F12
  4. 方法调用层级弹窗
    ctrl+alt+H
  5. 定义快速查找
    ctrl+shift+i(能看到当前代码快的快照)
  6. 收缩或者展开代码快
    ctrl+shift+ +/-
  7. 书签,保存你的代码现场
    F11
  8. 带字母或者数字的标签
    ctrl+F11
  9. 展开标签
    shift+F11
  10. 查找菜单选项
    ctrl+shift+A
  11. 代码行级移动
    alt+shift+↑/↓
  12. 删除行
    ctrl+Y
  13. 行复制
    ctrl+D
  14. 扩大或者缩小选择范围
    ctrl+W/ctrl+shift+W
  15. 包裹代码段
    ctrl+alt+T
  16. 查询最近编辑文件
    ctrl+E
  17. 代码模块
    ctrl+J (自动生成像单例一样的简单代码块)
  18. 方法整体移动
    ctrl+shift+↑/↓
  19. 代码补全
    ctrl+shift+Enter
  20. 回到上一次最后编辑位置
    ctrl+shift+backspace
  21. 代码行合并
    ctrl+shift+J
  22. 操作当前文件信息
    alt+F1
  23. 移除包裹代码 (移除代码结构中的包裹代码,比如 if 语句, while 循环, 或者 try/catch 语句)
    ctrl+shift+delete

Intellij + Gradle可以做得更多

笔者之前做了一个后台服务器的项目,发现gradle在项目管理上是如此的得心应手。所以单独拿一个section来说说Intellij和Gradle结合的另一些好的地方

  1. 多环境配置
    先抛开studio这个编译器,在正常的开发中一般都会涉及到开发环境和上线环境。
    那么我们用gradle是如何做到这件事情的呢?来看一下本人自己的一段脚本:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def env = System.getProperty("env") ?: "dev"
    sourceSets {
    main {
    output.resourcesDir = "${buildDir}/classes/main"
    java {
    resources {
    srcDirs = ["src/main/resources/public", "src/main/resources/$env"]
    }
    }
    }
    }

此段脚本可以很灵活的切换当前环境是dev还是publish(这里是dev的)。

  1. 强大的插件化友好支持
    做服务器的同学应该对war包非常的熟悉。如果在eclipse上直接运行工程的话需要配置需要运行服务器的插件,而这个配置过程其实是一个很痛苦的过程。而使用如下的方式将让我们从繁琐的配置中解脱出来:

    1
    2
    3
    4
    apply plugin: 'java'
    apply plugin: 'groovy'
    apply plugin: 'war'
    apply plugin: 'jetty'
  2. 脚本整理
    当工程越来越复杂的时候,脚本规整是一件很重要的事情。gradle中可以使用List来规整包依赖。可以使用apply调用多个gradle脚本文件达到拆分脚本的目的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     List spring = ['org.springframework:spring-jdbc:4.1.3.RELEASE',
    'org.springframework:spring-context-support:4.1.3.RELEASE']
    List utils = ['org.tuckey:urlrewritefilter:4.0.4',
    'org.apache.httpcomponents:httpclient:4.3.6',
    'com.googlecode.xmemcached:xmemcached:2.0.0',
    'org.aspectj:aspectjtools:1.8.4',
    'org.freemarker:freemarker:2.3.21',

    'c3p0:c3p0:0.9.1.2']

    dependencies {
    compile project(':core')
    providedCompile 'javax.servlet:servlet-api:2.5'

    runtime 'javax.servlet:jstl:1.2'
    compile spring, utils
    }

    apply "custom_build.gradle"

笔者看过spring的源码已经切入采用gradle构建,不过脚本的书写还不够专业。很多冗长的书写方式看起来非常的早够。也希望使用的同学注意自己的脚本质量。