Redis源码学习简记(七)object原理与个人理解

2024-04-28 00:38

本文主要是介绍Redis源码学习简记(七)object原理与个人理解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Redis源码学习简记(七)object原理与个人理解

        object是redis中的封装系统。其把string,list,set,zset与hash封装成一个统一的对象,命名为robj。该数据结构中,存储了类型,编码,引用次数,数据与LRU替换算法的一些数据。具体先看看这个数据结构的定义,在server.h中定义。

[cpp] view plain copy
  1.  //redis属于key-value 数据库  
  2.  //nosql数据库,这种映射关系使用dict用来维护  
  3.  //而dict实体数据(dictentry)中有字段void *value  
  4. typedef struct redisObject {  
  5.     unsigned type:4;  
  6.     /* 
  7.         string 0 
  8.         list 1 
  9.         set 2 
  10.         zset 3 
  11.         hash 4 
  12.         4个bit 
  13.  
  14.     */  
  15.   
  16.     unsigned encoding:4;  
  17.     //编码方式 4个bit  
  18.     unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or 
  19.                             * LFU data (least significant 8 bits frequency 
  20.                             * and most significant 16 bits access time). */  
  21.     //24bit,LRU替换算法                              
  22.     int refcount;//引用计数32bit 4字节  
  23.     void *ptr;//32位系统4字节 64位系统8字节  
  24.     //指向真正数据  
  25. } robj;  

来详细讲一下这里面的东西。首先type,定义如下,其实就是4个bit用来存储0~4的数据。

[cpp] view plain copy
  1. /* The actual Redis Object */  
  2. #define OBJ_STRING 0    /* String object. */  
  3. #define OBJ_LIST 1      /* List object. */  
  4. #define OBJ_SET 2       /* Set object. */  
  5. #define OBJ_ZSET 3      /* Sorted set object. */  
  6. #define OBJ_HASH 4      /* Hash object. */  

然后编码方式encoding。编码方式的话,总的来说有11种,也是用4个bit来存,4个bit最大为16种编码。

[cpp] view plain copy
  1. #define OBJ_ENCODING_RAW 0     /* Raw representation */  
  2. #define OBJ_ENCODING_INT 1     /* Encoded as integer */  
  3. #define OBJ_ENCODING_HT 2      /* Encoded as hash table */  
  4. #define OBJ_ENCODING_ZIPMAP 3  /* Encoded as zipmap */  
  5. #define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */  
  6. #define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */  
  7. #define OBJ_ENCODING_INTSET 6  /* Encoded as intset */  
  8. #define OBJ_ENCODING_SKIPLIST 7  /* Encoded as skiplist */  
  9. #define OBJ_ENCODING_EMBSTR 8  /* Embedded sds string encoding */  
  10. #define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */  
  11. #define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */  

接下来的为lru,用于存储LRU替换算法,现在先不深究,使用了LRU_BITS (24bits)三个字节存储。

refcount为引用指针,作用跟智能指针相似,用于计算对象的引用次数,当对象引用次数为0时,则释放其使用空间。

最后的void *ptr则为最终指向数据的指针。这些数据基本就是之前分析的那些类型的数据。

数据结构不难理解,然后看看他的基本实现函数吧。基本都在object.c中实现。

首先是创建,该函数根据类型与ptr指向的数据返回一个robj指针。

[cpp] view plain copy
  1. robj *createObject(int type, void *ptr) {  
  2.   
  3.     robj *o = zmalloc(sizeof(*o));//分配空间  
  4.     o->type = type;//设置类型  
  5.     o->encoding = OBJ_ENCODING_RAW;//原生编码模式  
  6.     //这里分了很多种,后面看到了再详细讨论  
  7.     o->ptr = ptr;//执行真正的数据  
  8.     o->refcount = 1;//引用计数  
  9.   
  10.     /* Set the LRU to the current lruclock (minutes resolution), or 
  11.      * alternatively the LFU counter. */  
  12.     //对于页面置换算法先不考虑,后面研究到了再看。  
  13.     if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {  
  14.         o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;  
  15.     } else {  
  16.         o->lru = LRU_CLOCK();  
  17.     }  
  18.     return o;  
  19. }  

