<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://loli.xing.moe/feed.xml" rel="self" type="application/atom+xml" /><link href="https://loli.xing.moe/" rel="alternate" type="text/html" /><updated>2023-03-07T15:09:57+00:00</updated><id>https://loli.xing.moe/feed.xml</id><title type="html">Qixingchen</title><subtitle>星辰星辰星~</subtitle><entry><title type="html">基于 ChatGPT 的客服系统</title><link href="https://loli.xing.moe/ChatGPT_as_customer_service/" rel="alternate" type="text/html" title="基于 ChatGPT 的客服系统" /><published>2023-03-06T00:00:00+00:00</published><updated>2023-03-06T00:00:00+00:00</updated><id>https://loli.xing.moe/ChatGPT_as_customer_service</id><content type="html" xml:base="https://loli.xing.moe/ChatGPT_as_customer_service/">&lt;p&gt;基于 ChatGPT 的客服系统&lt;/p&gt;

&lt;p&gt;在 OpenAI 放出 gpt-3.5-turbo 模型之后,我们决定使用 AI 来加速目前我们重复率最高的工作:客服.&lt;/p&gt;

&lt;p&gt;不过考虑到我们的帮助手册及FAQ总字数已经超过30万字,而 gpt-3.5-turbo 每次对话最多只能支持 4096 个 Token (经过我们的测试,这大概是三千多个简中字),还需要考虑客户的问题,回答和系统配置占用的字符.所以我们需要一个办法来让 gpt-3.5-turbo 可以在 2000 字左右时了解我们的的帮助手册.&lt;/p&gt;

&lt;p&gt;在参考了 &lt;a href=&quot;Question_answering_using_embeddings&quot;&gt;OpenAI Cookbook:Question_answering_using_embeddings&lt;/a&gt; , &lt;a href=&quot;https://github.com/openai/openai-cookbook/blob/main/apps/web-crawl-q-and-a/web-qa.ipynb&quot;&gt;OpenAI Cookbook:web-crawl-q-and-a&lt;/a&gt; 以及 &lt;a href=&quot;https://github.com/mckaywrigley/paul-graham-gpt&quot;&gt;
paul-graham-gpt&lt;/a&gt; 之后,我们决定按照以下方法来实现:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;使用&lt;a href=&quot;https://www.yuque.com/yuque/developer&quot;&gt;语雀的 API&lt;/a&gt; 获取我们的帮助手册及FAQ&lt;/li&gt;
  &lt;li&gt;获得到每篇文章后,按照 markdown 的标题进行分割,每个小标题对应一块语料.并在数据库中记录对应的网址和锚点,方便后续输出时同步给出参考文档.&lt;/li&gt;
  &lt;li&gt;使用 &lt;a href=&quot;https://platform.openai.com/docs/guides/embeddings/embeddings&quot;&gt;openai embeddings&lt;/a&gt; 获取每块语料的向量表示.&lt;/li&gt;
  &lt;li&gt;对用户的提问也使用 openai embeddings 提取向量.&lt;/li&gt;
  &lt;li&gt;使用 余弦相似度 计算用户提问和每块语料的相似度,并返回相似度最高10块的语料.&lt;/li&gt;
  &lt;li&gt;对每块语料按照相似度从高到低的顺序 对 token 进行求和,获取小于 2000 时的语料列表.&lt;/li&gt;
  &lt;li&gt;按照以下格式给与 gpt-3.5-turbo 的 system role 输入.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; 你是 $$$ 系统的客服,请按照后续给予的使用手册回答用户的问题.
 使用手册内容如下:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;获取 gpt-3.5-turbo 的输出.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;目前处于安全考虑, gpt-3.5-turbo 的输出仅发送到客服汇总群,并由客服人员进行回复.同时给与客服相关语料的标题及链接,方便客服人员进行回复.&lt;/p&gt;</content><author><name></name></author><summary type="html">基于 ChatGPT 的客服系统</summary></entry><entry><title type="html">GitLab Runner for autoscaling with Docker Machine on Google preemptible VM</title><link href="https://loli.xing.moe/GitLab_Runner_for_autoscaling_with_Docker_Machine_on_Google_preemptible_VM/" rel="alternate" type="text/html" title="GitLab Runner for autoscaling with Docker Machine on Google preemptible VM" /><published>2018-04-12T00:00:00+00:00</published><updated>2018-04-12T00:00:00+00:00</updated><id>https://loli.xing.moe/GitLab_Runner_for_autoscaling_with_Docker_Machine_on_Google_preemptible_VM</id><content type="html" xml:base="https://loli.xing.moe/GitLab_Runner_for_autoscaling_with_Docker_Machine_on_Google_preemptible_VM/">&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;

&lt;h3 id=&quot;目前状态&quot;&gt;目前状态&lt;/h3&gt;

&lt;p&gt;目前我们的 runner 跑在 Aliyun 与 开发者实体机上,Aliyun 服务器无法向 Google Play 提交应用,而开发者实体机上运行时会阻碍其他程序运行.而且在临近发版时,由于 Runner 数量不足,会造成编译阻塞影响发版节奏.&lt;/p&gt;

&lt;h3 id=&quot;云服务&quot;&gt;云服务&lt;/h3&gt;

&lt;p&gt;由于编译 Android 应用需要内存不少,主流云服务商报价都不低,例如 digitalocean 一台 2vCPU 4G 内存的机器需要 $20/mo,Google Cloud 上 2vCPU 4G 内存 机器报价为 $12.96/mo,Aliyun ECS 计算型 c5 2vCPU 4G 内存 每年报价为 ￥2393.40(约 $31.76 每月)&lt;/p&gt;

