1. 程式人生 > >一套原始碼編譯多個APP,不同的簽名,包名,介面,欄位...

一套原始碼編譯多個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

模式.png

1、新建關於包括小李一切的目錄

看著上圖,對比著看啊,上圖中的 zhang -> app -> src 目錄上 右鍵 -> New -> Directory 新建一個小李的目錄,我這裡就叫做小李了,下面是圖操作

directory.png

然後,我們對比著 src/main 這個目錄裡面的結構,在 li 這個目錄下建立一模一樣的結構,如下圖

這是li 目錄下新建的 這是main 的結構
li_directory.png

好了,新建完成

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 裡面的 lizhang ,他們裡面都有 applicationIdversionCodeversionName 這些,這些就是針對他們不同的人,有不同的版本,不同的包名。

這樣寫的話必須保證我們新建的那個目錄要跟 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改成 小李呢,如果上面已經一步步配置好了之後,會有下圖這些東西

build_小李.png

選擇完成之後再點選 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_app_name.png li_app_name.png
app_name_zhang.png app_name_li.png

不知道你注意到沒有,小張(main)strings.xml 裡面還有 head 等欄位, 小李(li) 卻沒有

原因 :資原始檔會自動合併。就這麼簡單,就比如說,現在 裡面有 head 這些, 沒有,等後面我們修改過,資原始檔合併之後會變成以下狀態

li_strings.png

相同的被替換,不同的被增加 ,相同的會被替換成當前目標的,比如說你現在小張有app_name小李

也有,如果現在編譯目標 是小李的話,app_name 的值就會是小李,好了我們來執行看一下效果

小李.jpg

好了,看來我們修改成功了,上面小張這個APP是我改了些東西保留下來的,為了作對比,正常情況下以前裝的小張會被小李覆蓋掉, 下面我們來看一下小李的主介面長什麼樣子

小李_home.png

可以看到我們的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 ,順便還加了 versionCodeversionName, 這可以單獨設定每個app不同的版本號,不同的版本名稱,不同的包名,怕暈了,給個截圖讓你瞅瞅

applicationId.png

4.身份證頭像不一樣 (APP 圖示)

我們把版本號修改了,現在我們安裝了之後就可以兩個APP共存了,而且包名也是不相同的,這樣基本上實現兩個人就可以出現在一個手機裡面了。

但是隻是名字改了,圖示還是一樣的,猛一看,很懵逼啊,這一不小心就點錯了,多尷尬

眾所周知,我們的app圖示是放在 res/mipmap 目錄下的, 我們之前拷貝的 li 目錄裡面是和 小張(main) 的是一樣的,我們在準備工作的時候還設定了 sourceSet ,而且,前面還說了相同名字的資源被覆蓋,不同的被增加 ,我們要做的只是把 小李(li) 的圖示放在 li/src/res/mipmap 下就可以了, 然後執行安裝到手機

li_mipmap.png

下面看改過的效果

li.png

好了,替換完成了。

5.不同的膚色

我們現在小李的膚色還跟小張的膚色是一樣的,下面我們來改一下,一切關於資源的要麼被替換,要麼被合併

我們要改的是 li/res/values/colors.xml , 因為現在小李這個目錄下面還沒有 colors.xml 這個檔案,我們直接從 小張(main) 的目錄裡面 copy 過來一個,改一下里面的色值就可以了。

小張的膚色

main_color.png

小李的膚色

li_color.png

小李改過膚色的樣子

li_home_color.png

好了,膚色也改完了。

6.頭介面不一樣(重點)

現在小李的頭介面還是上面這張圖的介面,什麼也沒有。我們要做的是在頭這個介面裡面加上眼睛,鼻子,小張是個無面男 ,我們要小李有個鼻子,點選鼻子的時候彈出一個提示。

注意: 這個方法是通用的,就是說,只要是關於 java 程式碼的都按照這樣的套路來做,把要修改的那個類拷貝出來,把基礎包裡面的刪掉,給其他的目錄裡面也拷貝進去這個類,要不你在編譯其他目錄的apk的時候會報錯的,找不到類。

1.首先我們先把小李的頭copy過來。

為什麼要拷貝過來? 不拷貝過來怎麼會有自己的頭,如果沒有拷貝過來,預設使用的是小張的頭。

注意 :這時我們需要把原始碼大改一下了,我們要把原本小張的原始碼給改成通用的,就是把原來小張的原始碼當成一個基礎包,我們要是加人就需要另外新建一個目錄來做,就跟我們增加小李一樣的。

下面是我們改過的目錄結構

project.png

可以看到,我們單獨把 小張 給新建了一個目錄,他的存在方式跟 小李 的是一樣的。

為了以防萬一,我們還要在 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')
        }
    }

怕看懵逼,加一張圖

zhang_sourceSet.png

我們想要小李的頭跟其他的不一樣的話,需要單獨把 copy 出來進行修改,這個跟res 下的檔案不一樣, res 下的檔案相同了會覆蓋合併,java 目錄下面是會報錯的,

因為基礎包目錄下已經有了相同名字的類了。你在 li 的裡面加了相同的 類, 李使用的原始碼是用 基礎包裡的來做基礎的, 現在這個狀態就相當於 你在同一個包裡面,建立了兩個相同名字的類

head_diractory.png

2. 現在我們給小李的頭介面加上鼻子

首先我們先把 基礎包 裡面 頭 的佈局檔案 copy 到 li 的layout目錄下。

head_layout.png

看清楚,是 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()
        }

    }

}

鼻子.jpg

就是介面上有一個叫鼻子的按鈕,設定點選之後彈出吐司,是不是還很騷氣,而我們加上這個鼻子之後,小張還是原來的小張 ,鼻子只是被加在了小李的頭上。

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 這些。

打包.png

2. Manifest value 值設定佔位符

在 Manifest 中

Manifest.png

在 Gradle 中

Manifest_gradle.png

3. 關於微信回撥

微信的回撥是需要放在 包名/wxapi/ 目錄下的,我們這樣拷貝之後沒有了包名都是不一樣的,我們要為每個app的 src/java 目錄下新建一個和包名一樣的目錄,比如小張, 我們為了方便區分,把小張的包名改成 cn.zhang ,下面是目錄結構

zhang_application.png

wxapi_zhang.png

到這裡基本結束了。

有什麼沒寫的,寫錯的,評論告訴我,反正我也不一定改。。。

相關推薦

原始碼編譯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並自由切換