https://gravatar.cat.net/avatar/08cf681fb7c6ad1b4fe70a8269c2103c?s=96&d=mp&r=g

LengYue's Blog

0.0

给LengYue's Blog打分

start start start start start

感谢您打了2

详情

全站展示位

Android声明式UI框架 Litho 初探 ——两种数据类型 |

Litho中包含的的两种数据类型 Litho的两种属性分别是: 不可变属性称为Props 可变属性称为State 不可变属性Props 定义和使用props Props属性:Component中使用@Prop注解的参数集合,具有单向性和不可变性,可以在左右的方法中访问它的指。在同一个Component中我们可以定义和访问相同的prop 下面这个例子,定义了两个Prop,一个string类型text,一个int类型index,text的注解中optional = true表示它是一个可选参数。 当Component的生命周期方法被调用的时候,@Prop参数会保存component创建时从它们的父级传递过来的值(或者它们的默认值) 设置props prop参数其实在前几篇文章中都有使用过,用起来也没有什么特别的地方,这里不在赘述,制作一个简单的说明。 Component中的prop参数会在编译时候自动加入到Builder中,以上面的代码举例: PropComponent.create(c).index(10)./*text("测试文本").*/build() Prop的默认值 对于可选的Prop如果不设置值,就是java的默认值。或者你也可以使用@PropDefault注解然后添加默认值。 如果你使用Kotlin,那还需要加上@JvmFiel把该字段编辑为public才行。 @MountSpec object PropComponentSpec { @JvmField @PropDefault val prop1 = "default" @JvmField @PropDefault val prop2 = -1 资源类型 在Android开发中,我们经常会限定参数的类型。比如: fun doSomething(@ColorInt color: Int, @StringRes str: Int, @DimenRes width: Int){} 在Compontent的Prop中也有类似的操作,具体看代码: fun onMount( c: ComponentContext, textView: TextView, @Prop(optional = true,resType = ResType.STRING) text: String?。。。

推荐

Android清除本地数据缓存代码 |

/* 文 件 名: DataCleanManager.java 描 述: 主要功能有清除内/外缓存,清除数据库,清除sharedPreference,清除files和清 除自定义目录 */

在AndroidManifest.xml文件中的android:windowSoftInputMode属性使用| keyboard,squeezing,layout |

The AndroidManifest.xml File <activity android:windowSoftInputMode=["stateUnspecified", "stateUnchanged", "stateHidden", "stateAlwaysHidden", "stateVisible", "stateAlwaysVisible", "adjustUnspecified", "adjustResize", "adjustPan"] …… > </activity>

搭建为知笔记Docker版,以及注意事项 |

好久没有关注为知笔记了,最近考虑迁移笔记的时候,看了一下各大笔记应用,开源笔记要么不支持全平台,要么客户端完善度或者同步问题不行。突然发现为知笔记支持docker版,可以自己部署,客户端还是使用官方客户端。 安装docker docker version > /dev/null || curl -fsSL get.docker.com | bash service docker restart 启动为知笔记docker 创建文件夹,跟官方保持一致,我们也在主目录创建文件 cd ~ mkdir wizdata 启动,如果你打算使用nginx,或者配置ssl,则需要把端口调整一下。比如我的6789 docker run --name wiz --restart=always -it -d -v ~/wizdata:/wiz/storage -v /etc/localtime:/etc/localtime -p 80:80 -p 9269:9269/udp wiznote/wizserver 接下来会自动下载并运行,喝杯咖啡,等一下就可以了。 或者,可以在这个时候配置Nginx 配置Nginx 开启SSL ssl_certificate XXX/fullchain.pem; #你的证书地址 ssl_certificate_key XXX/privkey.pem; #你的证书地址 ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5:!EXP; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; error_page 497 配置云存储 默认笔记是保存在本地的,你可以使用本地+云同步的方式,或者像我一样直接使用云储存, 配置阿里OSS

IOS的专利?Android也能流畅实现毛玻璃效果效果 |

背景介绍 上图就是我们在IOS设备上经常能够见到的毛玻璃(高斯模糊)效果。不得不说,这种效果在适合的场景下使用,能够获得绝佳的美感。但是鉴于Android设备性能和兼容性问题,我们通常很难在Android设备上见到这种效果。 但这并不是IOS的专利效果,Android也能轻松流畅的实现。本篇文章将会详细的讲解如何实现。 Android中的高斯模糊 我为什么选择RenderScript实现高斯模糊 目前Android设备上实现高斯模糊效果的方式通常有: 云端处理,移动客户端直接从网络获取处理好的图片。这种方式局限性很大。 FastBlur等开源库。这种方式兼容性不错,但是效率极低。 c实现。不懂c的理解困难。 OpenGL实现。效果很好,但电量和内存消耗比较高。 RenderScript实现。效果略弱于第4种,但是使用方便,速度很快,性能消耗在可接受范围内,加上Google的兼容性解决方案,可以说是能够作为优先考虑的方式。 RenderScript RenderScript主要在android中的对图形进行处理,RenderScript采用C99语法进行编写,主要优势在于性能较高。在Api11的时候被加入到Android中。同时,Google提供了android.support.v8.renderscript兼容包,能够实现更低版本的兼容。 RenderScript提供了一个用于实现高斯模糊的封装类ScriptIntrinsicBlur ,这货在Api17才被收编Android所以在不使用兼容包的情况下只能兼容到4.2的设备。但是,我们有兼容包啊向下兼容不是梦。 准备阶段 ###引入兼容包 方法很简单,只需在build.gradle中加入: defaultConfig { 。 。 。 //就是这么简单 renderscriptTargetApi 19 renderscriptSupportModeEnabled true } 你以为这样就好了?nonono。 由于一些坑人的厂商会深度定制Android系统,所以一些必要的依赖文件会被它们直接去掉!!这导致一些型号的设备上调用RenderScriptd的部分方法时会报错。所以我们得加上这些可能丢失的文件。 其实也简单,打开android_sdk/build-tools/选择19以上版本/renderscript/lib/packaged我们可以看见3个包含.os文件的文件夹。 直接复制这三个文件加到项目工程的jniLibs 包下。什么?找不见jniLibs包?自己建一个喽。 注意,这时候,我们很可能遇到一个崩溃,找不到.os文件。莫慌莫慌… 在build.gradle的android{}中加入: sourceSets { main { jniLibs.srcDirs = ['libs'] } } 没完没了!最后一步只针对使用的混淆的同学,需要在混淆中加入: -keep class android.support.v8.renderscript.** { *; } 实现高斯模糊 终于可以开始写代码了。先来看看效果。下图高斯模糊半径逐渐增大的效果,请忽略渣渣录屏效果 将ScriptIntrinsicBlur封装成工具类。咱们代码里接着款 import android.support.v8.renderscript.*; //这句很重要啊,v8包的,不然不能向下兼容啊。 public class RenderScriptGaussianBlur { private RenderScript rs; public RenderScriptGaussianBlur(Context context) { // 创建RenderScript内核对象 this.

心灵鸡汤

小故事、机会

  A在合资公司做白领,觉得自己满腔抱负没有得到上级的赏识,经常想:如果有一天能见到老总,有机会展示一下自己的才干就好了!!

  A的同事B,也有同样的想法,他更进一步,去打听老总上下班的时间,算好他大概会在何时进电梯,他也在这个时候去坐电梯,希望能遇到老总,有机会可以打个招呼。

  他们的同事C更进一步。他详细了解老总的奋斗历程,弄清老总毕业的学校,人际风格,关心的问题,精心设计了几句简单却有份量的开场白,在算好的时间去乘坐电梯,跟老总打过几次招呼后,终于有一天跟老总长谈了一次,不久就争取到了更好的职位。

  心得:愚者错失机会,智者善抓机会,成功者创造机会。机会只给准备好的人,这准备二字,并非说说而已。

列表展示

主站展示位

GetX Router 设置返回值 |

通过别名导航: var result = await Get.toNamed(Routes.WEB_VIEW, arguments: { "url": item?.link ?? "", "index": index, "collect": item?.collect ?? false, }); 返回值: Get.back(result: {"collect": collect.value}); 完整代码: () async { /// 导航到新的界面 var result = await Get.toNamed(Routes.WEB_VIEW, arguments: { "url": item?.link ?? "", "index": index, "collect": item?.collect ?? false, }); /// 接收返回值 bool collect = result["collect"]; }

RecyclerView里notifyItemRemoved的坑 |

RecyclerView很多时候是展示静态的数据,并不会有删除的操作,讲到RecyclerView时,会提到它提供了一个很好的展现删除操作动画的函数,代码片段一般是这样的 @Override public void onBindViewHolder(final CommonViewHolder holder, final int position) { holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { contentList.remove(position); notifyItemRemoved(position); } }); } 这样写的话,很快会产生数据删除错乱和超出索引异常导致崩溃。 原因是函数里面的传入的参数position,它是在进行onBind操作时确定的,在删除单项后,已经出现在画面里的项不会再有调用onBind机会,这样它保留的position一直是未进行删除操作前的postion值。 对于尚未进入画面的单项来说,它会使用新的position值,这个值是正确的,如果在单项里加上下面的代码 holder.textView.setText(contentList.get(position) + "#" + String.valueOf(position)); 在删除第一屏的一项后,向上滚动,会发现新滚上来的一行和它上面的一行的textview显示是一样的。 解决办法也很简单 先remove,再notifyItemRemoved, 最后再notifyItemRangeChanged remove:把数据从list中remove掉, notifyItemRemoved:显示动画效果 notifyItemRangeChanged:对于被删掉的位置及其后range大小范围内的view进行重新onBindViewHolder

Flutter |

琥珀之月,空谷之兰,皆是孤芳,既是孤芳,但求自赏便罢!

Flutter中嵌入Native组件 |

Flutter官方提供的控件AndroidView、UiKitView就是一种比较优雅的解决方案了。这里做了一个简单的嵌入TextView的demo(使用这种方式会增加性能上的开销,应该尽量避免使用) 使用方式 native端 跟MethodChannel的使用方法类似,在native侧,我们实现一个PlatformViewFactory(iOS是FlutterPlatformViewFactory),在create方法中,使用平台方法创建View返回。 override fun create(context: Context?, i: Int, any: Any?): PlatformView { return object : PlatformView { override fun getView(): View { val text = TextView(context) text.layoutParams = ViewGroup.LayoutParams(SizeUtils.dp2px(200f), SizeUtils.dp2px(200f)) text.apply { setText("Android View") setTextColor(Color.BLUE) setBackgroundColor(Color.RED) } return text } override fun dispose() { } } } func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?

利用Android源码,轻松实现汉字转拼音功能 |

今天和大家分享一个从Android系统源代码提取出来的汉字转成拼音实现方案,只要一个类,560多行代码就可以让你轻松实现汉字转成拼音的功能,且无需其他任何第三方依赖。

已经有女朋友了,又遇到更喜欢的对象怎么办? |

在知乎上开到这个问题,我只能说提问题的人,你还搞不懂“喜欢一个人”和“对一个人有感情”之间的区别。 我举两个不恰当的例子。比如说养狗,虚构两个情景。 情景一:  某天你去宠物市场闲逛,发现有一只泰迪很可爱,伶俐活泼聪明漂亮,还特别粘着你。你当时就动心了,打算买下它。  一问价格,要1500块钱。你身上没带这么多钱,也没带银行卡。当时是下午了,宠物市场马上要关门了。你只能第二天来买了。  于是你嘱咐店主千万不要卖给别人。一定要等你明天早上过来卖给你。店主答应了。 可你回家了还是不放心。晚上在床上都睡不着觉。生怕店主不守信用,把这狗狗卖给别人了。那感觉,就像是爱上了一个姑娘一样。  第二天一早,你就揣了1500块钱奔向宠物市场。  这次,你是从市场的另外一个门进去的,还没有走到昨天那个摊位,你突然发现了另外一只泰迪,比昨天那只更漂亮,更聪明,更可爱,更粘你,价格还更便宜,只要1200元。  我相信,只要你智商正常,你都会选择买1200的这一只。虽然,你为了1500的那一只牵挂了一晚上。但那又如何,遇上更好的,你立刻就移情别恋了。 我们都是这样的人。 因为,我们对那只泰迪,没有感情。 情景二:  某雨天你下班回家,看到一只流浪的小狗趴在你的家门前瑟瑟发抖。你一下子恻隐之心上来了,把它抱回家,给它洗澡,给它喂食,让它睡在毛毯子上。  从此,它就成了你的家人。  接下来的一年,每天早上是它叫你起床。每天你疲惫的下班回到家中,是它在门口等着你。你出去逛街,它走在你前面。有小偷偷你的包,它疯狂吠叫。你看电视,它就睡在你腿上。你上网,它就趴在你脚尖。 它已经成为了你生活的一部分。  一年后。有一个人,也不知道脑子怎么进水了,想要拿一只名贵的纯种狗狗来换你这只不值钱的收养的流浪狗。 你会换吗? 如果是我,当然不换。 因为我对它有感情了。 这就是感情。

用组合代替继承能为 Activity 带来什么 |

原文链接 : Composition over Inheritance,What it means for your Activities 原文作者 : Josh Brown 译者 : chaossss 校对者: Mr.Simple 状态 : 完成  事实上我们在很多 Java 进阶书籍上看到过“开发时应该更倾向于选择组合而不是继承”的建议,为什么建议我们更倾向于而不是完全代替呢,因为当类 A 能完全代替另一个类 B(我们想让 B 成为 A 的父类)时,我们就应该使用继承,如果 A 仅仅是和 B 有着某些共同的行为,是不应该使用继承的(更多的讨论戳我)。然而,在我阅读别人的源码时,滥用继承的情况实在是太多了,多少人创建了一个 BaseActivity 后,就让所有 Activity 继承它,在子 Activity 中实现业务逻辑。  而且这样做问题大大的有,最鲜活的例子就是:Joe Newguy 加入到我们组,并实现了 ShinyFeatureActivity,那会组里没有任何规定强迫他必须让 ShinyFeatureActivity 继承于 BaseActivity,但他还是这么干了……万幸我们在 Code Review 时发现了这个问题。此外,如果每一个 Activity 都继承于 BaseActivity,在某些情况下,你可能要继承的是其他 Activity(例如:PreferenceActivity、ListActivity)。尽管大部分 Activity 的子类都有相应的 Fragment 代替,但还是有一部分是没有对应 Fragment 的,某些库仍然需要相应的 Activity 子类。  有些更潜在的问题是:有时候某些 Activity 需要这些行为,其他的 Activity 需要其他行为,而 Java 并不支持多继承,这就意味着我们得将行为有交集的 Activity 的所有行为放到一个独立的类里面。但这样做会降低可维护性,甚至带来性能影响。

不使用第三方软件实现Obsidian多平台实时同步 |

这篇文章涉及服务器配置、docker 技术,当然都是很初级的使用 相信很多人跟我一样,不喜欢使用第三方软件来同步 Obsidian 。每次要打开两个软件,很麻烦。这种情况在手机上最为明显。 这也是我为什么从 9 月多就接触了 Obsidian 但是一没有最为主力的原因。虽然印象笔记已经非常的不好用,但是他的同步真的非常的方便。 直到最近在翻看 Obsidian 的插件的时候接触到了这个插件 Self-hosted LiveSync(虽然也有一款支持 WebDav 的插件,但是试了一下连不上服务器,作者说目前 WebDav 还在测试中)这个插件真的非常的棒 👍🏻。实现了无感同步,甚至可以多平台实时同步。 引用一张作者的图: 搭建服务器端 Self-hosted LiveSync 使用的是CouchDB数据库,这是一个开源的具有版本控制的文档数据库。 你可使用 IBM 提供的 CouchDB 数据库,这里有作者写的教程 我们这里还是直接自己搭建,我是在家里的群辉 NAS 上搭建的,如果你没有 NAS 也可以在腾讯云、阿里云等云服务上搭建。优先推荐在 NAS 上搭建。 安装 docker 群辉直接再套件商店安装。云服务器用户使用下面的命令 docker version > /dev/null || curl -fsSL get.docker.com | bash service docker restart 安装数据库 首先创建配置文件 local.ini [couchdb] single_node=true [c

Android 开发绕不过的坑:你的 Bitmap 究竟占多大内存? |

!!! 原文出处:  0、写在前面本文涉及到屏幕密度的讨论,这里先要搞清楚 DisplayMetrics 的两个变量,摘录官方文档的解释: density:The logical density of the display. This is a scaling factor for the Density Independent Pixel unit, where one DIP is one pixel on an approximately 160 dpi screen (for example a 240x320, 1.5”x2” screen), providing the baseline of the system’s display. Thus on a 160dpi screen this density value will be 1; on a 120 dpi screen it would be .75; etc. This value does not exactly follow the real screen size (as given by xdpi and ydpi, but rather is used to scale the size of the overall UI in steps based on gross changes in the display dpi.

未命名 |

她是江南一带有名的妓 他是战功显赫的将军 前几日他失去妻子 在歌妓面前喝的烂醉 把妓当成她 第二天看到床上的落红 将军嘲笑妓 终究是婊子 他穿衣离去 妓九岁被卖到青楼 几年来只卖艺不卖身 直到那晚遇到他 她羞涩 笨拙 把自己给了他 他凶狠 粗鲁 嘴里叫着妻子的名字 妓只喜欢了这一个人 却喜欢了一辈子 将军在外战沙场 歌姬城内待他归 将军入城 从不多看一眼 只是在深夜敲开她的房门 他在外时日久 她抱着他褪下他的衣服 细数他身上的疤痕 他抱她上床 没有情话 只有缠绵 她轻触他疤痕 你娶我吧 我想给你一个家 将军穿好衣服起身离去 留她一人愣愣的 看他离去的方向 做妓的最怕有了感情 可她偏偏中了这世间最愁的毒 妓说 我不想再做这行了 那夜她主动寻他 月光下她红色的唇格外迷人 那夜她用尽万种风情 将军你吻我 将军毫不动意 一夜缠绵 她离去 妓知道将军不会寻她 毕竟她只是个妓 一日为妓 终身为娼 不久将军带兵出征 两军交战 在敌方的主帅身旁发现一身红衣的她 她一身红衣依靠在另一个男人怀里 将军满眼尽是讽刺 战争的号角吹响 她藏在袖中的匕首直刺敌将心脏 敌将并不惊讶 像是早已预料 将军愣住 这是她第一次看到他除了冷漠意外的表情 战火硝烟 她闭上双眼 妓知道 他安全了 从此以后他都安全了 她可以放心了 也总算帮了他一次

FastJson简单使用 |

在工作中,经常客服端需要和服务端进行通信,目前很多项目都采用JSON的方式进行数据传输,简单的参数可以通过手动拼接JSON字符串,但如果请求的参数过多,采用手动拼接JSON字符串,出错率就非常大了。并且工作效率也特别低。 我在网上看了一些开源的JSON框架,比如Google提供的Gson,Jackson,FastJson等框架。 经过测试,个人觉得FastJson执行效率比较高,而且简单易用。 FastJson不依赖于第三方包, 直接可以运行在Java JDK1.5之上,FastJson完全支持 一个JSON库涉及的最基本功能就是序列化和反序列化。Fastjson支持java bean的直接序列化。使用com.alibaba.fastjson.JSON这个类进行序列化和反序列化。 简单的序列化 pubic class UserInfo implements Serializable{ private String name; private int age; public void setName(String name){ this.name=name; } public String getName(){ return name; } public void setAge(int age){ this.age=age; } public int getAge(){ return age; } } public class TestOne{ public static void main(String[] args){ UserInfo info=new UserInfo(); info.

android 10 [API 29]适配指南 |

Android 11[API 30]已经发布正式版,来看看全新的 android 11 [api 30] 适配指南 非SDK接口 google 针对非SDK接口的限制 非SDK接口检测工具 官方给出了一个检测工具,下载地址:veridex veridex使用方法: appcompat.sh --dex-file=apk.apk blacklist、greylist、greylist-max-o、greylist-max-p含义 以上截图中,blacklist、greylist、greylist-max-o、greylist-max-p含义如下: blacklist 黑名单:禁止使用的非SDK接口,运行时直接Crash(因此必须解决) greylist 灰名单:即当前版本仍能使用的非SDK接口,但在下一版本中可能变成被限制的非SDK接口 greylist-max-o: 在targetSDK<=O中能使用,但是在targetSDK>=P中被禁止使用的非SDK接口 greylist-max-p: 在targetSDK<=P中能使用,但是在targetSDK>=Q中被禁止使用的非SDK接口 设备ID 从Android 10开始已经无法完全标识一个设备,曾经用mac地址、IMEI等设备信息标识设备的方法,从Android 10开始统统失效。而且无论你的APP是否是配过Android 10。 IMEI等设备信息 从Android10开始普通应用不再允许请求权限android.permission.READ_PHONE_STATE。而且,无论你的App是否适配过Android Q(既targetSdkVersion是否大于等于29),均无法再获取到设备IMEI等设备信息。 受影响的API Build.getSerial(); TelephonyManager.getImei(); TelephonyManager.getMeid(); TelephonyManager.getDeviceId(); TelephonyManager.getSubscriberId(); TelephonyManager.getSimSerialNumber(); targetSdkVersion<29 的应用,其在获取设备ID时,会直接返回null targetSdkVersion>=29 的应用,其在获取设备ID时,会直接跑出异常SecurityException 如果您的App希望在Android 10以下的设备中仍然获取设备IMEI等信息,可按以下方式进行适配: <uses-permission android:name="android.permission.READ_PHONE_STATE" android:maxSdkVersion="28" /> Mac地址随机分配 从Android10开始,默认情况下,在搭载 Android 10 或更高版本的设备上,系统会传输随机分配的 MAC 地址。(既从Android 10开始,普通应用已经无法获取设备的真正mac地址,标识设备已经无法使用mac地址) 唯一ID的替代 Google给出的解决方案是:如果您的应用有 追踪非登录用户重装 的需求,可用ANDROID_ID来标识设备。 ANDROID_ID的生成规则为:签名+设备信息+设备用户 ANDROID_ID重置规则:设备恢复出厂设置时,ANDROID_ID将被重置 String androidId = Settings.

Connection with adb was interrupted.解决办法 |

[2012-10-16 10:49:35 – My First App] [2012-10-16 10:49:35 – My First App] Android Launch! [2012-10-16 10:49:35 – My First App] Connection with adb was interrupted. [2012-10-16 10:49:35 – My First App] 0 attempts have been made to reconnect. [2012-10-16 10:49:35 – My First App] You may want to manually restart adb from the Devices view.

Android声明式UI框架 Litho 初探 ——两种数据类型 |

Litho中包含的的两种数据类型 Litho的两种属性分别是: 不可变属性称为Props 可变属性称为State 不可变属性Props 定义和使用props Props属性:Component中使用@Prop注解的参数集合,具有单向性和不可变性,可以在左右的方法中访问它的指。在同一个Component中我们可以定义和访问相同的prop 下面这个例子,定义了两个Prop,一个string类型text,一个int类型index,text的注解中optional = true表示它是一个可选参数。 当Component的生命周期方法被调用的时候,@Prop参数会保存component创建时从它们的父级传递过来的值(或者它们的默认值) 设置props prop参数其实在前几篇文章中都有使用过,用起来也没有什么特别的地方,这里不在赘述,制作一个简单的说明。 Component中的prop参数会在编译时候自动加入到Builder中,以上面的代码举例: PropComponent.create(c).index(10)./*text("测试文本").*/build() Prop的默认值 对于可选的Prop如果不设置值,就是java的默认值。或者你也可以使用@PropDefault注解然后添加默认值。 如果你使用Kotlin,那还需要加上@JvmFiel把该字段编辑为public才行。 @MountSpec object PropComponentSpec { @JvmField @PropDefault val prop1 = "default" @JvmField @PropDefault val prop2 = -1 资源类型 在Android开发中,我们经常会限定参数的类型。比如: fun doSomething(@ColorInt color: Int, @StringRes str: Int, @DimenRes width: Int){} 在Compontent的Prop中也有类似的操作,具体看代码: fun onMount( c: ComponentContext, textView: TextView, @Prop(optional = true,resType = ResType.STRING) text: String?

Android-使用@AutoService实现spi |

什么是 SPI? Java SPI 全称 Service Provider Interface,是 Java 提供的一套用来被第三方实现或者扩展的 API,它可以用来启用框架扩展和替换组件。实际上是“基于接口的编程 + 策略模式 + 配置文件”组合实现的动态加载机制. 具体解释就是: 定义一个接口文件 写出多个该接口文件的实现 在 src/main/resources/ 下建立 /META-INF/services 目录, 新增一个以接口命名的文件 , 内容是要接口的实现类全路径 使用 ServiceLoader 类 来获取到这些实现的接口 示例 定义一个接口文件 - Book package com.apkdv.spi_test; public interface Book { String name(); } 实现两个接口 package com.apkdv.spi_test; public class Android implements Book { @Override public String name() { return "Android"; } } package com.

岁月如歌 |

琥珀之月,空谷之兰,皆是孤芳,既是孤芳,但求自赏便罢!

Flutter 基础 | Dart 语法 mixin |

假设有这样一种场景:小明和小方都是程序员。其中小方会跳舞,当然它们都会编程。 用面向对象的方法可以建模如下: 因为小明和小方都会写编程,为了复用这个行为,提取了超类 Programmer,它包含所有程序员共用的行为 code()。这样一来,Ming 和 Fang 就能复用编程行为,而不是各自重新实现一遍相同的逻辑。(继承复用了行为) 小慧是一个舞者,再用面向对象的方法建模如下: 这样的继承关系违反了 DRY 原则,即 Don’t repeat yourself. 因为小慧并未复用小方的跳舞行为,所以同样的跳舞逻辑出现了两次。 那把跳舞行为上提到它们公共的基类 Human 中,是不是就解决问题了?的确,但这不是强迫所有程序员都必须会跳舞吗。。。 那让小方同时继承 Programmer 和 Dancer 能解决问题吗?能!但多重继承容易出事情,比如 “Diamond Problem”: 假设 Human 类中有 eat() 方法,且 Programmer 和 Dancer 都重写了它,此时 Fang 会发生编译报错。因为它不知道自己的 eat() 方法该采用哪一个父类的实现。上面的类图就好像一个钻石的形状,所以称为Diamond problem。 Dart 禁用了多重继承,而是引入了mixin来解决这个问题。 mixin 是一个特殊的类,它的属性和行为可以被其他类复用,而且不需要通过继承。 语法 如果希望一组属性和行为能够复用于多个类,碰巧这些类不在一条继承链路上,此时就应该使用mixin: mixin DanceMixin { void dance() {} } 这是声明 mixin 的方式,几乎和声明 class 一模一样,就是把 class 换成 mixin 而已。 还可以通过 on 限定 mixin 适用范围:

Android全新支持库androidx |

如果使用最新版Android Studio 创建基于API28的项目,就会发现,原来的android.support.*全部变成了androidx.*??这是什么鬼?我们来看看Google的介绍 我们正在推出一种新的软件包结构,以便更清楚哪些软件包与Android操作系统捆绑在一起,并与您的应用程序的APK一起打包。展望未来,android.*软件包层次结构将保留给随操作系统提供的Android软件包; 其他包将在新的androidx.*包层次结构中发布。 正在重构现有包以使用新层次结构。历史文物 - 那些版本为27及更早版本,并打包为android.support.*- 将继续在Google Maven上提供; 但是,所有新开发都将发生在androidx.*从1.0.0开始版本化的新打包工件中。 有关所有旧类和构建工件的完整映射到新的,请参阅AndroidX重构页面。有关AndroidX重构的更多信息, 请参阅博客文章。 版本控制更改 新工件将遵循语义版本控制,并将独立更新,而不是一次更新。重组后,可以独立更新项目中的AndroidX库。这避免了将项目中的许多支持库模块从例如一次更新26.1.0到27.0.0所有支持库模块的问题。 新项目 如果使用androidx打包的依赖项创建新项目(而不是使用Android Studio工具重构现有项目),则新项目需要针对API级别28,并且您需要将以下行添加到您的gradle.properties文件中: android.useAndroidX=true android.enableJetifier=true 简而言之,support包会继续维护,但是所有新特性都会放到androidx中,如果想使用androidx,需要API为28(IDE应该也需要最新版本),同时gradle.properties中添加 android.useAndroidX=true android.enableJetifier=true,反之不想使用设置为false即可,需要注意的是即使依赖中不添加 implementation androidx.*相关的支持库,如果gradle.properties中设置为true 仍然使用androidx相关支持库,android.support相关引用会提示not found

博客寄语:

    从14年开始写博客,到现在再有2年就10年了。

实时播报:

博客号-学习成长

相信每一分耕耘都有每一分收获,致力帮助博客主所创作的博客能更快的被收录! 如果有其它需求,可联系下方邮箱。