android的Parcel那点事

Spring Boot中parent与dependencies依赖管理的深度解析 在Java企业级开发中,依赖管理是项目构建的核心环节。Maven作为主流构建工具,通过POM机制实现依赖版本控制。Spring Boot提供了spring-boot-starter-parent和spring-boot-dependencies两种依赖管理方案,前者是完整的父POM继承体系,后者是纯粹的依赖版本管理。理解两者的设计差异对项目架构至关重要,特别是在微服务架构和多模块项目中。spring-boot-starter-parent提供开箱即用的构建配置,适合快速启动项目;而spring-boot-d 阅读详情

一.先从Serialize说起
       我们都知道JAVA中的Serialize机制,译成串行化、序列化……,其作用是能将数据对象存入字节流当中,在需要时重新生成对象。主要应用是利用外部存储设备保存对象状态,以及通过网络传输对象等。

       二.Android中的新的序列化机制
       在Android系统中,定位为针对内存受限的设备,因此对性能要求更高,另外系统中采用了新的IPC(进程间通信)机制,必然要求使用性能更出色的对象传输方式。在这样的环境下,Parcel被设计出来,其定位就是轻量级的高效的对象序列化和反序列化机制。


三.Parcel的使用

Parcel就是一个存放读取数据的容器,  Android系统中的binder进程间通信(IPC)就使用了Parcel类来进行客户端与服务端数据的交互,而且AIDL的数据也是通过Parcel来交互的。在Java空间和C++都实现了Parcel,由于它在C/C++中,直接使用了内存来读取数据,因此,它更有效率。

分析Binder机制中的客户端与服务器端进行实际操作ontransact()函数 :

[java]
  1. //参数说明:   
  2. // code :是请求的ID号    
  3. // data :客户端请求发送的参数   
  4. // reply:服务器端返回的结果   
  5. // flags:一些额外的标识,如FLAG_ONEWAY等,通常为0.   
  6. virtual status_t    onTransact( uint32_t code,  
  7.                                 const Parcel& data,  
  8.                                 Parcel* reply,  
  9.                                 uint32_t flags = 0);  

从中我们可以看到Parcel的重要性以及窥探它的使用情况,接下来,我主要分析它的存储机制。 

     

    常用方法介绍:

            obtain()                          获得一个新的parcel ,相当于new一个对象

            dataSize()                      得到当前parcel对象的实际存储空间

            dataCapacity()               得到当前parcel对象的已分配的存储空间, >=dataSize()值  (以空间换时间)

            dataPostion()                 获得当前parcel对象的偏移量(类似于文件流指针的偏移量)

            setDataPosition()           设置偏移量

            recyle()                           清空、回收parcel对象的内存

            writeInt(int)                     写入一个整数

            writeFloat(float)              写入一个浮点数

            writeDouble(double)       写入一个双精度数

            writeString(string)           写入一个字符串

         当然,还有更多的writeXXX()方法,与之对应的就是readXXX(),具体方法请参阅SDK。

          其中几个值得注意的方法为:

             writeException()        在Parcel队头写入一个异常

             writeException()        Parcel队头写入“无异常“

             readException()        在Parcel队头读取,若读取值为异常,则抛出该异常;否则,程序正常运行。

 

分析

 

       相信看了前面的值,对Parcel的使用该有了初步印象。那么,Parcel的内部存储机制是怎么样的?偏移量又是

  什么情况?让我们回忆一下基本数据类型的取值范围:

                   boolean     1bit          1字节

                   char          16bit         2字节

                   int             32bit        4字节

                   long          64bit        8字节

                   float          32bit        4字节

                  double       64bit         8字节

 

        如果大家对C语言熟悉的话,C语言中结构体的内存对齐和Parcel采用的内存存放机制一样,即读取最小字节

为32bit,也即4个字节。高于4个字节的,以实际数据类型进行存放,但得为4byte的倍数。基本公式如下:

             实际存放字节:

                       判别一:  32bit      (<=32bit)             例如:boolean,char,int

                       判别二:  实际占用字节(>32bit)     例如:long,float,String,数组等

        当我们使用readXXX()方法时,读取方法也如上述:

              实际读取字节:

                        判别一:  32bit      (<=32bit)            例如:boolean,char,int

                        判别二:  实际字节大小(>32bit)     例如:long,float,String,数值等

      由上可以知道,当我们写入/读取一个数据时,偏移量至少为4byte(32bit),于是,偏移量的公式如下:

                 f(x)= 4x  (x=0,1,…n)

        事实上,我们可以显示的通过setDataPostion(int postion) 来直接操作我们欲读取数据时的偏移量。毫无疑问,

