Nico随笔


  • 首页

  • 归档

  • 标签

Gradle配置分析

发表于 2015-11-01 | 分类于 android

TopicLevel的gradle

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

        // 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
}

Mudule level的build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 20
    buildToolsVersion "24.0.0"


    compileOptions{
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    defaultConfig {
        applicationId "com.lyc.study"
        minSdkVersion 15
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"
        jackOptions{
            enabled true
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:support-v4:+'
    compile 'com.android.support:support-annotations:22.2.0'
    compile 'com.android.support:support-v13:20.1.1'
}

以上是标准的打包方式,下面是常用的我们自定义的内容

dependencies {
    compile fileTree(include: '*.jar', dir: 'libs')
    compile project(':google_services')

    // compile 'com.android.support:multidex:1.0.1'
    compile 'top.zibin:Luban:1.0.9'
    debugCompile 'com.bugtags.library:bugtags-lib:latest.integration'

    testCompile 'junit:junit:4.12'

}

android {
    compileSdkVersion 21
    buildToolsVersion '23.0.2'

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }

        test.setRoot('test')

        debug.setRoot('build-types/debug')
        release.setRoot('build-types/release')
    }

    signingConfigs {
        debug {
            // 请配置好pub.key及其密码,或者改为使用debug.keystore
            storeFile file('pub.key') // storeFile file('debug.keystore')
            storePassword STORE_PASSWORD
            keyAlias KEY_ALIAS
            keyPassword KEY_PASSWORD
        }
        release {
            storeFile file('pub.key')
            storePassword STORE_PASSWORD
            keyAlias KEY_ALIAS
            keyPassword KEY_PASSWORD
        }
    }

    buildTypes {
        debug {
            buildConfigField "String", "RELEASE_TIME", "\"Not yet\""
            buildConfigField "boolean", "DEVELOP_MODE", "true"
            signingConfig signingConfigs.debug

            ndk{
                abiFilters 'armeabi', 'armeabi-v7a', 'x86' //,'arm64-v8a', 'x86_64', 'mips', 'mips64'
            }
        }
        release {
            buildConfigField "String", "RELEASE_TIME", "\"2017/1/1\"" //  发布时修改为当天日期
            buildConfigField "boolean", "DEVELOP_MODE", "false" // 关闭开发者模式
            proguardFiles 'proguard.cfg'
            minifyEnabled true
            shrinkResources true
            debuggable false
            jniDebuggable false
            signingConfig signingConfigs.release

            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
                        // APK命名格式 Going-release.apk TODO 发布时使用
                        def fileName = "Going-v${defaultConfig.versionName}-${defaultConfig.versionCode}-release.apk"
//                         def fileName = "Going.apk"
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
                }
            }
        }
    }

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 22  // 请勿随意改动 //Android 6.0系统默认为targetSdkVersion小于23的应用默认授予了所申请的所有权限
        versionCode 1000
        versionName "1.0.0"
        //manifestPlaceholders = [ UMENG_CHANNEL_VALUE:"googleMarket" ]
        multiDexEnabled false
        multiDexKeepProguard file('multiDexKeep.pro')
        resConfigs "en", "zh_CN", "zh_TW" //暂时限定语言, 当前情况下缩小大概 0.2M TODO googlePlay 版本取消限制
    }

    productFlavors {
          own {}
          googleMarket {}

          tencent {}
          baidu {}


    }

//    productFlavors.all { flavor ->
//        flavor.manifestPlaceholders = [ UMENG_CHANNEL_VALUE:name ]
//    }

    packagingOptions {
        exclude 'META-INF/LICENSE.txt'
    }

    // 可以适当的打开Lint,检查是否存在隐藏问题
    lintOptions {
        checkReleaseBuilds false
        abortOnError false
    }

    android.dexOptions {
        jumboMode = true
        javaMaxHeapSize "2g"
        maxProcessCount 8
    }

}

// 替换编码方式,否则编译可能有中文乱码
tasks.withType(org.gradle.api.tasks.compile.JavaCompile) {
    options.encoding = "UTF-8"
}

