package com.taobao.atlas.dexmerge; import android.os.RemoteException; import android.util.Log; import com.taobao.atlas.dex.Dex; import com.taobao.atlas.dexmerge.dx.merge.CollisionPolicy; import com.taobao.atlas.dexmerge.dx.merge.DexMerger; import java.io.*; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * Created by lilong on 16/7/5. */ public class MergeExcutorServices { private IDexMergeCallback mCallback = null; ExecutorService es = null; public static ZipFile sZipPatch; public static AtomicInteger successCount = new AtomicInteger(); public static AtomicInteger needMergeCount = new AtomicInteger(); List<Future<Boolean>> fList = new ArrayList<Future<Boolean>>(); private static final String TAG = "mergeTask"; public static OS os = OS.mac; public MergeExcutorServices(IDexMergeCallback mCallback) { this.mCallback = mCallback; es = Executors.newFixedThreadPool(3); } public void excute(String patchFilePath,List<MergeObject> list, boolean b) throws ExecutionException, InterruptedException { if (!b){ needMergeCount.set(list.size()); } try { sZipPatch = new ZipFile(patchFilePath); Enumeration<? extends ZipEntry> zes = sZipPatch.entries(); ZipEntry entry = null; String key = null; HashMap<String,List<ZipEntry>> bundleEntryGroup= new HashMap<String,List<ZipEntry>>(); while (zes.hasMoreElements()) { entry = zes.nextElement(); if (entry.getName().equals("libcom_taobao_maindex.so")){ List<ZipEntry>mainDex = new ArrayList<ZipEntry>(); mainDex.add(entry); bundleEntryGroup.put("com_taobao_maindex",mainDex); }else if(entry.getName().startsWith("lib")){ if (entry.getName().indexOf("/")!= -1){ key = entry.getName().substring(3,entry.getName().indexOf("/")); os = OS.mac; }else if (entry.getName().indexOf("\\")!= -1){ key = entry.getName().substring(3,entry.getName().indexOf("\\")); os = OS.windows; } List<ZipEntry> bundleEntry = null; if((bundleEntry=bundleEntryGroup.get(key)) == null){ bundleEntry = new ArrayList<ZipEntry>(); bundleEntryGroup.put(key,bundleEntry); bundleEntry.add(entry); }else { bundleEntryGroup.get(key).add(entry); } } } for (MergeObject mergeObject : list) { MergeTask mergeTask = new MergeTask(new File(mergeObject.originalFile),bundleEntryGroup.get(mergeObject.patchName.replace(".","_")),mergeObject.patchName, new File(mergeObject.mergeFile), b); Future future = es.submit(mergeTask); fList.add(future); } waitTaskCompleted(); } catch (IOException e) { e.printStackTrace(); }finally { if (sZipPatch != null){ try { sZipPatch.close(); } catch (IOException e) { e.printStackTrace(); } } } es.shutdown(); try { if (successCount.get() == needMergeCount.get()) { Log.e(TAG,"merge all finished"); mCallback.onMergeAllFinish(true, null); successCount.set(0); needMergeCount.set(0); } else { mCallback.onMergeAllFinish(false, "merge failed!"); Log.e(TAG,"merge all finish but failed!"); successCount.set(0); needMergeCount.set(0); } } catch (RemoteException e) { e.printStackTrace(); } } public boolean waitTaskCompleted() throws InterruptedException, ExecutionException { boolean flag = true; if (null == fList) { return true; } else { for (Future<Boolean> future : fList) { if (!future.get()) { flag = false; } } } return flag; } public class MergeTask implements Callable { public MergeTask(File sourceFile, List<ZipEntry> patchEntries,String patchName, File outFile, boolean diff) { this.sourceFile = sourceFile; this.patchName = patchName; this.patchEntries = patchEntries; this.outFile = outFile; this.diffDex = diff; } private File sourceFile; private String patchName; private File outFile; private List<ZipEntry> patchEntries; private boolean diffDex; @Override public Boolean call() throws Exception { MergeTool.mergePrepare(sourceFile, patchEntries,patchName, outFile, diffDex, new PrepareCallBack() { @Override public void prepareMerge(String patchBundleName,ZipFile sourceFile, ZipEntry patchDex, OutputStream newDexStream) throws IOException { boolean classMerge = false; InputStream[] inputStreams = new InputStream[2]; try { inputStreams[0] = sourceFile.getInputStream(new ZipEntry("classes.dex")); inputStreams[1] = MergeExcutorServices.sZipPatch.getInputStream(patchDex); // if (patchDex.getName().endsWith(".tdex")){ // classMerge = true; // } dexMergeInternal(inputStreams, newDexStream,patchBundleName); } catch (IOException e) { e.printStackTrace(); } finally { for (InputStream inputStream:inputStreams){ if (inputStream != null){ inputStream.close(); } } } } }); return true; } } private void dexMergeInternal(InputStream[] inputStreams, OutputStream newDexStream, String bundleName) { FileOutputStream fileOutputStream = null; if (inputStreams[0] == null || inputStreams[1] == null) { try { mCallback.onMergeFinish(bundleName,false, "argNUll"); } catch (RemoteException e) { e.printStackTrace(); } return; } try { //方式一 // DexPatchApplier dexPatchApplier = new DexPatchApplier(inputStreams[0],inputStreams[1]); // dexPatchApplier.executeAndSaveTo(newDexStream); //方式二 Dex dex1 = new Dex(inputStreams[1]); Dex dex2 = new Dex(inputStreams[0]); List<Dex> dexs = new ArrayList<Dex>(); dexs.add(dex1); dexs.add(dex2); DexMerger mDexMerge = new DexMerger(new Dex[]{dex1, dex2}, CollisionPolicy.KEEP_FIRST); mDexMerge.setCompactWasteThreshold(1); Dex outDex = mDexMerge.merge(); outDex.writeTo(newDexStream); newDexStream.flush(); mCallback.onMergeFinish(bundleName, true, "Success"); successCount.incrementAndGet(); } catch (Throwable e) { e.printStackTrace(); try { mCallback.onMergeFinish(bundleName, false, "IOException 2"); } catch (RemoteException e1) { e1.printStackTrace(); } }finally { if (fileOutputStream != null){ try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } public interface PrepareCallBack { void prepareMerge(String patchBundleName,ZipFile sourceFile, ZipEntry patchDex, OutputStream newDexStream) throws IOException; } enum OS{ mac,windows,linux } }