一套原始碼編譯多個APP,不同的簽名,包名,介面,欄位...
一套原始碼編譯多個 APP,不同簽名,不同介面等配置
線上原始碼地址,結合原始碼看比較容易理解 原始碼地址
作為一個剛入行一兩年的 程式碼 Copy 者 來說,這麼久沒有深入瞭解 Gradle 真的是一大遺憾,熟悉了 Gradle 之後開發效率能夠提高很多,當然,現在我還沒有到那個地步,這篇文章就是我實際開發中的需求,剛開始的時候用的是最基本的開發方式,後來東西越來越多,漸漸的感覺這種開發方式太拖慢進度了,然後就搜尋學習一番,沒有理解,只是學習了基本的操作來解決開發中的需求,特寫篇文章記錄一下,怕忘記。
emmm, 我可能有一個這樣的需求,不知道可不可以…
我的需求很簡單…
- 小張是一個四肢健全,各項功能正常的人,我需要根據這個人克隆出更多人
我的目的很明確…
- 我要跟別人長得不一樣(介面不一樣)
- 我要身體會特殊的技能(額外功能)
- 我要改名身份證上的名字(APP 名字)
- 我要換身份證上的頭像 (APP 桌面圖示)
- 我要換身份證號碼 (APP 包名)
- 我要換膚色 (不同的介面顏色,icon等)
- 我要不同的簽證 (不同的應用簽名)
ennn, 我可能有這樣一種解決辦法, 應該可以…
1.首先,我們得有個小張這個人
小張的基本資訊是這樣
資訊描述 | 基本資訊 |
---|---|
身份證頭像 (APP 名字) | 小張 |
身份證頭像 (APP 頭像) | |
身份證號碼 (APP 包名) | com.persons.zhang |
小張是長這樣的
這是小張的原始碼
package com.persons.zhang
import android.os.Bundle
import android.support.design.widget.BottomNavigationView
import android.support.v4.app.Fragment
import android.support.v4.view.ViewPager
import android.support.v7.app.AppCompatActivity
import android.util.Log
import com.persons.zhang.adapter.MainViewPagerAdapter
import com.persons.zhang.fragment.FootFragment
import com.persons.zhang.fragment.HandFragment
import com.persons.zhang.fragment.HeadFragment
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
viewPager.currentItem = 0
[email protected] true
}
R.id.navigation_dashboard -> {
viewPager.currentItem = 1
[email protected] true
}
R.id.navigation_notifications -> {
viewPager.currentItem = 2
[email protected] true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
var fragments : MutableList<Fragment> = mutableListOf()
fragments.add(HeadFragment())
fragments.add(HandFragment())
fragments.add(FootFragment())
viewPager.adapter = MainViewPagerAdapter(supportFragmentManager,fragments)
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
override fun onPageScrollStateChanged(state: Int) {
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}
override fun onPageSelected(position: Int) {
navigation.menu.getItem(position)?.setChecked(true)
}
})
}
}
這是小張的佈局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.persons.zhang.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
app:layout_constraintBottom_toTopOf="@id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="0dp"
android:layout_marginStart="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation" />
</android.support.constraint.ConstraintLayout>
2.我們要在小張的基礎上增加出來一個小李
這是我們要增加的小李的大概需求
- 名字要不一樣 (APP 名字)
- 身份證號碼要不一樣 (APP 包名)
- 身份證頭像要不一樣 (APP 圖示)
- 膚色不一樣 (介面顏色)
- 頭跟別人不一樣 (主介面的頭)
生產之前的準備工作
我們把 Android 切換到 Project
1、新建關於包括小李一切的目錄
看著上圖,對比著看啊,上圖中的 zhang -> app -> src 目錄上 右鍵 -> New -> Directory 新建一個小李的目錄,我這裡就叫做小李了,下面是圖操作
然後,我們對比著 src/main 這個目錄裡面的結構,在 li 這個目錄下建立一模一樣的結構,如下圖
這是li 目錄下新建的 | 這是main 的結構 |
---|---|
好了,新建完成
2. 新建針對小李的Gradle構建變體
我也理解不清楚,大概就是把小李這個人當成備胎,這樣這個人什麼時候想換備胎了,小李這個人可以是其中一個。就是在編譯的時候多一個小李的選項,可以單獨針對小李進行編譯,也就是備胎轉正。
我們在 app的gradle下 新增如下程式碼
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
defaultConfig {
applicationId "com.persons.zhang"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
...
// 重點在這下面這些
flavorDimensions "app"
// 重點--------------------------
productFlavors {
li {
dimension "app"
}
zhang {
dimension "app"
}
}
}
注意看 productFlavors 裡面的 li 和 zhang ,他們裡面都有 applicationId、versionCode、versionName 這些,這些就是針對他們不同的人,有不同的版本,不同的包名。
這樣寫的話必須保證我們新建的那個目錄要跟 productFlavors 裡面的這個 li { … } 保持一致 ,如果想使用不一樣的名字可以按照以下操作來,至於上面的flavorDimensions 我也不太理解,沒使用到。
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
...
sourceSets {
li {
java.srcDir('src/li/java')
res.srcDir('src/li/res')
}
}
}
這樣顯示指定原始碼路徑,名字不一致也無所謂了。
這樣我們就新建出來了一個小李 ,但是新建出來了我們怎麼才能把預設點選 Run 安裝的 debug 版本的app改成 小李呢,如果上面已經一步步配置好了之後,會有下圖這些東西
選擇完成之後再點選 Run 安裝的就是小李的 debug版本了
此時的小李還是和小張一模一樣的,下面我們要把小李 改成他該有的模樣。
3.不一樣的名字
既然我們這個專案都叫做小李了,那我們不能名字還是小張的名字吧,下面我們來給小李改改名字,一般情況下我們的名字都是在 strings.xml 裡面的 app_name 欄位來定義的,當然不排除你直接在 Manifest 裡面寫死,那你很棒棒哦
我們的 strings.xml 現在只有 src/main/res/values 裡面才有,我們要做的是在 src/li/res/values 裡面新建一個 strings.xml ,裡面新建一個 app_name 的欄位,值為 小李 ,如下圖
小張(也就是main) | 小李(也就是li) |
---|---|
不知道你注意到沒有,小張(main) 的 strings.xml 裡面還有 head 等欄位, 小李(li) 卻沒有
原因 :資原始檔會自動合併。就這麼簡單,就比如說,現在 張 裡面有 head 這些,李 沒有,等後面我們修改過,資原始檔合併之後會變成以下狀態
相同的被替換,不同的被增加 ,相同的會被替換成當前目標的,比如說你現在小張有app_name,小李
也有,如果現在編譯目標 是小李的話,app_name 的值就會是小李,好了我們來執行看一下效果
好了,看來我們修改成功了,上面小張這個APP是我改了些東西保留下來的,為了作對比,正常情況下以前裝的小張會被小李覆蓋掉, 下面我們來看一下小李的主介面長什麼樣子
可以看到我們的app_name 已經修改成功了,其他沒修改的還是按照小張的樣子來展示的,包括下面的頭,手,腳,這些。
3.身份證號碼不一樣(包名不一樣)
上面我說了,小張被保留下來是我改了一些東西才能保留下來和小李作對比的,正常情況下安裝了小李之後,小張就會被覆蓋掉,因為他們的包名一樣的。
下面我們來更改包名讓 App 共存,我們的操作還是在 app/gradle 裡面的 productFlavors 進行更改。
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
...
productFlavors {
li {
applicationId "com.persons.li"
versionCode 2
dimension "app"
versionName "1.0.2"
}
zhang {
applicationId "com.persons.zhang"
versionCode 1
dimension "app"
versionName "1.0"
}
}
}
仔細看,我每個都加了 applicationId ,順便還加了 versionCode,versionName, 這可以單獨設定每個app不同的版本號,不同的版本名稱,不同的包名,怕暈了,給個截圖讓你瞅瞅
4.身份證頭像不一樣 (APP 圖示)
我們把版本號修改了,現在我們安裝了之後就可以兩個APP共存了,而且包名也是不相同的,這樣基本上實現兩個人就可以出現在一個手機裡面了。
但是隻是名字改了,圖示還是一樣的,猛一看,很懵逼啊,這一不小心就點錯了,多尷尬
眾所周知,我們的app圖示是放在 res/mipmap 目錄下的, 我們之前拷貝的 li 目錄裡面是和 小張(main) 的是一樣的,我們在準備工作的時候還設定了 sourceSet ,而且,前面還說了相同名字的資源被覆蓋,不同的被增加 ,我們要做的只是把 小李(li) 的圖示放在 li/src/res/mipmap 下就可以了, 然後執行安裝到手機
下面看改過的效果
好了,替換完成了。
5.不同的膚色
我們現在小李的膚色還跟小張的膚色是一樣的,下面我們來改一下,一切關於資源的要麼被替換,要麼被合併 。
我們要改的是 li/res/values/colors.xml , 因為現在小李這個目錄下面還沒有 colors.xml 這個檔案,我們直接從 小張(main) 的目錄裡面 copy 過來一個,改一下里面的色值就可以了。
小張的膚色
小李的膚色
小李改過膚色的樣子
好了,膚色也改完了。
6.頭介面不一樣(重點)
現在小李的頭介面還是上面這張圖的介面,什麼也沒有。我們要做的是在頭這個介面裡面加上眼睛,鼻子,小張是個無面男 ,我們要小李有個鼻子,點選鼻子的時候彈出一個提示。
注意: 這個方法是通用的,就是說,只要是關於 java 程式碼的都按照這樣的套路來做,把要修改的那個類拷貝出來,把基礎包裡面的刪掉,給其他的目錄裡面也拷貝進去這個類,要不你在編譯其他目錄的apk的時候會報錯的,找不到類。
1.首先我們先把小李的頭copy過來。
為什麼要拷貝過來? 不拷貝過來怎麼會有自己的頭,如果沒有拷貝過來,預設使用的是小張的頭。
注意 :這時我們需要把原始碼大改一下了,我們要把原本小張的原始碼給改成通用的,就是把原來小張的原始碼當成一個基礎包,我們要是加人就需要另外新建一個目錄來做,就跟我們增加小李一樣的。
下面是我們改過的目錄結構
可以看到,我們單獨把 小張 給新建了一個目錄,他的存在方式跟 小李 的是一樣的。
為了以防萬一,我們還要在 app/gradle 裡面修改一下 sourceSet 保證我們的APP原始碼指向是正確的。
sourceSets {
li {
java.srcDir('src/li/java')
res.srcDir('src/li/res')
// assets.srcDir('src/li/assets')
// manifest.srcFile('src/li/AndroidManifest.xml')
}
zhang {
java.srcDir('src/zhang/java')
res.srcDir('src/zhang/res')
// assets.srcDir('src/zhang/assets')
// manifest.srcFile('src/zhang/AndroidManifest.xml')
}
}
怕看懵逼,加一張圖
我們想要小李的頭跟其他的不一樣的話,需要單獨把 copy 出來進行修改,這個跟res 下的檔案不一樣, res 下的檔案相同了會覆蓋合併,java 目錄下面是會報錯的,
因為基礎包目錄下已經有了相同名字的類了。你在 li 的裡面加了相同的 類, 李使用的原始碼是用 基礎包裡的來做基礎的, 現在這個狀態就相當於 你在同一個包裡面,建立了兩個相同名字的類
2. 現在我們給小李的頭介面加上鼻子
首先我們先把 基礎包 裡面 頭 的佈局檔案 copy 到 li 的layout目錄下。
看清楚,是 li 目錄下面的 layout 資料夾,拷貝到這裡以後我們就跟平常寫介面一樣修改就可以了。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/click"
android:layout_centerInParent="true"
android:text="我是鼻子"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
class HeadFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater?.inflate(R.layout.fragment_head, container, false)
return view
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
click.setOnClickListener {
Toast.makeText(activity,"我的鼻子被點了一下",Toast.LENGTH_LONG).show()
}
}
}
就是介面上有一個叫鼻子的按鈕,設定點選之後彈出吐司,是不是還很騷氣,而我們加上這個鼻子之後,小張還是原來的小張 ,鼻子只是被加在了小李的頭上。
3. 不同的簽名檔案
這樣好幾個app在一塊編譯,我們在androidstudio裡面點選打包還要選擇不同的 簽名檔案,很麻煩,我們還是在 app/src/gradle 裡面進行配置就可以了。
android {
....
signingConfigs {
// 只是用來做演示,沒有特地生成簽名檔案
li {
storeFile file("src/li/liKeystore.jks")
storePassword "li123"
keyAlias "li"
keyPassword "li1234"
// 開啟 V2 簽名
v2SigningEnabled true
}
zhang {
storeFile file("src/zhang/liKeystore.jks")
storePassword "zhang123"
keyAlias "zhang"
keyPassword "zhang1234"
v2SigningEnabled true
}
}
productFlavors {
li {
applicationId "com.persons.li"
versionCode 2
dimension "app"
versionName "1.0.2"
// 注意看這裡---------------------------------------------
signingConfig signingConfigs.li
}
zhang {
applicationId "com.persons.zhang"
versionCode 1
dimension "app"
versionName "1.0"
signingConfig signingConfigs.zhang
}
}
}
4. 其他操作
1.批量打包和執行簽名包測試第三方登入或分享,或支付。
根據我們上面 gradle 的配置,簽名打包很方便,打包使用這種方式,上面可以單獨打一個,也可以一次打完,打一個就選擇第四步其他的名字,比如 assembleZhang 這些。
打包使用這種方式,上面可以單獨打一個,也可以一次打完,打一個就選擇第四步其他的名字,比如 assembleZhang 這些。
2. Manifest value 值設定佔位符
在 Manifest 中
在 Gradle 中
3. 關於微信回撥
微信的回撥是需要放在 包名/wxapi/ 目錄下的,我們這樣拷貝之後沒有了包名都是不一樣的,我們要為每個app的 src/java 目錄下新建一個和包名一樣的目錄,比如小張, 我們為了方便區分,把小張的包名改成 cn.zhang ,下面是目錄結構
到這裡基本結束了。
有什麼沒寫的,寫錯的,評論告訴我,反正我也不一定改。。。
相關推薦
一套原始碼編譯多個APP,不同的簽名,包名,介面,欄位...
一套原始碼編譯多個 APP,不同簽名,不同介面等配置 線上原始碼地址,結合原始碼看比較容易理解 原始碼地址 作為一個剛入行一兩年的 程式碼 Copy 者 來說,這麼久沒有深入瞭解 Gradle 真的是一大遺憾,熟悉了 Gradle 之後開發效率能夠提高
(二)nginx反向代理html,實現前後端分離(部署一套html呼叫多個服務)
上篇部落格是一套頁面呼叫一個後端提供的服務,但是很多時候 我們後端會部署多個服務,為此部署配置一套nginx代理。 可以實現為nginx配置多種策略,如下說明: 負載均衡策略 1、輪詢(預設) 每個請求按時間順序逐一分配到不同的後端伺服器,如果後端伺服器down掉,能
Android同一套程式碼打多個APP包並能夠在同一個手機上安裝執行
Android同一套程式碼打多個APP包並能夠在同一個手機上安裝執行 Android同一套程式碼打多個APP包並能夠==在同一個手機上安裝執行==,同時==APP名稱、桌面icon圖示也都不同== 給同一套程式碼起不同的多個包名,並把APP名稱和桌面圖示設定為
一個專案如何編譯多個不同簽名、包名、資源等,的apk?
簡介 如題所示!本篇文章就是為了解決這種問題。方便打包和執行的時候能做到無需手動替換配置,即可打包想要的apk。打包的時候,只需選一下想打哪種配置的apk就OK啦。 (^o^)/~ 先來看,有需求如下: 同一個專案 不同的apk圖示 不同的伺服器域名 不同的包名 不同的名稱 不同的簽名 不同的第三方k
Ant編譯多個依賴工程jar、war包
ant編譯war包 背景介紹: 專案由3個工程組成,3個工程分別為businessManager(以下簡稱為busi)工程、FrameWork(以下簡稱為frame)工程、ppfmgr(以下簡稱為mgr)工程,3個工程代表的含義分別為,busi代表業務工
一例對一個或多個實體的驗證失敗。有關詳細信息,請參閱“EntityValidationErrors”屬性的解決
tro https span cep 實例 tps lte .data 防止 這個問題相信只要是做MVC的,都碰到過,也都知道錯誤的原因,就是觸發了定義的實例字段校驗規則。比如定義的不為空,但是為空了,或者定義的字段長度為50,但是超過50了。 可是有時雖然知道是這樣,
SQL 將一條記錄中多個字段的值拼接為一個字段 && 將多行數據合並成一行,並且拼接CONVERT() 、for xml path的使用
varchar start 技術分享 現在 date rom 結果 技術 ont 接著上篇文章的訂單表(商品編號,價格設置時間id(類似於創建時間,創建時間約早,則act_id越小) ,價格的時間段,商品價格) 一、將一條記錄中多個字段的值拼接為一個字段 現要求將兩個時間
資料庫擴充套件性設計:使用二進位制解決一條記錄關聯多個狀態的問題(轉),可以嘗試一下
程式開發中,經常遇到一條記錄有多個狀態位,比如一條商品,他屬於熱門,新品,特賣。我們的資料庫如何設計呢? 一般有幾種方法 (1)建立關聯表 關聯表字段:關係Id,商品Id,屬性Id 查詢:使用關聯表的方式,查詢某屬性的商品。 程式:寫入時,寫商品表和關聯表;
一個專案如何編譯多個不同簽名,包名,資源等
簡介 如題所示!本篇文章就是為了解決這種問題。方便打包和執行的時候能做到無需手動替換配置,即可打包想要的apk。打包的時候,只需選一下想打哪種配置的apk就OK啦。 (^o^)/~ 先來看,有需求如下: 同一個專案 不同的apk圖示
一鍵打包出多個不同包名,不同應用名稱和圖示的APK
此Demo的原理與多渠道打包的原理相同(動態設定App名稱,應用圖示,替換常量,更改包名,變更渠道)最近有一個需求,就是一套程式碼要根據不同的客戶打包出不同包名,不同appName,圖示的apk,如果一個客戶更改一次打包出一個apk的話效率非常的低,並且不利於維護本demo
一臺電腦配置多個tomcat,同時執行,互不衝突
當第一個tomcat啟動後,後面tomcat的server.xml中的埠不管怎麼改,仍然會報埠衝突。後來在dos下執行才發現所有的tomcat都會去找CATALINA_HOME和CATALINA_BASE這兩個環境變數,因此步驟如下: 1.使用壓縮版的tom
配置tomcat,使得一臺主機執行多個tomcat容器
專案軟體使用了tomcat,在安裝時自動註冊為服務。有一客戶在安裝時發現與另外一個軟體的tomcat發生了衝突,兩個軟體都要使用,於是在網上查了一下資料,解決這個問題。設定方法很簡單,修改server.xml配置檔案中的3個埠即可。下面將簡單總結一下如何在同一臺伺服器上部署
Gradle 編譯多個project(包括多Library庫project依賴)指導
pil 出現 基於 viewpage eclips support class 什麽 cor Gradle Android最新自己主動化編譯腳本教程(提供demo源代碼)這篇文章我簡單寫了基於Gradle2.1 進行的android project和android lib
對一個或多個實體的驗證失敗。有關詳細信息,請參閱“EntityValidationErrors”屬性。
sys date 錯誤 onerror conf erro fig 站點 更新 問題原因可能是: 1. 非空列未插入值錯誤 2. 多個表間外鍵列長度不一樣 3. ef上下文對象db為空 4. ef上下文設置屬性為 db.Configurat
使用gcc命令編譯多個文件
生成 src cloud 多個 .bat pan del 準備 後綴名 使用g++命令直接一次性編譯多個文件 這裏以簡單的HelloWorld程序為例,假設我們一共有三個文件:main.cpp,HelloWorld.cpp和HelloWorld.h. 其中HelloWo
一臺機器啟動多個tomcat簡單配置
col shu class 運行 配置 多個tomcat dir http服務 nbsp 一臺機器啟動多個Tomcat只需要解決Tomcat端口沖突的問題。 相關配置:打開 Tomcat 目錄下 conf \ server.xml 共修改三處端口,分別是: <Ser
使用一條sql查詢多個表中的記錄數
nbsp lec sel code spa select bold style 查詢 方法一: select t1.num1,t2.num2,t3.num3 from (select count(*) num1 from table1) t1, (se
awk -F選項同時指定多個符號做為分割符時遇到空格坑,題目:請執行命令取出 linux 中 eth0 的 IP 地址
取出linux中eth0的ip地址 awk空格遇坑 老男孩第三關課前考試題的第三個筆試題第三關課前考試題:3.請執行命令取出 linux 中 eth0 的 IP 地址(請用 cut,有能力者也可分別用 awk,sed 命令答)因為以前有個題講過awk -F選項可以指定輸入分割符,也可以同時使用多個符號作為分割
Django(博客系統):基於pycharm如何一個django工程下創建多個app
arm .py 9.png line pycha int 默認 通用 直接 背景:通常我們創建一個django系統時,為了把業務模塊劃分清楚往往會把一個獨立的業務模塊放到一個app中,如果多個獨立的業務模塊就會創建多個app,一般情況下為了更好的管理這些app,會把他們都存
一臺電腦安裝多個JDK並自由切換
image home clas 分享 mage classpath com http ~~ 環境變量 path和classpath裏邊用%JAVA_HOME%; 就這樣,只要改變JAVA_HOME的值,就可以自由切換Java版本了呢~~ 一臺電腦安裝多個JDK並自由切換