&lt;h3 id=&quot;竞争实例与自伸缩&quot;&gt;竞争实例与自伸缩&lt;/h3&gt;

&lt;p&gt;大部分云服务商都对”竞争实例”或者”抢占式实例”有大幅优惠.&lt;br /&gt;
“竞争实例”的意义是,在服务商有机器设备空闲时,你才能申请到使用权,而且当服务器资源紧张时,你的设备会被强制收回.&lt;br /&gt;
比较适合能够分布式处理或者运行时长较小的服务运行.&lt;br /&gt;
GCE 的抢占式实例价格为正常实例的 20%,而且对于小于 10 分钟内就被 GCE 收回的时长不收取费用.&lt;/p&gt;

&lt;p&gt;Google 的文档上声称 即使在最繁忙的时段,还是有部分抢占式实例可供申请,只要你的需求可以使用这种随时会被关闭机器的服务来完成,选择抢占式实例能够节约大量成本.Google 的数据表明 每个项目每7天的平均抢占率在5％和15％之间变化.我在运行时只遇到过一次抢占.&lt;/p&gt;

&lt;p&gt;而 Gitlab 的 Runner 支持使用 Docker-Machine 来运行自伸缩实例,当有任务时在 GCE 上新建一个VM 机器,并在任务完成时删除机器.&lt;/p&gt;

&lt;h2 id=&quot;那么怎么才能弄一个自伸缩的-runner-呢&quot;&gt;那么怎么才能弄一个自伸缩的 runner 呢&lt;/h2&gt;

&lt;p&gt;首先呢 你要有一个用来管理扩展出来的runner的服务器,当然最好和runner在同一个内网.所以最好的解决方案就是直接在 GCE 上建一个实例啦.而且根据 GCE “始终免费” 政策,可以免费运行一个 f1-micro (1 vCPU 0.6G RAM 突发性能实例),完全足够作为管理服务器了.&lt;/p&gt;

&lt;p&gt;按照 https://docs.gitlab.com/runner/executors/docker_machine.html 的引导,在这个管理机上安装 “GitLab Runner for autoscaling with Docker Machine” 顺带可以把 代理容器注册服务和缓存服务 (“proxy container registry and cache server”)一起装在这个设备上.&lt;/p&gt;

&lt;p&gt;下面是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.toml&lt;/code&gt; (gitlab runner 配置文件)在使用 GCE 的示例&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;runners&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]]&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;name = &quot;gce-main-scal&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;url = &quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;token = &quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;executor = &quot;docker+machine&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;# 最大伸缩量&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;limit = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;runners.docker&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;tls_verify = &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;image = &quot;ubnutu:16.04&quot;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;privileged = &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;disable_cache = &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;volumes = [&quot;/cache&quot;]&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;shm_size = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# 缓存服务配置&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;runners.cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
     &lt;span class=&quot;s&quot;&gt;Type = &quot;s3&quot;&lt;/span&gt;
     &lt;span class=&quot;s&quot;&gt;ServerAddress = &quot;10.138.0.2:9005&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#your minio-instance&lt;/span&gt;
     &lt;span class=&quot;s&quot;&gt;AccessKey = &quot;&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# minio AccessKey&lt;/span&gt;
     &lt;span class=&quot;s&quot;&gt;SecretKey = &quot;&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# minio Secret&lt;/span&gt;
     &lt;span class=&quot;s&quot;&gt;BucketName = &quot;cache&quot;&lt;/span&gt;
     &lt;span class=&quot;s&quot;&gt;Insecure = &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
     &lt;span class=&quot;s&quot;&gt;Shared = &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;s&quot;&gt;[runners.machine]&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;# 配置见 https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-machine-section&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;IdleCount = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;IdleTime = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;120&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;MaxBuilds = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;MachineDriver = &quot;google&quot;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;MachineName = &quot;ci-as-w-%s&quot;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;MachineOptions = [&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;# docker machine 配置 见 https://docs.docker.com/machine/drivers/gce/&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;google-project=jwb-android&quot;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;google-zone=us-west1-c&quot;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;google-machine-type=custom-2-5120&quot;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;google-machine-image=ubuntu-os-cloud/global/images/ubuntu-1604-xenial-v20180405&quot;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;google-tags=gitlab-ci-runner-worker,project-noip&quot;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;# 抢占式实例&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;google-preemptible=true&quot;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;google-use-internal-ip=true&quot;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;# 代理容器注册服务&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;engine-registry-mirror=http://10.138.0.2:6000&quot;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后在这个管理机上&lt;a href=&quot;https://cloud.google.com/sdk/downloads&quot;&gt;安装 gcloud SDK&lt;/a&gt; 并登入有创建 VM 实例权限的账号.&lt;/p&gt;

&lt;p&gt;然后就要弄一个能编译所需项目的 docker 啦.嗯, Gitlab 的 autoscaling 只有 Docker Machine 模式,不能在创建出的 VM 上直接编译.如果不需要加入什么个性化的配置文件就能编译那就太好了,直接在 项目的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitlab-ci.yml&lt;/code&gt; 中的 image 上填入所需的镜像就可以开始了.&lt;br /&gt;
然而我的项目需要加入一些保密的配置文件才能运行,在对比了几家私有 docker image 托管服务商之后我还是选择了 Google Container Registry ,一是和要使用的服务器处在同一个内网,下载更快(大概吧),另一个是 GCR 只需要支付很廉价的储存价格和出站流量(内网并不需要233)价格就可以了,不像其他托管商按照镜像数量收费,基本都是 $10/mo 起.&lt;br /&gt;
按照 https://cloud.google.com/endpoints/docs/openapi/docker-image 推送了 docker 镜像,然后编译.结果遇到了 无权 pull  docker image 的错误.按照 https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#define-an-image-from-a-private-container-registry ,将 docker login 信息在 gitlab 项目页面的 CI  secret variable 配置中添加 DOCKER_AUTH_CONFIG 即可.(是的 我用了一天的时间试图在 创建出来的 VM 上登入有权限的 Gcloud 用户 然后才发现可以直接放入 docker login 信息)&lt;/p&gt;

