构建Android应用

Android基础构建设计

author: liuwang.zhong

version:0.1.0

前言

对于Android的构建流程来说,更多的是关注我们的一些所谓“规定”。这些规定不一定是需要去追随,可以由我们员工们自行选择。其中的规定也会随着工程、项目而改变。这只是一些意见,我们希望可以达到一种效应:Android的代码有着自己公司员工的共同审美,接受我们这种命名、分类、流程,进而做好构建出一个个健壮应用的准备。

开发工具

一般工具有:AndroidStudio,JDK,Gradle,Git等。

对于Android Application的开发工具采用AndroidStudio。当前采用的版本号为:2.3.1。下载时尽量,下载不带SDK版本的,自带SDK版本的问题比较多。JDK的JAVA版本下载:1.8.0_112版本。Gradle版本为:3.3。工具下载地址表

工具名称下载地址
AndroidStudiohttp://www.androiddevtools.cn/
JDKhttp://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
Gradlehttp://pan.baidu.com/s/1pLEkm4F#list/path=%2F
Githttps://github.com/git-for-windows/git-sdk-64

环境变量配置情况如下:

  • java环境变量
  • adb shell 环境变量
  • SDK 环境变量
  • gradle 环境变量

配置代码如下


#Java环境变量
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home
export JAVA_HOME

#NDK环境变量
export NDK_HOME=/Users/**/Library/Android/sdk/ndk-bundle/
export PATH=$NDK_HOME:$PATH

# android工具环境变量
export ANDROID_HOME=/Users/**/Library/Android/sdk
export emulator=/Users/**/Library/Android/sdk/emulator/
export PATH=$emulator:$PATH
export PATH=$ANDROID_HOME/tools:$PATH
export SDK_MANAGER=/Users/**/Library/Android/sdk/tools/bin/
export PATH=$SDK_MANAGER:$PATH
export PATH=$ANDROID_HOME/platform-tools:$PATH

#gradle
export GRADLE=/Users/**/gradle/gradle-3.3
export PATH=$GRADLE/bin:$PATH

#**代表用户名

应用构建

默认配置
  • gitignore配置

工程结构是一个Project非Module。工具结构和AndroidStudio默认的一样。代码使用Git库管理,Project的gitignore文件内容

# Gradle files
.gradle
build

# Local configuration file (sdk path, etc)
local.properties
.idea/workspace.xml
.idea/libraries

# IntelliJ project files
**.iml
.idea

# ignore the mac file
.DS_Store

#other
.externalNativeBuild
captures

  • projectGradle配置

工程的Project的build.gradle需要配置一下公用的配置,主要是default setting的内容,达到工程的统一配置,而且也可以避免support版本不一致导致的BUG。内容如下:

 // Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}


task clean(type: Delete) {
    delete rootProject.buildDir
}

//default setting
ext {

    //sdk version
    base_compile_sdk_version = 25
    //build tool version
    base_build_tools_version = '25.0.2'

    //min sdk version
    base_min_sdk_version = 15
    //target sdk version
    base_target_sdk_version = 24

    //support library version
    base_support_version = '25.3.1'
}

引用列子

apply plugin: 'com.android.application'

