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");
}
}
}