Java tutorial
/** * * Copyright 2016 Xiaofei * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package xiaofei.library.hermes.internal; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.support.v4.util.Pair; import android.text.TextUtils; import android.util.Log; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import xiaofei.library.hermes.HermesListener; import xiaofei.library.hermes.HermesService; import xiaofei.library.hermes.util.CallbackManager; import xiaofei.library.hermes.util.CodeUtils; import xiaofei.library.hermes.util.ErrorCodes; import xiaofei.library.hermes.util.HermesException; import xiaofei.library.hermes.util.TypeCenter; import xiaofei.library.hermes.wrapper.ParameterWrapper; /** * Created by Xiaofei on 16/4/11. */ public class Channel { private static final String TAG = "CHANNEL"; private static volatile Channel sInstance = null; private final ConcurrentHashMap<Class<? extends HermesService>, IHermesService> mHermesServices = new ConcurrentHashMap<Class<? extends HermesService>, IHermesService>(); private final ConcurrentHashMap<Class<? extends HermesService>, HermesServiceConnection> mHermesServiceConnections = new ConcurrentHashMap<Class<? extends HermesService>, HermesServiceConnection>(); private final ConcurrentHashMap<Class<? extends HermesService>, Boolean> mBindings = new ConcurrentHashMap<Class<? extends HermesService>, Boolean>(); private final ConcurrentHashMap<Class<? extends HermesService>, Boolean> mBounds = new ConcurrentHashMap<Class<? extends HermesService>, Boolean>(); private HermesListener mListener = null; private Handler mUiHandler = new Handler(Looper.getMainLooper()); private static final CallbackManager CALLBACK_MANAGER = CallbackManager.getInstance(); private static final TypeCenter TYPE_CENTER = TypeCenter.getInstance(); //StubIHermesServiceCallback??ServiceonBindStub private IHermesServiceCallback mHermesServiceCallback = new IHermesServiceCallback.Stub() { private Object[] getParameters(ParameterWrapper[] parameterWrappers) throws HermesException { if (parameterWrappers == null) { parameterWrappers = new ParameterWrapper[0]; } int length = parameterWrappers.length; Object[] result = new Object[length]; for (int i = 0; i < length; ++i) { ParameterWrapper parameterWrapper = parameterWrappers[i]; if (parameterWrapper == null) { result[i] = null; } else { Class<?> clazz = TYPE_CENTER.getClassType(parameterWrapper); String data = parameterWrapper.getData(); if (data == null) { result[i] = null; } else { result[i] = CodeUtils.decode(data, clazz); } } } return result; } public Reply callback(CallbackMail mail) { final Pair<Boolean, Object> pair = CALLBACK_MANAGER.getCallback(mail.getTimeStamp(), mail.getIndex()); if (pair == null) { return null; } final Object callback = pair.second; if (callback == null) { return new Reply(ErrorCodes.CALLBACK_NOT_ALIVE, ""); } boolean uiThread = pair.first; try { // TODO Currently, the callback should not be annotated! final Method method = TYPE_CENTER.getMethod(callback.getClass(), mail.getMethod()); final Object[] parameters = getParameters(mail.getParameters()); Object result = null; Exception exception = null; if (uiThread) { boolean isMainThread = Looper.getMainLooper() == Looper.myLooper(); if (isMainThread) { try { result = method.invoke(callback, parameters); } catch (IllegalAccessException e) { exception = e; } catch (InvocationTargetException e) { exception = e; } } else { mUiHandler.post(new Runnable() { @Override public void run() { try { method.invoke(callback, parameters); } catch (Exception e) { e.printStackTrace(); } } }); return null; } } else { try { result = method.invoke(callback, parameters); } catch (IllegalAccessException e) { exception = e; } catch (InvocationTargetException e) { exception = e; } } if (exception != null) { exception.printStackTrace(); throw new HermesException(ErrorCodes.METHOD_INVOCATION_EXCEPTION, "Error occurs when invoking method " + method + " on " + callback, exception); } if (result == null) { return null; } return new Reply(new ParameterWrapper(result)); } catch (HermesException e) { e.printStackTrace(); return new Reply(e.getErrorCode(), e.getErrorMessage()); } } @Override public void gc(List<Long> timeStamps, List<Integer> indexes) throws RemoteException { int size = timeStamps.size(); for (int i = 0; i < size; ++i) { CALLBACK_MANAGER.removeCallback(timeStamps.get(i), indexes.get(i)); } } }; private Channel() { } public static Channel getInstance() { if (sInstance == null) { synchronized (Channel.class) { if (sInstance == null) { sInstance = new Channel(); } } } return sInstance; } /** * bind HermesService * @param context * @param packageName * @param service */ public void bind(Context context, String packageName, Class<? extends HermesService> service) { HermesServiceConnection connection; synchronized (this) { if (getBound(service)) { return; } Boolean binding = mBindings.get(service); if (binding != null && binding) { return; } mBindings.put(service, true); connection = new HermesServiceConnection(service); mHermesServiceConnections.put(service, connection); } Intent intent; if (TextUtils.isEmpty(packageName)) { intent = new Intent(context, service); } else { intent = new Intent(); intent.setClassName(packageName, service.getName()); } context.bindService(intent, connection, Context.BIND_AUTO_CREATE); } /** * unbind HermesService * @param context * @param service */ public void unbind(Context context, Class<? extends HermesService> service) { synchronized (this) { Boolean bound = mBounds.get(service); if (bound != null && bound) { HermesServiceConnection connection = mHermesServiceConnections.get(service); if (connection != null) { context.unbindService(connection); } mBounds.put(service, false); } } } public Reply send(Class<? extends HermesService> service, Mail mail) { IHermesService hermesService = mHermesServices.get(service); try { if (hermesService == null) { return new Reply(ErrorCodes.SERVICE_UNAVAILABLE, "Service Unavailable: Check whether you have connected Hermes."); } return hermesService.send(mail); } catch (RemoteException e) { return new Reply(ErrorCodes.REMOTE_EXCEPTION, "Remote Exception: Check whether " + "the process you are communicating with is still alive."); } } public void gc(Class<? extends HermesService> service, List<Long> timeStamps) { IHermesService hermesService = mHermesServices.get(service); if (hermesService == null) { Log.e(TAG, "Service Unavailable: Check whether you have disconnected the service before a process dies."); } else { try { hermesService.gc(timeStamps); } catch (RemoteException e) { e.printStackTrace(); } } } public boolean getBound(Class<? extends HermesService> service) { Boolean bound = mBounds.get(service); return bound != null && bound; } public void setHermesListener(HermesListener listener) { mListener = listener; } public boolean isConnected(Class<? extends HermesService> service) { IHermesService hermesService = mHermesServices.get(service); return hermesService != null && hermesService.asBinder().pingBinder(); } private class HermesServiceConnection implements ServiceConnection { private Class<? extends HermesService> mClass; HermesServiceConnection(Class<? extends HermesService> service) { mClass = service; } public void onServiceConnected(ComponentName className, IBinder service) { synchronized (Channel.this) { mBounds.put(mClass, true); mBindings.put(mClass, false); IHermesService hermesService = IHermesService.Stub.asInterface(service); mHermesServices.put(mClass, hermesService); try { hermesService.register(mHermesServiceCallback, Process.myPid()); } catch (RemoteException e) { e.printStackTrace(); Log.e(TAG, "Remote Exception: Check whether " + "the process you are communicating with is still alive."); return; } } if (mListener != null) { mListener.onHermesConnected(mClass); } } public void onServiceDisconnected(ComponentName className) { synchronized (Channel.this) { mHermesServices.remove(mClass); mBounds.put(mClass, false); mBindings.put(mClass, false); } if (mListener != null) { mListener.onHermesDisconnected(mClass); } } } }