美文网首页Android必备了解Android开发Android技术知识
利用productFlavors创建不同版本的App

利用productFlavors创建不同版本的App

作者: LinkZhang | 来源:发表于2016-10-22 12:06 被阅读4973次

需求

最近运营人员需要创建一个"壳版"应用进行渠道推广,即将当前的App更换名称、包名、图标、第三方服务等,成为一个新的应用。

方案

  1. 比较简单的方案就是将代码copy一份,缺点也比较明显,以后维护起来比较麻烦,每次更新代码, 都要把代码复制一次
  2. 通过gradle的productFlavors可以创建多个不同版本的App,维护起来也比较方便

考虑到实际情况选择方案二,主要涉及包名的更换,资源的更换,AndroidManifest.xml的合并,第三方服务的配置

具体步骤

更换包名

更换包名之前需要搞清楚packageName和applicationId的区别.
简而言之:packageName作为R资源,四大组件的路径; applicationId作为应用唯一标识, 具体可以参考官方文档ApplicationId versus PackageName(需要翻墙)

在app的build.gradle文件中加入

  android {
     productFlavors {   
           demo1 {       
                 applicationId "com.demo1.android"    
           }    
           demo2 {        
                  applicationId "com.demo2.android"    
           }
     }
 }

资源更换

需要更换的资源有应用名称和启动图标
在src目录下建立demo1和demo2两个文件夹,如图所示:

Paste_Image.png

其src/main存放的是公共文件,src/demo1、src/demo2中分别存放各自独有的文件
gradle在打包apk的时候回将相应的文件、资源进行合并,例如打包demo1时将src/main和src/demo1进行合并打包。
合并规则如下

  • java文件直接合并,存在相同路径的同名文件会造成冲突,例如src/main/java/com/demo1/android/MainActivity.java 就会和src/demo1/java/com/demo1/android/MainActivity.java 冲突

  • 资源的内容进行合并,同名文件的资源内容进行合并,同属性名会造成冲突
    例如scr/main/res/values/strings.xml 和src/demo/res/valuse/strings.xml的内容进行合并成一个strings.xml文件,如果2个文件中都包含同一属性,例如

<string name="app_name">android</string>

就会造成冲突

  1. 更换应用名称
    在src/demo1/res/values/strings.xml中添加
<string name="app_name">demo1</string>

在src/demo2/res/values/strings.xml中添加

<string name="app_name">demo2</string>

删除src/main/res/values/strings.xml中的app_name属性

  1. 更换启动图标
    和上面的操作类似,在相应的文件夹中放入启动图标,删除main中的启动图标。不过似乎启动图标只有放在drawable文件夹才能生效,在mipmap文件夹中不生效

集成第三方服务

第三方服务例如友盟统计,推送,分享,支付都需要申请Appkey,而Appkey是与ApplicationId绑定的,所以需要重新申请一份。
Appkey是直接写在AndroidManifest.xml文件中,所以需要创建相应的AndroidManifest.xml文件
AndroidManifest.xml合并规则是:将每个元素及其子元素的节点和属性进行合并,如果遇到相同属性会造成冲突,例如

<activity
    android:name=”.MainActivity”
    android:theme=”@theme1”/>

<activity
    android:name=”.MainActivity”
    android:screenOrientation="portrait"/>

合并成

<activity
    android:name=”.MainActivity”
    android:theme=”@theme1”
    android:screenOrientation="portrait"/>

如果2个文件同时存在android:theme就会造成冲突

所以第三方服务的Appkey在相应的AndroidManifest.xml配置就行,不要在main中的AndroidManifest.xml中进行配置

错误

按照以上步骤将项目改造之后,本以为大功告成,没想到仅仅是开始

  • 错误一:微信分享无法回调
    使用sharesdk进行分享功能,根据文档微信分享必须在包名下创建
    wxapi/WXEntryActivity才能回调.
    原因:猜测分享代码中是根据
getPackageName()+"wxapi/WXEntryActivity"

进行回调的,由于更改的ApplicationId,得到的结果为com.demo2.android与实际的路径是不一致的,所以无法进行回调
解决方案:创建src/main/java/com/demo2/android/wxapi/WXEntryActivity.java才能进行回调,AndroidMenifest.xml配置如下
demo1

 <activity
            android:name="com.demo1.android.wxapi.WXEntryActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:exported="true"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.Translucent.NoTitleBar"/>

demo2

<activity
            android:name="com.demo2.android.wxapi.WXEntryActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:exported="true"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
  • 错误二:友盟反馈点击崩溃
    原因:启动反馈的Activity时需要查找资源,反馈SDK使用Class.forName(getPackageName()+”.R”)来获取R类的,由于ApplicationId和实际的包名不一致,所以无法获取到R类
    解决方案:
com.umeng.fb.util.Res.setPackageName(R.class.getPackage().getName());//增加这行
FeedbackAgent agent = new FeedbackAgent(getActivity());
agent.startFeedbackActivity2(); 
  • 错误三:极光推送能收到推送但点击无法唤起app
    原因:极光sdk无法找到自定义的receiver
    解决方案:在AndroidManifest.xml中注册四大组件时,包名全部使用applicationId
<receiver    
    android:name="com.demo1.android.receiver.JpushReceiver"    //实际路径
    android:enabled="true">    
    <intent-filter>        
        <action android:name="cn.jpush.android.intent.REGISTRATION"/>        
        <action android:name="cn.jpush.android.intent.MESSAGE_RECEIVED"/>        
        <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED"/>        
        <action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED"/> 
         <category android:name="${applicationId}"/>    //这里要使用ApplicationId
    </intent-filter>
</receiver>

参考

如何使用Gradle构建不同版本的app?

相关文章

网友评论

  • 4d7207d7c043:我的项目中用到provider,就一直提示说providerData的name被之前安装的apk给用了,但我在 provider的android:authorities="${applicationId}.provider"是这样赋值的,这该怎么解决啊,谢谢
  • 搬砖的乐趣:感谢,前面main和特殊版本里面的类重名弄了半天,看到博主的文章才懂,非常感谢
    LinkZhang:@搬砖的乐趣 不客气,有用就好
  • 26e2836375cd:安卓软件 激活码验证机制 怎么运行的 什么原理
    谜一样的阿瑜:额 没注意回复时间,反正思路大概也就这样 具体的还是要根据实际开发的,博主见谅啊,遇见问题就容易想一大堆!
    谜一样的阿瑜:小哥你是过来给人家面试的么?看到这个回复吓我一跳,激活码验证其实很简单的,后台联网加密那种:是根据本地的某个唯一值 比如 imei,进行 sha1 值加密,加密出来的结果当做激活码给你,然后你手机上客户端自身再加密一次,如果两边的数值相同就当做激活成功,直接保存一个数据到彩信的数据库,这样即使你卸载重装也没关系,即使本地保存的那份被删除了,联网之后还是会询问的,imei值如果已经存在了,服务端也可以当做你已经解密了。

    另外一种离线的就比较麻烦了,你需要一个加密算法,然后呢,在一个比如 “”open “”的字符串前后加上100个随机数,然后给这些随机数用 一个双向加密进行加密,管理员加密,客户端解密,解出来的数据 取中间那四个跟密码对比,当然,你也可以使用插入的方式,比如,第二个,第六个,第99个这种方式。更加无规律。然后就判断值是否正确,正确就激活成功,错误就激活失败,如果类似于激活码还有时效,根据日期,自动清除激活成功标记即可

本文标题:利用productFlavors创建不同版本的App

本文链接:https://www.haomeiwen.com/subject/ixthuttx.html