package com.blankj.utilcode.util; import android.annotation.SuppressLint; import android.app.Notification; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** *
 *     author: Blankj
 *     blog  : http://blankj.com
 *     time  : 2019/07/10
 *     desc  : utils about messenger
 * 
*/ public class MessengerUtils { private static ConcurrentHashMap subscribers = new ConcurrentHashMap<>(); private static Map sClientMap = new HashMap<>(); private static Client sLocalClient; private static final int WHAT_SUBSCRIBE = 0x00; private static final int WHAT_UNSUBSCRIBE = 0x01; private static final int WHAT_SEND = 0x02; private static final String KEY_STRING = "MESSENGER_UTILS"; public static void register() { if (UtilsBridge.isMainProcess()) { if (UtilsBridge.isServiceRunning(ServerService.class.getName())) { Log.i("MessengerUtils", "Server service is running."); return; } startServiceCompat(new Intent(Utils.getApp(), ServerService.class)); return; } if (sLocalClient == null) { Client client = new Client(null); if (client.bind()) { sLocalClient = client; } else { Log.e("MessengerUtils", "Bind service failed."); } } else { Log.i("MessengerUtils", "The client have been bind."); } } public static void unregister() { if (UtilsBridge.isMainProcess()) { if (!UtilsBridge.isServiceRunning(ServerService.class.getName())) { Log.i("MessengerUtils", "Server service isn't running."); return; } Intent intent = new Intent(Utils.getApp(), ServerService.class); Utils.getApp().stopService(intent); } if (sLocalClient != null) { sLocalClient.unbind(); } } public static void register(final String pkgName) { if (sClientMap.containsKey(pkgName)) { Log.i("MessengerUtils", "register: client registered: " + pkgName); return; } Client client = new Client(pkgName); if (client.bind()) { sClientMap.put(pkgName, client); } else { Log.e("MessengerUtils", "register: client bind failed: " + pkgName); } } public static void unregister(final String pkgName) { if (!sClientMap.containsKey(pkgName)) { Log.i("MessengerUtils", "unregister: client didn't register: " + pkgName); return; } Client client = sClientMap.get(pkgName); sClientMap.remove(pkgName); if (client != null) { client.unbind(); } } public static void subscribe(@NonNull final String key, @NonNull final MessageCallback callback) { subscribers.put(key, callback); } public static void unsubscribe(@NonNull final String key) { subscribers.remove(key); } public static void post(@NonNull String key, @NonNull Bundle data) { data.putString(KEY_STRING, key); if (sLocalClient != null) { sLocalClient.sendMsg2Server(data); } else { Intent intent = new Intent(Utils.getApp(), ServerService.class); intent.putExtras(data); startServiceCompat(intent); } for (Client client : sClientMap.values()) { client.sendMsg2Server(data); } } private static void startServiceCompat(Intent intent) { try { intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Utils.getApp().startForegroundService(intent); } else { Utils.getApp().startService(intent); } } catch (Exception e) { e.printStackTrace(); } } static class Client { String mPkgName; Messenger mServer; LinkedList mCached = new LinkedList<>(); @SuppressLint("HandlerLeak") Handler mReceiveServeMsgHandler = new Handler() { @Override public void handleMessage(Message msg) { Bundle data = msg.getData(); data.setClassLoader(MessengerUtils.class.getClassLoader()); String key = data.getString(KEY_STRING); if (key != null) { MessageCallback callback = subscribers.get(key); if (callback != null) { callback.messageCall(data); } } } }; Messenger mClient = new Messenger(mReceiveServeMsgHandler); ServiceConnection mConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d("MessengerUtils", "client service connected " + name); mServer = new Messenger(service); int key = UtilsBridge.getCurrentProcessName().hashCode(); Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_SUBSCRIBE, key, 0); msg.getData().setClassLoader(MessengerUtils.class.getClassLoader()); msg.replyTo = mClient; try { mServer.send(msg); } catch (RemoteException e) { e.printStackTrace(); } sendCachedMsg2Server(); } @Override public void onServiceDisconnected(ComponentName name) { Log.w("MessengerUtils", "client service disconnected:" + name); mServer = null; if (!bind()) { Log.e("MessengerUtils", "client service rebind failed: " + name); } } }; Client(String pkgName) { this.mPkgName = pkgName; } boolean bind() { if (TextUtils.isEmpty(mPkgName)) { Intent intent = new Intent(Utils.getApp(), ServerService.class); return Utils.getApp().bindService(intent, mConn, Context.BIND_AUTO_CREATE); } if (UtilsBridge.isAppInstalled(mPkgName)) { if (UtilsBridge.isAppRunning(mPkgName)) { Intent intent = new Intent(mPkgName + ".messenger"); intent.setPackage(mPkgName); return Utils.getApp().bindService(intent, mConn, Context.BIND_AUTO_CREATE); } else { Log.e("MessengerUtils", "bind: the app is not running -> " + mPkgName); return false; } } else { Log.e("MessengerUtils", "bind: the app is not installed -> " + mPkgName); return false; } } void unbind() { int key = UtilsBridge.getCurrentProcessName().hashCode(); Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_UNSUBSCRIBE, key, 0); msg.replyTo = mClient; try { mServer.send(msg); } catch (RemoteException e) { e.printStackTrace(); } try { Utils.getApp().unbindService(mConn); } catch (Exception ignore) {/*ignore*/} } void sendMsg2Server(Bundle bundle) { if (mServer == null) { mCached.addFirst(bundle); Log.i("MessengerUtils", "save the bundle " + bundle); } else { sendCachedMsg2Server(); if (!send2Server(bundle)) { mCached.addFirst(bundle); } } } private void sendCachedMsg2Server() { if (mCached.isEmpty()) return; for (int i = mCached.size() - 1; i >= 0; --i) { if (send2Server(mCached.get(i))) { mCached.remove(i); } } } private boolean send2Server(Bundle bundle) { Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_SEND); bundle.setClassLoader(MessengerUtils.class.getClassLoader()); msg.setData(bundle); msg.replyTo = mClient; try { mServer.send(msg); return true; } catch (RemoteException e) { e.printStackTrace(); return false; } } } public static class ServerService extends Service { private final ConcurrentHashMap mClientMap = new ConcurrentHashMap<>(); @SuppressLint("HandlerLeak") private final Handler mReceiveClientMsgHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case WHAT_SUBSCRIBE: mClientMap.put(msg.arg1, msg.replyTo); break; case WHAT_SEND: sendMsg2Client(msg); consumeServerProcessCallback(msg); break; case WHAT_UNSUBSCRIBE: mClientMap.remove(msg.arg1); break; default: super.handleMessage(msg); } } }; private final Messenger messenger = new Messenger(mReceiveClientMsgHandler); @Nullable @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Notification notification = UtilsBridge.getNotification( NotificationUtils.ChannelConfig.DEFAULT_CHANNEL_CONFIG, null ); startForeground(1, notification); } if (intent != null) { Bundle extras = intent.getExtras(); if (extras != null) { Message msg = Message.obtain(mReceiveClientMsgHandler, WHAT_SEND); msg.replyTo = messenger; msg.setData(extras); sendMsg2Client(msg); consumeServerProcessCallback(msg); } } return START_NOT_STICKY; } private void sendMsg2Client(final Message msg) { final Message obtain = Message.obtain(msg); //Copy the original for (Messenger client : mClientMap.values()) { try { if (client != null) { client.send(Message.obtain(obtain)); } } catch (RemoteException e) { e.printStackTrace(); } } obtain.recycle(); //Recycled copy } private void consumeServerProcessCallback(final Message msg) { Bundle data = msg.getData(); if (data != null) { String key = data.getString(KEY_STRING); if (key != null) { MessageCallback callback = subscribers.get(key); if (callback != null) { callback.messageCall(data); } } } } } public interface MessageCallback { void messageCall(Bundle data); } }