不清楚会等q8nc甜蜜暴击什么时候播出,才可能继续播出wwWq8nccom结目了

第一次去澳大利亚,一定要了解的30个问题!
去澳大利亚必备,建议收藏。Q1去澳大利亚旅游的最佳时间?A1尽量避开1、2 两月,1、2月正值澳洲夏天最热的月份,正午气温最高可以达到45甚至50度。10到12月是澳洲的春季,气候宜人,但是由于春季空气中时不时会夹带很多花粉,因此不建议过敏体质的人在这个时间段去。3、4月处于澳洲秋季,这个时侯澳洲早晚温度一般在10-15度,正午温度在25-30度,干湿适中,非常适合旅游。而冬季的澳洲天气湿冷,阴天多,降雨多,因此不太适合旅游。总体来说澳洲的气候清凉干爽,光照充足,十分宜人,除冬季(6月、7月、8月)外其他三季旅游都比较适宜,具体还是看个人喜好。PS:去旅行尽量不要安排在11月中旬至12月初,因为这段时间是澳大利亚毕业季,也就是高中毕业生毕业旅行、狂欢的日子。人会很多,也容易出事故。Q2去澳大利亚旅游不同季节应该带哪些衣服?A2澳大利亚位于热带地区,这里的气候是十分宜人的,在这里全年都可以穿一些比较单薄的衣服。所以来这里旅游观光拿一些比较轻便的衣物就够了。但还是需要准备几件毛衣或者是外套的,以免晚上转凉。需要注意的是,如果想要出入这里的特殊场合的话,像商务会议、剧院或是高档餐厅等都是需要注意穿着的。对于男士来说最好是可以穿上外套、西装或者是打上领带,女士则需要穿一些比较庄重严肃的衣服,其他的时候就可以穿一些比较随意的衣服了。Q3去澳大利亚旅游的签证怎么办?A3申请赴澳大利亚旅游签证可以申请访客签证(600类别),如果经过澳大利亚前往其他国家需申请过境签证(771类别)。需要文件:澳洲驻中国大使馆官方网站上有具体信息http://www.china.embassy.gov.au/bjngchinese/DIACcn.html上述网站上有详细的表格(表格可以直接打印出来填写)和 Checklist ,对应表格把相关文件准备好就行。办理方法:前往澳大利亚签证申请中心(简称“ AVAC ”) 或者邮寄或快递到指定的澳大利亚签证申请中心。是否需要面签:否。停留时间:访客签证最长不超过12个月,过境签证不超过72小时。建议办理时间:提前一个月。签证费用:访客签证270元;过境签证免费;每个签证申请服务费 180元及50元回邮费。Q4去澳大利亚需要买旅游保险吗?A4有条件还是建议买,旅途中的风险是未知的,再说旅行保险的价格也是很便宜的,买一份除了能安心,而且也是对家人的负责,你去澳大利亚的话,澳大利亚的医疗本身就很贵,简单的验血,可能要到一百多刀,15分钟以内的普通门诊,一次也要70刀,简单的住院小手术,基本都在2000刀左右。没有保险,一旦头疼脑热,几天行程的预算可能就不见了。另外如果你是自驾游就一定要买,澳洲是左侧驾驶,袋鼠也是横冲直撞的。如果自驾租车,对汽车的保险不在承保范围内的,车险需另外专门购买,在租车预定的时候同时向租车公司购买。Q5入境澳大利亚,哪些东西不能带?A5澳大利亚不允许游客携带中药材,西药也要根据逗留时间计算好剂量。确实需要多带的,要凭医生处方。所有食物,超过10000澳币的现金,药物,超过50支烟,超过500ML酒都需要申报,其他申报的一般和你没关系,比如问你前几个月有没去过非洲之类的高感染的地区等。一定不能带的是任何新鲜食物,所有肉类,蛋类,奶类。烟酒超过标准是被罚款,现金超过你只要老实说明用途就没事,超的略微多点也只是填张表格,超太多就是罚款了。其他都没什么,过了入境处去拿托运行行李前会有一些海关人员随机问你有没带XX之类的,老实回答就行,只要表现的你知道什么能带,什么不能带。Q6去澳大利亚过海关会问什么问题?A6主要是由移民局的官员检查护照,签证和入境登记卡,并询问一些简单的问题,很多情况都是不怎么问就放行了,一般来说不会刁难人,如:“来澳洲的目的?”“计划停留多久?”“上学还是度假?”“打算什么时候回国?”“怎么解决住宿问题?”“行李是你自己整理的么?有没有携带违禁物品?”“喜欢你要来的这座城市吗?准备去哪些景点玩?”“在这边是否有亲戚?”“有打工的计划么?”根据自己的实际情况简答用英语回答就可以了。如果不懂或略懂英文但无法准确表达自己的意思,入境检查一般会安排免费的翻译服务。最好不要猜谜似的回答问题,以免造成意外的误会。近几年非法移民潮猖獗,澳洲的入境检查也越来越严格。当检查人员在您的护照上盖上入境章时,就表示一切无误,核准入境。Q7在澳大利亚旅行使用什么货币?A7澳大利亚的官方货币是澳元,100分等于1澳元。纸币的面值有100, 50, 20, 10和5澳元,硬币的面值则有2, 1澳元以及50, 20,10以及5分。澳币兑换人民币约等于1:6-6.2,汇率经常变化。兑换货币可以在澳大利亚各地银行或有授权的货币兑换处,比如 Travelex 或 Amex ,兑换现金或支取旅游支票。PS:美元在澳大利亚是不可以用的。Q8国内哪些银行能兑换澳元?A8换澳元可以到各外汇银行兑换,中国银行、工商银行、农业银行、建设银行、交通银行均是外汇经营银行,同一银行在各地的汇率都是一样的,不同的银行汇率稍有差别,兑换不需要手续费(因为手续费已包含在银行卖买外汇的差价中了)。Q9澳大利亚与国内的时差怎么算?A9澳大利亚悉尼时间与中国北京时间的时差为+2小时(比北京时间快2小时)。澳大利亚实行夏令时制,从夏季10月的最后一个星期日开始到次年3月的最后一个星期日结束。夏令时期间,与中国北京时间的时差为+3小时(比北京时间快3小时)。境外旅行一切以当地时间为准,航班的抵达时间主要指当地城市时间。Q10澳大利亚的电源、电压跟中国有哪些不同?A10澳大利亚使用的电压(230 v ,交流,50 Hz )和国内是不同的,而且当地的三脚插座也比较特别,所以要么从当地买三脚插座,或者就自己带上一个转换器和万能插座。Q11哪种语言在澳大利亚能行得通?A11澳大利亚的官方语言是英语,但说起来会带有澳大利亚特有的口音,是澳大利亚最普遍通用的语言。其次,澳大利亚政府鼓励不同民族或是本民族的澳大利亚人说各种语言,所以意大利语、粤语、普通话、希腊语、阿拉伯语和越南语等在澳大利亚也较为普遍。此外,澳大利亚原住民则部分保留着使用母语者,也就是土著语言,然而大多年轻澳大利亚原住民被同化,而大多使用英语,所以,不同担心语言交流问题。Q12在澳大利亚旅游如何上网?A12有以下三种方式:购买上网手机卡:在淘宝上买一张澳洲的上网卡,像7天2G流量这种,一般40多元。在澳大利亚打本地电话是免费的,打回中国也有一定的免费通话时间,具体可以上淘宝搜一下。当然,机场和便利店都有本地的手机卡可以买,但是比较贵。租赁无线egg:淘宝租赁一个随身wifi,一般押金500元,然后租一下30多元,最多5台设备,适合多人共享。(推荐)使用澳大利亚免费wifi:澳洲的公共设施很完善,在很多公共场所都会有免费的WIFI热点。大多数的酒店上网都是收费的,很多收费都在每天20澳元以上。一些酒店的大堂会提供免费电脑和网络使用。还有第四种土豪法!直接手机卡开全国漫游咯!Q13澳大利亚旅行人均预算是多少?A13去澳洲旅游的费用要看旅行的天数,不过一般去澳大利亚旅行10-15天,大概所需的人均费用是2-2.5万的样子,最终的旅游费用还是由出行时间、人数、旅游天数、游玩的档次来决定的,所以没有办法核算出来,只能自己最后玩完了才知道。Q14澳大利亚当地交通怎么解决?A14城市内交通:墨尔本、悉尼、布里斯班和黄金海岸都是澳洲常规旅游目的地,城市内公共交通蛮方便的。这些城市都有自己的交通卡。城市间交通:建议以飞行为主。澳洲的经济航空Jetstar、virginblue的票价可以很合适的。通常不建议考虑火车,耗时长而且车票贵。自驾仅推荐给时间特别充裕的人,因为澳洲地域广阔,自驾很耗费时间,当然体验也独特。租车:租车费用一般可在70澳币/天左右,当然具体选择你租什么样的车、是否在偏远地方租车、租车时间和地点、购买何种保险等等。Q15澳大利亚有哪些值得去的地方?A15大堡礁、乌鲁鲁-卡塔丘塔国家公园、卡卡杜国家公园、金伯利、袋鼠岛、被称为新时代天堂的拜伦湾、塔斯马尼亚国家公园、宁格鲁礁、费沙岛、蓝山、纳玛吉国家公园、菲欣纳国家公园等。梦想旅行APP上有很多的评价和推荐,可以看看~Q16去澳大利亚想租车自由行,有国内驾照就可以吗?A16根据澳大利亚官方和租车行的条款的信息,去澳大利亚旅游中国国内驾照不能直接在澳大利亚租车的,需要有相应的 NAATI 翻译件(淘宝,100RMB左右)或者国内的公证处出具的翻译公证件,二选一加上驾照原件哦。中国新版驾照不完全是英文。租车行的网点也许会比较松,但是警察和保险公司还是认可相应的翻译件或者公证件的。Q17澳大利亚有哪些值得推荐的美食?A17澳大利亚的传统饮食来源于英国,现正处在变革之中,澳大利亚的饮食受到地中海、亚洲、中东饮食的影响,鱼和海鲜成为澳大利亚饮食的特色。推荐的特色美食有袋鼠肉、皇帝蟹、牡蛎、鲍鱼、龙虾、三文鱼等等,梦想旅行APP上还有更多的推荐可以去安利。墨尔本的维多利亚女王市场、布里斯班的唐人街、北领地沙滩夜市的鳄鱼肉、南澳巴罗莎山谷的农家菜等都值得一试。Q18在澳大利亚是否需要付小费?A18澳大利亚一般不会强制性的收取小费,在任何时间和地方,是否给小费完全是靠客人自己的意愿。如果想要给小费,一般在餐厅,小费占到消费费用的的十分之一即可。给搬运行李的服务生的消费,一件行李只需付小费一澳币。Q19澳大利亚酒店消费水平怎么样?A19澳洲旅店可分为酒店、B&B(早餐民宿)、Apartment、motel等。像是酒店价格和推荐这种,在梦想旅行APP上都有,并且支持在线预订。motel(汽车旅馆)单人间基本没有,几乎都是2-3人间,费用人均为$40-80元。Apartment (会员制饭店/假日饭店)最近比较受欢迎,房间里有高级公寓的感觉,家具等一应俱全。费用2人一周$300左右。Q20澳大利亚适合带孩子去旅行吗?A20澳大利亚无论从自然、文化还是居住环境层面,都是很值得推荐带孩子去游玩的国家。从自然条件来说,澳大利亚四面环海,拥有很多自己特有的动植物和自然景观。在这里可以让孩子与独特可爱的野生动物接触,探索多彩的珊瑚礁和海底世界。从文化元素来说,澳大利亚是一个移民国家,奉行多元文化,约四分之一的居民出生在澳大利亚以外,孩子可以增长眼界和见识。从居住环境来说,澳大利亚人口高度都市化,近一半国民居住在悉尼和墨尔本两大城市,全国多个城市曾被评为世界上最适宜居住的地方之一。其第二大城市墨尔本曾多次被评为世界上最适宜居住的城市。Q21澳大利亚城市间的交通是国内先订还是到了再订好?A21建议在国内先订好。捷星、维珍、虎航是澳大利亚国内最常用的廉价航空公司,捷星和虎航都有中文官网(分别是 www.jetstar.com 和 www.tigerair.com),只要有 VISA 或 MASTERCARD 的信用卡就能订。在国内订一来网站不定时有优惠,而现场买通常没优惠;二来网上订是中文的,去到当地用英文不一定能沟通好,三来游玩的时间是宝贵的,在国内订好等于节省以后现场购票的时间。Q22澳大利亚什么时候是打折季?A22一般来说,折扣力度最大的就要数Boxing Day了,就是每年的12月26日,圣诞节后的第二天,这一天各大购物中心一般都是爆满,什么Gucci、LV、Burberry、Prada等大牌都会有折扣。而且有时候尺度确实令人大开眼界......3折、 5折!一年中第二大力度的打折季在每年的6月。Q23澳大利亚有什么值得买的特产或者纪念品?A23袋鼠皮。400-600rmb一块整皮,毛很舒服垫在家里客厅很大气。绵羊油。绝对买,超级便宜,有几个城市的中国城甚至买到2块澳币一盒。保健品。蜂皇胶,葡萄籽,Q10等等都不错,澳佳宝之类的品牌和国内价格差很多,建议在药店买,不要到什么中国免税店买,真真假假分不清楚!澳宝。有点贵的几千一个,不过还是挺漂亮的,悉尼的岩石区有几家澳宝专卖店不错,值得一看。衣服。对的你没看错。衣服!澳洲衣服也有很多中国产的,不过款式和材料比中国好多了。美丽诺羊毛系列。羊毛衫或者羊皮钱包又或者羊毛被,能装的下都带回来吧PS:不要在华人归国礼品店买!!!!尤其是保健品和药妆,一定要去当地正规药店买,例如chemist warehouse,priceline。Q24在澳大利亚买 UGG 便宜吗?A24科普一下,UGG 不是个牌子,而是雪地靴的统称。我们国内说的正品的那个 UGG 牌子是 UGG Australia ,UGG Australia 是澳洲人跑到美国注册开的店,所以在美国折扣较多。在澳大利亚价格会便宜点,但是设计会没有那么好看Q25购物退税有哪些要注意的?A25澳洲海关在2013年4月出台了针对游客的新退税政策。·游客可以为离境前60天之内购买物品的申请退税(购买时间必须在日之后)。· 游客如果多次在同一家商铺(如果是在大型连锁店购买商品,需要注意有些连锁店的澳洲工商注册号ABN,一般印在发票的最上方)。保险起见,需要退税的话还是去同一家店购买物品,且所有发票的金额相加超过300澳元,可以凭这些发票办理退税 。·这些政策只适用于手提行李携带或是穿着(如果是衣物鞋帽)的商品,如果是超过100ml的液体胶体之类必须托运的物品,则需要到机场一层退税柜台,由海关工作人员检查盖章后,方可装入行李箱托运,并在结束安检程序后,到退税处办理相关手续。·请注意退税的商品必须是含有GST(消费税)的商品,不含GST的商品,如奶制品,是不可以退税的。Q26去澳大利亚旅游有哪些值得推荐的APP?A26找当地吃喝玩乐购,订酒店——梦想旅行app地图——Google Map私人翻译——Google Translate以上的体验,我还是觉得梦想旅行APP更加适合懒人。当然,每个人的喜好都不一样,适合自己最重要。Q27关于抱考拉,澳大利亚哪儿可以体验?A27澳大利亚不是所有的公园都可以抱考拉,昆士兰州是法律唯一允许抱考拉的州,可以抱考拉的地方包括布里斯班、黄金海岸、凯恩斯等第。龙柏考拉动物园是一个非常好的选择,公园位于布里斯班市郊,是世界上最早,同时也是最大的考拉动物园。在这个野生动物中,除了考拉外,还有如袋鼠、袋獾(不对外展示)、袋熊、针鼹鼠以及其他各种爬虫类动物。园内一共拥有约130只可爱的考拉。而且整个园区内的环境相当出色,游人也将这里视为亲近大自然的理想去处,所以龙柏考拉动物园一直是游玩布里斯班的必到之处。更加难能可贵的是,在龙柏考拉动物园您只需支付一些费用便可以抱抱可爱的考拉并且与之合影留念,当然园内也有严格的规定每只考拉每天不能被抱着超过几分钟。Q28在澳大利亚遇到紧急情况有哪些电话可以拨打?A28警察、火警、急救:000中国驻澳大利亚大使馆:34780旅游投诉:02-Q29澳大利亚有什么风俗习惯和禁忌?A29澳大利亚人很讲究礼貌,在公共场合从来不大声喧哗。在银行、邮局、公共汽车站等公共场所,都是耐心等待,秩序井然。握手是一种相互打招呼的方式,拥抱亲吻的情况罕见。澳大利亚社会上同英国一样有“妇女优先”的习惯;他们非常注重公共场所的仪表,男子大多数不留胡须,出席正式场合时西装革履,女性是西服上衣西服裙。澳大利亚人的时间观念很强,约会必须事先联系并准时赴约,最合适的礼物是给女主人带上一束鲜花,也可以给男主人送一瓶葡萄酒。澳大利亚人待人接物都很随和。Q30澳大利亚现在安全吗?A30澳大利亚很少会出现针对外国游客的暴力犯罪,但是在悉尼或墨尔本还是偶尔会出现在天黑之后打劫行凶的现象。盗窃是澳大利亚最值得担心的问题。不管是在沙滩, 运输中心或任何旅游景点都要小心看管好您的物品。如果行事低调并尽量跟当地人和谐相处,那么就会减少被盗贼盯上的几率。
正文已结束,您可以按alt+4进行评论
相关搜索:
Copyright & 1998 - 2018 Tencent. All Rights Reserved
!-- fjc-->(阳光中的青时雨)
第三方登录:Detu/得图F4 四目全景8K专业全景相机评测
▼点击下方收听音频
Detu/得图F4 四目全景8K专业全景相机评测
来自百度VR
Detu/得图F4 四目全景8K专业全景相机介绍:产品图片:FAQ:1、360°*360°超高清全景视频拍摄技术到底有多强悍?F4拥有8K高清像素照片拍摄 30FPS/4K 60FPS 全景视频技术运用H.264实时硬件编码器、搭载新型图像传感器,分辨率可高达、呈现影院级高清画质,彻底满足专业级全景相机的摄影需求。2、视频是否可支持直播?在线和朋友实时分享?F4拥有4K全景直播技术将拍摄到的视频实时全景直播,4K高清像素的出色画质为您将拍摄过程中的每一个细节、每一个特写展现的淋漓尽致,和朋友分享现场的实时盛况。3、呈现的照片是否可解决拼接错位?F4成像高清照片,超小拼接缝隙,30mm节点精确定位商业全景相机中节点最小同时结合全新光学算法、完美解决拼接错位。4、相机是否一键360度成像?一键成像,统一曝光四个镜头统一曝光,只需按下拍摄按钮、即刻开启摄像工作,快速捕捉眼前美景。5、相机是否可多平台兼容播放?通过调用SDK接口、轻松访问全景数据和服务,构建功能丰富、交互性强的应用程序。打造完美全景播放器,解决H5、PC、Android、iOS等平台播放难题;实现全景视频及漫游等无缝嵌入。兼容市面上大多数无屏VR头盔。6、相机是否可支持多种全景观看模式?F4支持小行星、鱼眼、VR等多种全景观看模式观看。使用评测:全景相机得图 F4 评测:商用级机器也能如此小巧易用通常来说,商用级全景相机给我们的印象要么是操作复杂、上手门槛高,要么是体积大、不便携带。像这样:多个 GoPro 组成而成的全景相机或者这样:Jaunt VR 全景相机当得图说要给商用级全景相机 F4 我们评测时,我脑海中对这台相机的想象都与上面的没有太多区别。直到拿到真机,我对商用级全景相机的认知才有了不少改变——得图 F4 居然能做到如此小巧紧凑。外观和操作得图 F4 体积跟一个保温瓶差不多大,一体化程度非常高,一只手就能拿起带走。按钮和指示灯也不多,且布局清晰。机身侧面非常简洁,只有散热小孔网,以及得图 F4 的 logo。充电接口microSD 卡卡槽放置在机身底部,卡槽最大支持 128GB 的 micro SD 卡。此外,还有 4 个 micro HDMI 卡口、1 个 USB 3.0 传输口和标准三角架安装口。安装三脚架效果拍摄过程也很简单,通过 Wi-Fi 连接得图 F4 相机 app,在手机上点击拍摄照片/视频即可。相比我们之前使用过的消费级全景相机应用,得图 F4 相机 app 在保有易用性的同时,加入了更多可控性。这体现在,连接镜头 A 后可以调节曝光、ISO 和白平衡,还能设置 A 镜头和其他三个镜头是否同步设置。点进 app 设置里,还可以调整 WDR(宽动态范围)和光源频率。可惜的是,得图 F4 相机 app 只支持单个镜头预览,而不能全景预览,并且预览有 1 秒左右的延迟。在不同镜头间切换预览需要通过连接对应 Wi-Fi 完成。另外,镜头 B, C, D 都只能预览,而不能像镜头 A 那样自定义曝光、ISO, 白平衡以及同步设置。硬件和画质得图 F4 最高支持单个镜头在 24 帧的情况下采集
分辨率的视频,官方宣传全景视频最高分辨率为 。虽然我们视频组同事测试拼接后最高只能达到 ,但已经能够满足专业级别的全景视频拍摄需求。每个镜头都是视角为 190° 的鱼眼,焦距为 1.45mm,最大光圈达 ?/2.2。图像传感器则采用索尼 1200 万像素、1/2.3 英寸的 IMX078。得图 F4 完整硬件配置如下:得益于得图 F4 的同步控制功能,4 个镜头的曝光比较一致,后期基本无需做过多调整。我们视频组的同事在使用得图 F4 一星期后,对其画面质量的评价是:「基本无镜头畸变,色彩还原度适中。光线充足情况下画质优秀,但对暗部细节的表现不是很好,弱光环境下噪点控制得不是特别好。」光线充足素材弱光环境素材拼接和分享得图 F4 没有自动拼接功能,用户需要将素材导出到电脑自己拼接。得益于得图配套的 DeStitch 电脑拼接软件,后期制作过程中节省了大量拼接时间。DeStitch 只支持 PC,可自动识别 F4 相机中的内容,同时支持手动选择文件进行拼接处理。操作也非常简单,没有什么门槛。用 DeStitch 尝试拼接素材后,我们视频组同事给出了「拼接缝在可承受范围内」的评价。除了配套的 DeStitch 外,得图 F4 的素材也支持自定义拼接,实测 Autopano 可以完成。值得一提的是,配合直播一体机使用,得图 F4 可以在直播软件中实现实时拼接,进而完成全景视频直播。当前,全景视频分享是一个比较尴尬的事情。微信内置的腾讯视频至今还没有支持,通常都是上传到优酷。事实上,得图也建设了一个全景分享社区,不仅供用户上传全景视频分享,还发布一些全景拍摄的教学帖子。最后给出我们视频组同事王锴对得图 F4 的使用心得:机身体积小,一体化程度高,便携易用;最高可以录制
的高清全景视频;手机 app 可以同步调节4个摄像头的 ISO、曝光度、色温(异步模式不可用);续航时间在 150 分钟左右;F4 配套的拼接软件在后期制作过程中节省了大量的拼接时间,同时支持自定义拼接(实测在 Autopano 中可以进行拼接);拼接缝在可承受范围内(基本无镜头畸变);支持全景直播(需要配合直播一体机)。不足在于:F4 对暗部细节的表现不是很好,有明显的噪点;手机App不支持全景预览,只能分别连接各个摄像头的wiff进行预览;色温只支持自动、日光、阴天、钨丝灯、荧光灯五种模式,不支持手动调整,后期颜色调整空间较小。图片拼接教程:http://www.detu.com/huati/detail/1647得图F4 图像拼接软件 DeStitch:点击下载得图F4 APP下载:安卓版 下载地址: http://shouji.baidu.com/software/.htmlios版 下载地址:DETU F4 app图文操作教程注意:使用最新版得图F4 app,需将F4相机升级最新的固件包,否则无法使用。点击查看,最新 F4固件包下载及使用说明(超链接)一、新版得图F4下载扫描下载最新版appIos版app点击下载Android版app点击下载二、使用说明:1、短按相机开机键开启相机后,长按录制键开启相机wifi2、打开得图F4 app,如下图,以相机编号F4BK02NC140787为例:3、点击当前网络,如下图,选择F4无线网络,4个无线网络末尾名称分别以ABCD命名,选择F4BK02NC140787A,默认密码为, F4BK02NC140787为F4相机的编号,A为相机的A镜头,需要注意的是,四个无线网络都可以连接进行预览调试,但是只有连接无线A才能进行视频录制和照片拍摄;4、如图,点击右上角的设置按钮,进行画质设置,视频分辨率可以选择 P24, P30和 P30;照片分辨率可以选择12M ,10M ,8M ;5、预览调试点击预览调试,此时由于连接的是无线A,所以预览的是A镜头画面,如果连接的是无线B、C、D,那么预览的是B、C、D镜头画面; 在预览界面,有自动、日光、阴天、钨丝灯、荧光灯5项可以选择调试,另外EV、ISO也可在本界面进行预览调试,如下图为镜头A的预览页面。6、录制视频录制界面会显示码率、fps、SD卡、电量四项参数;点击红色快门开启视频拍摄,再次点击结束拍摄;7、拍摄照片拍摄界面会显示分辨率、已拍张数、SD卡、电量四项参数;点击黄色快门开启视频拍摄,再次点击结束拍摄;8、在F4软件主界面,点击右上角的设置,可以进行WiFi远程关机。55662.jpeg (33.96 KB, 下载次数: 0)
来源:百度VR
声明:ivr.baidu.com所发布的内容均来源于互联网,目的在于传递信息,但不代表本站赞同其观点及立场,版权归属原作者,如有侵权请联系删除。
百度VR等你来Android中插件开发篇之----类加载器
前言关于插件,已经在各大平台上出现过很多,eclipse插件、chrome插件、3dmax插件,所有这些插件大概都为了在一个主程序中实现比较通用的功能,把业务相关或者让可以让用户自定义扩展的功能不附加在主程序中,主
前言关于插件,已经在各大平台上出现过很多,eclipse插件、chrome插件、3dmax插件,所有这些插件大概都为了在一个主程序中实现比较通用的功能,把业务相关或者让可以让用户自定义扩展的功能不附加在主程序中,主程序可在运行时安装和卸载。在android如何实现插件也已经被广泛传播,实现的原理都是实现一套插件接口,把插件实现编成apk或者dex,然后在运行时使用DexClassLoader动态加载进来,不过在这个开发过程中会遇到很多的问题,所以这一片就先不介绍如何开发插件,而是先解决一下开发过程中会遇到的问题,这里主要就是介绍DexClassLoader这个类使用的过程中出现的错误导读中的类加载器:http://blog.csdn.net/jiangwei/article/details/中的动态加载机制:http://blog.csdn.net/jiangwei/article/details/一、预备知识Android中的各种加载器介绍插件开发的过程中DexClassLoader和PathClassLoader这两个类加载器了是很重要的,但是他们也是有区别的,而且我们也知道PathClassLoader是Android应用中的默认加载器。他们的区别是:DexClassLoader可以加载任何路径的apk/dex/jarPathClassLoader只能加载/data/app中的apk,也就是已经安装到手机中的apk。这个也是PathClassLoader作为默认的类加载器的原因,因为一般程序都是安装了,在打开,这时候PathClassLoader就去加载指定的apk(解压成dex,然后在优化成odex)就可以了。我们可以看一下他们的:DexClassLoader.java/*
* Copyright (C) 2008 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
package dalvik.
import java.io.F
import java.io.IOE
import java.net.MalformedURLE
import java.net.URL;
import java.util.zip.ZipF
* Provides a simple {@link ClassLoader} implementation that operates on a
* list of jar/apk files with classes.dex entries.
The directory that
* holds the optimized form of the files is specified explicitly.
* can be used to execute code not installed as part of an application.
* The best place to put the optimized DEX files is in app-specific
* storage, so that removal of the app will automatically remove the
* optimized DEX files.
If other storage is used (e.g. /sdcard), the
* app may not have an opportunity to remove them.
public class DexClassLoader extends ClassLoader {
private static final boolean VERBOSE_DEBUG =
/* constructor args, held for init */
private final String mRawDexP
private final String mRawLibP
private final String mDexOutputP
* Parallel arrays for jar/apk files.
* (could stuff these into an object and
* improves clarity but adds overhead)
private final File[] mF
// source file Files, for rsrc URLs
private final ZipFile[] mZ
// source zip files, with resources
private final DexFile[] mD
// opened, prepped DEX files
* Native library path.
private final String[] mLibP
* Creates a {@code DexClassLoader} that finds interpreted and native
Interpreted classes are found in a set of DEX files contained
* in Jar or APK files.
* The path lists are separated using the character specified by
* the "path.separator" system property, which defaults to ":".
* @param dexPath
the list of jar/apk files containing classes and resources
* @param dexOutputDir
directory where optimized DEX files should be written
* @param libPath
the list of directories contain may be null
* @param parent
the parent class loader
public DexClassLoader(String dexPath, String dexOutputDir, String libPath,
ClassLoader parent) {
super(parent);
......我们看到,他是继承了ClassLoader类的,ClassLoader是类加载器的鼻祖类。同时我们也会发现DexClassLoader只有一个构造函数,而且这个构造函数是:dexPath、dexOutDir、libPath、parentdexPath:是加载apk/dex/jar的路径dexOutDir:是dex的输出路径(因为加载apk/jar的时候会解压除dex文件,这个路径就是保存dex文件的)libPath:是加载的时候需要用到的lib库,这个一般不用parent:给DexClassLoader指定父加载器我们在来看一下PathClassLoader的源码PathClassLoader.java/*
* Copyright (C) 2007 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
package dalvik.
import java.io.ByteArrayOutputS
import java.io.F
import java.io.FileNotFoundE
import java.io.IOE
import java.io.InputS
import java.io.RandomAccessF
import java.net.MalformedURLE
import java.net.URL;
import java.util.ArrayL
import java.util.E
import java.util.L
import java.util.NoSuchElementE
import java.util.zip.ZipE
import java.util.zip.ZipF
* Provides a simple {@link ClassLoader} implementation that operates on a list
* of files and directories in the local file system, but does not attempt to
* load classes from the network. Android uses this class for its system class
* loader and for its application class loader(s).
public class PathClassLoader extends ClassLoader {
private final S
private final String libP
* Parallel arrays for jar/apk files.
* (could stuff these into an object and
* improves clarity but adds overhead)
private final String[] mP
private final File[] mF
private final ZipFile[] mZ
private final DexFile[] mD
* Native library path.
private final List libraryPathE
* Creates a {@code PathClassLoader} that operates on a given list of files
* and directories. This method is equivalent to calling
* {@link #PathClassLoader(String, String, ClassLoader)} with a
* {@code null} value for the second argument (see description there).
* @param path
the list of files and directories
* @param parent
the parent class loader
public PathClassLoader(String path, ClassLoader parent) {
this(path, null, parent);
* Creates a {@code PathClassLoader} that operates on two given
* lists of files and directories. The entries of the first list
* should be one of the following:
* Directories containing classes or resources.
* JAR/ZIP/APK files, possibly containing a "classes.dex" file.
* "classes.dex" files.
* The entries of the second list should be directories containing
* native library files. Both lists are separated using the
* character specified by the "path.separator" system property,
* which, on Android, defaults to ":".
* @param path
the list of files and directories containing classes and
* @param libPath
the list of directories containing native libraries
* @param parent
the parent class loader
public PathClassLoader(String path, String libPath, ClassLoader parent) {
super(parent);
....看到了PathClassLoader类也是继承了ClassLoader的,但是他的构造函数和DexClassLoader有点区别就是,少了一个dexOutDir,这个原因也是很简单,因为PathClassLoader是加载/data/app中的apk,而这部分的apk都会解压释放dex到指定的目录:/data/dalvik-cache这个释放解压操作是系统做的。所以PathClassLoader可以不需要这个参数的。vcD48cD7Jz8Pmv7TBy8v7w8fBvbXEx/ix8KOsz8LD5tTawLS/tNK7z8JBbmRyb2lk1tC1xLj31tbA4LzT1NjG97fWsfC809TYxMTQqcDgo7o8L3A+PHA+PC9wPjxwcmUgY2xhc3M9"brush:">package com.example.
import android.app.A
import android.content.C
import android.os.B
import android.util.L
import android.widget.ListV
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("DEMO", "Context的类加载加载器:"+Context.class.getClassLoader());
Log.i("DEMO", "ListView的类加载器:"+ListView.class.getClassLoader());
Log.i("DEMO", "应用程序默认加载器:"+getClassLoader());
Log.i("DEMO", "类加载器:"+ClassLoader.getSystemClassLoader());
Log.i("DEMO", "系统类加载器和Context的类加载器是否相等:"+(Context.class.getClassLoader()==ClassLoader.getSystemClassLoader()));
Log.i("DEMO", "系统类加载器和应用程序默认加载器是否相等:"+(getClassLoader()==ClassLoader.getSystemClassLoader()));
Log.i("DEMO","打印应用程序默认加载器的委派机制:");
ClassLoader classLoader = getClassLoader();
while(classLoader != null){
Log.i("DEMO", "类加载器:"+classLoader);
classLoader = classLoader.getParent();
Log.i("DEMO","打印系统加载器的委派机制:");
classLoader = ClassLoader.getSystemClassLoader();
while(classLoader != null){
Log.i("DEMO", "类加载器:"+classLoader);
classLoader = classLoader.getParent();
}运行结果:依次来看一下1) 系统类的加载器Log.i("DEMO", "Context的类加载加载器:"+Context.class.getClassLoader());
Log.i("DEMO", "ListView的类加载器:"+ListView.class.getClassLoader());从结果看到他们的加载器是:BootClassLoader,关于他源码我没有找到,只找到了class文件(用jd-gui查看):看到他也是继承了ClassLoader类。2) 应用程序的默认加载器Log.i("DEMO", "应用程序默认加载器:"+getClassLoader());运行结果:默认类加载器是PathClassLoader,同时可以看到加载的apk路径,libPath(一般包括/vendor/lib和/system/lib)3) 系统类加载器Log.i("DEMO", "系统类加载器:"+ClassLoader.getSystemClassLoader());运行结果:系统类加载器其实还是PathClassLoader,只是加载的apk路径不是/data/app/xxx.apk了,而是系统apk的路径:/system/app/xxx.apk4) 默认加载器的委派机制关系Log.i("DEMO","打印应用程序默认加载器的委派机制:");
ClassLoader classLoader = getClassLoader();
while(classLoader != null){
Log.i("DEMO", "类加载器:"+classLoader);
classLoader = classLoader.getParent();
}打印结果:默认加载器PathClassLoader的父亲是BootClassLoader5) 系统加载器的委派机制关系Log.i("DEMO","打印系统加载器的委派机制:");
classLoader = ClassLoader.getSystemClassLoader();
while(classLoader != null){
Log.i("DEMO", "类加载器:"+classLoader);
classLoader = classLoader.getParent();
}运行结果:可以看到系统加载器的父亲也是BootClassLoader二、分析遇到的问题的原因和解决办法DexClassLoader加载原理和分析在实现插件时不同操作造成错误的原因分析这里主要用了三个工程:PluginImpl:插件接口工程(只是接口的定义)PluginSDK:插件工程(实现插件接口,定义具体的功能)HostProject:宿主工程(需要引用插件接口工程,然后动态的加载插件工程)(例子项目中名字是PluginDemos)第一、项目介绍下面来看一下源代码:1、PluginImpl工程:1) IBean.javapackage com.pluginsdk.
public abstract interface IBean{
public abstract String getName();
public abstract void setName(String paramString);
}2) IDynamic.javapackage com.pluginsdk.
import android.content.C
public abstract interface IDynamic{
public abstract void methodWithCallBack(YKCallBack paramYKCallBack);
public abstract void showPluginWindow(Context paramContext);
public abstract void startPluginActivity(Context context,Class cls);
public abstract String getStringForResId(Context context);
}其他的就不列举了。2、PluginSDK工程:1) Dynamic.java/**
* Dynamic1.java
* com.youku.pluginsdk.imp
* Function: TODO
* ──────────────────────────────────
Administrator
* Copyright (c) 2014, TNT All Rights Reserved.
package com.pluginsdk.
import android.app.AlertD
import android.app.AlertDialog.B
import android.app.D
import android.content.C
import android.content.DialogI
import android.content.I
import com.pluginsdk.bean.B
import com.pluginsdk.interfaces.ID
import com.pluginsdk.interfaces.YKCallB
import com.youku.pluginsdk.R;
* ClassName:Dynamic1
* @version
下午5:57:10
public class Dynamic implements IDynamic{
public void methodWithCallBack(YKCallBack callback) {
Bean bean = new Bean();
bean.setName("PLUGIN_SDK_USER");
callback.callback(bean);
public void showPluginWindow(Context context) {
AlertDialog.Builder builder = new Builder(context);
builder.setMessage("对话框");
builder.setTitle(R.string.hello_world);
builder.setNegativeButton("取消", new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Dialog dialog = builder.create();//.show();
dialog.show();
public void startPluginActivity(Context context,Class cls){
*这里要注意几点:
*1、如果单纯的写一个MainActivity的话,在主工程中也有一个MainActivity,开启的Activity还是主工程中的MainActivity
*2、如果这里将MainActivity写成全名的话,还是有问题,会报找不到这个Activity的错误
Intent intent = new Intent(context,cls);
context.startActivity(intent);
public String getStringForResId(Context context){
return context.getResources().getString(R.string.hello_world);
2) Bean.java/**
* User.java
* com.youku.pluginsdk.bean
* Function: TODO
* ──────────────────────────────────
Administrator
* Copyright (c) 2014, TNT All Rights Reserved.
package com.pluginsdk.
* ClassName:User
* @version
下午1:35:16
public class Bean implements com.pluginsdk.interfaces.IBean{
private String name = "这是来自于插件工程中设置的初始化的名字";
public String getName() {
public void setName(String name) {
this.name =
3、宿主工程HostProject1) MainActivity.javapackage com.
import java.io.F
import java.lang.reflect.M
import android.annotation.SuppressL
import android.app.A
import android.content.C
import android.content.res.AssetM
import android.content.res.R
import android.content.res.Resources.T
import android.os.B
import android.os.E
import android.util.L
import android.view.V
import android.widget.B
import android.widget.ListV
import android.widget.T
import com.pluginsdk.interfaces.IB
import com.pluginsdk.interfaces.ID
import com.pluginsdk.interfaces.YKCallB
import com.youku.plugindemo.R;
import dalvik.system.DexClassL
public class MainActivity extends Activity {
private AssetManager mAssetM//资源管理器
private Resources mR//资源
private Theme mT//主题
private String apkFileName = "PluginSDKs.apk";
private String dexpath =//apk文件地址
private File fileRelease = //释放目录
private DexClassLoader classLoader =
@SuppressLint("NewApi")
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Environment.getExternalStorageDirectory() + File.separator+apkFileN
fileRelease = getDir("dex", 0);
/*初始化classloader
* dexpath dex文件地址
* fileRelease 文件释放地址
父classLoader
Log.d("DEMO", (getClassLoader()==ListView.class.getClassLoader())+"");
Log.d("DEMO",ListView.class.getClassLoader()+"");
Log.d("DEMO", Context.class.getClassLoader()+"");
Log.d("DEMO", Context.class.getClassLoader().getSystemClassLoader()+"");
Log.d("DEMO",Activity.class.getClassLoader()+"");
Log.d("DEMO", (Context.class.getClassLoader().getSystemClassLoader() == ClassLoader.getSystemClassLoader())+"");
Log.d("DEMO",ClassLoader.getSystemClassLoader()+"");
classLoader = new DexClassLoader(dexpath, fileRelease.getAbsolutePath(),null,getClassLoader());
Button btn_1 = (Button)findViewById(R.id.btn_1);
Button btn_2 = (Button)findViewById(R.id.btn_2);
Button btn_3 = (Button)findViewById(R.id.btn_3);
Button btn_4 = (Button)findViewById(R.id.btn_4);
Button btn_5 = (Button)findViewById(R.id.btn_5);
Button btn_6 = (Button)findViewById(R.id.btn_6);
btn_1.setOnClickListener(new View.OnClickListener() {//普通调用
反射的方式
public void onClick(View arg0) {
Class mLoadClassB
mLoadClassBean = classLoader.loadClass("com.pluginsdk.bean.Bean");
Object beanObject = mLoadClassBean.newInstance();
Log.d("DEMO", "ClassLoader:"+mLoadClassBean.getClassLoader());
Log.d("DEMO", "ClassLoader:"+mLoadClassBean.getClassLoader().getParent());
Method getNameMethod = mLoadClassBean.getMethod("getName");
getNameMethod.setAccessible(true);
String name = (String) getNameMethod.invoke(beanObject);
Toast.makeText(MainActivity.this, name, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Log.e("DEMO", "msg:"+e.getMessage());
btn_2.setOnClickListener(new View.OnClickListener() {//带参数调用
public void onClick(View arg0) {
Class mLoadClassB
mLoadClassBean = classLoader.loadClass("com.pluginsdk.bean.Bean");
Object beanObject = mLoadClassBean.newInstance();
//接口形式调用
Log.d("DEMO", beanObject.getClass().getClassLoader()+"");
Log.d("DEMO",IBean.class.getClassLoader()+"");
Log.d("DEMO",ClassLoader.getSystemClassLoader()+"");
IBean bean = (IBean)beanO
bean.setName("宿主程序设置的新名字");
Toast.makeText(MainActivity.this, bean.getName(), Toast.LENGTH_SHORT).show();
}catch (Exception e) {
Log.e("DEMO", "msg:"+e.getMessage());
btn_3.setOnClickListener(new View.OnClickListener() {//带回调函数的调用
public void onClick(View arg0) {
Class mLoadClassD
mLoadClassDynamic = classLoader.loadClass("com.pluginsdk.imp.Dynamic");
Object dynamicObject = mLoadClassDynamic.newInstance();
//接口形式调用
IDynamic dynamic = (IDynamic)dynamicO
//回调函数调用
YKCallBack callback = new YKCallBack() {//回调接口的定义
public void callback(IBean arg0) {
Toast.makeText(MainActivity.this, arg0.getName(), Toast.LENGTH_SHORT).show();
dynamic.methodWithCallBack(callback);
} catch (Exception e) {
Log.e("DEMO", "msg:"+e.getMessage());
btn_4.setOnClickListener(new View.OnClickListener() {//带资源文件的调用
public void onClick(View arg0) {
loadResources();
Class mLoadClassD
mLoadClassDynamic = classLoader.loadClass("com.pluginsdk.imp.Dynamic");
Object dynamicObject = mLoadClassDynamic.newInstance();
//接口形式调用
IDynamic dynamic = (IDynamic)dynamicO
dynamic.showPluginWindow(MainActivity.this);
} catch (Exception e) {
Log.e("DEMO", "msg:"+e.getMessage());
btn_5.setOnClickListener(new View.OnClickListener() {//带资源文件的调用
public void onClick(View arg0) {
loadResources();
Class mLoadClassD
mLoadClassDynamic = classLoader.loadClass("com.pluginsdk.imp.Dynamic");
Object dynamicObject = mLoadClassDynamic.newInstance();
//接口形式调用
IDynamic dynamic = (IDynamic)dynamicO
dynamic.startPluginActivity(MainActivity.this,
classLoader.loadClass("com.plugindemo.MainActivity"));
} catch (Exception e) {
Log.e("DEMO", "msg:"+e.getMessage());
btn_6.setOnClickListener(new View.OnClickListener() {//带资源文件的调用
public void onClick(View arg0) {
loadResources();
Class mLoadClassD
mLoadClassDynamic = classLoader.loadClass("com.pluginsdk.imp.Dynamic");
Object dynamicObject = mLoadClassDynamic.newInstance();
//接口形式调用
IDynamic dynamic = (IDynamic)dynamicO
String content = dynamic.getStringForResId(MainActivity.this);
Toast.makeText(getApplicationContext(), content+"", Toast.LENGTH_LONG).show();
} catch (Exception e) {
Log.e("DEMO", "msg:"+e.getMessage());
protected void loadResources() {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, dexpath);
mAssetManager = assetM
} catch (Exception e) {
e.printStackTrace();
Resources superRes = super.getResources();
superRes.getDisplayMetrics();
superRes.getConfiguration();
mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(),superRes.getConfiguration());
mTheme = mResources.newTheme();
mTheme.setTo(super.getTheme());
public AssetManager getAssets() {
return mAssetManager == null ? super.getAssets() : mAssetM
public Resources getResources() {
return mResources == null ? super.getResources() : mR
public Theme getTheme() {
return mTheme == null ? super.getTheme() : mT
三个工程的下载地址:http://download.csdn.net/detail/jiangwei/8188011第二、项目引用关系工程文件现在大致看完了,我们看一下他们的引用关系吧:1、将接口工程PluginImpl设置成一个Library2、插件工程PluginSDKs引用插件的jar注意是lib文件夹,不是libs,这个是有区别的,后面会说道3、HostProject项目引用PluginImpl这个library项目引用完成之后,我们编译PluginSDKs项目,生成PluginSDKs.apk放到手机的sdcard的根目录(因为我代码中是从这个目录进行加载apk的,当然这个目录是可以修改的),然后运行HostProject看到效果了吧。运行成功,其实这个对话框是在插件中定义的,但是我们知道定义对话框是需要context变量的,所以这个变量就是通过参数从宿主工程中传递到插件工程即可,成功了就不能这么了事,因为我还没有说道我遇到的问题,下面就来看一下遇到的几个问题三、问题分析问题一:Could not find class...(找不到指定的类)这个问题产生的操作:插件工程PluginSDKs的引用方式不变,宿主工程PluginDemos的引用方式改变在说这个原因之前先来了解一下Eclipse中引用工程的不同方式和区别:第一种:最常用的将引用工程打成jar放到需要引用工程的libs下面(这里是将PluginImpl打成jar,放到HostProject工程的libs中)这种方式是Eclipse推荐使用的,当我们在建立一个项目的时候也会自动产生这个文件夹,当我们将我们需要引用的工程打成jar,然后放到这个文件夹之后,Eclipse就自动导入了(这个功能是Eclipse3.7之后有的)。第二种:和第一种的区别是,我们可以从新新建一个文件夹比如是lib,然后将引用的jar放到这个文件夹中,但是此时Eclipse是不会自动导入的,需要我们手动的导入(add build path...),但是这个是一个区别,还有一个区别,也是到这个这个报错原因的区别,就是libs文件夹中的jar,在运行的时候是会将这个jar集成到程序中的,而我们新建的文件夹(名字非libs即可),及时我们手动的导入,编译是没有问题的,但是运行的时候,是不会将jar集成到程序中。第三种:和前两种的区别是不需要将引用工程打成jar,直接引用这个工程这种方式其实效果和第一种差不多,唯一的区别就是不需要打成jar,但是运行的时候是不会将引用工程集成到程序中的。第四种:和第三种的方式是一样的,也是不需要将引用工程打成jar,直接引用工程:这个前提是需要设置PluginImpl项目为Library,同时引用的项目和被引用的项目必须在一个工作空间中,不然会报错,这种的效果和第二种是一样的,在运行的时候是会将引用工程集成到程序中的。第五种:和第一种、第二种差不多,导入jar:这里有很多种方式选择jar的位置,但是这些操作的效果和第一种是一样的,运行的时候是不会将引用的jar集成到程序中的。总结上面的五种方式,我们可以看到,第二种和第四种的效果是一样的,也是最普遍的导入引用工程的方式,因为其他三种方式的话,其实在编译的时候是不会有问题的,但是在运行的时候会报错(找不到指定的类,可以依次尝试一下),不过这三种方式只要一步就可以和那两种方式实现的效果一样了只要设置导出的时候勾选上这个jar就可以了。那么其实这五种方式都是可以的,性质和效果是一样的。说完了Eclipse中引用工程的各种方式以及区别之后,我们在回过头来看一下,上面遇到的问题:Could not find class...其实这个问题就简单了,原因是:插件工程PluginSDKs使用的是lib文件夹导入的jar(这个jar是不会集成到程序中的),而宿主工程PluginDemos的引用工程的方式也变成了lib文件夹(jar也是不会集成到程序中的)。那么程序运行的时候就会出现错误:Could not find class "com.pluginsdk.interfaces.IBean' 问题二:Class ref in pre-verified class resolved to unexpected implementation(相同的类加载了两次)这个问题产生的操作:插件工程PluginSDKs和宿主工程PluginDemos引用工程的方式都变成library(或者是都用libs文件夹导入jar)这个错误的原因也是很多做插件的开发者第一次都会遇到的问题,其实这个问题的本质是PluginImpl中的接口被加载了两次,因为插件工程和宿主工程在运行的时候都会把PluginImpl集成到程序中。对于这个问题,我们来分析一下,首先对于宿主apk,他的类加载器是PathClassLoader(这个对于每个应用来说是默认的加载器,原因很简单,PathClassLoader只能加载/data/app目录下的apk,就是已经安装的apk,一般我们的apk都是安装之后在运行,所以用这个加载器也是理所当然的)。这个加载器开始加载插件接口工程(宿主工程中引入的PluginImpl)中的IBean。当使用DexClassLoader加载PluginSDKs.apk的时候,首先会让宿主apk的PathClassLoader加载器去加载,这个好多人有点迷糊了,为什么会先让PathClassLoader加载器去加载呢?这个就是Java中的类加载机制的双亲委派机制:http://blog.csdn.net/jiangwei/article/details/Android中的加载机制也是类似的,我们这里的代码设置了DexClassLoader的父加载器为当前类加载器(宿主apk的PathClassLoader),不行的话,可以打印一下getClassLoader()方法的返回结果看一下。classLoader = new DexClassLoader(dexpath, fileRelease.getAbsolutePath(),null,getClassLoader());那么加载器就是一样的了(宿主apk的PathClassLoader),那么就奇怪了,都是一个为什么还有错误呢?查看系统源码可以了解:Resolve.c源码(这个是在dalvik中的):源码下载地址为:http://blog.csdn.net/jiangwei/article/details/我们来看一下他的一个主要函数:/*
* Find the class corresponding to "classIdx", which maps to a class name
It might be in the same DEX file as "referrer", in a different
* DEX file, generated by a class loader, or generated by the VM (e.g.
* array classes).
* Because the DexTypeId is associated with the referring class' DEX file,
* we may have to resolve the same class more than once if it's referred
* to from classes in multiple DEX files.
This is a necessary property for
* DEX files associated with different class loaders.
* We cache a copy of the lookup in the DexFile's "resolved class" table,
* so future references to "classIdx" are faster.
* Note that "referrer" may be in the process of being linked.
* Traditional VMs might do access checks here, but in Dalvik the class
* "constant pool" is shared between all classes in the DEX file.
* on the verifier to do the checks for us.
* Does not initialize the class.
* "fromUnverifiedConstant" should only be set if this call is the direct
* result of executing a "const-class" or "instance-of" instruction, which
* use class constants not resolved by the bytecode verifier.
* Returns NULL with an exception raised on failure.
ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx,
bool fromUnverifiedConstant)
DvmDex* pDvmDex = referrer->pDvmD
ClassObject* resC
const char* classN
* Check the table first -- this gets called from the other "resolve"
* methods.
resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
if (resClass != NULL)
return resC
LOGVV("--- resolving class %u (referrer=%s cl=%p)\n",
classIdx, referrer->descriptor, referrer->classLoader);
* Class hasn't been loaded yet, or is in the process of being loaded
* and initialized now.
Try to get a copy.
If we find one, put the
* pointer in the DexTypeId.
There isn't a race condition here --
* 32-bit writes are guaranteed atomic on all target platforms.
* case we have two threads storing the same value.
* If this is an array class, we'll generate it here.
className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
if (className[0] != '\0' && className[1] == '\0') {
/* primitive type */
resClass = dvmFindPrimitiveClass(className[0]);
resClass = dvmFindClassNoInit(className, referrer->classLoader);
if (resClass != NULL) {
* If the referrer was pre-verified, the resolved class must come
* from the same DEX or from a bootstrap class.
The pre-verifier
* makes assumptions that could be invalidated by a wacky class
(See the notes at the top of oo/Class.c.)
* The verifier does *not* fail a class for using a const-class
* or instance-of instruction referring to an unresolveable class,
* because the result of the instruction is simply a Class object
* or boolean -- there's no need to resolve the class object during
* verification.
Instance field and virtual method accesses can
* break dangerously if we get the wrong class, but const-class and
* instance-of are only interesting at execution time.
* we got here as part of executing one of the "unverified class"
* instructions, we skip the additional check.
* Ditto for class references from annotations and exception
* handler lists.
if (!fromUnverifiedConstant &&
IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED))
ClassObject* resClassCheck = resC
if (dvmIsArrayClass(resClassCheck))
resClassCheck = resClassCheck->elementC
if (referrer->pDvmDex != resClassCheck->pDvmDex &&
resClassCheck->classLoader != NULL)
LOGW("Class resolved by unexpected DEX:"
" %s(%p):%p ref [%s] %s(%p):%p\n",
referrer->descriptor, referrer->classLoader,
referrer->pDvmDex,
resClass->descriptor, resClassCheck->descriptor,
resClassCheck->classLoader, resClassCheck->pDvmDex);
LOGW("(%s had used a different %s during pre-verification)\n",
referrer->descriptor, resClass->descriptor);
dvmThrowException("Ljava/lang/IllegalAccessE",
"Class ref in pre-verified class resolved to unexpected "
"implementation");
return NULL;
LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d\n",
resClass->descriptor, referrer->descriptor, referrer->pDvmDex,
referrer->classLoader, classIdx);
* Add what we found to the list so we can skip the class search
* next time through.
* TODO: should we be doing this when fromUnverifiedConstant==true?
* (see comments at top of oo/Class.c)
dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
/* not found, exception should be raised */
LOGVV("Class not found: %s\n",
dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
assert(dvmCheckException(dvmThreadSelf()));
return resC
}我们看下面的判断可以得到,就是在这里抛出的异常,代码逻辑我们就不看了,因为太多的头文件相互引用,看起来很费劲,直接看一下函数的说明:红色部分内容,他的意思是我们需要解决从不同的dex文件中加载相同的class,需要使用不同的类加载器。说白了就是,同一个类加载器从不同的dex文件中加载相同的class。所以上面是同一个类加载器PathClassLoader去加载(宿主apk和插件apk)来自不同的dex中的相同的类IBean。所以我们在做动态加载的时候都说过:不要把接口的jar一起打包成jar/dex/apk问题三:Connot be cast to....(类型转化异常)这个问题产生的操作:插件工程PluginSDKs和宿主工程都是用Library方式引用工程(或者是libs),同时将上面的一行代码classLoader = new DexClassLoader(dexpath, fileRelease.getAbsolutePath(),null,getClassLoader());修改成:classLoader = new DexClassLoader(dexpath, fileRelease.getAbsolutePath(),null,ClassLoader.getSystemClassLoader());就是将DexClassLoader的父加载器修改了一下:我们知道getClassLoader()获取到的是应用的默认加载器PathClassLoader,而ClassLoader.getSystemClassLoader()是获取系统类加载器,这样修改之后会出现这样的错误的原因是:插件工程和宿主工程都集成了PluginImpl,所以DexClassLoader在加载Bean的时候,首先会让ClassLoader.getSystemClassLoader()类加载器(DexClassLoader的父加载器)去查找,因为Bean是实现了IBean接口,这时候ClassLoader.getSystemClassLoader就会从插件工程的apk中查找这个接口,结果没找到,没找到的话就让DexClassLoader去找,结果在PluginSDKs.apk中找到了,就加载进来,同时宿主工程中也集成了插件接口PluginImpl,他使用PathClassLoader去宿主工程中去查找,结果也是查找到了,也加载进来了,但是在进行类型转化的时候出现了错误:IBean bean = (IBean)beanO原因说白了就是:同一个类,用不同的类加载器进行加载产生出来的对象是不同的,不能进行相互赋值,负责就会出现转化异常。总结上面就说到了一些开发插件的过程中会遇到的一些问题,当我们知道这些问题之后,解决方法自然就会有了,1) 为了避免Could not find class...,我们必须要集成PluginImpl,方式是使用Library或者是libs文件夹导入jar(这里要注意,因为我们运行的其实是宿主工程apk,所以宿主工程一定要集成PluginImpl,如果他不集成的话,即使插件工程apk集成了也还是没有效果的)2) 为了避免Class ref in pre-verified class resolved to unexpected implementation,我们在宿主工程和插件工程中只能集成一份PluginImpl,在结合上面的错误避免方式,可以得到正确的方式:一定是宿主工程集成PluginImpl,插件工程一定不能集成PluginImpl。(以后再制作插件的时候记住一句话就可以了,插件工程打包不能集成接口jar,宿主工程打包一定要集成接口jar)关于第三个问题,其实在开发的过程中一般不会碰到,这里说一下主要是为了马上介绍Android中的类加载器的相关只是来做铺垫的(PS:问题都解决了,后续就要介绍插件的制作了~~)
红黑联盟&版权所有
Copyright&& 2017
All rights reserved.}

我要回帖

更多关于 如懿传什么时候播出 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信