你可以设置任何偏移量,但所读取的值是类型可能有误。因此显示设置偏移量读取值的时候,需要小心。

      

      另外一个注意点就是我们在writeXXX()和readXXX()时,导致的偏移量是共用的,例如,我们在writeInt(23)后,

此时的datapostion=4,如果我们想读取5,简单的通过readInt()是不行的,只能得到0。这时我们只能通过

setDataPosition(0)设置为起始偏移量,从起始位置读取四个字节,即23。因此,在读取某个值时,可能需要使用

setDataPostion(int postion)使偏移量装换到我们的值处。

 

      巧用setDataPosition()方法,当我们的parcel对象中只存在某一类型时,我们就可以通过这个方法来快速的读取

所有值。具体方法如下:

 

[html]
  1. /**  
  2.      * 前提条件,Parcel存在多个类型相同的对象,本例子以10个float对象说明:  
  3.      */  
  4.     public void readSameType() {  
  5.         Parcel parcel =Parcel.obtain() ;  
  6.         for (int i = 0; i < 10; i++) {  
  7.             parcel.writeDouble(i);  
  8.             Log.i(TAG, "write double ----> " + getParcelInfo());  
  9.         }  
  10.         //方法一 ,显示设置偏移量   
  11.         int i = 0;  
  12.         int datasize = parcel.dataSize();  
  13.         while (i < datasize) {  
  14.             parcel.setDataPosition(i);  
  15.             double fvalue = parcel.readDouble();  
  16.             Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  
  17.             i += 8; // double占用字节为 8byte   
  18.         }  
  19. //      方法二,由于对象的类型一致,我们可以直接利用readXXX()读取值会产生偏移量  
  20. //      parcel.setDataPosition(0)  ;  //  
  21. //      while(parcel.dataPosition()<parcel.dataSize()){  
  22. //          double fvalue = parcel.readDouble();  
  23. //          Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  
  24. //      }  
  25.     }  


    由于可能存在读取值的偏差,一个默认的取值规范为:

             1、  读取复杂对象时: 对象匹配时,返回当前偏移位置的该对象;

                               对象不匹配时,返回null对象 ;

             2、  读取简单对象时: 对象匹配时,返回当前偏移位置的该对象 ;

                               对象不匹配时,返回0;  


四.Parcel在activity和service之间的相互通信方法使用

在android中Activity负责前台界面展示,service负责后台的需要长期运行的任务。Activity和Service之间的通信主要由IBinder负责。在需要和Service通信的Activity中实现ServiceConnection接口,并且实现其中的onServiceConnected和onServiceDisconnected方法。然后在这个Activity中还要通过如下代码绑定服务:

Java代码 
  1. Intent intent = new Intent().setClass( this , IHRService.class );  
  2. bindService( intent , this , Context.BIND_AUTO_CREATE );  

  当调用bindService方法后就会回调Activity的onServiceConnected,在这个方法中会向Activity中传递一个IBinder的实例,Acitity需要保存这个实例。代码如下:

Java代码 
  1. public void onServiceConnected( ComponentName inName , IBinder serviceBinder) {  
  2.     if ( inName.getShortClassName().endsWith( "IHRService" ) ) {  
  3.     try {  
  4.         this.serviceBinder= serviceBinder;  
  5.         mService = ( (IHRService.MyBinder) serviceBinder).getService();  
  6.         //mTracker = mService.mConfiguration.mTracker;  
  7.         } catch (Exception e) {}  
  8.               
  9.     }  
  10. }  

 

在Service中需要创建一个实现IBinder的内部类(这个内部类不一定在Service中实现,但必须在Service中创建它)。

Java代码 
  1. public class MyBinder extends Binder {  
  2. //此方法是为了可以在Acitity中获得服务的实例  
  3.     public IHRService getService() {  
  4.         return IHRService.this;  
  5.     }  
  6. //这个方法主要是接收Activity发向服务的消息,data为发送消息时向服务传入的对象,replay是由服务返回的对象  
  7.     public boolean onTransact( int code , Parcel data , Parcel reply , int flags ) {  
  8.         //called when client calls transact on returned Binder  
  9.         return handleTransactions( code , data , reply , flags );  
  10.     }  
  11.   
  12. }  

  