&lt;h2 id=&quot;其他&quot;&gt;其他&lt;/h2&gt;

&lt;p&gt;如果在创建 Google VM 实例时申请了静态 IP 地址,记得在删除实例后去网络管理中把不需要的静态 IP 地址释放了.GCE 并不会在删除实例时自动释放,而且 IP 地址很贵(我的 75% 账单来自一个忘记释放的 IP 地址 ORZ )&lt;/p&gt;

&lt;p&gt;如果你需要在编译的 Docker 放什么全局配置文件,有个小提示, Docker 运行时的默认登入用户是 Root.&lt;/p&gt;

&lt;h2 id=&quot;结束&quot;&gt;结束&lt;/h2&gt;

&lt;p&gt;目前设定最多同时会有3个 runner 进行编译,目前平均每天相关费用不到 $0.05.&lt;br /&gt;
后续跟进下 Google container builder,每天提供了 120 分钟的免费基础编译时间,看看能不能与目前的开发模式相容.&lt;/p&gt;

&lt;h3 id=&quot;参考&quot;&gt;参考&lt;/h3&gt;

&lt;p&gt;gitlab 官方迁移到 GCP 相关 ISSUR:&lt;br /&gt;
https://gitlab.com/gitlab-org/gitlab-runner/issues/3023&lt;br /&gt;
https://gitlab.com/gitlab-com/infrastructure/issues/3131&lt;/p&gt;

&lt;p&gt;Gitlab 相关文档:&lt;br /&gt;
&lt;a href=&quot;https://docs.gitlab.com/runner/configuration/autoscale.html&quot;&gt;Runners autoscale configuration&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://docs.gitlab.com/runner/install/registry_and_cache_servers.html&quot;&gt;Install your own container registry and cache server&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://docs.gitlab.com/runner/executors/docker_machine.html&quot;&gt;Install and register GitLab Runner for autoscaling with Docker Machine&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#define-an-image-from-a-private-container-registry&quot;&gt;Define an image from a private Container Registry&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://gitlab.com/help/ci/docker/using_docker_build.md&quot;&gt;Using Docker Build&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://about.gitlab.com/2017/01/22/gitlab-8-16-released/&quot;&gt;GitLab 8.16 released with auto deploy on Google Container Engine and Prometheus monitoring as part of GitLab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;docker 文档:&lt;br /&gt;
&lt;a href=&quot;https://docs.docker.com/machine/drivers/gce/&quot;&gt;Docker Machine drivers:Google Compute Engine&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cloud.google.com/container-registry/docs/quickstart&quot;&gt;Quickstart for Container Registry&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Google Cloud 文档:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://cloud.google.com/endpoints/docs/openapi/docker-image&quot;&gt;Building a Docker Image&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cloud.google.com/container-registry/pricing&quot;&gt;Container Registry 价格&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cloud.google.com/container-registry/docs/advanced-authentication&quot;&gt;Container Registry Advanced Authentication Methods&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cloud.google.com/compute/pricing&quot;&gt;Google Compute Engine 价格&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cloud.google.com/sdk/downloads&quot;&gt;Google Cloud SDK install&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cloud.google.com/compute/docs/instances/create-start-preemptible-instance&quot;&gt;Creating and Starting a Preemptible VM Instance&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cloud.google.com/container-builder/?hl=zh-cn&quot;&gt;CONTAINER BUILDER&lt;/a&gt;
&lt;a href=&quot;https://cloud.google.com/container-builder/docs/running-builds/automate-builds&quot;&gt;Cloud Container Builder Automating Builds using Build Triggers&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cloud.google.com/container-registry/docs/pushing-and-pulling&quot;&gt;Container Registry Pushing and Pulling Images&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://cloudplatform.googleblog.com/2016/08/Preemptible-VMs-now-up-to-33-percent-cheaper.html&quot;&gt;Preemptible VMs now up to 33% cheaper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;其他文章:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.marvin-menzerath.de/artikel/gitlab-ci-runner-auf-google-compute-engine-betreiben/&quot;&gt;GitLab CI Runner auf Google Compute Engine betreiben&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://github.com/jerryjj/gitlab-runner-gce&quot;&gt;Auto-scaling Gitlab Runner in GCE&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://webnugget.de/autoscaling-gitlab-ci-builds-on-preemptible-google-cloud-instances-2/&quot;&gt;Autoscaling Gitlab-CI builds on preemptible Google-Cloud instances&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><summary type="html">前言</summary></entry><entry><title type="html">360 快视频事件相关评论</title><link href="https://loli.xing.moe/360kuai/" rel="alternate" type="text/html" title="360 快视频事件相关评论" /><published>2018-02-20T00:00:00+00:00</published><updated>2018-02-20T00:00:00+00:00</updated><id>https://loli.xing.moe/360kuai</id><content type="html" xml:base="https://loli.xing.moe/360kuai/">&lt;h2 id=&quot;数据库泄露&quot;&gt;数据库泄露&lt;/h2&gt;
&lt;p&gt;并没有任何证据表明 B 站数据库出现了异常泄露.无论是投稿视频,评论还是用户头像与用户简介,都是公开信息,并不需要拖库才能拿到.&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%B6%B2%E8%B7%AF%E7%88%AC%E8%9F%B2&quot;&gt;爬虫&lt;/a&gt;就足够了.&lt;/p&gt;

