直接在apk中添加資源的研究

来源:https://www.cnblogs.com/fog2012/archive/2018/05/12/apk_merge.html
-Advertisement-
Play Games

原文 http://blog.votzone.com/2018/05/12/apk-merge.html 之前接手過一個sdk的開發工作,在開發過程中有一個很重要的點就是儘量使用代碼來創建控制項,資源文件最好放到assets目錄下,如果必須使用res資源,需要通過 getResources().get ...


原文 http://blog.votzone.com/2018/05/12/apk-merge.html

之前接手過一個sdk的開發工作,在開發過程中有一個很重要的點就是儘量使用代碼來創建控制項,資源文件最好放到assets目錄下,如果必須使用res資源,需要通過 getResources().getIdentifier("activity_splash","layout", getPackageName()) 這種方式來獲取資源id,而不能直接通過R文件獲取。

今天就來研究一下這個問題。

一、lib項目中r文件中資源唯一標誌為static變數

一般的app項目中自動生成的R文件為常量,而在library項目中為變數。根據Android官方文檔,在android 14 之後添加的這一特性,之前編譯後的lib項目中是常量,之後的為static 變數。 目的是為了在資源衝突時能夠修改資源唯一值。 如圖在library項目中,自動生成的R文件如下

r_ids_java

有一個需要關註的點是R文件是java 代碼,在build時會生成.class文件並添加到dex中。 因為R文件中的常量值僅僅受編譯器控制,在lib發佈之後添加到jar包中的.class並不會受到當前編譯器的影響。而通常lib發佈之後要給第三方使用。

看一下反編譯後的apk

/r_lib_smali

反編譯後查看mylib 下對應的R文件smali代碼,可見其值又被設置為final(常量)了。 因此我們可以知道編譯器在生成apk時雖然沒有lib的java代碼可以重置和修改,但是在將jar轉化為dex時可以轉換為常量。 轉化為常量後並不影響其使用, 如圖在lib中使用layout資源的反編譯代碼

/r_lib_use_smali

我們可以看出使用靜態常量可以在生成apk時動態修改其指定的值。

如果是常量,編譯後v0的值將直接使用常量值,這樣修改R.class文件中的值將沒有意義。例如在app中反編譯後代碼如下:

/r_app_use_smali

二、從反編譯後代結構中查看資源對應問題

可以看到,反編譯後的代碼與我們寫的java代碼基本是一一對應的,那麼問題來了,Android是怎麼通過一個id值來找到需要的資源呢?通過分析Android源碼我們當然可以找出過程,但是分析apk反編譯後的結構可以給我們更直接的思路。

/apk_res_public

在res/values/文件夾下有個public.xml文件,其中每行有三對值,分別為typenameid 通過分析我們可以直接得出結論: public.xml中的對應關係直接關係到哪個id找那個資源。

三、添加資源

Unity3d和cocos2d引擎有自己一套方式來添加資源id,假如我們自己搞一個游戲,又想為其添加一些java代碼和資源該怎麼操作?

根據之前的分析我們可以設計如下測試方案

  1. 假定我們要添加代碼的apk為targetapp,我們編寫一個叫mergelib的apk,然後將mergelib中資源和代碼添加到targetapp中。
  2. 在編寫mergelib時,使用 getResources().getIdentifier("activity_splash","layout", getPackageName()) 的方式獲取資源id;
  3. 通過apktool反編譯代碼,將資源文件複製到要加入的目標apk中;
  4. 將mergelib的public.xml文件中需要的資源項添加到targetapp中;
  5. 編譯並簽名測試。

根據如上實驗我們可以確定這樣的操作是可行的。測試流程如下:

需要的資源:

  • targetapp- 目標app, 在這裡代表游戲
  • mergelib- 要將其中包含資源的代碼合併進去
    mergelib 中對資源通過getIdentifier()的方式使用: 例如設置啟動頁Activity中的ContentView的設置
int id = getResources().getIdentifier("activity_splash","layout", getPackageName());    
setContentView(id);

我們的目標:為targetapp添加一個啟動頁,啟動頁代碼在mergelib中編寫。