然后在Service中创建这个类的实例:

Java代码 
  1. public IBinder onBind( Intent intent ) {  
  2.     IBinder result = null;  
  3.     if ( null == result ) result = new MyBinder() ;  
  4.     return result;  
  5. }  

  这时候如果Activity向服务发送消息,就可以调用如下代码向服务端发送消息:

Java代码 
  1. inSend = Parcel.obtain();  
  2. serviceBinder.transact( inCode , inSend , null , IBinder.FLAG_ONEWAY );  

 

这种方式是只向服务端发送消息,没有返回值的。如果需要从服务端返回某些值则可用如下代码:

Java代码 
  1. result = Parcel.obtain();  
  2. serviceBinder.transact( inCode , inSend , result , 0 );  
  3. return result;  

发送消息后IBinder接口中的onTransact将会被调用。在服务中如果有结果返回(比如下载数据)则将结果写入到result参数中。在Activity中从result中读取服务执行的结果。



五.Parcel类的背后
       在Framework中有parcel类,源码路径是:Frameworks/base/core/java/android/os/Parcel.java
       典型的源码片断如下:

java代码:

  1. /**
  2. * Write an integer value into the parcel at the current dataPosition(),
  3. * growing dataCapacity() if needed.
  4. */
  5. public final native void writeInt(int val); 

  6. /**
  7. * Write a long integer value into the parcel at the current dataPosition(),
  8. * growing dataCapacity() if needed.
  9. */
  10. public final native void writeLong(long val); 

复制代码

        从中我们看到,从这个源程序文件中我们看不到真正的功能是如何实现的,必须透过JNI往下走了。于是,Frameworks/base/core/jni/android_util_Binder.cpp中找到了线索

java代码:
  1. static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val)
  2. {
  3. Parcel* parcel = parcelForJavaObject(env, clazz);
  4. if (parcel != NULL) {
  5. const status_t err = parcel->writeInt32(val);
  6. if (err != NO_ERROR) {
  7. jniThrowException(env, “java/lang/OutOfMemoryError”, NULL);
  8. }
  9. }
  10. } 

  11. static void android_os_Parcel_writeLong(JNIEnv* env, jobject clazz, jlong val)
  12. {
  13. Parcel* parcel = parcelForJavaObject(env, clazz);
  14. if (parcel != NULL) {
  15. const status_t err = parcel->writeInt64(val);
  16. if (err != NO_ERROR) {
  17. jniThrowException(env, “java/lang/OutOfMemoryError”, NULL);
  18. }
  19. }
  20. } 

复制代码



Spring Boot Starter机制解析与自定义开发实战 Spring Boot Starter是现代化Java开发中的核心依赖管理机制,基于约定优于配置原则实现组件自动装配。其技术原理主要依靠条件注解(如@ConditionalOnClass)和spring.factories文件实现智能配置加载,通过BOM管理确保依赖版本兼容性。这种机制大幅提升了开发效率,使开发者能够快速集成数据库连接、Web服务等常见功能模块。在微服务架构中,自定义Starter可封装通用能力如短信服务、支付网关等,实现配置代码复用。本文以短信服务Starter为例,详解从自动配置类编写到 阅读详情

相关推荐

Spring boot 原生 elasticsearch 版本和 自定义版本冲突的问题解决

问题描述:我在自己项目定的pom 文件中引入 es 7.5.0,但是运行的过程中会报一个异常 NoSuchMethodError:org.elasticsearch.action...... 怀疑应该是版本问题引起的,查了一下 Libraties 发现下面这个问题 有两个版本,于是查了下maven 依赖的来源 运行这个命令 mvn dependency:tree -Dverbose -Dincludes=org.elasticsearch:elasticsearch 得到如下结果 原来真正使

u010878842的博客 2004

Android12之service调试(一百五十二)

本篇目的:掌握service调试方法解决疑难问题。

Android系统攻城狮 1875

Spring Cloud与Spring Boot版本匹配指南