设置一种特殊的refcount。使得该对象时shred。增加引用于减少引用,都会检查这种特殊的引用计数。下面是使得对象变为共享的方法,其实就是将refcout数值设为int_max.

[cpp] view plain copy
  1. robj *makeObjectShared(robj *o) {  
  2.     serverAssert(o->refcount == 1);  
  3.     o->refcount = OBJ_SHARED_REFCOUNT;  
  4.     //使得对象为共享,设置为obj_shared_refcount 其大小为int_max  
  5.     return o;  
  6. }  

接下来是创建字符串对象的两种方法。

先来介绍原生字符串的创建方法。简单的调用sdsnewlen的方法,然后得到字符串指针后在调用create进行创建操作。

[cpp] view plain copy
  1. /* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain 
  2.  * string object where o->ptr points to a proper sds string. */  
  3. robj *createRawStringObject(const char *ptr, size_t len) {  
  4.     //创建RawStringObject类型  
  5.     //其实是一个sds类型,而在默认情况下,encoding为OBJ_ENCODING_RAW  
  6.     /* 
  7.         #define OBJ_STRING 0     
  8.         #define OBJ_LIST 1      
  9.         #define OBJ_SET 2        
  10.         #define OBJ_ZSET 3       
  11.         #define OBJ_HASH 4       
  12.     */  
  13.     return createObject(OBJ_STRING, sdsnewlen(ptr,len));  
  14. }  

另外一种创建的方法这是一种字符串直接是添加在对象后面,该字符串直接存储在对象的后面。规定整个对象不大于64个字节。

[cpp] view plain copy
  1. /* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is 
  2.  * an object where the sds string is actually an unmodifiable string 
  3.  * allocated in the same chunk as the object itself. */  
  4. robj *createEmbeddedStringObject(const char *ptr, size_t len) {  
  5.     //先一步步看代码  
  6.     robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);  
  7.     //分配了robj数据与sdshdr8的sds头空间加上了len+1的长度,多出来的1长度存储null  
  8.     struct sdshdr8 *sh = (void*)(o+1);  
  9.     //o+1根据robj的结构体的字节进行增长  
  10.     //robj总共16字节,那么加上16刚刚好是sdshdr8的头部  
  11.     o->type = OBJ_STRING;  
  12.     o->encoding = OBJ_ENCODING_EMBSTR;  
  13.     o->ptr = sh+1;  
  14.     //sh+1即加上sdshdr8的字节数,而sdshdr8为3字节即len+alloc+flags  
  15.     //那么加1后,ptr指向的为sh->buf  
  16.     /* 
  17.     在研究字符所占的字节数中发现一个有趣的现象; 
  18.         struct __attribute__ ((__packed__)) sdshdr8 { 
  19.                 uint8_t len;  
  20.                 uint8_t alloc;  
  21.                 unsigned char flags;  
  22.                 char buf[]; 
  23.             }; 
  24.     由于char buf[]    会导致计算sdshdr8的大小不算上buf[]很奇怪 
  25.     而使用指针的话,则会加上8字节用于存储 
  26.     而[]中有值时也会增加其长度,若不写值时则会不计算。而buf的地址为flags最后一位。 
  27.  
  28.     */  
  29.     o->refcount = 1;  
  30.     //初始化引用计数  
  31.     if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {  
  32.         o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;  
  33.     } else {  
  34.         o->lru = LRU_CLOCK();  
  35.     }  
  36.     //对于页面算法先不研究。  
  37.     //sds的初始化  
  38.     sh->len = len;  
  39.     sh->alloc = len;  
  40.     sh->flags = SDS_TYPE_8;  
  41.     if (ptr == SDS_NOINIT)  
  42.         //sds_noint 为一个静态const char *  
  43.         //判断是否要初始化  
  44.   
  45.         sh->buf[len] = '\0';  
  46.     else if (ptr) {  
  47.         //初始化的话则看ptr的值是否为NULL  
  48.         memcpy(sh->buf,ptr,len);  
  49.         sh->buf[len] = '\0';  
  50.     } else {  
  51.         memset(sh->buf,0,len+1);  
  52.     }  
  53.     return o;  
  54. }  

这两种字符串的编码,通过一个总的字符串接口进行调用。这里面如上面所说的,EmbeddedString总共包括下面几个部分