執行流程

  1. 修改targetapp中AndroidManifest.xml文件 
    1) 將SplashActivity 的聲明添加進去 
    2) 修改啟動Activity

  2. 複製需要的代碼進入targetapp: 
    複製mergelib中SplashActivity的代碼並修改啟動MainActivity的啟動代碼 如圖

    replace_activity_launch_code

  3. 複製資源
    1) 將res/anim 下alpha.xml複製到target
    2) 將res/layout 下 spalsh.xml複製到target 下

  4. 修改ids 
    將 res/values/ids.xml 中多出來的行複製到 對應ids.xml文件中apk_merge_change_ids

    如圖, 本例中僅有一個id 即ImageView的id, 因此將該行複製到targetapp 中對應的ids.xml文件中即可 位置不重要

  5. 修改public.xml文件 mergelib Splash中用到了 animlayoutid, 並且間接用到了 mipmap,因此這些對應的值都需要添加到targetapp。觀察public.xml的文件結構,可以發現如下特點: 
    1) 所有同類型(type相同)的id連續 
    2) 同類型的id 前4位元組相同, 如下圖 anim 的前四位元組0x7f01 與 attr 不同

    apk_merge_public

    3) 所有id唯一 
    根據以上三個特點,我們將多出來的id添加到target為了保證唯一且方便修改,我們做瞭如下替換(右側為target)apk_merge_public_2

  6. 一切就緒,編譯並安裝

四、工具化處理public.xml的替換過程

上述手動測試僅僅只有一個資源id的情況,假如我們加入了一個support包或者其他一些包含資源的包,那麼資源數量將會增加到幾百個,這樣的話手動添加肯定是不行的,我們需要一個腳本工具來實現。

腳本接收兩個public.xml格式的文本,並輸出一個合併版本。 其中targetapp中的id值是不可變得,在遇到id衝突時,我們改變mergelib的public.xml。

1) 複製mergelib的public文件為mergeid.xml 
script_merge_public

2) 複製target app 的public 並命名為oriid.xml


3) 將mergeid.xml 和oriid.xml 放到RIDreset.py同目錄下, 運行py腳本

 script_run

4) 出現 oriid.xml_ 文件, 即生成的合併文件

案例及腳本

https://github.com/votzone/DroidCode/tree/master/VotAndroid/Mergeapp


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一 、介紹 二 、插入數據INSERT 三 、更新數據UPDATE 四 、刪除數據DELETE 五 、查詢數據SELECT 六 、許可權管理 一、 介紹 MySQL數據操作: DML 在MySQL管理軟體中,可以通過SQL語句中的DML語言來實現數據的操作,包括 本節內容包括: 插入數據更新數據刪除數 ...
  • 一、Oracle下載 官網地址:http://www.oracle.com/technetwork/database/enterprise edition/downloads/index.html 百度網盤:鏈接: https://pan.baidu.com/s/1q2vSPlHk_g1zLSANY ...
  • 背景 當站點的規模不斷膨脹,這給資料庫帶來巨大的查詢壓力,單單資料庫性能優化已經是不夠的,需對資料庫進行伸縮擴展。有三種方式: 1、資料庫主從 2、數據表分庫(垂直分區) 3、數據分區(水平分區) PS:事實上,很多大規模的站點基本上經歷了從簡單主從複製到垂直分區,再到水平分區的步驟。 資料庫主從 ...
  • 將數據導入 oracle 的方法應該很多 , 對於不同需求有不同的導入方式 , 最近使用oracle的sqlldr命令 導入資料庫數據感覺是個挺不錯的技術點 。 使用sqlldr命令 將文本文件導入 oracle中大致需要兩步 : 第一步:編寫ctl控制文件 Load data --裝載數據(第二步 ...
  • 最的mysql在裝的時候就可以設置 ,但是低版本的好像不行,需要在裝了以後才能設置。 mac下,mysql5.7.18連接出錯,錯誤信息為:Access denied for user 'root'@'localhost' (using password: YES) ()裡面的為shell中輸入的命 ...
  • 在ubuntu系統中操作命令:登錄:mysql -uroot -p啟動:service mysql start停止:service mysql stop重啟:service mysql restart 創建資料庫:create database 資料庫名字 charset = utf8;刪除資料庫: ...
  • 1、Default模式,也是沒有設置緩存模式時的預設模式 這個模式實現http協議中的內容,比如響應碼是304時,當然還會結合E-Tag和LastModify等頭。 StringRequest request = new StringRequest(url, method); request.set ...
  • NoHttp請求自動維持Cookie: 1.支持Session、Cookie、臨時Cookie的位置。 2.支持App重啟、關機開機後繼續持久化維持。 3.提供了介面,允許開發者監聽Cookie的變化,也可以改變某個Cookie的值。 伺服器端: 客戶端: 文章:https://blog.csdn. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...