相对于标准的我们在 android Task中新增了sourceSets这个Task制定了一些文件夹的目录,如果工程目录是标注的就不需要设置了

在build.gradle同级新建一个gradle.properties
里面可以用来存放build.gradle里面的一些参数
STORE_PASSWORD=nico
KEY_ALIAS=nick
KEY_PASSWORD=nico

这个地方对应的脚本是signingConfigs》debug里面的参数

buildTypes 》debug 中的buildConfigField参数则会在编译时候

gradle01

buildConfigField "String", "RELEASE_TIME", "\"Not yet\""
buildConfigField "boolean", "DEVELOP_MODE", "true"

会在编译时候生成并存放在build/generated/source/buildConfig/…BuildConfig.java

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.lyc.study";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = "own";
  public static final int VERSION_CODE = 100;
  public static final String VERSION_NAME = "1.0.0";
  // Fields from build type: debug
  public static final boolean DEVELOP_MODE = true;
  public static final String RELEASE_TIME = "Not yet";
}

可以参考友盟的多渠道打包(github)

一个介绍gradle比较好的网站
http://stormzhang.com/posts/

https://segmentfault.com/a/1190000006915937

这两个网站都是一系列的教程,建议一个一个的看

Deeplink 设计

发表于 2015-11-01 | 分类于 Android

概念

deeplink指的是在第三方客户端/浏览器中,打开一个APP的H5页面时候,如果发现本地安装了这个APP,就会使用本地APP打开。
需要前端和客户端互相配合来实现本功能。

服务器端:

<html>
<head>
<meta property="al:ios:url" content="applinks://docs" />
<meta property="al:ios:app_store_id" content="12345" />
<meta property="al:ios:app_name" content="App Links" />
<meta property="al:android:url" content="applinks://docs" />
<meta property="al:android:app_name" content="App Links" />
<meta property="al:android:package" content="org.applinks" />
<meta property="al:web:url"
content="http://applinks.org/documentation" />
</head>
<body>
Hello, world!
</body>
</html>

客户端

客户端deeplink的实现需要依靠jump系统。ps:jump系统可以通过URI 实现在app中的定向跳转 ,比如我们的app叫“天上人间”(缩写tsrj)。

  1. 需要先在menifest中注册相应的scheme的filter。
    ps:一般加载主页上面.
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<!-- Accepts URIs that begin with "tsrj://" -->
<data android:scheme="tsrj"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<!-- Accepts URIs that begin with "http://m.tsrj.com/" -->
<data
android:host="m.tsrj.com"
android:pathPrefix="/"
android:scheme="http"/>
<data
android:host="m.tsrj.com"
android:pathPrefix="/"
android:scheme="https"/>
</intent-filter>
  1. 在MainActivity新增解析jump信息的方法
private void jump(Intent intent) {
/** 根据传过来的参数 进行jump跳转 **/
String action_url = intent.getDataString();
MLog.i("启动Main_action_url:" + action_url);
if (!TextUtils.isEmpty(action_url)) {
JumpManager.jump(this, action_url);
} 
}

