什么是马甲包呢?
简而言之,同样的代码,包装成不同的APP,就是马甲包。对于开发者来说代码被盗用被包装成马甲包是很气人了,所以建议大家注意自己代码的保护!
那么问题来了,真的有需要要开发不同的马甲包(咱也不知道为啥,就是有这种需求)该怎么开发呢?
看了很多方案最多的就是复制一套代码,修改包名,当然是可行的,但是也有一个弊端,马甲包越多,代码越多,版本越多,马家项目越多,一次更新要修改很多马甲包,这样维护会死人的,亲身经历!
如何优雅的开发马甲包呢?前提是一套代码!
马甲包开发,首先要了解android怎么区分不同的APP,作为开发者当然第一反应就是PackageName,这样说虽然也对,但是并不严谨,为了这个问题,我特意询问了应用商店的客服(毕竟马甲包也是为了上架),新建应用的包名是applicationID,还是PackageName,当然客服的回答并不理想,他们也不熟悉,然而我们去查看android的开发者文档,会明确的告诉你,android应用是根据applicationID区分的,只不过用早期eclipse开发项目时没有applicationID和PackageName的区分,就默认二者是一样的,到了android studio开发时,二者默认也是一样的,但是也可以不一样,那么我们就可以通过修改applicationID来打包不同的马甲包,接下来介绍马甲包需要修改的地方如何修改。
APP名称、icon怎么修改呢?
这个问题不算大问题,渠道打包就是用来做这个事情的,同时也可以配置applicationID。(具体往下看,有代码)
微信分享支付、支付宝支付会有影响吗?
微信分享和支付都是会有影响的,支付宝是没有影响的,我们只需要在项目中新建和applicationID一致的项目把回调的文件放进去就可以了(见AndroidManifest最后的说明,需要自己在项目中创建,这里只贴出AndroidManifest配置)
最复杂的就是某些sdk跟签名绑定的(例如:阿里人脸识别)怎么接入项目呢?
当然是采用组件化开发啦,就是把不同签名的sdk,作为一个model引入项目,就可以啦。
OK,原理已经描述完了,不知道是否能看懂,下面贴代码:
注:文件为保证隐私,有删除部分代码,需要理解自行修改
build.gradle文件:
apply plugin: 'com.android.application'
def getInteger(String name) {
def versionFile = file('flavors.properties')
if (versionFile.canRead()) {
Properties versionProps = new Properties()
versionProps.load(new FileInputStream(versionFile))
return versionProps[name].toInteger()
} else {
throw new GradleException("Could not find flavors.properties!")
}
}
def getString(String name) {
def versionFile = file('flavors.properties')
if (versionFile.canRead()) {
Properties versionProps = new Properties()
versionProps.load(new FileInputStream(versionFile))
return versionProps[name].toString()
} else {
throw new GradleException("Could not find flavors.properties!")
}
}
//根据自己需要自定义
//APPname 需要在此文件下修改,flavors.properties中配置会导致中文乱码,
//英文就无所谓,虽然试了修改编码,但是没有成功
def app_name = "马甲包名称"
def app_icon = getString('myicon')
def wechat_app_id = getString('wechat_app_id')
def wechat_app_secret = getString('wechat_app_secret')
def amap_kay = getString('amap_kay')
def umeng_appkey = getString('umeng_appkey')
def application_id = getString('application_id')
def version_name = getString('version_name')
def version_code = getInteger('version_code')
android {
compileSdkVersion 28
buildToolsVersion "28.0.0"
defaultConfig {
applicationId application_id
minSdkVersion 18
targetSdkVersion 28
versionCode version_code
versionName version_name
flavorDimensions vest
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
ndk { abiFilters 'armeabi'}
}
dexOptions {
jumboMode = true
}
buildTypes {
applicationVariants.all { variant ->
variant.outputs.all { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
def date = new Date().format("yyMMddHHmm")
def fileName = app_name + "-" + vest + "-${variant.productFlavors[0].name}-${versionName}(${versionCode})-${date}.apk"
outputFileName = fileName
}
}
}
}
aaptOptions.cruncherEnabled = false
aaptOptions.useNewCruncher = false
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
productFlavors {
//具体渠道打包配置不在详细说明,自己google一大堆
//小米渠道
XiaoMi {
applicationId application_id //区别不同的APP的 “PackageName”
resValue "string", "wechat_app_id", wechat_app_id //微信应用app_id
resValue "string", "wechat_app_secret", wechat_app_secret //微信应用app_secret
manifestPlaceholders = [
UMENG_CHANNEL_VALUE: "Android_xiaomi",//渠道名称
AMAP_APPKEY : amap_key, //高德APP key
UMENG_APPKEY : umeng_appkey, //umegn APP key
MY_LABEL : app_name, //马甲包 应用名称
MY_ICON : app_icon //马甲包 应用图标
]
}
HuaWei {.......}
Vivo {.......}
Oppo {....... }
MeiZu {.......}
Tencent {.......}
Q360 {....... }
AnZhi {....... }
WanDouJia {.......}
}
//此处说明,下面的目录说明是lib和aar的引用,必须修改
//此修改说明:指定lib可以指向的文件夹,
//'libs' = 本项目的lib
// '../'+getString('module_path')+'/libs' 通过配置修改的其他model下的lib
sourceSets {
main {
jniLibs.srcDirs 'libs', '../'+getString('module_path')+'/libs'
}
}
repositories {
flatDir {
dirs 'libs', '../'+getString('module_path')+'/libs'
}
}
}
dependencies {
//多余的已经删除
api(name: 'aar_name', ext: 'aar')
//需要同时修改setting.gradle 下面会贴出代码
implementation project(':module1')
// implementation project(':module2')
//implementation project(':module3')
// implementation project(':module4')
}
需要自己新建在proguard-android.txt同样的目录下
flavors.properties文件:
################################################################################
############################共用参数共用参数共用参数共用参数#############################################
############################共用参数共用参数共用参数共用参数#############################################
############################共用参数共用参数共用参数共用参数#############################################
############################共用参数共用参数共用参数共用参数#############################################
############################共用参数共用参数共用参数共用参数#############################################
################################################################################
version_code = 1
version_name = 1.0.0
amap_kay = **************************
################################################################################
module_path = module1
myicon = @drawable/logo1
umeng_appkey = *********************1
application_id = com.*****.*****1
wechat_app_id = *********************1
wechat_app_secret = *********************1
################################################################################
#module_path = module2
#myicon = @drawable/logo2
#umeng_appkey = *********************2
#application_id = com.*****.*****2
#wechat_app_id = *********************2
#wechat_app_secret = *********************2
#打包时其他马甲包的配置需要注释!!!
################################################################################
setting.gradle文件:
//根据不同的包名选择不同的module
include ':YouApp', ':module1'
//include ':YouApp', ':module2'
//include ':YouApp', ':module3'
//include ':YouApp', ':module4'
这些model自己可以随便新建,注意setting.gradle需要自己修改,因为新建时会自动修改
说明:model中的资源可以在项目中直接引用,不必和application一致,一致会造成build冲突
AndroidManifest.XML文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.package">
<!-- 此处包名和项目包名一致,和applicationID不必一致 -->
<application
android:name=".application.Application"
android:allowBackup="true"
android:icon="${MY_ICON}"
android:label="${MY_LABEL}"
android:persistent="true"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme"
tools:replace="android:label,android:supportsRtl,android:allowBackup">
<!--除了上面的package=‘’ 以外所有用到sdk文件需要用到包名,全部用 ${applicationId} 如下:${applicationId}.fileprovider-->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
<!-- 高德定位设置key -->
<!-- 设置key -->
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="${AMAP_APPKEY}" />
<!-- 友盟统计 -->
<meta-data
android:name="UMENG_APPKEY"
android:value="${UMENG_APPKEY}" />
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_VALUE}" />
<meta-data
android:name="android.max_aspect"
android:value="2.2" />
<!-- 极光配置举例: -->
<service
android:name="cn.jpush.android.service.DaemonService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="cn.jpush.android.intent.DaemonService" />
<category android:name="${applicationId}" />
</intent-filter>
</service>
<!-- 微信配置举例 -->
<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".wxapi.WXEntryActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<!-- 微信配置举例 马甲包1-->
<activity
android:name="com.package1.wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name="com.package1.wxapi.WXEntryActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<!-- 微信配置举例 马甲包2-->
<activity
android:name="com.package2.wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name="com.package2.wxapi.WXEntryActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
</application>
</manifest>
至此,除了个别SDK需要跟签名绑定,新建了几个model,可以通过修改引用,达到通过配置文件控制的目的,这样开发一个马甲包如果不需要特别的sdk,只需要一个打包的时间,最多五分钟,大大节省时间,及时项目维护,只需要修改一套代码,这才是真正的马甲包!
网友评论