robj 16字节 sdshdr8头部3个字节 加上一个 '\0' 一个字节,最后存储内容是44个字节

3+16+1+44=64。刚好是64节装完所有的数据。

[cpp] view plain copy
  1. /* Create a string object with EMBSTR encoding if it is smaller than 
  2.  * OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is 
  3.  * used. 
  4.  * 
  5.  * The current limit of 44 is chosen so that the biggest string object 
  6.  * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */  
  7. #define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44  
  8. robj *createStringObject(const char *ptr, size_t len) {  
  9.     //保证使用embeddedstringboject的大小为64否则调用creteRawStringObject  
  10.     //设置其长度最长为44+16+3+1=64  
  11.     //事实上sdshdr8可存储256个字符  
  12.     if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)  
  13.         return createEmbeddedStringObject(ptr,len);  
  14.     else  
  15.         return createRawStringObject(ptr,len);  
  16. }  

看完了字符串的封装。观察一下整型的封装方法。总的来说当能使用8个字节32位存储的时候,robj中的ptr直接就是该整数。相当于汇编中的立即数。若超出该范围,那么就是用字符串的形式存储该数据。这时ptr就当指针使用了。

[cpp] view plain copy
  1. robj *createStringObjectFromLongLong(long long value) {  
  2.     robj *o;  
  3.     if (value >= 0 && value < OBJ_SHARED_INTEGERS) {  
  4.         incrRefCount(shared.integers[value]);  
  5.         //大概了解了shared 它是一个机构体,包含了大量共享的的数据。  
  6.         //struct sharedObjectsStruct  
  7.         o = shared.integers[value];  
  8.     } else {  
  9.         if (value >= LONG_MIN && value <= LONG_MAX) {  
  10.             o = createObject(OBJ_STRING, NULL);  
  11.             o->encoding = OBJ_ENCODING_INT;  
  12.             o->ptr = (void*)((long)value);  
  13.             //若能使用8字节存储,直接将其存储到ptr中相当于汇编的立即数。  
  14.         } else {  
  15.             //存不了则使用sds的模式存储  
  16.             o = createObject(OBJ_STRING,sdsfromlonglong(value));  
  17.         }  
  18.     }  
  19.     return o;  
  20. }  

存储浮点数的方式,都是转化为字符串的形式。

[cpp] view plain copy
  1. /* Create a string object from a long double. If humanfriendly is non-zero 
  2.  * it does not use exponential format and trims trailing zeroes at the end, 
  3.  * however this results in loss of precision. Otherwise exp format is used 
  4.  * and the output of snprintf() is not modified. 
  5.  * 
  6.  * The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */  
  7. robj *createStringObjectFromLongDouble(long double value, int humanfriendly) {  
  8.     char buf[MAX_LONG_DOUBLE_CHARS];  
  9.     //humanfriendly用于处理浮点数,可以先不理会  
  10.     int len = ld2string(buf,sizeof(buf),value,humanfriendly);  
  11.     //将long double转换成sds,然后进行存储  
  12.     return createStringObject(buf,len);  
  13. }  

对象间字符串的复制,仅限于字符串存储的数据(这里包括了使用字符串存储的整型),或者使用立即数存储的整型。

[cpp] view plain copy
  1. /* Duplicate a string object, with the guarantee that the returned object 
  2.  * has the same encoding as the original one. 
  3.  * 
  4.  * This function also guarantees that duplicating a small integere object 
  5.  * (or a string object that contains a representation of a small integer) 
  6.  * will always result in a fresh object that is unshared (refcount == 1). 
  7.  * 
  8.  * The resulting object always has refcount set to 1. */  
  9. robj *dupStringObject(const robj *o) {  
  10.     robj *d;  
  11.   
  12.     serverAssert(o->type == OBJ_STRING);  
  13.   
  14.     switch(o->encoding) {  
  15.     case OBJ_ENCODING_RAW:  
  16.         return createRawStringObject(o->ptr,sdslen(o->ptr));  
  17.     case OBJ_ENCODING_EMBSTR:  
  18.         return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));  
  19.     case OBJ_ENCODING_INT:  
  20.         d = createObject(OBJ_STRING, NULL);  
  21.         d->encoding = OBJ_ENCODING_INT;  
  22.         d->ptr = o->ptr;  
  23.         return d;  
  24.     default:  
  25.         serverPanic("Wrong encoding.");  
  26.         break;  
  27.     }  
  28. }  