android {
    compileSdkVersion base_compile_sdk_version
    buildToolsVersion base_build_tools_version
    defaultConfig {
        applicationId "com.richdataco.testandroidstudio"
        minSdkVersion base_min_sdk_version
        targetSdkVersion base_sdk_version
        versionCode 1
        versionName "1.0"
        //省略
}

dependencies {
    //省略
    compile "com.android.support:appcompat-v7:$base_support_version"
    compile "com.android.support:appcompat-v4:$base_support_version"
}

  • 兼容包开发

工程的开发必须采用兼容包开发。使用v7,v4等来构建app,不能直接使用Activity这种开发,其次,我们的测试样机必须有三个点:Android4.4,Android5.0,Android6.0。兼有问题表如下:

常见兼有问题常见兼容详情解决方案
键盘兼容对于目前的手机,有点手机采用的是软菜单键和物理按键,所以要防止,UI界面出现的软键盘挡住了,无法点击Tab设置软键盘在UI之下,测试可以用华为的软键盘测试
AsyncTask兼容对于AsyncTask尽量不用使用来处理大任务,要不会出现异常采用Thread任务队列进行处理
打开网络兼容对于低版本的手机是可以通过反射等操作进行打开移动网络,其实这些操作是危险的,而且不必要的不要实现这些功能,是伪需求
动态权限的兼容对于Android6.0的机器都是采用动态权限了,我们必须进行兼容,要不然,会出现在Android6.0上运行无缘无故打不开app,一安装就shutdown等问题添加动态权限处理
动画兼容对于使用到属性动画的技术,必须要进行兼容,因为这是在Android3.0加进来的独立动画框架采用com.nineoldandroids:library兼容包开发
support包兼容由于采用的support兼容包得版本不一致也会导致问题按照我们队project的Gradle配置进行,就可达到统一版本
对于状态栏兼容由于需要实现沉淀式的状态栏,这时候需要进行兼容,要不会出现setContentView异常尽可能得进行多个Android版本的手机测试
向后兼用对于向后兼容,可以看到google的support包的努力,采用这些包开发会大大减少我们向后兼容的工作使用Support进行开发
SO库兼容对于SO库来说,这是基于芯片进行编译的,所以一定程度上是削弱了兼容性要拆开来看看so库提供的情况进行判断,最好可以提供arm64-v8a,x86_64,x86,armeabi,armeabi-v7a
WebView兼容对于WebView的问题说不清,但是要有意识去兼容,因为它是出现bug的交通事故多发地段多测试,多思考
应用打包

应用打包采用Gradle进行打包。下面有一个Gradle打包模板,模板如下:

import java.text.SimpleDateFormat

apply plugin: 'com.android.application'

android {
    compileSdkVersion base_compile_sdk_version
    buildToolsVersion base_build_tools_version
    defaultConfig {
        applicationId "com.richdataco.base"
        minSdkVersion base_min_sdk_version
        targetSdkVersion base_target_sdk_version
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }

    signingConfigs {
        config {
            storeFile file('./keys/BaseKey.jks')
            keyAlias 'BaseKey'
            keyPassword 'rdc@2016'
            storePassword 'rdc@2016'
        }
    }

    buildTypes {

        debug {
            //配置是打印log
            buildConfigField "boolean", "LOG_DEBUG", "true"
            minifyEnabled false
            //关联签名
            signingConfig signingConfigs.config
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        release {
            //配置不打log
            buildConfigField "boolean", "LOG_DEBUG", "false"
            minifyEnabled false
            //关联签名
            signingConfig signingConfigs.config
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    //打包类型
    productFlavors {

        //debug版本,注意applicationId最好不要和release版本一致,方便测试,
        // 而且可以防止测试人员用debug版本去发布
        run_debug {
            applicationId "com.richdataco.demo_bug"
        }

        //release版本
        app_release {
            applicationId "com.richdataco.demo"
        }

    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
    lintOptions {
        abortOnError false
    }
    
    //输出文件
    applicationVariants.all { main ->
        main.outputs.each { output ->
            def file = output.outputFile
            output.outputFile = new File(file.parent,
                    "comm_demo_" + main.buildType.name +
                            "_v_" + main.versionName + "_"
                            + buildTime() + ".apk");
        }
    }
}

def buildTime() {
    def df = new SimpleDateFormat("yyyyMMdd")
    df.setTimeZone(TimeZone.getDefault())
    return df.format(new Date())
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile "com.android.support:appcompat-v7:$base_support_version"
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    compile project(':richdatacommon')
}

打包命令的使用,打开Terminal进行一下命令
gradle build或者直接运行期release版本 gradle assembleRelease既可以。

工程框架

工程采用MVC或者MVP模式。这里对MVC模式忽略不讲。对于MVP模式的简要结构如下:

View Presenter Model View持有Presenter Presenter持有Model Presenter将信息发给View绘制 Model只能通过Presenter进行View的交互 View只通过Presenter进行Model的交互 进行事务流程,保存,刷新等View 暴露所有的绘制接口给Presenter进行依赖 View Presenter Model

在MVP模式中,Presenter和Model是可以拖离SDK的,这样更加的方便单元测试。View层的测试可以通过SDK进行测试(纯UI测试)。

网络接口设计

####Response基本格式

对于http的请求的返回格式采用这个基本格式,default_json

//重要数据在result里
{ 
   "isError":false,
   "errorType":0,
   "errorMessage":"",
   "result":{
         "cinemaId":1,
         "cinemaName":"小明"
   }
}
//异常的类型
{ 
   "isError":true,
   "errorType":1,
   "errorMessage":"网络异常",
   "result":""
}

isError是调用MobileAPI成功与否,errorType是错误类型(如果成功为0),errorMessage是错误消息(如果成功则为空),result是请求返回的数据结果(如果失败则为空)。这样的统一方便我们队请求数据的进行统一处理。

虚拟远程服务

对于Application的开发来说。一般都是要应用后台的接口工作,如果有完善的接口文档的话,在文档提供详细的回调格式,并且后台写一些假的数据给我前端开发,这样我们的Application还是可以前行无阻的,但是会陷入一个怪圈,Application的人在等接口,或相符沟通接口的变化,之后,还要等后台人员修改锁定的假数据。这里我参考了以往的经历和网上的资源采用了一个MockService来解决这个问题,只需要Application这端进行模拟数据。要实现的这个设计的前提是:

  • 提供良好接口文档,包括反回数据(json结构);
  • 数据返回的结构符合BaseResponse的结构;

之后在代码实现MockService既可。例子如下

//在res/xml/url.xml里添加接口
//其中MockClass为虚拟的请求数据返回模型

<?xml version="1.0" encoding="utf-8"?>
<url>
    <Node
        Key="getWeatherInfo"
        Expires="300"
        NetType="get"
        MockClass="com.youngheart.mockdata.MockWeatherInfo"
        Url="http://www.weather.com.cn/data/sk/101010100.html" />

    <Node
        Key="login"
        Expires="0"
        NetType="post"
        MockClass="com.youngheart.mockdata.MockLoginSuccessInfo"
        Url="http://www.weather.com.cn/data/sk/101010100.html" />
</url>

//虚拟数据
public class MockWeatherInfo extends MockService {	
	@Override
	public String getJsonData() {
		WeatherInfo weather = new WeatherInfo();
		weather.setCity("Shenghai");
		weather.setCityid("10203");

		Response response = getSuccessResponse();		
		response.setResult(JSON.toJSONString(weather));
		return JSON.toJSONString(response);
	}
}

//进行请求
RemoteService.getInstance().invoke("getMusic", targetContext, "getMusic2", params, null, requestCallback);

主要是在MockClass进行管理需要模拟的数据,如果MockClass为空即走真实的请求。

代码规范与规定

代码规范的原则是简单易懂,不罗嗦,也可以参考google的java编程规范风格。

JAVA基本命名规约
  • 1.代码中的命均不能以下划线或者美元符号开始,也不能以下划线和美元号结束。
    反例: _name / __name / \$Object / name_ / name$

  • 2.类名使用UpperCamelCase风格,必须遵从驼峰形式
    正例: UserDO / XmlService / LoginActivity / TcpUdpDeal

  • 3.方法名、参数名、成员变量、局部变量都统一使用lowerCameCase风格,必须遵从驼峰形式。
    正例: localValue / getHttpMessage / inputUserId

  • 4.常量名全部大写,单纯间使用下划线隔开,力求语义表达清楚,不要嫌名字长。
    正例:MAX_STOCK_COUNT
    反例:MAX_COUNT

  • 5.包名统一小写,点分隔符直径仅有一个自然语义的英文单词。包名统一使用单数形式,但是类名如果有复数含义,可以使用复数形式,而且报名一般是和公司网址为开头的格式为:com.company_name.project_name。
    正例: com.richdataco.iot.util 类名为MessageUtils

  • 6.m前缀的成员变量具有广泛性,如果实现了一个类它没有涉及事务,即可用m作为前缀。
    正例:com.richdataco.view.MyView里的成员变量mPaint,mHeight,mFlag

Android内的规约
  • 1.四大组件Activity,Service,Content Provider,Broadcast Receiver的实现必须带对应的后缀。

  • 2.控件ID命名规范,控件的Id必须按照以下规则命名: 模块+类型+功能

    正例:login_btn_enter / user_space_iv_photo
    反例:btn_login / iv_user_photo(因为用户头像在很多地方都有,而且AndroidStudio的提示时可以进行快速定位,你需要的控件是在user_space下的类型为ImageView(iv)功能为photo的图片)

  • 3.控件自定义属性,除了layout相关的可以使用下划线隔开,其他的都是采用lowerCameCase风格,不用下划线。正例如下:

    <com.companyname.view.DivView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_weight="0"
        android:clickable="true"
        android:enabled="true"
        android:onClick="intentToMain"
        android:text="crop image"
        android:textAllCaps="false"
        android:textColor="@color/colorAccent"/>

  • 4.layout下布局命名必须采用小写加下划线的方式,而且对于Activity和Fragment使用的需要加activity_fragment_作为前缀;ListView的item使用要带item_;
    正例:activity_main fragment_home fragment_profile item_my_favorites_food

  • 5.values下面的colors,dimens,strings都是采用全部小写加下划线的命名方式,但是对于styles的命名需要采用UpperCamelCase风格,也可以这样认为,对于Style是一个类,Style的属性是方法。正列如下:


    //---------------颜色的-------------------
    <color name="mid_night">#22313f</color>
    <color name="bon_jour">#E4E2E4</color>
    <color name="charade">#3C3F41</color>
    <color name="gallery">#EFEFEF</color>
    <color name="cornell_red">#BB0F23</color>
    <color name="cornell_red2">#bb0f45</color>
    <color name="jaguar">#2B2B2B</color>

    //---------------单位的-----------------------
    <dimen name="text_size_button">14sp</dimen>
    <dimen name="text_size_menu">14sp</dimen>
    <dimen name="text_size_iv_tell">12sp</dimen>
    <dimen name="text_size_body_1">14sp</dimen>
    <dimen name="text_size_body_2">16sp</dimen>
    <dimen name="text_size_sub_head">16sp</dimen>
    <dimen name="text_size_title">20sp</dimen>
    <dimen name="text_size_head_line">24sp</dimen>
    <dimen name="text_size_display_1">34sp</dimen>
    <dimen name="text_size_display_2">45sp</dimen>
    <dimen name="text_size_display_3">56sp</dimen>
    <dimen name="text_size_display_4">112sp</dimen>

    //--------------------styles------------------
    <style name="SplashStyle" parent="AppTheme">
        <item name="colorPrimary">@color/athens_gray</item>
        <item name="colorPrimaryDark">@color/athens_gray</item>
        <item name="android:background">@color/athens_gray</item>
        <item name="android:windowBackground">@null</item>
    </style>

工具类

AndroidLog

是app调试log的工具,可以通过gradle进行配置log的开关。默认release版本是关闭,debug版本为打开,不用在代码中写BuildConfig,因为编译器已经默认添加了一个BuildConfig。

DensityUtils

给类是实现android的各种屏幕适配的单位转化。

BindViewTools.jar
在之前说过了android控件命名的规范后,下面简单介绍一下公共类中写的一个BindView工具的使用,使用例子如下

//默认是Activity下的findViewById
java -jar BindViewTool.jar -p $layout_file_path
//Fragment下的
java -jar BindViewTool.jar -f true -p $layout_file_path
//添加忽略标识命令,对于m开头的进行忽略
java -jar BindViewTool.jar -f true -i m -p $layout_file_path

//返回结果示例
private LinearLayout testBindLyContainer;
private TextView testBindTvInfo;
private Button testBindBtnLogin;
private Button testBindBtnUser;
private ListView testBindLvNews;



public void bindViews(){

        testBindLyContainer = (LinearLayout)getView().findViewById(R.id.test_bind_ly_container);
        testBindTvInfo = (TextView)getView().findViewById(R.id.test_bind_tv_info);
        testBindBtnLogin = (Button)getView().findViewById(R.id.test_bind_btn_login);
        testBindBtnUser = (Button)getView().findViewById(R.id.test_bind_btn_user);
        testBindLvNews = (ListView)getView().findViewById(R.id.test_bind_lv_news);
}

其他的工具类

都可以通过java的名字推测得知。

其他

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值