微服务架构中,Spring Cloud作为基于Spring Boot的扩展框架,其版本兼容性直接影响系统稳定性。通过BOM(物料清单)管理机制,Spring Cloud与Spring Boot形成严格的版本对应关系,这种设计既保证了自动配置等核心功能的可靠性,也避免了依赖冲突。开发实践中常见的ClassNotFoundException、Bean创建失败等问题,往往源于版本不匹配。在金融、电商等企业级应用中,正确的版本组合能确保服务注册发现、配置中心等微服务组件稳定运行。本文结合Spring Cloud 2

weixin_34393428的博客 372

adb shell service命令与SurfaceFlinger调试

adb shell service命令与SurfaceFlinger调试

向晚流年 2369

Spring Boot项目依赖管理:starter-parent与dependencies对比

Maven依赖管理是Java项目构建的核心机制,通过POM文件实现依赖版本控制和构建流程管理。Spring Boot提供了两种主流的依赖管理方式:spring-boot-starter-parent作为完整父POM提供开箱即用的默认配置,而spring-boot-dependencies则以更灵活的方式仅管理依赖版本。理解这两种方式的差异对项目构建至关重要,特别是在处理依赖冲突、多模块项目构建等场景时。合理的依赖管理策略能显著提升开发效率,避免常见的版本兼容性问题,是每个Java开发者都应该掌握的基础技能。

weixin_26757925的博客 162

Spring Boot依赖冲突排查:从NoSuchMethodError到Maven依赖树分析

在Java企业级开发中,依赖管理是构建稳定应用的基础。Maven和Gradle等工具通过依赖传递机制管理第三方库,但当同一依赖存在多个版本时,就会产生版本冲突。其核心原理在于依赖解析策略——Maven的'最近定义优先'与Gradle的'最高版本优先'规则可能被版本锁定或排除操作打破。这种冲突的技术价值在于确保类加载一致性,避免运行时出现NoSuchMethodError或ClassNotFoundException等异常。典型的应用场景包括Spring Boot项目升级、多模块架构整合以及第三方SDK引入。

weixin_33831196的博客 415

android执行monkeyrunner权限获取

手机前提是root过,但是还是不能Hierarchy,原因是View Server没有开启 开启View Server命令: adb shell service call window 1 i32 4939 关闭View Server命令: adb shell service call window 2 i32 4939 查看View Server状态命令: adb s

杨小扬的专栏 1612

spring-boot入门(一)快速搭建一个web应用

spring-boot 之快速搭建一个web应用Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。简单点讲使用spring boot只需要添加相关依赖包将自动为我们配置一些常规的东西,避免各种xml配置。下面让我们来快速入手搭建一个web应用。spring

lan墨的博客 3704

解决HierarchyViewer不能连接真机的问题

今天想通过HierarchyViewer分析一下Android应用的布局,但是发现无法连接上真机,错误如下: 12345[hierarchyviewer]Unable to get view server version from device 00856cd5d0

autumn_xl的专栏 2万+

Spring Boot自定义Starter开发与自动配置实战

Spring Boot的自动配置机制是其核心特性之一,通过约定优于配置的理念大幅简化了Java应用的开发流程。自动配置基于条件化装配原理,利用@Conditional系列注解智能判断运行环境,动态加载所需的Bean定义。这种机制在解决依赖管理、配置聚合等问题上展现出显著技术价值,特别适合微服务架构下的模块化开发。自定义Starter作为自动配置的工程实践载体,能够将特定领域的配置逻辑封装为可复用组件,广泛应用于短信服务、支付对接等业务场景。通过分析高频问题如'Spring Boot非Starter项目启动异

weixin_33836874的博客 403

Spring Boot内核深度解析:自动配置、Starter与Actuator原理

Spring Boot作为Java微服务开发的事实标准,其核心价值在于‘约定优于配置’的工程化实现。它通过条件化自动配置(@Conditional系列注解)动态装配Bean,依托Starter机制封装标准化依赖契约,并借助Actuator暴露生产级运行时指标。这些能力并非黑盒魔法,而是基于Spring容器生命周期、Environment属性绑定、ConditionEvaluator评估链等底层机制协同工作。理解自动配置的加载顺序、Starter的三层结构(契约/实现/适配)、Actuator端点的安全暴露策

weixin_30570101的博客 390

为什么HierachyViewer无法连接真机调试