&lt;h2 id=&quot;密码安全&quot;&gt;密码安全&lt;/h2&gt;
&lt;p&gt;这里给出三个忠告:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;不要在不同网站使用同一份密码&lt;/li&gt;
  &lt;li&gt;不要在其他网站使用账号密码登入另一个网站的账户.任何情况下,账户密码只能在对应网站的域名下输入.如果您不能很好的分辨什么是对应网站域名,建议您遵从浏览器自动填充功能的指导.&lt;/li&gt;
  &lt;li&gt;违反上述两个的,建议您修改相关密码.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;举报&quot;&gt;举报&lt;/h2&gt;
&lt;p&gt;我不建议没有版权的用户滥发 DMCA 通知与以版权为理由进行举报.除了给监督部门和您的 UP 带来麻烦以外没有任何作用.&lt;br /&gt;
如果您看到您喜欢的 UP 的视频被盗用了,请联系他让他与 B 站版权部门进行沟通.联系方式见&lt;a href=&quot;https://www.bilibili.com/read/cv232560&quot;&gt;此链接&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;停用某些国产软件&quot;&gt;停用某些国产软件&lt;/h2&gt;
&lt;p&gt;我相信您会在停用某型国产软件时遇到困难,例如反复的静默安装等.&lt;br /&gt;
您可以通过&lt;a href=&quot;https://github.com/chengr28/RevokeChinaCerts&quot;&gt;此工具&lt;/a&gt;永远远离这些软件.&lt;/p&gt;

&lt;h2 id=&quot;关于-360-先锋队&quot;&gt;关于 360 先锋队&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2018/02/20/5a8c0b7dae17e.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;emm 先锋队也就是个高级用户群,这也能叫做联系了有关人员么…&lt;/p&gt;

&lt;h2 id=&quot;android-权限&quot;&gt;Android 权限&lt;/h2&gt;

&lt;p&gt;https://www.bilibili.com/video/av19811009/ 评论区#4&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2018/02/20/5a8c0cbe08f33.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我是不会明白这样的评论能够拿到 700 个赞的理由的&lt;br /&gt;
从 “查看你系统中的所有账户” 得到 “可以查看密码”,&lt;br /&gt;
从 “通过电子邮件发送邀请” 得到 “安装的一瞬间你的手握所有权就归别人了”,&lt;br /&gt;
还 “支付宝都有可能被盗” 以及 “有root的赶紧看看有没有什么奇怪的文件夹，没有的赶紧查毒，自己查，不知道为什么所有杀毒软件都不会报毒”&lt;br /&gt;
这种钓鱼言论似乎评论区被骗得不少,我不知道的是此条评论的发出者是真的不懂而且想来搞事还是就是来钓鱼的.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.android.com/reference/android/Manifest.permission.html#GET_ACCOUNTS&quot;&gt;列出账户&lt;/a&gt;只能获取到系统上的账户,没有可能获取到密码.什么想法才会认为 Google 会给一个获取密码的权限出来,除了密码管理器以外哪有明文密码让你来查?这个权限的风险在于能够更加准确的识别您的身份.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.android.com/reference/android/Manifest.permission.html#WRITE_CALENDAR&quot;&gt;写入日历&lt;/a&gt;权限的发送电子邮件只是日历程序附带的功能.这个权限在大陆的风险更加小了,因为电子邮件和日历的使用率都不高.&lt;/p&gt;

&lt;p&gt;支付宝不会因为你安装一个软件就可以这么简单的被盗,特别是快视频还没&lt;a href=&quot;https://developer.android.com/reference/android/Manifest.permission.html#READ_SMS&quot;&gt;短信读取&lt;/a&gt;这个权限.&lt;/p&gt;

&lt;p&gt;至于后面关于 Root 的描述,如果你是一个 Root 用户,还相信上述言论,我建议您立即联系您的设备制造商进行反 Root.&lt;/p&gt;

&lt;p&gt;不过非常欣慰的是,终于有用户开始关注 Android 应用的权限状况了.大陆应用滥用权限的现象非常严重,用户的关注能够更好的监督厂商认真思考获取每个权限的意义.&lt;/p&gt;</content><author><name></name></author><summary type="html">数据库泄露 并没有任何证据表明 B 站数据库出现了异常泄露.无论是投稿视频,评论还是用户头像与用户简介,都是公开信息,并不需要拖库才能拿到.爬虫就足够了.</summary></entry><entry><title type="html">Data Binding Formatter 的更新</title><link href="https://loli.xing.moe/idea-plugin/" rel="alternate" type="text/html" title="Data Binding Formatter 的更新" /><published>2017-08-11T00:00:00+00:00</published><updated>2017-08-11T00:00:00+00:00</updated><id>https://loli.xing.moe/idea-plugin</id><content type="html" xml:base="https://loli.xing.moe/idea-plugin/">&lt;p&gt;写完第一版的 &lt;a href=&quot;https://loli.xing.moe/Data_Binding_Formatter_plugin/&quot;&gt;Data Binding Formatter&lt;/a&gt; 后,闲置了快一年,突然我又拾起来更新了下.&lt;/p&gt;

&lt;h2 id=&quot;使用-idea-源码版本查看-plugin-sdk-源码&quot;&gt;使用 IDEA 源码版本查看 Plugin SDK 源码&lt;/h2&gt;

