implement destroy Block

This commit is contained in:
REAndroid 2023-03-11 05:14:01 -05:00
parent 53d8e192fa
commit f81616f2f1
14 changed files with 331 additions and 61 deletions

View File

@ -201,7 +201,7 @@ public class ApkModule implements ApkFile {
}
logVerbose("Dir validated: '"+path+"' -> '"+pathNew+"'");
}
TableStringPool stringPool= getTableBlock().getTableStringPool();
TableStringPool stringPool= getTableBlock().getStringPool();
stringPool.refreshUniqueIdMap();
getTableBlock().refresh();
}
@ -226,7 +226,7 @@ public class ApkModule implements ApkFile {
}
logVerbose("Root changed: '"+path+"' -> '"+pathNew+"'");
}
TableStringPool stringPool= getTableBlock().getTableStringPool();
TableStringPool stringPool= getTableBlock().getStringPool();
stringPool.refreshUniqueIdMap();
getTableBlock().refresh();
}
@ -236,7 +236,7 @@ public class ApkModule implements ApkFile {
public List<ResFile> listResFiles(int resourceId, ResConfig resConfig) throws IOException {
List<ResFile> results=new ArrayList<>();
TableBlock tableBlock=getTableBlock();
TableStringPool stringPool= tableBlock.getTableStringPool();
TableStringPool stringPool= tableBlock.getStringPool();
for(InputSource inputSource:getApkArchive().listInputSources()){
String name=inputSource.getAlias();
StringGroup<TableString> groupTableString = stringPool.get(name);
@ -274,7 +274,7 @@ public class ApkModule implements ApkFile {
}
return results;
}
public String getPackageName() throws IOException {
public String getPackageName(){
if(hasAndroidManifestBlock()){
return getAndroidManifestBlock().getPackageName();
}
@ -289,7 +289,7 @@ public class ApkModule implements ApkFile {
}
return pkg.getName();
}
public void setPackageName(String name) throws IOException {
public void setPackageName(String name) {
String old=getPackageName();
if(hasAndroidManifestBlock()){
getAndroidManifestBlock().setPackageName(name);
@ -319,6 +319,46 @@ public class ApkModule implements ApkFile {
return mTableBlock!=null
|| getApkArchive().getInputSource(TableBlock.FILE_NAME)!=null;
}
public void destroy(){
getApkArchive().clear();
AndroidManifestBlock manifestBlock = this.mManifestBlock;
if(manifestBlock!=null){
manifestBlock.destroy();
this.mManifestBlock = null;
}
TableBlock tableBlock = this.mTableBlock;
if(tableBlock!=null){
tableBlock.destroy();
this.mTableBlock = null;
}
}
public void setManifest(AndroidManifestBlock manifestBlock){
APKArchive archive = getApkArchive();
if(manifestBlock==null){
mManifestBlock = null;
archive.remove(AndroidManifestBlock.FILE_NAME);
return;
}
manifestBlock.setApkFile(this);
BlockInputSource<AndroidManifestBlock> source =
new BlockInputSource<>(AndroidManifestBlock.FILE_NAME, manifestBlock);
archive.add(source);
mManifestBlock = manifestBlock;
}
public void setTableBlock(TableBlock tableBlock){
APKArchive archive = getApkArchive();
if(tableBlock==null){
mTableBlock = null;
archive.remove(TableBlock.FILE_NAME);
return;
}
tableBlock.setApkFile(this);
BlockInputSource<TableBlock> source =
new BlockInputSource<>(TableBlock.FILE_NAME, tableBlock);
archive.add(source);
mTableBlock = tableBlock;
}
@Override
public AndroidManifestBlock getAndroidManifestBlock() {
if(mManifestBlock!=null){
return mManifestBlock;
@ -374,7 +414,7 @@ public class ApkModule implements ApkFile {
// loading packages and other chunk blocks for faster and less memory usage
public TableStringPool getVolatileTableStringPool() throws IOException{
if(mTableBlock!=null){
return mTableBlock.getTableStringPool();
return mTableBlock.getStringPool();
}
InputSource inputSource = getApkArchive()
.getInputSource(TableBlock.FILE_NAME);
@ -388,7 +428,7 @@ public class ApkModule implements ApkFile {
inputStream.close();
return stringPool;
}
return getTableBlock().getTableStringPool();
return getTableBlock().getStringPool();
}
TableBlock loadTableBlock() throws IOException {
APKArchive archive=getApkArchive();

View File

@ -13,47 +13,144 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.apk;
package com.reandroid.apk;
import com.reandroid.archive.APKArchive;
import com.reandroid.archive.InputSource;
import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.arsc.chunk.xml.ResXmlAttribute;
import com.reandroid.arsc.chunk.xml.ResXmlElement;
import com.reandroid.arsc.util.FrameworkTable;
import com.reandroid.arsc.value.ValueType;
import com.reandroid.archive.APKArchive;
import com.reandroid.archive.ByteInputSource;
import com.reandroid.archive.InputSource;
import com.reandroid.archive.InputSourceUtil;
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.arsc.chunk.xml.ResXmlAttribute;
import com.reandroid.arsc.chunk.xml.ResXmlElement;
import com.reandroid.arsc.util.FrameworkTable;
import com.reandroid.arsc.value.ValueType;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Objects;
/*
* Produces compressed framework apk by removing unnecessary files and entries,
* Produces compressed framework apk by removing irrelevant files and entries,
* basically it keeps only resources.arsc and AndroidManifest.xml
*/
public class FrameworkApk extends ApkModule{
public class FrameworkApk extends ApkModule{
private final Object mLock = new Object();
private int versionCode;
private String versionName;
private String packageName;
private boolean mOptimizing;
private boolean mDestroyed;
public FrameworkApk(String moduleName, APKArchive apkArchive) {
super(moduleName, apkArchive);
super.setLoadDefaultFramework(false);
}
public FrameworkApk(APKArchive apkArchive) {
this("framework", apkArchive);
}
@Override
public APKArchive getApkArchive() {
APKArchive archive=super.getApkArchive();
clearFiles(archive);
return archive;
}
private void clearFiles(APKArchive archive){
if(archive.entriesCount()==2){
return;
public void destroy(){
synchronized (mLock){
this.versionCode = -1;
this.versionName = "-1";
this.packageName = "destroyed";
super.destroy();
this.mDestroyed = true;
}
}
public boolean isDestroyed() {
synchronized (mLock){
if(!mDestroyed){
return false;
}
if(hasTableBlock()){
this.versionCode = 0;
this.versionName = null;
this.packageName = null;
mDestroyed = false;
return false;
}
return true;
}
}
public int getVersionCode() {
if(this.versionCode == 0){
initValues();
}
return this.versionCode;
}
public String getVersionName() {
if(this.versionName == null){
initValues();
}
return this.versionName;
}
@Override
public String getPackageName() {
if(this.packageName == null){
initValues();
}
return this.packageName;
}
@Override
public void setPackageName(String packageName) {
super.setPackageName(packageName);
this.packageName = null;
}
private void initValues() {
if(hasAndroidManifestBlock()){
AndroidManifestBlock manifest = getAndroidManifestBlock();
Integer code = manifest.getVersionCode();
if(code!=null){
this.versionCode = code;
}
if(this.versionName == null){
this.versionName = manifest.getVersionName();
}
if(this.packageName == null){
this.packageName = manifest.getPackageName();
}
}
if(hasTableBlock()){
FrameworkTable table = getTableBlock();
if(this.versionCode == 0 && table.isOptimized()){
int version = table.getVersionCode();
if(version!=0){
versionCode = version;
if(this.versionName == null){
this.versionName = String.valueOf(version);
}
}
}
if(this.packageName == null){
PackageBlock packageBlock = table.pickOne();
if(packageBlock!=null){
this.packageName = packageBlock.getName();
}
}
}
}
@Override
public void setManifest(AndroidManifestBlock manifestBlock){
synchronized (mLock){
super.setManifest(manifestBlock);
this.versionCode = 0;
this.versionName = null;
this.packageName = null;
}
}
@Override
public void setTableBlock(TableBlock tableBlock){
synchronized (mLock){
super.setTableBlock(tableBlock);
this.versionCode = 0;
this.versionName = null;
this.packageName = null;
}
InputSource tableSource= archive.getInputSource(TableBlock.FILE_NAME);
InputSource manifestSource= archive.getInputSource(AndroidManifestBlock.FILE_NAME);
archive.clear();
archive.add(tableSource);
archive.add(manifestSource);
}
@Override
public FrameworkTable getTableBlock() {
@ -69,28 +166,60 @@ public class FrameworkApk extends ApkModule{
InputStream inputStream = inputSource.openStream();
FrameworkTable frameworkTable=FrameworkTable.load(inputStream);
frameworkTable.setApkFile(this);
if(hasAndroidManifestBlock()){
optimizeTable(frameworkTable);
}
BlockInputSource<FrameworkTable> blockInputSource=new BlockInputSource<>(inputSource.getName(), frameworkTable);
blockInputSource.setMethod(inputSource.getMethod());
blockInputSource.setSort(inputSource.getSort());
archive.add(blockInputSource);
return frameworkTable;
}
private void optimizeTable(FrameworkTable table) throws IOException {
if(table.isOptimized()){
return;
public void optimize(){
synchronized (mLock){
if(mOptimizing){
return;
}
if(!hasTableBlock()){
mOptimizing = false;
return;
}
FrameworkTable frameworkTable = getTableBlock();
if(frameworkTable.isOptimized()){
mOptimizing = false;
return;
}
FrameworkOptimizer optimizer = new FrameworkOptimizer(this);
optimizer.optimize();
mOptimizing = false;
}
int prev=table.countBytes();
logMessage("Optimizing ...");
AndroidManifestBlock manifestBlock = getAndroidManifestBlock();
String version=String.valueOf(manifestBlock.getVersionCode());
String name=manifestBlock.getPackageName();
table.optimize(name, version);
long diff=prev - table.countBytes();
long percent=(diff*100L)/prev;
logMessage("Optimized: "+percent+" %");
}
public String getName(){
if(isDestroyed()){
return "destroyed";
}
String pkg = getPackageName();
if(pkg==null){
return "";
}
return pkg + "-" + getVersionCode();
}
@Override
public int hashCode(){
return Objects.hash(getClass(), getName());
}
@Override
public boolean equals(Object obj){
if(obj==this){
return true;
}
if(getClass()!=obj.getClass()){
return false;
}
FrameworkApk other = (FrameworkApk) obj;
return getName().equals(other.getName());
}
@Override
public String toString(){
return getName();
}
public static FrameworkApk loadApkFile(File apkFile) throws IOException {
APKArchive archive=APKArchive.loadZippedApk(apkFile);
@ -100,7 +229,7 @@ public class FrameworkApk extends ApkModule{
APKArchive archive=APKArchive.loadZippedApk(apkFile);
return new FrameworkApk(moduleName, archive);
}
public static boolean isFramework(ApkModule apkModule) throws IOException {
public static boolean isFramework(ApkModule apkModule) {
if(!apkModule.hasAndroidManifestBlock()){
return false;
}
@ -115,4 +244,27 @@ public class FrameworkApk extends ApkModule{
}
return attribute.getValueAsBoolean();
}
}
public static FrameworkApk loadApkBuffer(InputStream inputStream) throws IOException{
return loadApkBuffer("framework", inputStream);
}
public static FrameworkApk loadApkBuffer(String moduleName, InputStream inputStream) throws IOException {
APKArchive archive = new APKArchive();
FrameworkApk frameworkApk = new FrameworkApk(moduleName, archive);
Map<String, ByteInputSource> inputSourceMap = InputSourceUtil.mapInputStreamAsBuffer(inputStream);
ByteInputSource source = inputSourceMap.get(TableBlock.FILE_NAME);
FrameworkTable tableBlock = new FrameworkTable();
if(source!=null){
tableBlock.readBytes(source.openStream());
}
frameworkApk.setTableBlock(tableBlock);
AndroidManifestBlock manifestBlock = new AndroidManifestBlock();
source = inputSourceMap.get(AndroidManifestBlock.FILE_NAME);
if(source!=null){
manifestBlock.readBytes(source.openStream());
}
frameworkApk.setManifest(manifestBlock);
archive.addAll(inputSourceMap.values());
return frameworkApk;
}
}

View File

@ -36,6 +36,14 @@ public class PackageArray extends BlockArray<PackageBlock>
this.mPackageCount=packageCount;
mPackageCount.setBlockLoad(this);
}
public void destroy(){
Iterator<PackageBlock> itr = iterator(true);
while (itr.hasNext()){
PackageBlock packageBlock=itr.next();
packageBlock.destroy();
}
clearChildes();
}
public PackageBlock pickOne(){
PackageBlock[] items=getChildes();
if(items==null||items.length==0){

View File

@ -51,9 +51,12 @@ public abstract class StringArray<T extends StringItem> extends OffsetBlockArray
};
}
public List<T> removeUnusedStrings(){
List<T> allUnused=listUnusedStrings();
remove(allUnused);
return allUnused;
List<T> unusedList=listUnusedStrings();
remove(unusedList);
for(T item:unusedList){
item.onRemoved();
}
return unusedList;
}
public List<T> listUnusedStrings(){
List<T> results=new ArrayList<>();

View File

@ -31,6 +31,13 @@ public class StyleArray extends OffsetBlockArray<StyleItem> implements JSONConve
setEndBytes(END_BYTE);
}
@Override
public void clearChildes(){
for(StyleItem styleItem:listItems()){
styleItem.onRemoved();
}
super.clearChildes();
}
@Override
void refreshEnd4Block(BlockReader reader, ByteArray end4Block) throws IOException {
end4Block.clear();
if(reader.available()<4){

View File

@ -66,6 +66,14 @@ package com.reandroid.arsc.chunk;
addChild(mSpecStringPool);
addChild(mBody);
}
public void destroy(){
getEntriesGroupMap().clear();
getPackageBody().destroy();
getTypeStringPool().destroy();
getSpecStringPool().destroy();
setId(0);
setName("");
}
public Entry getOrCreate(String qualifiers, String type, String name){
ResConfig resConfig = new ResConfig();
resConfig.parseQualifiers(qualifiers);
@ -162,6 +170,9 @@ package com.reandroid.arsc.chunk;
public SpecStringPool getSpecStringPool(){
return mSpecStringPool;
}
public PackageBody getPackageBody() {
return mBody;
}
public SpecTypePairArray getSpecTypePairArray(){
return mBody.getSpecTypePairArray();
}

View File

@ -38,7 +38,7 @@ import java.util.*;
implements MainChunk, JSONConvert<JSONObject>, EntryStore {
private final TableStringPool mTableStringPool;
private final PackageArray mPackageArray;
private final Set<TableBlock> mFrameWorks=new HashSet<>();
private final List<TableBlock> mFrameWorks=new ArrayList<>();
private ApkFile mApkFile;
public TableBlock() {
super(new TableHeader(), 2);
@ -48,6 +48,16 @@ import java.util.*;
addChild(mTableStringPool);
addChild(mPackageArray);
}
public void destroy(){
getPackageArray().destroy();
getStringPool().destroy();
clearFrameworks();
refresh();
}
public int countPackages(){
return getPackageArray().childesCount();
}
public PackageBlock pickOne(){
return getPackageArray().pickOne();
}
@ -232,7 +242,7 @@ import java.util.*;
jsonObject.put(BuildInfo.NAME_arsc_lib_version, BuildInfo.getVersion());
jsonObject.put(NAME_packages, getPackageArray().toJson());
JSONArray jsonArray = getTableStringPool().toJson();
JSONArray jsonArray = getStringPool().toJson();
if(jsonArray!=null){
jsonObject.put(NAME_styled_strings, jsonArray);
}
@ -247,8 +257,8 @@ import java.util.*;
if(tableBlock==null||tableBlock==this){
return;
}
if(getPackageArray().childesCount()==0 && getTableStringPool().countStrings()==0){
getTableStringPool().merge(tableBlock.getTableStringPool());
if(countPackages()==0 && getStringPool().countStrings()==0){
getStringPool().merge(tableBlock.getStringPool());
}
getPackageArray().merge(tableBlock.getPackageArray());
refresh();

View File

@ -55,6 +55,16 @@
addChild(mResXmlIDMap);
addChild(mResXmlElementContainer);
}
public void destroy(){
ResXmlElement root = getResXmlElement();
if(root!=null){
root.clearChildes();
setResXmlElement(null);
}
getResXmlIDMap().destroy();
getStringPool().destroy();
refresh();
}
public void setAttributesUnitSize(int size, boolean setToAll){
ResXmlElement root = getResXmlElement();
if(root!=null){

View File

@ -31,6 +31,9 @@
this.mResXmlIDArray=new ResXmlIDArray(getHeaderBlock());
addChild(mResXmlIDArray);
}
public void destroy(){
getResXmlIDArray().clearChildes();
}
public ResXmlIDArray getResXmlIDArray(){
return mResXmlIDArray;
}

View File

@ -32,6 +32,12 @@ public class BlockList<T extends Block> extends Block {
super();
mItems=new ArrayList<>();
}
public void clearChildes(){
ArrayList<T> childList = new ArrayList<>(getChildes());
for(T child:childList){
remove(child);
}
}
public void sort(Comparator<T> comparator){
mItems.sort(comparator);
}

View File

@ -50,6 +50,14 @@ public class PackageBody extends FixedBlockContainer {
addChild(4, mOverlayablePolicyList);
addChild(5, mUnknownChunkList);
}
public void destroy(){
getSpecTypePairArray().clearChildes();
getLibraryBlock().getLibraryInfoArray().clearChildes();
getStagedAliasList().clearChildes();
getOverlayableList().clearChildes();
getOverlayablePolicyList().clearChildes();
getUnknownChunkList().clearChildes();
}
public OverlayableList getOverlayableList() {
return mOverlayableList;
}

View File

@ -80,6 +80,13 @@ public class StringItem extends BlockItem implements JSONConvert<JSONObject> {
ref.set(newIndex);
}
}
public void onRemoved(){
StyleItem style = getStyle();
if(style!=null){
style.onRemoved();
}
setParent(null);
}
@Override
public void onIndexChanged(int oldIndex, int newIndex){
reUpdateReferences(newIndex);

View File

@ -15,7 +15,6 @@
*/
package com.reandroid.arsc.item;
import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.model.StyleSpanInfo;
import com.reandroid.arsc.pool.StringPool;
@ -33,8 +32,10 @@ public class StyleItem extends IntegerArray implements JSONConvert<JSONObject> {
super();
this.mReferences = new HashSet<>();
}
void onRemoved(){
public void onRemoved(){
setStylePieceCount(0);
mSpanInfoList = null;
setParent(null);
}
public void onDataLoaded(){
linkAll();

View File

@ -68,6 +68,10 @@ package com.reandroid.arsc.pool;
mUniqueMap = new HashMap<>();
}
public void destroy(){
getStyleArray().clearChildes();
getStringsArray().clearChildes();
}
public List<String> toStringList(){
return getStringsArray().toStringList();
}