JumpManger就实现解析数据然后根据URI跳转的功能
public static boolean jump(Context ctx, String action_url) {

if (TextUtils.isEmpty(action_url)) {
return false;
}

MLog.d("JumpManager  url:" + action_url);

Uri uri = null;

/**如果不是一个标准的uri格式说明传入的数据无效的 可以直接忽视*/

try {
uri = Uri.parse(action_url);
} catch (Exception e) {
return false;
}

if (uri == null) {
return false;
}

/**检测到是一个合法的uri 先判断是否是满足跳转需求的头部 如果是 开始进行跳转检测 如果不是 直接往内置浏览器跳*/

if (!action_url.startsWith(JumpUriFactory.JUM_URL_HEAD) && !action_url.startsWith(JumpUriFactory.JUMP_URL_HEAD_S) && !action_url.startsWith(JumpUriFactory.JUMP_URL_HEAD_CUSTOM)) {

return false;
}

Intent jumpintent = null;

List<String> paths = uri.getPathSegments();

/**只有头没有页面参数就返回*/
if (paths == null || paths.size() <= 0) {

return false;
}

/**有path 但是path里面为空值 直接打开*/
String action = paths.get(0);
if (TextUtils.isEmpty(action)) {
if (!isFromWebView) {
jumpToInteriorPageActivity(ctx, uri);
}
return false;
}

/** 使用系统浏览器打开 */
if (action.equals("web_system")) {
jumpToSystemWebPage(ctx, uri);
return true;
}


/*else if (action.equals(JumpPageSet.detail)) {
goWaitress(ctx, action_url);
return true;
}*/

/** city */
else if (action.equals(JumpPageSet.city)) {
goCity(ctx, action_url);
return true;
}

/** usercenter */
else if (action.equals(JumpPageSet.personal_info)) {
goUserCenter(ctx, action_url);
return true;
}
  1. 处理uri解析出里面的参数,放入intent,并跳往相应的主页
/**
 * 将url的数据进行解析 得到一个存放所有参数的集合
 */
public static Set<String> getMyQueryParameterNames(Uri uri) {
    String query = uri.getEncodedQuery();
    if (query == null) {
        return Collections.emptySet();
    }
    Set<String> names = new LinkedHashSet<String>();
    int start = 0;
    do {
        int next = query.indexOf('&', start);
        int end = (next == -1) ? query.length() : next;
        int separator = query.indexOf('=', start);
        if (separator > end || separator == -1) {
            separator = end;
        }
        String name = query.substring(start, separator);
        names.add(Uri.decode(name));
        // Move start to end of name.
        start = end + 1;
    } while (start < query.length());
    return Collections.unmodifiableSet(names);
}
private static void goWaitress(final Context ctx, String actionUrl) {
    MLog.i("actionUrl"+actionUrl);
    Uri  uri;
    uri=Uri.parse(actionUrl);
    Set<String>  parameters=JumpManager.getMyQueryParameterNames(uri);
    Hashtable<String,String> params=new Hashtable<>();
    if(parameters!=null&&parameters.size()>0){
        for(String key:parameters){
            String value=uri.getQueryParameter(key);
            if(!TextUtils.isEmpty(value)){
                params.put(key,value);
            }
        }
    }
    Intent it= new Intent(act, WaitressPage.class);
    Set<String>  keySet=params.keySet();
    for (String k:keySet){
        it.putExtra(k,params.get(k));
    }
    startActivity(it);
    }

DeepLink的坑:

  1. 由于有些页面有权限限制,所以要跳页面时候,要判断在当前账户当前版本是否可以打开
  2. 如果从H5页面带过来的信息带有登录状态,而且当前app的账号信息和H5页面的不一致,可能需要一个账号切换工作

#微信里面的deeplink:
微信里面的主要就是二维码加上linkme,linkme和微信有合作可以直接把分享的内容通过里面的二维码倒回到app,不过这里要注意一个问题
微信里面的图片对size 是有要求的

图片size越大被压的越厉害,,这个即使通过以下方式绕过微信sdk也无法避免被压缩

try {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setAction(Intent.ACTION_VIEW);
    intent.addCategory(Intent.CATEGORY_DEFAULT);
    ComponentName cmp = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareScreenImgUI");
    intent.setComponent(cmp);
    String path = localImgPath;
    if (!TextUtils.isEmpty(path) && !path.startsWith("file://")) {
        path = "file://" + path;
    }
    intent.setDataAndType(Uri.parse(path), "image/jpeg");
    act.startActivity(intent);
} catch (Exception e) {
    ELog.e("wechat share failed");
}

所以只有自己先压缩图片到一定的size, 再在这张压缩过的图片上绘制真正的二维码才行,(如果遇到好心的UI把二维码设计的很大就,可以跳过这个坑)

Android分包

发表于 2015-11-01 | 分类于 Android

第一步: gradle引入以及配置

compile 'com.android.support:multidex:1.0.1'

defaultConfig {
    minSdkVersion 14
    targetSdkVersion 22  // 请勿随意改动 //Android 6.0系统默认为targetSdkVersion小于23的应用默认授予了所申请的所有权限
    versionCode 5210
    versionName "5.2.1"
    //manifestPlaceholders = [ UMENG_CHANNEL_VALUE:"googleMarket" ]
    multiDexEnabled true
    multiDexKeepProguard file('multiDexKeep.pro')
    // resConfigs "en", "zh_CN", "zh_TW" 暂时限定语言, 当前情况下缩小大概 0.2M TODO googlePlay 版本取消限制
}

第二步:修改Application

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    MultiDex.install(base);
}