&lt;p&gt;作为 Java 开发者,看源码是习惯性动作.然而默认情况下,只能看到混淆后的代码.可以按照 &lt;a href=&quot;https://www.jetbrains.com/help/idea/configuring-intellij-platform-plugin-sdk.html&quot;&gt;JetBrans 的指导&lt;/a&gt;将 IDEA 源码附加在 Plugin SDK 上.&lt;br /&gt;
值得注意的是,Plugin SDK 依旧需要使用 IDEA 可执行版本,而不能选择源码版本,不然你会得到一片 import 失败的壮观景象(不要问我是怎么知道的)&lt;/p&gt;

&lt;h2 id=&quot;psi-相关&quot;&gt;PSI 相关&lt;/h2&gt;
&lt;p&gt;PSI 是什么呢?全称是 Program Structure Interface,IDEA 都使用 PSI 表现代码.通过对 PSI 的操作,可以直接修改对应的代码.&lt;br /&gt;
有个很好的工具,Tools 下的 PSI viewer,可以查看当前文件的 PSI 组织.不过需要加载至少一个 plugin module 才会显示.可以按照  &lt;a href=&quot;https://www.jetbrains.com/help/idea/viewing-psi-structure.html&quot;&gt;JetBrans 的指导&lt;/a&gt; 强制开启.&lt;br /&gt;
Data Binding Formatter 中的 &lt;a href=&quot;https://github.com/Qixingchen/DataBindingModelFormatter/blob/master/src/moe/xing/databindingformatter/WriterUtil.java&quot;&gt;WriterUtil&lt;/a&gt; 使用了 &lt;a href=&quot;https://github.com/Qixingchen/DataBindingModelFormatter/blob/e7961a6be920deb29119eb3a28ad463208676765/src/moe/xing/databindingformatter/WriterUtil.java#L126-L140&quot;&gt;增加一整个方法&lt;/a&gt;,&lt;a href=&quot;https://github.com/Qixingchen/DataBindingModelFormatter/blob/e7961a6be920deb29119eb3a28ad463208676765/src/moe/xing/databindingformatter/WriterUtil.java#L142-L151&quot;&gt;增加方法的 Annotation&lt;/a&gt;,&lt;a href=&quot;https://github.com/Qixingchen/DataBindingModelFormatter/blob/e7961a6be920deb29119eb3a28ad463208676765/src/moe/xing/databindingformatter/WriterUtil.java#L168-L182&quot;&gt;向一个方法内部增加一行语句&lt;/a&gt;,&lt;a href=&quot;https://github.com/Qixingchen/DataBindingModelFormatter/blob/e7961a6be920deb29119eb3a28ad463208676765/src/moe/xing/databindingformatter/WriterUtil.java#L213-L216&quot;&gt;增加一个变量&lt;/a&gt;等操作.&lt;br /&gt;
而且,&lt;a href=&quot;https://github.com/Qixingchen/DataBindingModelFormatter/blob/e7961a6be920deb29119eb3a28ad463208676765/src/moe/xing/databindingformatter/utils/FieldUtils.java&quot;&gt;FieldUtils&lt;/a&gt;提供了寻找变量 getter 与 setter 的方法.&lt;/p&gt;

&lt;h2 id=&quot;data-binding-formatter&quot;&gt;Data Binding Formatter&lt;/h2&gt;
&lt;p&gt;目前 Data Binding Formatter 已经具备了列出所有变量,寻找其 getter 与 setter 判断是否含有 Data Binding 部分.能够自动向现有的 getter 与 setter 增加 Data Binding 部分代码的所有功能.后续可能允许自定义 notifyChange 方法的命名,因为这个命名可能会引发歧义,与其他模板类似的命名冲突.&lt;br /&gt;
不过截至目前,Data Binding Formatter 已经比较完善了,可以在各种需要 Data Binding 的地方进行使用www.&lt;/p&gt;</content><author><name></name></author><summary type="html">写完第一版的 Data Binding Formatter 后,闲置了快一年,突然我又拾起来更新了下. 使用 IDEA 源码版本查看 Plugin SDK 源码 作为 Java 开发者,看源码是习惯性动作.然而默认情况下,只能看到混淆后的代码.可以按照 JetBrans 的指导将 IDEA 源码附加在 Plugin SDK 上. 值得注意的是,Plugin SDK 依旧需要使用 IDEA 可执行版本,而不能选择源码版本,不然你会得到一片 import 失败的壮观景象(不要问我是怎么知道的) PSI 相关 PSI 是什么呢?全称是 Program Structure Interface,IDEA 都使用 PSI 表现代码.通过对 PSI 的操作,可以直接修改对应的代码. 有个很好的工具,Tools 下的 PSI viewer,可以查看当前文件的 PSI 组织.不过需要加载至少一个 plugin module 才会显示.可以按照 JetBrans 的指导 强制开启. Data Binding Formatter 中的 WriterUtil 使用了 增加一整个方法,增加方法的 Annotation,向一个方法内部增加一行语句,增加一个变量等操作. 而且,FieldUtils提供了寻找变量 getter 与 setter 的方法. Data Binding Formatter 目前 Data Binding Formatter 已经具备了列出所有变量,寻找其 getter 与 setter 判断是否含有 Data Binding 部分.能够自动向现有的 getter 与 setter 增加 Data Binding 部分代码的所有功能.后续可能允许自定义 notifyChange 方法的命名,因为这个命名可能会引发歧义,与其他模板类似的命名冲突. 不过截至目前,Data Binding Formatter 已经比较完善了,可以在各种需要 Data Binding 的地方进行使用www.</summary></entry><entry><title type="html">一个挺不错的短链服务</title><link href="https://loli.xing.moe/short_links/" rel="alternate" type="text/html" title="一个挺不错的短链服务" /><published>2017-06-06T00:00:00+00:00</published><updated>2017-06-06T00:00:00+00:00</updated><id>https://loli.xing.moe/short_links</id><content type="html" xml:base="https://loli.xing.moe/short_links/">&lt;h2 id=&quot;缘起&quot;&gt;缘起&lt;/h2&gt;
&lt;p&gt;SSR 支持服务器列表订阅了,Gist 的链接比较长,就想找一个短链服务来用&lt;/p&gt;

