Android使用Messenger和SharedMemory实现跨app的海量数据传输
Messenger是android下轻量级的跨进程通信机制,SharedMemory可以作为Messenger中Message的Bundle数据来发送,从而实现海量数据传输。
一、直接使用SharedMemory方式
Server端:
下面为MessengerService类的代码,在MainActivity里面把它启动即可,也可以客户端app通过bindservice来拉起它:
package com.maxshu.messenger_sharedmemory;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
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.os.SharedMemory;
import android.system.ErrnoException;
import android.util.Log;
import java.nio.ByteBuffer;
import androidx.core.app.NotificationCompat;
public class MessengerService extends Service {
private static final String TAG = “MessengerService”;
private MessengerHandler mHandler=new MessengerHandler();
private Messenger mMessenger=new Messenger(mHandler);
public MessengerService() {
Log.i(TAG,”MessengerService().”);
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,”onBind().”);
return mMessenger.getBinder();
}
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//取出客户端的消息内容
Bundle bundle = msg.getData();
String clientMsg = bundle.getString(“client”);
Log.i(TAG,”来自客户端的消息:”+clientMsg);
//通过SharedMemory共享内存得到的数据
int sharedMemoryLen = bundle.getInt(“sharedmemory_len”);
SharedMemory sharedMemory = bundle.getParcelable(“sharedmemory_data”);
if (sharedMemory == null) {
Log.i(TAG,”sharedMemory为空。”);
return;
}
try {
ByteBuffer byteBuffer = sharedMemory.mapReadOnly();
int len = byteBuffer.limit() – byteBuffer.position();
Log.i(TAG, “sharedMemory: len:” + len);
byte[] bytes = new byte[sharedMemoryLen];
byteBuffer.get(bytes);
Log.i(TAG, “sharedMemory: bytes[0]:” + bytes[0]);
Log.i(TAG, “sharedMemory: bytes[0]:” + bytes[1]);
Log.i(TAG, “sharedMemory: bytes[sharedMemoryLen-2]:” + bytes[sharedMemoryLen – 2]);
Log.i(TAG, “sharedMemory: bytes[sharedMemoryLen-1]:” + bytes[sharedMemoryLen – 1]);
Log.i(TAG, “sharedMemory: bytes.length:” + bytes.length);
Log.i(TAG, “sharedMemory: sharedMemory:” + sharedMemory.toString());
Log.i(TAG, “sharedMemory: sharedMemory.size:” + sharedMemory.getSize());
//使用完unmap同时close sharedMemory释放内存
SharedMemory.unmap(byteBuffer);
sharedMemory.close(); //对端create,本端使用完close.
}catch (ErrnoException e){
Log.i(TAG, “sharedMemory错误:”);
e.printStackTrace();
return ;
}
//新建一个Message对象,作为回复客户端的对象
Message message = Message.obtain();
Bundle bundle1 = new Bundle();
bundle1.putString(“service”,”今天就不去了,还有很多东西要学!!”);
message.setData(bundle1);
try {
msg.replyTo.send(message);
Log.i(TAG,”发送消息到客户端。”);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onCreate() {
Log.i(TAG,”onCreate().”);
super.onCreate();
startForeground();
}
private void startForeground() {
Log.i(TAG,”startForeground().”);
String CHANNEL_ONE_ID = “com.maxshu.messenger_sharedmemory.channel”;
String CHANNEL_ONE_NAME = “Channel One”;
NotificationChannel notificationChannel = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(notificationChannel);
startForeground(1, new NotificationCompat.Builder(this, CHANNEL_ONE_ID).build());
}
}
}
Server端的AndroidManifest.xml:
<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android“
package=”com.maxshu.messenger_sharedmemory”>
<uses-permission android:name=”android.permission.FOREGROUND_SERVICE”/>
<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:roundIcon=”@mipmap/ic_launcher_round”
android:supportsRtl=”true”
android:theme=”@style/Theme.Messenger_SharedMemory”>
<service
android:name=”.MessengerService”
android:enabled=”true”
android:exported=”true”>
<intent-filter>
<action android:name=”android.intent.action.MESSENGER” />
</intent-filter>
</service>
<activity
android:name=”.MainActivity”
android:exported=”true”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
</application>
</manifest>
Client端:
client端通过bindservice调用服务端代码即可:
package com.maxshu.messenger_sharedmemory_2;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
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.os.SharedMemory;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.util.Log;
import java.nio.ByteBuffer;
public class MainActivity extends AppCompatActivity {
private static final String TAG = “MainActivity”;
private Messenger mService;
private GetRelyHandler mGetRelyHandler = new GetRelyHandler();
private Messenger mRelyMessenger = new Messenger(mGetRelyHandler);
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, “onServiceConnected.”);
//获取服务端关联的Messenger对象
mService = new Messenger(service);
//创建Message对象
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString(“client”, “今天出去玩吗?”);
//使用SharedMemory共享内存来跨app进程传输大量数据
byte[] bytes = new byte[1024*1024*4]; //4MB
bytes[0] = ‘a’;
bytes[1] = ‘5’;
bytes[1024*1024*4-2] = ‘5’;
bytes[1024*1024*4-1] = ‘a’;
try {
SharedMemory sharedMemory = SharedMemory.create(“sharedmemory”, bytes.length);
ByteBuffer buffer = sharedMemory.mapReadWrite();
buffer.put(bytes);
//把sharedMemory权限设置为只读,create默认是读写权限都有,这里可以避免客户端更改数据
sharedMemory.setProtect(OsConstants.PROT_READ);
//使用完需要unmap
SharedMemory.unmap(buffer);
bundle.putInt(“sharedmemory_len”, bytes.length);
bundle.putParcelable(“sharedmemory_data”, sharedMemory);
Log.i(TAG, “sharedMemory: bytes.length:”+bytes.length);
Log.i(TAG, “sharedMemory: sharedMemory:”+sharedMemory.toString());
Log.i(TAG, “sharedMemory: sharedMemory.size:”+sharedMemory.getSize());
}catch (ErrnoException e){
Log.i(TAG, “sharedMemory错误:”);
e.printStackTrace();
return;
}
//设置数据内容
message.setData(bundle);
//在message中添加一个回复mRelyMessenger对象
message.replyTo = mRelyMessenger;
try {
mService.send(message);
Log.i(TAG, “发送消息到服务器端。”);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, “onServiceDisconnected.”);
mService = null;
}
};
public static class GetRelyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
String serviceMsg = bundle.getString(“service”);
Log.i(TAG, “来自服务端的回复:” + serviceMsg);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//启动服务端的MessengerService
Intent intent = new Intent();
//和服务端AndroidManifest.xml的MessengerServiceService的intent-filter的Action name保持一致:
intent.setAction(“android.intent.action.MESSENGER”);
intent.setPackage(“com.maxshu.messenger_sharedmemory”); //服务端的包名
//启动服务,如果服务端Service没有启动,则自动拉起
boolean rtn = bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
if(rtn){
Log.i(TAG, “启动服务端MessengerService成功。”);
}else {
Log.i(TAG, “启动服务端MessengerService失败。”);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销绑定
unbindService(mConnection);
}
}
Client端的AndroidManifest.xml:
<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android“
package=”com.maxshu.messenger_sharedmemory_2″>
<!–Android11之后,必须加入该queries条目,才能拉起其他app的Service –>
<queries>
<package android:name=”com.maxshu.messenger_sharedmemory” />
</queries>
<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:roundIcon=”@mipmap/ic_launcher_round”
android:supportsRtl=”true”
android:theme=”@style/Theme.Messenger_SharedMemory”>
<activity
android:name=”.MainActivity”
android:exported=”true”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
</application>
</manifest>
二、间接使用Binder方式(底层实现也是用的共享内存方式)
使用Binder调用传图,是往Intent里塞了个Binder对象,等到另一个组件启动之后,读出这个Binder对象,调用它的getBitmap函数拿到Bitmap。
注意下,putBinder 方式 只在Android 4.3 (api 18)及以上才有,所以我们做下判断。
发送端:
private void ipcBitmapBinder() {
Intent intent = new Intent(this, SecondActivity.class);
Bundle bundle = new Bundle();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
bundle.putBinder(“bitmap”, new ImageBinder(mBitmap));
}
intent.putExtras(bundle);
startActivity(intent);
}
接收端:
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
ImageBinder imageBinder = (ImageBinder) bundle.getBinder(“bitmap”);
Bitmap bitmap = imageBinder.getBitmap();
mTv.setText(String.format((“bitmap大小为%dkB”), bitmap.getByteCount() / 1024));
mIv.setImageBitmap(bitmap);
}
}
ImageBinder 是个继承自Binder的类,提供获取图片的方式:
class ImageBinder extends Binder {
private Bitmap bitmap;
public ImageBinder(Bitmap bitmap) {
this.bitmap = bitmap;
}
Bitmap getBitmap() {
return bitmap;
}
}