第三步:保证关键类在主dex中

就是通过multiDexKeep.pro文件控制的
个推 sdk强制在主dex 中

-dontwarn com.igexin.**
-keep class com.igexin.**{*;}

Github 教程

发表于 2015-11-01 | 分类于 工具

本地工程上传github

  1. 首先在github上创建一个resp
  2. 在本地工程里面引入 .gitignore
    • 在androidstudio 安装一个ignore 插件,可以生成部分ignore
    • 对于gradle目录的工程,每一个存在build.gradle目录都需要放一个.gitignore
    • .gitignore 内容 (对于android的gradle目录结构最完整的ignore文件)原文件
.gradle
/local.properties
/gradle.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
bin
gen
.settings
.idea
target
*.iml
*.key
  1. touch README.md 》》生成 reamdme文件

  2. git init当前工程 》》生成 git信息

  3. git add -A 》》把当前工程除了ingore的添加到git里面

  4. git commit 》》生成第一次的commit信息

  5. git remote add origin git@github.com:liuyicheng3/NewRepo.git 》》设置当前git工程的远程分支

  6. git push -u origin master 》》push 到github上去

  7. Attention:一般android studio的默认ssh是 androidstudio自己的,所以这时候用android studio会提交不上去
    需要在settiings>vertion control> git里面吧 SSH excutable 由built-in 改成 native

##维护git文档方法

  • 可以网页里面直接弄,就不要通本地修改再提交,太麻烦。
  • 涉及到外部链接的图片文件,git库里面建对应的文件夹统一维护。
  • 对于图片贴的地址一定要是raw的,不要那个github上显示的地址
    1
    [原文件](https://raw.githubusercontent.com/liuyicheng3/learning-summary/master/files/.gitignore)

一般的git工作流程

版本管理

按featrue 开发

codereview及合并

Markdown 快捷键

  1. 空两行,让后代码区域两个TAB
  2. shift tab 收回一个tab

Git 常见场景

1. 不小心提交错了分支,把修改直接提交到master里面了

git log 查到提交前的versioncode
git revert 123dadafa revert到这个节点
git push –force 强制用本地的版本覆盖git上的版本

git reset 和 git revert区别 http://www.cnblogs.com/wanqieddy/archive/2013/05/14/3077689.html

2. git 舍弃本地的修改

git checkout . && git clean -xdf

3. git 合并分支的冲突

解决冲突后需要

git rebase --continue   

4. git 生成patch

当前还没提交,生成当前修改的patch

git diff  > 1.patch    

生成已经提交的几次记录的patch

git log    //查看需要生成到那个提交日志的patch
git format-patch -3     // 从master往前3个提交的内容,可修改为你想要的数值
git format-patch e795fefabc   //生成‘e795fefabc’这次提交的patch

5. git删除已有的记录

修改git commit 除了 git commit –amend 还有 git commmit rebase, reset,

https://blog.csdn.net/tangkegagalikaiwu/article/details/8542827/

6. 打 tag

git tag
git checkout v1.1.8
git push origin –tags

6. review流程常用的命令

git commit
git commit –amend
git push origin HEAD:refs/for/master
git reset –soft -> 这个地方的”“要填写需要回滚的id(短的那个,不要长的)
prepare

git cherry-pick ** -> 当前在master分支,要把master分支的一个修改也提到release分支

  1. 先在master上查看这次提交的Change-Id:* (这个是长的那个不是短的)
  2. 切换到release分支,git cherry-pick *

参考资料

http://blog.csdn.net/kevinx_xu/article/details/11660915

1…34
Liuycheng

Liuycheng

34 日志
8 分类
34 标签
© 2018 Liuycheng
由 Hexo 强力驱动
主题 - NexT.Muse