&lt;h2 id=&quot;rebrandly&quot;&gt;rebrandly&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rebrandly.com&quot;&gt;rebrandly&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;支持使用-custom-domain&quot;&gt;支持使用 custom domain&lt;/h3&gt;
&lt;p&gt;在 Domains 页面选择 “New Domain”,然后选择页面最下方的 setup DNS 即可.&lt;/p&gt;

&lt;h4 id=&quot;后缀支持编辑&quot;&gt;后缀支持编辑&lt;/h4&gt;
&lt;p&gt;如果你提供了自己的域名,那么后缀就可以随你编辑了,只要不和旧的短链重叠即可.&lt;br /&gt;
不提供域名也可以编辑,只不过可能你选的短链已经被别人用了.&lt;br /&gt;
短链发布后还可以随意编辑和删除&lt;/p&gt;

&lt;h4 id=&quot;后缀支持emoji&quot;&gt;后缀支持emoji&lt;/h4&gt;
&lt;p&gt;可以使用 your.domain/😘 这样的链接&lt;/p&gt;

&lt;h4 id=&quot;支持-https-链接&quot;&gt;支持 HTTPS 链接&lt;/h4&gt;
&lt;p&gt;只要你的域名提供了证书即可.我使用 cloudflare 来做这件事.&lt;/p&gt;

&lt;h4 id=&quot;支持-api&quot;&gt;支持 API&lt;/h4&gt;
&lt;p&gt;API 还是挺全的,&lt;/p&gt;

&lt;h4 id=&quot;支持群组&quot;&gt;支持群组&lt;/h4&gt;
&lt;p&gt;可以邀请其他人一起使用你的域名&lt;/p&gt;

&lt;h4 id=&quot;免费&quot;&gt;免费&lt;/h4&gt;
&lt;p&gt;短链服务是免费的,收费的只是高级分析功能&lt;/p&gt;

&lt;h4 id=&quot;限制&quot;&gt;限制&lt;/h4&gt;
&lt;p&gt;后缀只支持 小写字母,数字,上下划线和 emoji&lt;br /&gt;
10000 links&lt;br /&gt;
100 domains&lt;br /&gt;
100 teammates&lt;br /&gt;
50 tags&lt;br /&gt;
50 retargeting scripts&lt;br /&gt;
10 API keys&lt;/p&gt;

&lt;h2 id=&quot;最后&quot;&gt;最后&lt;/h2&gt;
&lt;p&gt;个人觉得还是一个挺不错的短链服务&lt;/p&gt;</content><author><name></name></author><summary type="html">缘起 SSR 支持服务器列表订阅了,Gist 的链接比较长,就想找一个短链服务来用 rebrandly rebrandly 支持使用 custom domain 在 Domains 页面选择 “New Domain”,然后选择页面最下方的 setup DNS 即可. 后缀支持编辑 如果你提供了自己的域名,那么后缀就可以随你编辑了,只要不和旧的短链重叠即可. 不提供域名也可以编辑,只不过可能你选的短链已经被别人用了. 短链发布后还可以随意编辑和删除 后缀支持emoji 可以使用 your.domain/😘 这样的链接</summary></entry><entry><title type="html">为什么我们当初没有选择 Kotlin</title><link href="https://loli.xing.moe/why_we_not_choose_kotlin/" rel="alternate" type="text/html" title="为什么我们当初没有选择 Kotlin" /><published>2017-05-24T00:00:00+00:00</published><updated>2017-05-24T00:00:00+00:00</updated><id>https://loli.xing.moe/why_we_not_choose_kotlin</id><content type="html" xml:base="https://loli.xing.moe/why_we_not_choose_kotlin/">&lt;p&gt;今年的 Google I/O 终于钦定了 Kotlin 作为 Android 第二个官方的开发语言.
一年前,我还是实习生的时候,我司就在尝试 Kotlin 了,并运用在了 &lt;a href=&quot;http://a.app.qq.com/o/simple.jsp?pkgname=com.ci123.noctt&quot;&gt;家校通&lt;/a&gt;,赛诚PAD 上.&lt;br /&gt;
但是为什么后续的应用没有使用 Kotlin 开发呢?&lt;/p&gt;

&lt;h1 id=&quot;最初遇到的问题&quot;&gt;最初遇到的问题&lt;/h1&gt;

&lt;h3 id=&quot;lint&quot;&gt;lint&lt;/h3&gt;
&lt;p&gt;Kotlin 在当时不支持 lint ,很多本可以发现的问题,在使用 Kotlin 后就无法发现了.&lt;br /&gt;
甚至我实习的第一周,就在 家校通 的多个页面错误的使用了 getDrawable(int, android.content.res.Resources.Theme) 这个 API 21 的方法.导致上线后所有低于 5.0 的设备闪退.当然这大部分责任是我们没有完善测试的锅,但没有 lint 很大程度上导致了后续开发时我们都战战兢兢,生怕用错了方法.&lt;br /&gt;
后来 Kotlin 支持了 lint ,然而很长时间内都只支持到 Android Studio 1.5 .于是我在 AS 的四个频道之外,又放了一个 1.5 的包,每次开发 Kotlin 的应用来使用.&lt;/p&gt;

