package com.taobao.atlas.dexmerge; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import android.taobao.atlas.runtime.RuntimeVariables; import android.util.Log; import java.util.List; /** * Created by xieguo.xg on 1/21/16. */ public class DexMergeClient { public static final int REMOTE_TIMEOUT = 60 * 1000; private Object lock = new Object(); // private Object mergeLock = new Object(); private boolean isFinished; private boolean isBinded; private long mStartTime; private final static String TAG = "DexMergeClient"; private boolean isTimeout = true; // Whether unlock caused by timeout private boolean isBinderDied = false; private final static int numBinderDieTries = 3; IDexMergeBinder dexMergeBinder; private IBinder.DeathRecipient mDeathRecipient = new MyServiceDeathHandler(); private MergeCallback mergeCallBack; public DexMergeClient(MergeCallback mergeCallBack) { this.mergeCallBack = mergeCallBack; } public boolean prepare() { Intent intent = new Intent(); intent.setClassName(RuntimeVariables.androidApplication, "com.taobao.atlas.dexmerge.DexMergeService"); mStartTime = System.currentTimeMillis(); /** * try to Bind dexmerge service */ if (!RuntimeVariables.androidApplication.bindService(intent, conn, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT)) { return false; } /** * Block wait service bind */ if (!isBinded) { try { synchronized (lock) { // 10 minutes timeout lock.wait(REMOTE_TIMEOUT); } } catch (InterruptedException e) { } } if (!isBinded) { /** * unbind once timeout */ // AppMonitor.Counter.commit("dexMerge", "RemoteException", "bind timeout " + // VERSION.SDK_INT + // " " + // (mPowerManager.isScreenOn() ? 1 : 0), 1); RuntimeVariables.androidApplication.unbindService(conn); } return isBinded; } public void unPrepare() { RuntimeVariables.androidApplication.unbindService(conn); } public boolean dexMerge(String patchFilePath, List toMergeList, boolean diffBundleDex) { if (toMergeList.size() == 0) { return true; } mStartTime = System.currentTimeMillis(); if (!dexMergeInternal(patchFilePath,toMergeList, diffBundleDex) && isBinderDied) { /** * handle binder died case */ for (int i = 0; i < numBinderDieTries && isBinderDied; i++) { isBinderDied = false; if (prepare() == false) { return isFinished; } if (dexMergeInternal(patchFilePath,toMergeList, diffBundleDex)) { break; } } } return isFinished; } private boolean dexMergeInternal(String patchFilePath,List toMergeList, boolean diffBundleDex) { isFinished = false; try { dexMergeBinder.dexMerge(patchFilePath,toMergeList, diffBundleDex); // synchronized (mergeLock){ // try { // mergeLock.wait(); // } catch (InterruptedException e) { // e.printStackTrace(); // } // } } catch (RemoteException e) { e.printStackTrace(); } return isFinished; } ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { //返回一个IDexMergeBinder对象 Log.d(TAG, "Get binder" + (System.currentTimeMillis() - mStartTime) + " ms"); dexMergeBinder = IDexMergeBinder.Stub.asInterface(service); isBinded = true; //注册回调接口 try { dexMergeBinder.registerListener(new IDexMergeCallback.Stub() { @Override public void onMergeFinish(String filePath, boolean result, String reason) { if (!result) { if (mergeCallBack != null) { mergeCallBack.onMergeResult(false, filePath); } Log.e(TAG, "merge Failed:" + filePath); } else if (mergeCallBack != null) { mergeCallBack.onMergeResult(true, filePath); } } @Override public void onMergeAllFinish(boolean result, String reason) { isFinished = result; synchronized (lock) { isTimeout = false; lock.notifyAll(); } Log.d(TAG, "dexMerge " + result + (System.currentTimeMillis() - mStartTime) + " ms"); } }); } catch (RemoteException e) { isTimeout = false; Log.d(TAG, "dexMerge registerListener RemoteException" + (System.currentTimeMillis() - mStartTime) + " ms"); return; } finally { synchronized (lock) { lock.notifyAll(); } } try { service.linkToDeath(mDeathRecipient, 0); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; private class MyServiceDeathHandler implements IBinder.DeathRecipient { public MyServiceDeathHandler() { } @Override public void binderDied() { synchronized (lock) { isTimeout = false; isBinderDied = true; lock.notifyAll(); // mergeLock.notifyAll(); } Log.e(TAG, "dexMerge service died"); } } }