大家好,关于Android开发基础教程系列(一)很多朋友都还不太明白,今天小编就来为大家分享关于的知识,希望对各位有所帮助!
1.AIDL是什么?
AIDL:Android Interface Definition Language,即Android界面定义语言。即Android平台上使用的IDL交互数据语言定义了Android平台IPC的模板。
2.AIDL的语法是什么?
2.1)AIDL文件以.aidl为后缀;
2.2)支持八种基本数据类型:byte、char、short、int、long、float、double、boolean以及String、CharSequence;
2.3)List类型:List中的所有元素必须是AIDL支持的类型之一,或者是其他AIDL生成的接口,或者定义的parcelable。列表可以使用泛型。
2.4)Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是其他AIDL生成的接口,或者定义的parcelable。 Map 不支持泛型。
2.5)AIDL定向标签:in、out、inout。添加到AIDL接口中,指示数据通信的流向。
in:客户端-服务器,数据从客户端流向服务器
out: 服务器-客户端,数据从服务器流向客户端
inout:客户端-服务器,数据在服务器和客户端之间双向流动。
3、Android平台IPC的几种方法
3.1) 插座
作为较为通用的接口,传输效率较低,主要用于不同机器之间或跨网络的通信;
3.2) 管道
创建时分配一块页大小的内存,缓存区域相对有限;
3.3 MessageQueue(消息队列)
消息队列:两次复制信息,额外消耗CPU;不适合频繁或大量信息交流;
3.4 匿名共享内存
无需复制,共享缓冲区直接挂接到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,每个进程必须使用同步工具来解决;
3.5 信号(信号量)
它不适合信息交换,更适合进程中断控制,比如非法内存访问、杀死进程等。例如系统日志中常见的SIGABRT 6 C的abort(3)发出的退出命令,SIGKILL 9 AEF Kill 信号、SIGSEGV 11 C 无效内存引用等
3.6 信号量(Semaphore)
它经常被用作一种锁定机制,以在某个进程正在访问资源时防止其他进程访问共享资源。因此,它主要用作进程之间以及同一进程内不同线程之间的同步手段。
3.7 Binder通信
(1)从性能角度
数据拷贝次数:Binder只需要一份数据拷贝,而管道、消息队列、socket都需要两次,但共享内存甚至不需要一份内存拷贝;从性能角度来看,Binder性能仅次于共享内存。
(二)从稳定性角度来看
Binder是基于C/S架构的。我们简单介绍一下C/S架构。是指由客户端(Client)和服务器(Server)组成的架构。客户端有什么需求,直接发送给服务器完成。架构很清晰。明明,服务器端和客户端相对独立,稳定性好;而共享内存的实现比较复杂,而且客户端和服务端没有区别。需要充分考虑关键资源访问的并发同步,否则可能会出现死锁等问题。从这个稳定性角度来看,Binder架构优于共享内存。
(三)安全角度的传统Linux
IPC的接收方无法获得对方进程可靠的UID/PID,从而无法识别对方的身份; Android作为开源系统,开发平台众多,App来源广泛,手机的安全性显得格外重要;对于普通用户来说永远不想从应用商店下载隐藏数据,造成后台消耗手机电量等问题。传统的Linux IPC没有任何保护措施,完全由上层协议保证。
(四)从语言水平来看
Android基于Java语言(面向对象的说法),而Binder也符合面向对象的思想,将进程间通信转换为通过对象的引用来调用Binder对象的方法,其独特性就是Binder对象是一个可以跨进程引用的对象。它的实体位于一个进程中,但它的引用却分布在系统中的各个进程中。相比之下,Binder通信在安全性和性能方面具有明显的优势。
Binder通信常常通过ioctl等方法与内核空间驱动进行交互:
图1Binder架构图:
图2C/S架构设计:
图3 在Android系统中,Broadcast、ContentProvider、Message都可以跨进程通信,其基本原理都是Binder通信。可见Binder通信不一定要经过AIDL。
4. 序列化和反序列化
Android进程有独立的内存空间,不能互相访问。如果要在进程之间通信数据,则需要对数据进行序列化和反序列化。 Android中数据序列化有两种方法:一种是实现Serialized接口,另一种是实现Parcelable接口。 Parcelable 是Android 特定的序列化API。它的出现是为了解决序列化过程中Serialized消耗严重资源的问题。然而,由于它的使用需要手动处理序列化和反序列化过程,因此它会绑定到特定的代码。使用起来比较麻烦,一般只在获取内存数据时使用。
我们简单介绍一下Parcelable的两个过程:序列化和反序列化。
序列化:
@覆盖
publicvoid writeToParcel(Parcel dest, int flags) {
}
反序列化:
类Bean {
}
publicstatic Final Creator Bean CREATOR=new Creator Bean () {
@覆盖
公共用户createFromParcel(Parcelsource){
返回空值;
}
@覆盖
公共用户[] newArray(int大小){
返回新的Bean [大小];
}
};
5.AIDL使用示例
将App 1中的媒体文件(如MP3)共享给另一个App 2。该媒体文件可以在App 1中播放,并且可以显示文件上传的进度。应用程序2可以播放传输的媒体文件。列出服务器端和客户端需要完成的工作:
a) AIDL通信过程中,服务器端需要做什么?
创建aidl文件和数据通信类型,确认数据流向、后台服务,将stub埋入服务中,并在AndroidManifest.xml中声明服务。
b) AIDL通信期间客户端需要做什么?
创建aidl文件和数据通信类型(必须与服务器端一致),在activity或者适当位置创建bindService,bindService中参数ServiceConnection,调用aidl接口函数传输数据。
值得注意的是,客户端不必有Activity 来传输数据。很多例子都是使用Activity来演示的。一是为了方便看到数据通信结果,二是Activity有完整的生命周期。 Binder通信可以在onCreate中完成。在onStop中BindService,在onStop中unbindService,这样可以有效回收资源。
5.1 明确Client端和Server端
从需求关系来看:
1)App 1作为数据提供方,需要不断向App 2传输数据;
2)App 2显示页面(Activity)的结果,更适合客户端。
将应用程序1 定义为服务器端,将应用程序2 定义为客户端。分别创建App 1和App 2,以及对应的MainActivity和相关aidl文件。 App 1 创建一个Service 和相应的Stub。
(ps: 不过,如果App 1作为客户端,主动调用AIDL接口获取App 2传过来的数据,这个想法也是合理的,只不过数据流向需要调整一下。)
5.2 确定接口和数据流向
媒体文件传输,确定接口:
//通知文件传输过程开始
void onMediaShareStart(inString name);
//传输文件段中的数据
void onMediaSharing(在MediaData 数据中);
//通知文件传输结束
无效onMediaShareFinish();
数据从当前页面上传到服务器。数据流向是从客户端到服务器端。接口参数配置在.
5.3 创建AIDL文件
让我们从服务器端开始。创建aidl文件:IMediaShareInterface.aidl、MediaData.aidl和MediaData.java。
//IMediaShareInterface.aidl
包com.cloudyhill.mediashare;
//使用import 语句在此处声明任何非默认类型
导入com.cloudyhill.mediashare.MediaData;
接口IMediaShareInterface {
void onMediaShareStart(in String name);
void onMediaSharing(在MediaData 数据中);
无效onMediaShareFinish();
}
媒体数据.aidl:
packagecom.cloudyhill.mediashare;
//使用import 语句在这里声明任何非默认类型
可分割的媒体数据;
实现序列化的MediaData类,即文件MediaData.java。注意类的包名和类名与MediaData.aidl一致,否则会报错。
媒体数据.java:
包com.cloudyhill.mediashare;
导入android.os.Parcel;
导入android.os.Parcelable;
公共类MediaData 实现Parcelable {
私有字符串mFileName;
私有int mMediaDataSize;
私有字节[] mMediaDataArray;
公共媒体数据(){
}
protected MediaData(字符串名称,int 大小,byte[] 数据) {
mFileName=名称;
mMediaDataSize=大小;
mMediaDataArray=数据;
}
受保护的MediaData(包裹在){
从包裹中读取(输入);
}
公共字符串getFileName() {
返回mFileName;
}
公共无效setFileName(字符串名称){
mFileName=名称;
}
公共int getMediaDataSize() {
返回mMediaDataSize;
}
公共字节[] getMediaDataArray() {
返回mMediaDataArray;
}
公共无效setMediaDataArray(字节[]数组){
mMediaDataArray=数组;
mMediaDataSize=array.length;
}
公共静态最终CreatorCREATOR=new Creator() {
@覆盖
公共MediaData createFromParcel(包裹在){
返回新的MediaData(in);
}
@覆盖
公共MediaData[] newArray(int size) {
返回新的MediaData[大小];
}
};
@覆盖
公共int 描述内容(){
返回0;
}
@覆盖
公共无效writeToParcel(包裹包裹,int 标志){
包裹.writeString(mFileName);
Parcel.writeInt(mMediaDataSize);
包裹.writeByteArray(mMediaDataArray);
}
公共无效readFromParcel(包裹回复){
mFileName=回复.readString();
mMediaDataSize=回复.readInt();
mMediaDataArray=reply.createByteArray();
}
}
5.4 服务器端
在服务器端创建一个后台运行的MediaShareService,客户端会通过bindService与其建立联系。
包com.cloudyhill.mediashareserver;
导入android.app.Service;
导入android.content.Intent;
导入android.os.Environment;
导入android.os.IBinder;
导入android.os.RemoteException;
导入android.util.Log;
导入androidx.annotation.Nullable;
导入com.cloudyhill.mediashare.IMediaShareInterface;
导入com.cloudyhill.mediashare.MediaData;
导入com.cloudyhill.mediashareserver.common.Config;
导入java.io.File;
导入java.io.FileNotFoundException;
导入java.io.FileOutputStream;
导入java.io.IOException;
公共类MediaShareService 扩展服务{
私有静态最终StringTAG=Config.LOG_TAG +"Srv";
私有静态MediaShareServicesInstance=null;
私有长mMediaFileTotalSize;
私有StringmMediaFileName;
私有IMediaShareInterface.Stub mIMediaShareMgr=new IMediaShareInterface.Stub() {
@覆盖
公共无效onMediaShareStart(字符串名称)抛出RemoteException {
Log.d(TAG,"");
mMediaFileName=名称;
mMediaFileTotalSize=0;
Log.d(TAG,", mMediaFileName=" +mMediaFileName);
创建媒体文件(mMediaFileName);
}
@覆盖
公共无效onMediaSharing(媒体数据数据)抛出RemoteException {
Log.d(TAG,"");
int size=data.getMediaDataSize();
Log.d(TAG,",尺寸="+尺寸);
mMediaFileTotalSize +=大小;
writeMediaFile(data.getMediaDataArray(), 大小);
}
@覆盖
公共无效onMediaShareFinish() 抛出RemoteException {
Log.d(TAG,", mMediaFileTotalSize=" +mMediaFileTotalSize);
意图intent=new Intent();
intent.setAction(Config.ACTION_MEDIA_SHARE_FINISH);
Intent.putExtra(Config.EXTRA_MEDIA_SHARE_RESULT, mMediaFileTotalSize);
发送广播(意图);
}
};
公共MediaShareService() {
Log.d(TAG,"");
}
公共MediaShareService getInstance() {
if (sInstance==null) {
同步(这个){
if (sInstance==null) {
sInstance=new MediaShareService();
}
}
}
返回sInstance;
}
@可为空
@覆盖
公共IBinder onBind(Intent 意图) {
Log.d(TAG,"");
返回mIMediaShareMgr;
}
@覆盖
公共无效onCreate() {
super.onCreate();
Log.d(TAG,"");
}
@覆盖
公共int onStartCommand(Intent意图,int标志,int startId){
Log.d(TAG,"");
如果(意图==空){
返回START_STICKY;
}
返回START_STICKY;
}
@覆盖
公共无效onDestroy(){
Log.d(TAG,"");
super.onDestroy();
}
私人无效createMediaFile(字符串名称){
Log.d(TAG,",名称="+名称);
文件sdcard=Environment.getExternalStorageDirectory();
文件file=new File(sdcard, 名称);
Log.d(TAG,",文件="+文件);
mMediaFileName=file.getAbsolutePath();
if (文件.exists()) {
Log.d(TAG,",文件存在,删除");
文件.删除();
}
尝试{
Log.d(TAG,",创建新文件。");
文件.createNewFile();
} catch (IOException e) {
Log.e(TAG,", e=" + e);
}
}
私有无效writeMediaFile(字节[]数组,int大小){
Log.d(TAG,"");
文件file=new File(mMediaFileName);
if (!file.exists()) {
尝试{
Log.d(TAG,",创建新文件。");
文件.c
reateNewFile(); } catch (IOException e) { Log.e(TAG,", e = " + e); } } try { // append write the file FileOutputStream fos = new FileOutputStream(file, true); fos.write(array); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } 5.5 Client端 将之前的aidl文件和序列化的MediaData.java复制过去,注意包名以及位置。编写用于上传数据的MediaShareClientActivity package com.cloudyhill.mediashareclient; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.cloudyhill.mediashare.IMediaShareInterface; import com.cloudyhill.mediashare.MediaData; import com.cloudyhill.mediashareclient.common.Config; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import static com.cloudyhill.mediashareclient.common.Config.MSG_MEDIA_FILE_SHARE; import static com.cloudyhill.mediashareclient.common.Config.MSG_UPDATE_PROGRESS; public class MediaShareClientActivity extends Activity implements View.OnClickListener { private static final StringTAG = Config.LOG_TAG +"Activity"; private ContextmContext; private MediaPlayermMediaPlayer; private StringmMediaFileName = Config.FILE_NAME; private long mMediaFileTotalSize; private boolean mIsPlaying; private TextViewmMediaFileText; private TextViewmMediaProgressText; private ButtonmPlayBtn; private ButtonmShareBtn; private IMediaShareInterfacemIMediaShare; private HandlerThreadmHandlerThread; private MessageHandlermMsgHandler; private MainHandlermMainHandler; private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder service) { Log.d(TAG,""); if (service !=null) { try { service.linkToDeath(mDeathRecipient,0); }catch (RemoteException e) { e.printStackTrace(); } } // step 2, notify media share start mMediaFileTotalSize = 0; // 关键点,获取Server端接口类型 mIMediaShare = IMediaShareInterface.Stub.asInterface(service); if (mIMediaShare !=null) { try { // 调用Server端接口 mIMediaShare.onMediaShareStart(Config.FILE_NAME); } catch (RemoteException e) { e.printStackTrace(); } } // step 3, send message mMsgHandler.sendEmptyMessage(MSG_MEDIA_FILE_SHARE); } @Override public void onServiceDisconnected(ComponentName componentName) { Log.d(TAG,""); } }; IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { Log.d(TAG,""); attemptBindService(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = getApplicationContext(); initViews(); initData(); } @Override protected void onStart() { super.onStart(); Log.d(TAG,""); } @Override protected void onStop() { super.onStop(); Log.d(TAG,""); attemptUnbindService(); } @Override public void onClick(View view) { Log.d(TAG,""); int resId = view.getId(); switch (resId) { case R.id.play: if (mIsPlaying) { pausePlayMusic(); } else { startPlayMusic(); } break; case R.id.share: handleShare(); break; default: break; } } private void initViews() { Log.d(TAG,""); mMediaFileText = (TextView) findViewById(R.id.share_name); mMediaFileText.setText(mMediaFileName); Log.d(TAG,", media file name = " +mMediaFileName); mMediaProgressText = (TextView) findViewById(R.id.progress_text); mPlayBtn = (Button) findViewById(R.id.play); mPlayBtn.setOnClickListener(this); mShareBtn = (Button) findViewById(R.id.share); mShareBtn.setOnClickListener(this); } private void initData() { Log.d(TAG,""); mHandlerThread =new HandlerThread("MediaShareThread"); mHandlerThread.start(); mMsgHandler = new MessageHandler(mHandlerThread.getLooper()); mMainHandler = new MainHandler(Looper.getMainLooper()); } private void updateMediaShareProgress() { Log.d(TAG,""); if (mMediaProgressText != null) { mMediaProgressText.setText(String.valueOf(getMediaShareProgress())); } } private void startPlayMusic() { Log.d(TAG,""); mMediaPlayer = MediaPlayer.create(mContext, R.raw.music_demo); mMediaPlayer.start(); mPlayBtn.setText(R.string.pause); mIsPlaying =true; } private void pausePlayMusic() { Log.d(TAG,""); if (mMediaPlayer != null) { mMediaPlayer.pause(); } mPlayBtn.setText(R.string.play); mIsPlaying =false; } private synchronized long getMediaShareProgress() { return mMediaFileTotalSize; } private synchronized void setMediaShareProgress(int size) { mMediaFileTotalSize += size; } private void handleShare() { Log.d(TAG,""); // step 1 attemptBindService(); } private void shareMediaFile() { Log.d(TAG,""); boolean result =false; InputStream is = getResources().openRawResource(R.raw.music_demo); try { result = readMediaFile(is); }catch (IOException err) { Log.e(TAG,", err = " + err); } Log.d(TAG,", result = " + result); } private boolean readMediaFile(InputStream is) throws IOException { boolean result = false; BufferedInputStream bis = new BufferedInputStream(is); MediaData mediaData =new MediaData(); byte[] data =new byte[256*1024]; int size; while ((size = bis.read(data)) != -1) { Log.d(TAG,", size = " + size); setMediaShareProgress(size); mMainHandler.sendEmptyMessage(MSG_UPDATE_PROGRESS); mediaData.setMediaDataArray(data); try { if (mIMediaShare !=null) { mIMediaShare.onMediaSharing(mediaData); } } catch (RemoteException e) { e.printStackTrace(); } } Log.d(TAG,", break while, size = " + size); // step 4, notify media share finished. try { mIMediaShare.onMediaShareFinish(); }catch (RemoteException e) { e.printStackTrace(); } return result; } private void attemptBindService() { Log.d(TAG,""); Intent intent = new Intent(); intent.setAction("com.cloudyhill.action.MEDIA_SHARE"); intent.setPackage("com.cloudyhill.mediashareserver"); bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE); } private void attemptUnbindService() { Log.d(TAG,""); unbindService(mServiceConnection); } private class MessageHandler extends Handler { private MessageHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { Log.d(TAG,", msg.what = " + msg.what); switch (msg.what) { case MSG_MEDIA_FILE_SHARE: shareMediaFile(); break; default: break; } } } private class MainHandler extends Handler { private MainHandler(Looper looper) { super(looper); } public void handleMessage(Message msg) { Log.d(TAG,", MainHandler, msg.what = " + msg.what); switch (msg.what) { case MSG_UPDATE_PROGRESS: updateMediaShareProgress(); break; default: break; } } } } 5.6 简单看下byte[]的通信流程 从Client端的onMediaSharing入手,mIMediaShare是aidl编译生成的IMediaShareInterface.java类。 BufferedInputStream bis = new BufferedInputStream(is); MediaData mediaData =new MediaData(); byte[] data =new byte[256*1024]; int size; while ((size = bis.read(data)) != -1) { Log.d(TAG,", size = " + size); setMediaShareProgress(size); mMainHandler.sendEmptyMessage(MSG_UPDATE_PROGRESS); mediaData.setMediaDataArray(data); try { if (mIMediaShare !=null) { // 调用aidl定义接口 mIMediaShare.onMediaSharing(mediaData); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onMediaSharing(com.cloudyhill.mediashare.MediaData data) throws android.os.RemoteException { // 获取一个Parcel对象 android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((data!=null)) { _data.writeInt(1); // 调用自定义Parcelable类的writeToParcel,data即MediaData data.writeToParcel(_data,0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_onMediaSharing, _data, _reply,0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } 先是调用MediaData的writeToParcel,再调用mRemote(即Server端的IMediaShareInterface)发消息执行TRANSACTION_onMediaSharing。 查看MediaData的writeToParcel接口: @Override public void writeToParcel(Parcel parcel,int flags) { parcel.writeString(mFileName); parcel.writeInt(mMediaDataSize); parcel.writeByteArray(mMediaDataArray); } 数组写入方式是调用writeByteArray写入数据。在onTransact中有处理TRANSACTION_onMediaSharing: case TRANSACTION_onMediaSharing: { data.enforceInterface(descriptor); com.cloudyhill.mediashare.MediaData _arg0; if ((0!=data.readInt())) { // 这里的data就是之前要往里写入的数据_data _arg0 = com.cloudyhill.mediashare.MediaData.CREATOR.createFromParcel(data); } else { _arg0 =null; } this.onMediaSharing(_arg0); reply.writeNoException(); return true; } 从MediaData的用data数据即写入的数据,调用MediaData的createFromParcel创建一个MediaData对象: public static final CreatorCREATOR =new Creator() { @Override public MediaData createFromParcel(Parcel in) { return new MediaData(in); } }; protected MediaData(Parcel in) { readFromParcel(in); } public void readFromParcel(Parcel reply) { mFileName = reply.readString(); mMediaDataSize = reply.readInt(); mMediaDataArray = reply.createByteArray(); } 这样就把Parcelable数据从Client端传给Server端,这个时候Server端的onMediaSharing中调用onMediaSharing,就能得到Client端的数据了。 上面还留了一个坑,为什么说mRemote即Server端的IMediaShareInterface?我们注意到Server端的Service有个接口: public IBinder onBind(Intent intent) { Log.d(TAG,""); return mIMediaShareMgr; } 然后,看Client端获取IMediaDataInterface的方法,下面这个service就是bindService建立连接以后返回的Server端的mIMediaShareMgr,跟踪一下它的走向: mIMediaShare = IMediaShareInterface.Stub.asInterface(service); public static com.cloudyhill.mediashare.IMediaShareInterface asInterface(android.os.IBinderobj) { if ((obj==null)) { return null; } android.os.IInterface iin =obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iininstanceof com.cloudyhill.mediashare.IMediaShareInterface))) { return ((com.cloudyhill.mediashare.IMediaShareInterface)iin); } return new com.cloudyhill.mediashare.IMediaShareInterface.Stub.Proxy(obj); } Proxy(android.os.IBinder remote) { mRemote = remote; } 最终传给了mRemote,即mRemote就是Server端的mIMediaShareMgr。 6、踩坑记录 6.1 错误提示:ProcessException: Error while executing process D:androidsdkbuild-tools28.0.3aidl.exe with arguments 基本数据类型的数组(例如byte[])不能直接作为aidl接口参数传递,要用实现Parcelable的数据类型来封装数组。也就是说,aidl支持参数传递常用的数据类型byte、int等,但是并不直接支持byte[]、int[]等数组类型。也就是需要用Parcelable来序列化数据。 接口中传递的封装数据类型,需要添加一个aidl文件,以parcelable来声明该类型,在引用该类型的aidl文件中加import引用,并实现该类型的实现。详情可参考5.1部分。 6.2 aidl文件的package name和Parcelable实现类的package name不一致,也会提示6.1的错误,应该是找不到实现类,所以报错。 6.3 aidl文件中声明数据流向是out或者inout,但是server端没有实现readFromParcel。看aidl编译生成的java文件,和数据流向有关的函数接口实现: @Overridepublic voidonMediaSharing(com.cloudyhill.mediashare.MediaData data)throwsandroid.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try{ _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_onMediaSharing, _data, _reply, 0); _reply.readException(); if((0!=_reply.readInt())) { data.readFromParcel(_reply); } } finally{ _reply.recycle(); _data.recycle(); } } 从这里看,数据流向声明为out、inout需要复写readFromParcel。 6.4 Binder通信大小限制 Binder通信大小限制为1M – 8KB,除非定制化Binder通信大小。在android源码中ProcessState.cpp中有配置信息: #define BINDER_VM_SIZE((1*1024*1024) - (4096 *2)) 但是我设置为512*1024KB,同样会报TransactionTooLargeException的错误,因此,本例中设置byte[] data = new byte[256*1024],一次256KB。原因可以参考以下知乎上的回答: “Binder的线程池数量默认是15个,由15个线程共享这1MB-8KB的内存空间,所以实际传输大小并没有那么大。” 抱着严谨的态度,这种说法正确与否还有待验证。 源码请另外参考附件demo程序,Client App和Server App。附件请参考:https://download.csdn.net/download/aqiao58/12042751 参考链接: https://www.zhihu.com/question/39440766/answer/89210950 https://www.jianshu.com/p/533de5fa6e4c http://gityuan.com/2015/10/31/binder-prepare/ https://blog.csdn.net/luoyanglizi/article/details/51980630 https://blog.csdn.net/luoyanglizi/article/details/51958091【Android开发基础教程系列(一)】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
终于到了学习Android开发的时候了!
有7位网友表示赞同!
有哪些基础技能是必须要掌握的呢?
有12位网友表示赞同!
感觉这个标题很有吸引力,期待了解更多内容!
有6位网友表示赞同!
我已经开始学编程了,希望能从这里获得一些宝贵的经验。
有20位网友表示赞同!
学习Android开发一直是我的梦想,这篇文章能帮到我吗?
有17位网友表示赞同!
安卓手机用户这么多,肯定有很多想要了解背后的开发技术的人吧!
有9位网友表示赞同!
这种系列文章太棒了,可以循序渐进地掌握Android的各个方面。
有19位网友表示赞同!
感觉这个标题很贴切,必备技能的确是入门学习的关键。
有8位网友表示赞同!
希望这篇文章能介绍一些实用性和前沿性的技能!
有12位网友表示赞同!
我打算尝试学习手机开发,Android平台是不错的选择!
有19位网友表示赞同!
安卓的开发者社区庞大无比,总有很多新技术和资料可以学习
有11位网友表示赞同!
最近对编程很有兴趣,或许可以从Android开发开始探索。
有10位网友表示赞同!
希望能了解一些Android开发中常见的工具和框架。
有6位网友表示赞同!
我已经下载了最新的Android Studio,期待跟着文章教程学习!
有12位网友表示赞同!
学习Android开发需要哪些资源和平台?这篇文章能提供帮助吗?
有6位网友表示赞同!
对开发移动应用程序充满热情,也对Android平台很感兴趣!
有14位网友表示赞同!
Android平台的技能越来越重要了,学习它将来一定很有价值!
有5位网友表示赞同!
分享一些开发经验和技巧吧,希望我能学到更多关于Android的信息。
有9位网友表示赞同!
这个系列文章看起来很有深度,我准备认真阅读它们!
有6位网友表示赞同!
期待文章能够提供一些清晰易懂的学习路线!
有11位网友表示赞同!