&lt;h3 id=&quot;data-binding&quot;&gt;Data Binding&lt;/h3&gt;
&lt;p&gt;我司开发时使用了 Data Binding,并且使用的很多.当时 Data Binding 本身还不稳定,有时会出一些奇怪的错误.和 Kotlin 混用后,遇到的错误更多了,还无法确定究竟是哪方面出了错误.&lt;br /&gt;
那为什么选择了继续使用 Data Binding 而不是选择停用 Data Binding 使用 Kotlin 呢? 因为 Data Binding 真的有用,虽然最初踩了一些坑,但使用起来之后,不管在代码量上,代码结构上,Debug 难度上 都有很大的益处.&lt;br /&gt;
然而 使用 Kotlin 后,我们没有发现任何益处.&lt;/p&gt;

&lt;h1 id=&quot;kotlin-的问题&quot;&gt;Kotlin 的问题&lt;/h1&gt;

&lt;p&gt;使用一个新的技术,一定要有一个使用它的理由,比如可以节约时间,或者可以减少错误.当然官方选择也是一个重要的理由.&lt;br /&gt;
那么 Kotlin 有什么优势呢?&lt;/p&gt;

&lt;h3 id=&quot;data-class&quot;&gt;data class&lt;/h3&gt;

&lt;p&gt;每次介绍 Kotlin ,都会拿 data class 进行开场.的确,对于一个基础的数据类,Kotlin 看上去很简单.然而实际使用上就是另一回事了.&lt;br /&gt;
 如果你需要一些自定义的 setter 呢(比如使用一个类来设定多个属性)? ,如果你需要使用 Data Binding 呢?&lt;br /&gt;
 你就会发现 Kotlin 的 Data Class 没有任何意义.getter 和 setter 都需要再次重写.&lt;br /&gt;
 而且在Java 中 getter ,setter, toString 等都可以使用 IDE 自动生成.包括 Data Binding 属性也可以使用 &lt;a href=&quot;https://plugins.jetbrains.com/plugin/8616-data-binding-formatter&quot;&gt;Data Binding Formatter&lt;/a&gt; 来生成.如果我们当时使用 Kotlin 来写 data class, 我想我也许会再写一个 Kotlin 版本的 Data Binding Formatter,在 IDE 允许的情况下.&lt;/p&gt;

&lt;h3 id=&quot;null-安全&quot;&gt;null 安全&lt;/h3&gt;
&lt;p&gt;Kotlin 另一个重要优势,”Null 安全”实际上也没什么优势.&lt;br /&gt;
Android 已经有了 &lt;a href=&quot;https://developer.android.com/studio/write/annotations.html#adding-nullness&quot;&gt;Annotations&lt;/a&gt; 来处理 Null 的问题. &lt;br /&gt;
另外由于 Android 的生命周期的特性,例如在 OnCreate 中赋值的变量,在 Kotlin 中依旧需要声明为可空变量,并没有比 Java 高明多少.&lt;br /&gt;
“?.”的语法糖,实际上大部分如果可能为空的情况,空和非空都需要进行处理(例如对用户进行提示),所以这个语法糖基本用不上.&lt;/p&gt;

&lt;h3 id=&quot;多样的写法&quot;&gt;多样的写法&lt;/h3&gt;
&lt;p&gt;Kotlin 的写法很多,从一个方法返回就有多种写法.也许有人觉得这是优点,然而对于企业级的开发协作来说是难以接受的.最终使用那种方法来写,又可以争论个几天.&lt;/p&gt;

&lt;h3 id=&quot;生态的缺失&quot;&gt;生态的缺失&lt;/h3&gt;
&lt;p&gt;当然最重要的还是生态不完善.Java 有大量的 扩展, 活动模板 等帮助开发.然而当时 Kotlin 什么都没有.&lt;br /&gt;
另外,Kotlin 的 与 Java 完全兼容说的是 Kotlin 调用 Java 时,千万不要觉得反过来也是如此.&lt;br /&gt;
也就导致了想用 Kotlin 的项目发现,写底层方法很适合 Kotlin ,然而 Java UI 层调用就坑了.&lt;br /&gt;
不会遇到 Java 层调用的 UI 层,又发现 Kotlin 没什么用武之地,而且没有扩展与活动模板等帮助,还得纯手写.&lt;/p&gt;

&lt;h1 id=&quot;结论&quot;&gt;结论&lt;/h1&gt;

&lt;p&gt;当然,本文并不是说 Kotlin 没有优势,只不过想提醒你, Kotlin 并没有宣传的那么好.&lt;/p&gt;

&lt;p&gt;后续我们会再次使用 Kotlin 么?&lt;br /&gt;
当然会.既然 Kotlin 已经是 Android 官方的开发语言了,有理由相信,在某一个时间点后,我们会选择使用 Kotlin 来开发新的需求.&lt;/p&gt;</content><author><name></name></author><summary type="html">今年的 Google I/O 终于钦定了 Kotlin 作为 Android 第二个官方的开发语言. 一年前,我还是实习生的时候,我司就在尝试 Kotlin 了,并运用在了 家校通,赛诚PAD 上. 但是为什么后续的应用没有使用 Kotlin 开发呢?</summary></entry><entry><title type="html">Flutter 预览介绍</title><link href="https://loli.xing.moe/flutter_preview/" rel="alternate" type="text/html" title="Flutter 预览介绍" /><published>2017-04-18T00:00:00+00:00</published><updated>2017-04-18T00:00:00+00:00</updated><id>https://loli.xing.moe/flutter_preview</id><content type="html" xml:base="https://loli.xing.moe/flutter_preview/">&lt;h1 id=&quot;flutter&quot;&gt;Flutter&lt;/h1&gt;