关于什么是Hierarchy Viewer,请查看官方文档:http://developer.android.com/tools/debugging/debugging-ui.html。个人理解:Hierarchy Viewer能获得当前手机实时的UI信息,给界面设计人员和自动化测试人员带来极大的便利。 在Android的官方文档中提到: To preserve sec

u010698836的专栏 1309

Spring Boot 2.5.7 日志依赖冲突:Logback 与 SLF4J-Simple 的 2 种排除方案对比

本文深入解析Spring Boot 2.5.7中Logback与SLF4J-Simple的日志依赖冲突问题,提供两种核心解决方案:直接排除法和统一版本管理法。针对'Class path contains multiple SLF4J bindings'警告,详细讲解诊断技术、排除方案及最佳实践,帮助开发者高效解决日志体系冲突。

cnmik42448的专栏 366

Spring Boot 2.7.0新手入门:从环境搭建到RESTful API开发实战

Spring Boot作为Java后端开发的主流框架,通过自动配置和起步依赖机制,极大地简化了传统Spring应用的初始搭建和开发过程。其核心原理是基于条件化配置,根据类路径下的依赖自动装配Bean,实现了“约定大于配置”的理念,显著提升了开发效率。这一技术价值在于让开发者能够快速构建独立运行、生产级的应用,尤其适合微服务架构和云原生场景。在具体应用场景中,开发者只需通过Spring Initializr初始化项目,添加如Web、Lombok等起步依赖,即可快速开发RESTful API。本文以Spring

weixin_30791095的博客 424

Spring Boot 2.5.7 日志依赖冲突:Logback 与 SLF4J-Simple 的 2 种排除策略

本文深入解析Spring Boot 2.5.7中Logback与SLF4J-Simple的日志依赖冲突问题,提供两种精准排除策略:局部排除法和全局管控法。通过Maven依赖树分析工具定位冲突源,帮助开发者解决"SLF4J: Class path contains multiple SLF4J bindings"警告,确保日志系统稳定运行。

weixin_33853827的博客 397

Spring Boot项目JUnit 5迁移中“Internal Error”与TestEngine故障的深度解析与解决方案

在Java单元测试领域,JUnit是广泛应用的标准框架。其核心原理是通过测试引擎(TestEngine)来发现和执行测试用例。JUnit 5引入了模块化架构,将平台(Platform)、新式测试编程模型(Jupiter)和旧版本兼容层(Vintage)分离,这一设计提升了灵活性和可扩展性。其技术价值在于支持现代Java特性并提供了更强大的扩展能力。在应用场景上,当开发者将Spring Boot项目从JUnit 4迁移至JUnit 5时,常因新旧引擎并存或构建工具配置不当,触发“Internal Error”

weixin_33857230的博客 378

解决Spring Boot Maven插件未找到的8种方法

Maven作为Java项目的主流构建工具,其依赖管理机制通过本地仓库和远程仓库协同工作实现。当出现'Plugin not found'错误时,本质是依赖解析链路中断,这涉及到Maven的核心工作原理。在Spring Boot项目开发中,spring-boot-maven-plugin是实现可执行jar打包的关键组件,其缺失会导致构建失败。通过配置阿里云镜像仓库、检查pom.xml插件声明、清理本地缓存等工程实践,可以解决90%的类似问题。特别是在微服务架构下,统一的依赖版本管理和规范的settings.xm

weixin_34034261的博客 546

Spring Boot自动配置原理与Starter手写实战:从条件注解到Bean装配

Spring Boot的自动配置本质上是基于条件注解的按需装配机制:框架先读取候选配置清单,再通过@ConditionalOnClass、@ConditionalOnProperty等条件判断哪些Bean需要注册,最后按顺序完成装配。理解这套原理,不仅能帮助你排查自动配置失效、Bean冲突等问题,还能提升团队内公共组件的沉淀效率。本文从Spring Boot启动链路切入,拆解@EnableAutoConfiguration与条件注解的协作逻辑,并基于一个操作日志starter的完整实现,演示如何将自定义注解

weixin_34121304的博客 476
上一篇: android很好的一篇文章,关于AndroidManifest.xml文件各个属性,补补基础
下一篇: 优秀IT技术文章集(最新)(高质量)
jacklce
博客等级 码龄15年 18粉丝 26原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值