編輯:關於Android編程
正在搞IOS的微信支付和支付寶支付,焦頭爛額之時,天上掉下來一個Android分包工具的需求,覺得還蠻有意思,其實之前一直想搞一個類似的東西,正好趁著這次機會實踐一下。
從產品角度
拿到一個apk安裝包,然後用這個包去生成n個包,這n個包需要有特定的標示,能夠根據包的標示去收集信息,而且這個n個包彼此不能覆蓋安裝。
從技術角度
對於這個需求,關鍵點在於三個點
1. 怎麼去生成n個包?
2. 怎麼修改apk的標示?
3. 怎麼使得這n個包不能覆蓋安裝?
這個apk包包含兩個功能點:
獲取一些包的基本信息,例如應用包名 獲取一些Meta信息,用來區分我們所打的包因為這篇文章主要在講bash和apk打包,對於Android代碼就不贅述了,貼出來參考。
(獲取應用包名)
PackageInfo info = null; try { // 獲取包名 info = this.getPackageManager() .getPackageInfo(this.getPackageName(), 0); } catch (NameNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 當前版本的包名 pgName = info.packageName;
(獲取Meta信息)
PackageManager pm = this.getPackageManager(); ApplicationInfo appInfo = null; try { appInfo = pm.getApplicationInfo(this.getPackageName(), PackageManager.GET_META_DATA); } catch (NameNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // 讀取meta的內容 msg.append(appInfo.metaData.getString("SDK_CHANNEL"));
這裡我們讀取了SDK_CHANNEL這個信息作為包的標示,需要在Android Manifest中配置好相關的Meta data。
可以看到現在的包名是com.example.testmultipac,渠道是天降正義~
接下來我們就開始著手一個一個解決問題
想要生成多個包,必須涉及到需要把apk進行反編譯,然後重新生成apk包的過程,因此我使用了apktool這個工具。
注:apktool 有1和2兩個版本,兩者語法有些許不同,在這裡使用了apktool_2.1.0這個jar,當然你也可以使用apktool1;window可以直接調用apktool.bat的批處理,事實上也是調用了apktool.jar。
apktool指令:
解開apk包:
java -jar apktool d -f 輸入的apk路徑 -o 輸出的文件夾路徑
重新生成apk包:
java -jar apktool b 上一步解出的文件夾路徑 -o 輸出apk路徑
注:這裡使用的apktool2,因此有-o這個參數,apktool是沒有的,請注意。
了解了apktool的指令後,我們便可以方便的實現apk的解包和組包,在組包的會後通過修改包的名稱,就可以生成多個不同名的包了。
例如:
do_repac(){ outapk=$apkpacpath"/"$game_package_name".apk" # apktool重新回包 以免apktool的一些臨時改動 java -jar ./tool/apktool/$APKTOOL_JAR b $unpacpath -o $outapk }
上面的outapk即為 $apkpacpath”/”$game_package_name”.apk”
game_package_name為當前包的包名。(如果能做到包名不同,則生成的apk的名字便不同)
目前例子來說,我們apk的標示是”SDK_CHANNEL”這個Meta字段,我們可以通過修改這個字段來實現我們對包的區分。當客戶端對服務器發起請求的時候,帶上這個字段,服務器便可以方便的知道是哪個包進行的請求了。
那我們要怎麼修改這個字段呢?
在此我使用了sed,一個方便替換的文本的指令。
指令為:
sed -i '' "s~^.*~g" $manifest
在此我稍作解釋,如果有興趣的朋友可以去google或者百度才能系統的學習。
1)sed "s~原字符串~新字符串~g" 文件路徑 這是sed最基本的指令, “~”符號可以換成很多符號,三個”~”對應更換即可.
2)sed -i
通過 man sed 可以查到sed的基本指令(OSX 系統)
意思是不備份,直接在原文件上面進行操作。
注:在linux上可以直接使用 sed -i,而在Unix上需要sed -i ""
3) ^.*
4)"$game_channel"這個是bash的取值,相當於一個變量,這裡我是取game_channel這個變量
到現在,我們已經能夠把包解出來,然後使用sed去修改裡面的標示,看樣子已經成功了一半了。
涉及到這個問題,我們需要對Android的基本知識進行一個簡單回顧。
Android通過什麼來保證應用的唯一性?
答案是包名和簽名。
在修改包名的時候,我同樣是用了一系列指令。(下意識寫的指令,並沒有考慮到是否最好,如果有更好的指令可以給我留言)
old_pacname=`cat $manifest | grep "package=" | head -n 1 | awk -F 'package=\"' '{print $2}' | awk -F '\"' '{print $1}' | xargs echo ` echo "==>"$old_pacname sed -i '' "s~package=\""$old_pacname\""~package="\"$game_package_name\""~g" $manifest
講一下思路:
1. 先把”package=”關鍵字的那一行抓出來(這裡是為了抓包名)
2. 使用兩次awk取出 package=後面的包名(得到當前的包名)
3. 當前的包名賦值給old_pacname
4 .使用sed替換舊的包名為新包名,新的包名為game_package_name這個變量的值。
至此我們已經弄清楚了其中的知識點,對於一些難點也有了一定處理辦法,接下來我們便要開始做一個腳本去自動化實現Android的反編譯和分包了。
可以發現,在這個流程的最後一步,還有個簽名的過程,那怎麼進行簽名呢?
我們是通過jarsigner這個工具對apk進行簽名的,如果不簽名的應用可是無法安裝的~
那個這個jarsigner是什麼呢?其實這個是jdk自帶的對jar包進行簽名的工具,我們可以在安裝的java指令的同級目錄找到它。
通過直接輸入jarsigner指令,可以得到提示
用法:
# 顯示信息 簽名文件 簽名密碼 生成apk 未簽名apk alias jarsigner -verbose -keystore $keystore_name -storepass $SIGN_PASS -signedjar $sign_apkname -digestalg SHA1 -sigalg MD5withRSA $unsign_apkname $SIGN_ALIAS
每個參數的意思注釋都標志的很清楚了~
下面講講腳本的思路
第一種)所有的參數從外部傳遞調用,Mode為1時
第二種)讀取本地的配置文件,Mode為0時,可以實現批處理生成多個包
功能說明:
help_info() { cat << ENTER ============= Auto pac For game ============= Version: 1.0 Date: 20160907 Usage: Auto repackage For the game, modify package name and subchannel e.g.: Mode0: sh autopac.sh inputApkPath gamename Mode1: sh autopac.sh inputApkPath gamename channel outputApkPath inputApkPath: 待分包的apk路徑 gamename: 游戲名稱英文首字母小寫 channel: 渠道號 outputApkPath: 完成輸出apk的路徑 ============= Auto pac For game ============= ENTER }
參數由外部輸入就不贅述了,簡單的賦值即可。
對於Mode=0時的讀取配置文件一般是通過awk來實現的。
例如配置文件的內容是這樣
[package] package1=d101 package2=d102
我們通過awk取出對應的d101,和102出來
read_config() { inifile=$1 #$1為配置文件的位置 _readIni=`awk -F '=' '$1~/'package[d]*'/{print $2}' $inifile` echo $_readIni }
稍微解釋一下:
1. awk -F ‘=’ 為按照’=’,進行字符串分割
2. package[d]*為正則表達式,用來匹配package1,package2這種類型的一行,所以配置文件中可以有很多個package
3. {print $2} 就是打印出用’=’分割的第二個字符串,即’=’號後面的內容。
(假如我的標示不止1個怎麼辦?)
例如我的標示有三個,channel、subchannel、payway分別對應著渠道號、子渠道、支付渠道,那這個時候怎麼辦呢?
我們可以把配置文件寫成這樣:
package1=d101:subchannel101:1 package1=d102:subchannel102:2
然後在按照上面的做法,我們取出了 ‘=’ 右邊的值,例如d101:subchannel101:1
我們便可以通過bash的字符串分割來做:
game_payway=${1##*:} temp=${1%:*} game_subchannel=${temp##*:} game_channel=${temp%:*}
這樣便可以取出payway等值了,關於bash的字符串分割,可以自行百度
基本的東西都講完了,我們來看看最終的效果:
以mode為1的批處理為例子:
1)配置文件配置了4個不同channel的包:
[package] package1=yingxiongbuxiu:英雄不朽 package2=wushiyidao:午時已到 package3=laidianyinyue:來點音樂 package4=ronghuohexin:熔火核心
2)執行腳本:
sh autopac.sh ./TestMultiPac.apk mszl
TestMutiPac.apk就是我們最開始生成的一個apk,mszl為游戲的名稱
3)執行效果:
看到已經生成了4個包:
4)安裝效果:
如圖生成了5個包
隨便選取一個包的效果
源碼地址:
https://github.com/yang8456211/AutoPacAndroid
前言:從本篇開始,將進入Multimedia框架,包含MediaPlayer, Camera, Surface, MediaRecord, 接下來幾篇都是MediaPla
我們知道,利用javah生成的c/c++頭文件的時候,會對java中定義的 native 函數生成對應的jni層函數,如下: /* * Class:
Android中控件大致被分為兩類ViewGroup,View。ViewGroup作為容器管理View。Android視圖,是類似於Dom樹的架構。父視圖負責測量定位繪制
1 Content Provider組件簡介Content Provider組件是Android應用的重要組件之一,管理對數據的訪問,主要用於不同的應用程序之間實現數據共