&lt;h2 id=&quot;flutter-介绍&quot;&gt;Flutter 介绍&lt;/h2&gt;

&lt;p&gt;Flutter 是一个使用同一代码构建高性能,高保真的 iOS 与 Android 应用的 SDK.
目前由 Google 主持开发.&lt;/p&gt;

&lt;h3 id=&quot;flutter-设备要求&quot;&gt;Flutter 设备要求&lt;/h3&gt;

&lt;h4 id=&quot;开发设备&quot;&gt;开发设备&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;支持 win Mac Linux 三端.集成 iOS 应用要求 Xcode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;终端设备&quot;&gt;终端设备&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;iOS 端要求 64 位设备.iOS 8 及以上&lt;/li&gt;
  &lt;li&gt;Android 端要求 Android Jelly Bean, v16, 4.1.x 及以上(目前 Android 端开发时最低支持 Kitkat, v19, 4.4.x )&lt;/li&gt;
  &lt;li&gt;不推荐在平板上使用,目前不支持 wear&lt;/li&gt;
  &lt;li&gt;目前不支持 web 与 桌面端&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;flutter-优势&quot;&gt;Flutter 优势&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;拥抱差异.包括 iOS Android 两端在滚动行为,图标与排版上的差异都可以使用同一代码来实现.&lt;/li&gt;
  &lt;li&gt;高性能.基于底层代码(Android 上为 C++ with NDK,iOS 上为 C++ with LLVM),不使用 WebView 渲染,不涉及翻译.能够保证 60 fps 的绘制帧率.&lt;/li&gt;
  &lt;li&gt;可以与原生代码互调.&lt;/li&gt;
  &lt;li&gt;提供了大部分 Material Design 控件的实现(甚至比 Android Design Support 实现的更多).&lt;/li&gt;
  &lt;li&gt;足够小. SDK 大小在两端都在 8MB 左右,可以接受这样的体积增加.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;flutter-劣势&quot;&gt;Flutter 劣势&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;仍在 Preview 阶段,还未释出 1.0&lt;/li&gt;
  &lt;li&gt;不支持 3D 应用(不过我们也不做)&lt;/li&gt;
&lt;/ol&gt;</content><author><name></name></author><summary type="html">Flutter</summary></entry><entry><title type="html">Data Binding Formatter Plugin</title><link href="https://loli.xing.moe/Data_Binding_Formatter_plugin/" rel="alternate" type="text/html" title="Data Binding Formatter Plugin" /><published>2016-09-13T00:00:00+00:00</published><updated>2016-09-13T00:00:00+00:00</updated><id>https://loli.xing.moe/Data_Binding_Formatter_plugin</id><content type="html" xml:base="https://loli.xing.moe/Data_Binding_Formatter_plugin/">&lt;h5 id=&quot;旧文转移计划&quot;&gt;旧文转移计划&lt;/h5&gt;

&lt;p&gt;现已在 &lt;a href=&quot;https://plugins.jetbrains.com/plugin/8616-data-binding-formatter&quot;&gt;JetBrans Plugin 中心提供下载 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;直接搜索 Data Binding 就可以找到了.&lt;/p&gt;

&lt;p&gt;写一个 Android Data Binding 的实体类是一个非常痛苦的事情,要为所有 getter 函数添加 @Bindable 注解, 对所有 setter 函数加入 notify .如果一个实体类有 10 几个成员对象,自动生成 getter 和 setter 之后的操作把手也累断了.&lt;/p&gt;

&lt;p&gt;所以我写了一个 IDEA (Android Studio 基于 IDEA 社区版) 的扩展,来自动完成这些操作.&lt;/p&gt;

&lt;p&gt;对于一个实体类&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestBean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;调出生成菜单,选择 Data Binding Formatter 即可自动生成以下代码:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestBean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;transient&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PropertyChangeRegistry&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyChangeRegistry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PropertyChangeRegistry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;


    &lt;span class=&quot;nd&quot;&gt;@Bindable&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getTestField1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setTestField1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;testField1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;notifyChange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;testField1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Bindable&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getTestField2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setTestField2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;testField2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testField2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;notifyChange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;testField2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;notifyChange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyChangeRegistry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;propertyChangeRegistry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PropertyChangeRegistry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;propertyChangeRegistry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;notifyChange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addOnPropertyChangedCallback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;OnPropertyChangedCallback&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyChangeRegistry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;propertyChangeRegistry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PropertyChangeRegistry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;propertyChangeRegistry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;removeOnPropertyChangedCallback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;OnPropertyChangedCallback&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propertyChangeRegistry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;propertyChangeRegistry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不过现在还需要手动 import BR 类,并向类添加 android.databinding.Observable 接口.&lt;/p&gt;

&lt;p&gt;后续应该也能自动完成,不过刚刚开始开发 IDEA 扩展,还不是很会用 Psi 类.&lt;/p&gt;

&lt;p&gt;项目地址 : &lt;a href=&quot;https://github.com/Qixingchen/DataBindingModelFormatter&quot;&gt;GitHub - Qixingchen/DataBindingModelFormatter: quickly add data binding getter and setter for a model&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;欢迎指出错误指出,或者提交pr wwww&lt;/p&gt;

&lt;p&gt;后续计划:&lt;/p&gt;

&lt;p&gt;找出已存在的 getter 和 setter &lt;br /&gt;
使用列表,使得可以选择需要增加 binding 的成员对象.&lt;/p&gt;</content><author><name></name></author><summary type="html">旧文转移计划</summary></entry></feed>