接下来就是一些别的类型的创建,大多数都是使用该类型的create函数进行创建,后又释放空间的函数,都是比较简单。还有引用计数减少的函数,都比较直接简单。直接看看就好了。

[cpp] view plain copy
  1. robj *createQuicklistObject(void) {  
  2.     quicklist *l = quicklistCreate();  
  3.     robj *o = createObject(OBJ_LIST,l);  
  4.     o->encoding = OBJ_ENCODING_QUICKLIST;  
  5.     return o;  
  6. }  
  7.   
  8. robj *createZiplistObject(void) {  
  9.     unsigned char *zl = ziplistNew();  
  10.     robj *o = createObject(OBJ_LIST,zl);  
  11.     o->encoding = OBJ_ENCODING_ZIPLIST;  
  12.     return o;  
  13. }  
  14.   
  15. robj *createSetObject(void) {  
  16.     dict *d = dictCreate(&setDictType,NULL);  
  17.     robj *o = createObject(OBJ_SET,d);  
  18.     o->encoding = OBJ_ENCODING_HT;  
  19.     return o;  
  20. }  
  21.   
  22. robj *createIntsetObject(void) {  
  23.     intset *is = intsetNew();  
  24.     robj *o = createObject(OBJ_SET,is);  
  25.     o->encoding = OBJ_ENCODING_INTSET;  
  26.     return o;  
  27. }  
  28.   
  29. robj *createHashObject(void) {  
  30.     unsigned char *zl = ziplistNew();  
  31.     robj *o = createObject(OBJ_HASH, zl);  
  32.     o->encoding = OBJ_ENCODING_ZIPLIST;  
  33.     return o;  
  34. }  
  35.   
  36. robj *createZsetObject(void) {  
  37.     zset *zs = zmalloc(sizeof(*zs));  
  38.     robj *o;  
  39.   
  40.     zs->dict = dictCreate(&zsetDictType,NULL);  
  41.     zs->zsl = zslCreate();  
  42.     o = createObject(OBJ_ZSET,zs);  
  43.     o->encoding = OBJ_ENCODING_SKIPLIST;  
  44.     return o;  
  45. }  
  46.   
  47. robj *createZsetZiplistObject(void) {  
  48.     unsigned char *zl = ziplistNew();  
  49.     robj *o = createObject(OBJ_ZSET,zl);  
  50.     o->encoding = OBJ_ENCODING_ZIPLIST;  
  51.     return o;  
  52. }  
  53.   
  54. robj *createStreamObject(void) {  
  55.     stream *s = streamNew();  
  56.     robj *o = createObject(OBJ_STREAM,s);  
  57.     o->encoding = OBJ_ENCODING_STREAM;  
  58.     return o;  
  59. }  
  60.   
  61. robj *createModuleObject(moduleType *mt, void *value) {  
  62.     moduleValue *mv = zmalloc(sizeof(*mv));  
  63.     mv->type = mt;  
  64.     mv->value = value;  
  65.     return createObject(OBJ_MODULE,mv);  
  66. }  
  67.   
  68. void freeStringObject(robj *o) {  
  69.     if (o->encoding == OBJ_ENCODING_RAW) {  
  70.         sdsfree(o->ptr);  
  71.     }  
  72. }  
  73.   
  74. void freeListObject(robj *o) {  
  75.     if (o->encoding == OBJ_ENCODING_QUICKLIST) {  
  76.         quicklistRelease(o->ptr);  
  77.     } else {  
  78.         serverPanic("Unknown list encoding type");  
  79.     }  
  80. }  
  81.   
  82. void freeSetObject(robj *o) {  
  83.     switch (o->encoding) {  
  84.     case OBJ_ENCODING_HT:  
  85.         dictRelease((dict*) o->ptr);  
  86.         break;  
  87.     case OBJ_ENCODING_INTSET:  
  88.         zfree(o->ptr);  
  89.         break;  
  90.     default:  
  91.         serverPanic("Unknown set encoding type");  
  92.     }  
  93. }  
  94.   
  95. void freeZsetObject(robj *o) {  
  96.     zset *zs;  
  97.     switch (o->encoding) {  
  98.     case OBJ_ENCODING_SKIPLIST:  
  99.         zs = o->ptr;  
  100.         dictRelease(zs->dict);  
  101.         zslFree(zs->zsl);  
  102.         zfree(zs);  
  103.         break;  
  104.     case OBJ_ENCODING_ZIPLIST:  
  105.         zfree(o->ptr);  
  106.         break;  
  107.     default:  
  108.         serverPanic("Unknown sorted set encoding");  
  109.     }  
  110. }  
  111.   
  112. void freeHashObject(robj *o) {  
  113.     switch (o->encoding) {  
  114.     case OBJ_ENCODING_HT:  
  115.         dictRelease((dict*) o->ptr);  
  116.         break;  
  117.     case OBJ_ENCODING_ZIPLIST:  
  118.         zfree(o->ptr);  
  119.         break;  
  120.     default:  
  121.         serverPanic("Unknown hash encoding type");  
  122.         break;  
  123.     }  
  124. }  
  125.   
  126. void freeModuleObject(robj *o) {  
  127.     moduleValue *mv = o->ptr;  
  128.     mv->type->free(mv->value);  
  129.     zfree(mv);  
  130. }  
  131.   
  132. void freeStreamObject(robj *o) {  
  133.     freeStream(o->ptr);  
  134. }  
  135.   
  136. void incrRefCount(robj *o) {  
  137.     if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount++;  
  138. }  
  139.   
  140. void decrRefCount(robj *o) {  
  141.     if (o->refcount == 1) {  
  142.         switch(o->type) {  
  143.         case OBJ_STRING: freeStringObject(o); break;  
  144.         case OBJ_LIST: freeListObject(o); break;  
  145.         case OBJ_SET: freeSetObject(o); break;  
  146.         case OBJ_ZSET: freeZsetObject(o); break;  
  147.         case OBJ_HASH: freeHashObject(o); break;  
  148.         case OBJ_MODULE: freeModuleObject(o); break;  
  149.         case OBJ_STREAM: freeStreamObject(o); break;  
  150.         default: serverPanic("Unknown object type"); break;  
  151.         }  
  152.         zfree(o);  
  153.     } else {  
  154.         if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");  
  155.         if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--;  
  156.     }  
  157. }  
  158.   
  159. /* This variant of decrRefCount() gets its argument as void, and is useful 
  160.  * as free method in data structures that expect a 'void free_object(void*)' 
  161.  * prototype for the free method. */  
  162. void decrRefCountVoid(void *o) {  
  163.     decrRefCount(o);  

这篇关于Redis源码学习简记(七)object原理与个人理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/941921

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

《offer来了》第二章学习笔记

1.集合 Java四种集合:List、Queue、Set和Map 1.1.List:可重复 有序的Collection ArrayList: 基于数组实现,增删慢,查询快,线程不安全 Vector: 基于数组实现,增删慢,查询快,线程安全 LinkedList: 基于双向链实现,增删快,查询慢,线程不安全 1.2.Queue:队列 ArrayBlockingQueue:

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

硬件基础知识——自学习梳理

计算机存储分为闪存和永久性存储。 硬盘(永久存储)主要分为机械磁盘和固态硬盘。 机械磁盘主要靠磁颗粒的正负极方向来存储0或1,且机械磁盘没有使用寿命。 固态硬盘就有使用寿命了,大概支持30w次的读写操作。 闪存使用的是电容进行存储,断电数据就没了。 器件之间传输bit数据在总线上是一个一个传输的,因为通过电压传输(电流不稳定),但是电压属于电势能,所以可以叠加互相干扰,这也就是硬盘,U盘

回调的简单理解

之前一直不太明白回调的用法,现在简单的理解下 就按这张slidingmenu来说,主界面为Activity界面,而旁边的菜单为fragment界面。1.现在通过主界面的slidingmenu按钮来点开旁边的菜单功能并且选中”区县“选项(到这里就可以理解为A类调用B类里面的c方法)。2.通过触发“区县”的选项使得主界面跳转到“区县”相关的新闻列表界面中(到这里就可以理解为B类调用A类中的d方法