Comments

最近为了吃鸡入了一块 AORUS GTX1080 8G Gaming Box,本来打算上 Dell 的 外星人Alienware ALPHA(阿尔法)R2,可是实在是太太太太太贵了,再加上当时买这台 Macbook 的时候我升到了 i7 + 16G RAM,感觉也没必要为此新买一台电脑,因为理论上来讲目前的配置,只差一块给力的显卡就能顺利吃鸡,于是就有了接下来要做的。

前言

因为苹果的一些限制,直接在 Bootcamp 的 Windows 里插上外接显卡,系统是不会认的。而且在开机的时候,如果你不折腾一下,会直接卡死在 Windows 标志那里,所以我们需要改一下引导,让 Macbook 能带着这块显卡愉快地玩耍。

下面的教程,如果你不想来回折腾,那么请老老实实,一步一步按照我写的去做。我为了安装这块显卡,前后重装了3遍系统,翻了好多国内外论坛,踩了无数坑,最后终于搞定,因此必须先感谢一下如下这几位前辈:

  1. [Guide] AORUS GTX 1070 Gaming Box on Macbook Pro 2016
  2. `手机锋友9b7pztp 大哥写的一篇简易教程
  3. q642638452 开的求助帖

上面 3 篇文章,第 1 篇是最完整,也是质量最高的,我的教程基本是在第1篇的基础上改的,顺便加了一点原文没有提到的、我自己踩过的坑。后面两篇在我不知所措的时候也给了我不少帮助,在此一并表示感谢。

准备工作

  • 一台带有 Thunderbolt 3 接口的 Macbook
  • AORUS GTX1080 8G Gaming Box
  • 一点点英文基础与一点点 Linux 命令基础

安装步骤

  1. [macOS] 使用 Bootcamp 安装 Windows。这里我自己安装的是 1709 Sept 的版本,装完之后记得安装上苹果的 Bootcamp 驱动包,打齐所有驱动,此时进设备管理器不应该看到有异常设备。

  2. [Windows] 安装 DDU移除掉默认的 AMD/Nvidia 显卡驱动。注意,因为你在第一步已经全新安装了 Windows,所以我只执行了移除 AMD/Nvidia 的操作,Intel 的不用移除,因为第一步里苹果刚给你装上。移除完之后,请禁用 Windows 的驱动自动安装,免得等下插上显卡之后它自作聪明给你装兼容驱动,而且还会导致 Nvidia installer 无法正常进行安装。

  3. [macOS] 这一步比较重要安装 rEFInd 启动管理器。 把前面下载好的 zip 包解压到桌面,然后重启电脑按住 command + R进到恢复模式,然后点击菜单里的“终端”打开,先执行一下 csrutil disable,目的是为了关掉 macOS 的 SIP 验证,接着这样操作。 如果搞定的话,重启你就可以看到一个这样的引导页面。 (暂时还没有图上那三个圆的图标,先别急,你只要看到这个白底中间有几个按钮的界面就行了)。另外,如果你感觉少了分区的话,按一下 delete 键可以重新加载一下。

  4. [macOS] 下载 apple_set_os.efi

  5. [macOS] 挂载 EFI 分区 。然后进到挂载好的 EFI 分区里,分别在两处地方把上一步下载好的 apple_set_os.efi 扔进去。第一处第二处。这两处必须都要有,否则等会儿 rEFInder 会找不到你这个区。

  6. 关机,把 AORUS GTX1080 8G Gaming Box 插电,然后连接到电脑。13寸的 Macbook Pro 用户注意,因为带宽的原因,你的电脑只有左边两个 Type-C 接口是真正的满速 Thunberbolt 3,所以建议插在电脑左边。

  7. [Windows] 开机,理所当然会停在 rEFInder 让你选。把图标移动到你 第5步 放了apple_set_os.efi 文件的那个 EFI 分区,回车一下,这时屏幕会快速闪一下,这是正常的正常;然后选择用 EFI 模式加载 Windows(不是 Legacy 那个),开始启动。

  8. [Windows] 打开设备管理器,此时在“显示卡”里面,你应该可以看到 Macbook 自己的一块 Iris 550,另外一块则是 Microsoft VGA 兼容控制器,没错,这其实就是你的 AORUS GTX1080 8G Gaming Box 了,只不过我们还没安装驱动,此时离成功已经不远。

  9. [Windows] 安装 Nvidia graphics 驱动,然后重启。

  10. 大功告成。

补充说明

上面的教程仅适用于 13寸 Macbook Pro,如果你是 15 寸 用户,可能还需要禁掉自带的一块显卡,步骤比上面稍微多出一些,具体可以参考:

https://egpu.io/forums/implementation-guides/late-2016-15-macbook-pro-rp450-gtx107032gbps-tb3-aorus-gaming-box-win10-theitsage/

https://9to5mac.com/2017/04/19/akitio-node-gtx-1080-ti-gpu-macbook-pro-gaming-egpu/

最后附几张搞定后的图。不知道是不是我火星了,刚才装完吃鸡之后第一次打开,进到设置里面,默认的显示模式居然已经切到了“极致”,看来性能真的是可以的。。。=。=

Comments

Google 在今年的 IO 大会重点介绍了它们最新推出的 Android Architecture Components,其中最重要的一个就是 Room。在 Ormlite、GreenDao,甚至 Realm 大行其道的今天,Google 自己也总算造了一口锅自己背上了(只求 Google 日后不要轻易弃坑)。

这篇文章没有太多深奥的源码分析,因为我下午看完官方文档之后,还是觉得有点复杂,不利于初学者学习如何使用,所以打算写一篇文章来帮助大家入门。

Room 的一些特点

  1. 编译时 sql 语句检查。相信大家都有过 app 跑起来,执行到 db 语句的时候 crash,检查之后发现原来是 sql 语句少了一个 ) 或者其它符号之类的经历。Room 会在编译阶段检查你的 DAO 中的 sql 语句,如果写错了(包括 sql 语法错误跟表名、字段名等等错误),会直接编译失败并提醒你哪里不对。
  2. sql 查询直接关联到 Java 对象。这个应该不用详细解释了,虽然很多第三方 db 库早已经实现。
  3. 耗时操作主动要求异步处理。这一点还是挺值得注意的,Room 会在执行 db 操作时判断是不是在 UI 线程,比如当你需要插入一条记录到数据库时,Room 会让你放到异步线程去做,否则会直接 crash 掉 app 来告诉你不这样做容易阻塞 UI 线程。虽说死相难看了点(个人觉得打个警告不就完了么?),但对于开发者开发出高质量的应用还是有帮助的。
  4. 基于注解编译时自动生成代码。这个应该算是 Room 工作原理的核心所在了,你要写的代码之所以这么少,说白了还不是因为 Google 给你写好了很多?希望以后有时间能写一篇源码分析出来,那个时候再讲吧。
  5. API 设计符合 Sql 标准。方便扩展进行各种 db 操作。

Room 的三大组件

  • Entity。实体,说白了就是我们最常见的一个对象
  • Database。数据库,Room 提供了一个非常方便的静态方法来供我们创建数据库
  • DAO。Data Access Object,把你 Entity 所有的 CRUD 业务代码封装在这里就好
Read on →
Comments

最近省吃俭用,打算入头戴式耳机的坑。其实一开始我对头戴式耳机是没有多大兴趣的,总觉得它又笨又傻,大夏天戴着还捂得慌。但是自从好几次在飞机跟地铁上被吵得实在是受不了之后,便开始打算买一款头戴式耳机,所谓存在即价值,肯定还是有用武之地的。

工欲善其事,必先利其器。既然准备买耳机,肯定要在它休息的时候给它找个家的。淘宝上转了一圈,实在是没有我满意的。后来一次无意之间在 YouTube 上看到了 @UrAvgConsumer这个视频,里面推荐了一些 $25 以下的小玩意儿,瞬间对 Avantree 这款耳机支架长草。

Read on →
Comments

WDMyCloud 买了将近一年时间了,本来准备只给 Mac 做 Time Machine 备份用的,奈何 3T 空间实在撑不满,多着也是浪费,再加上平时白天上班,家里带宽闲着也是闲着,于是今天折腾了一下给他添加了迅雷远程下载功能,亲测可用。

首先需要说明的是,网上大部分教程抄来抄去,几乎全是 Windows 下操作的版本,其实对于 Mac 用户而言,完全不用 PuTTY,因为 Mac 自带的终端一直都支持 ssh ,而 WinSCP 在 Mac 下有更好的替代软件 Cyberduck,俗称小黄鸭。所以对于 Mac 用户而言,理论上你只需要准备后者,剩下的完全不需要考虑。

步骤

一、降级 WDMyCloud 固件版本

迅雷官方的远程下载模块目前不支持 4.0+ 版本的 WDMyCloud 固件,所以如果你不小心被西部数据升级到了最新版本,需要先降回去。方法很简单:

万事先理清思路,我们的思路:v04.04 –> V04.00 –> V03.04

所以,先准备好这两个版本的固件:

http://download.wdc.com/nas/sq-040000-607-20140630.deb

http://download.wdc.com/nas/sq-030401-230-20140415.deb

然后照着这篇文章完成降级就好,很简单。

这一步我遇到几个问题,注意一下:

  • Mac 用户如果不知道你的 WDMyCloud 在局域网内 IP 是多少,可以通过 ping 一下 wdmycloud.local 获得。

  • 在降到 V04.00 之后,再次上传固件准备降到 V03.04 的时候,WDMyCloud 会提示你没有空间上传固件了,我初步判断是 WDMyCloud 的一个 bug 导致的,它在升完之后没把我们上一次传的 deb 文件删掉导致。解决办法就是去 设置->实用工具->系统出厂还原->仅系统 还原一下系统,然后再继续降级。如果你没找到,看下图:

  • V04.00 –> V03.04 刷完之后 WDMyCloud 会提示“升级失败”。这是正常的,因为我们本身就是在做降级操作。去“固件”里看一下,已经是 V03.04 了就算大功告成。另外记得把自动更新关掉,防止再次被升级到最新固件。

Read on →
Comments

问题

钱包 2.0.0_beta 版本上线之后,我们给新获得理财权限的用户增加了一个欢迎对话框。按照 Google 官方关于开发对话框时的指导,钱包把所有会用到的 Dialog 全部采用 DialogFragment 进行了封装,并且放到了一个包下进行管理。

DialogFragment 的用法非常简单,而且内部封装好了show() 方法,很容易就可以把对话框展现出来,就像这样

1
2
3
4
5
public void showNoticeDialog() {
        // Create an instance of the dialog fragment and show it
        DialogFragment dialog = new NoticeDialogFragment();
        dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
    }

然而上线后不到一周时间,我在后台看到了很多用户遇到了这样的奔溃:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
  at android.support.v4.app.z.v(SourceFile:1440)
  at android.support.v4.app.z.a(SourceFile:1458)
  at android.support.v4.app.k.a(SourceFile:634)
  at android.support.v4.app.k.b(SourceFile:613)
  at android.support.v4.app.q.show(SourceFile:139)
  at com.meizu.flyme.wallet.fragment.p.c(SourceFile:195)
  at com.meizu.flyme.wallet.fragment.p.c(SourceFile:390)
  at com.meizu.flyme.wallet.fragment.p.b(SourceFile:61)
  at com.meizu.flyme.wallet.fragment.p$6.a(SourceFile:469)
  at com.meizu.flyme.wallet.fragment.p$6.onResponse(SourceFile:464)
  at com.android.volley.toolbox.u.deliverResponse(SourceFile:60)
  at com.android.volley.toolbox.u.deliverResponse(SourceFile:30)
  at com.android.volley.g.run(SourceFile:99)
  at android.os.Handler.handleCallback(Handler.java:815)
  at android.os.Handler.dispatchMessage(Handler.java:104)
  at android.os.Looper.loop(Looper.java:194)
  at android.app.ActivityThread.main(ActivityThread.java:5824)
  at java.lang.reflect.Method.invoke(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:372)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1010)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:805)

这个错误堆栈正是在调用了 DialogFragmentshow()方法之后才出现的,真的是非常奇怪。一开始,我们以为是简单的 fragment 生命周期问题导致的 crash,然而等我在 show() 方法之前加上了isAdded() 判断还是没有用之后,我知道事情可能没那么简单了。

Read on →
Comments

本文由 Lin Shen 译自 Common questions on AsyncTask,原文作者 Colt McAnlis, 转载请务必注明出处!

关于 AsyncTask 的常见问题

我在 Google 工作最喜欢的一点,就是把一些比较复杂的概念,分解成一小部分一小部分,这样就可以确保每一位工程师都能清楚地理解。在我最近的《Android 性能优化典范》视频中,我提到了一个表面上看上去很直观,但是它的一些属性可能会带来一些万万没想到的负面影响的东西,我是说,毫无悬念,这货就是 关于 AsyncTask:

我在这个视频里强调了一些开发者之前在使用 AsyncTask 时可能并不会注意到的事项。多亏了我们有一些非常棒的 android 开发者交流社区,能让我们看到大家反馈的一些问题:

让我们来挖掘其中的一些问题,看看能不能深入展开讨论一下:

Read on →
Comments

Project Home

https://github.com/shawnlinboy/android-OverscrollViewPager/

Description

最近项目里接到一个需求,效果如下:

简而言之,需要在 ViewPager 滑到最左或者最右的时候,仍然支持可滑动。

具体就是根据是在 ViewPager 最右边向右滑了,还是在最左边向左滑了,做出响应。这就要求对 ViewPager 的 overscroll 行为做出监听。

网上也有一些类似的轮子,但是我感觉做得真心复杂,而且都是为了实现轮播的,所以可扩展性并不强。因此我这里要做的,就是对 ViewPager 的overscroll 行为做出简单的监听并封装。剩下的,交给开发者自己去 do whatever you want 就好。

原理很简单,对手势做出监听即可。所以也算是对ViewPager 的overscroll 行为监听的另一种实现思路吧。

使用上,只需要把 ViewPager 替换成我这个就好,adapter 不需要改,也没必要改。因为我只会告诉你,你的 ViewPager 是否到头了,然后是哪边到头向哪边滑了,剩下的是你的 //TODO

欢迎各路大神对它进行拓展并发起 pr。

Demo

Comments

今天下班前被组里的小伙伴问了一个问题:

如果一个工程需要定义两个 flavor,每个 flavor 需要用一份单独的 AndroidManifest.xml,应该怎么配置?

这个问题,熟悉 gradle 的同学应该是能轻松搞定的。我们知道,gradle 在编译 apk 的时候支持给每个不同的 flavor 指定 src、res、甚至 AndroidManifest.xml 文件都没有问题。

首先我们定义两个 flavor:

1
2
3
4
productFlavors {
        normal {}
        meizu {}
    }

为了让两个 flavor 分别取不同的 AndroidManifest.xml,我们在 src 下面建立一个叫 meizu 的文件夹,里面单独放置这个 flavor 要用的清单文件,就像这样:

然后我们配置 sorceSets 闭包:

1
2
3
4
5
6
7
8
sourceSets {
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
        }
        meizu {
            manifest.srcFile 'src/meizu/AndroidManifest.xml'
        }
    }

src/meizu/AndroidManifest.xmlsrc/main/AndroidManifest.xml的区别在于,前者删掉了 SecondActivity 的 action,理论上,如果我们编译 meizu flavor,那么在点击按钮之后,因为采用了 action 的方式来启动 activity,会因为 action 找不到导致失效。然而结果是这样吗?大家可以试一下。

结果是我们依然可以很顺利地跳到 SecondActivity…

为什么?

这就需要大家了解 gradle 在编译时,对 manifest 采用的 merge 策略。

引用一下 Ezio Shiki知乎上的一段回答:

Manifest可以通过Merge的方式合并多个Manifest源。通常来说,有三种类型manifest文件需要被merge到最终的结果apk,下面是按照优先权排序:productFlavors和buildTypes中所指定的manifest文件应用主manifest文件库manifest文件简单来说,manifest的merge会将每个元素及其子元素的节点和属性进行合并。

例如:

1
2
3
<activity
    android:name=com.foo.bar.ActivityOne
   android:theme=@theme1/>

1
2
3
<activity
    android:name=com.foo.bar.ActivityOne
   android:screenOrientation=landscape/>

合并会成为

1
2
3
4
<activity
    android:name=com.foo.bar.ActivityOne
   android:theme=@theme1
   android:screenOrientation=landscape/>

不过

1
2
3
<activity
    android:name=com.foo.bar.ActivityOne
   android:theme=@theme1/>

1
2
3
4
<activity
   android:name=com.foo.bar.ActivityOne
   android:theme=@theme2
   android:screenOrientation=landscape/>

合并会产生一个冲突,因为都有theme,而theme的属性不同。

要了解manifest合并的更高级应用,查看Manifest Merger

所以,看明白了吗?简单来说,AndroidManifest.xml 文件在 gradle 打包编译的时候,不是你指定哪个,它就100%去用哪个的。

  • 首先,你的 main 里面必须要有一份基本的,不可以因为要分 flavor 就把 main 里面的删掉,否则会直接编不过
  • 接着,你要知道 Manifest 的 merge 关系,从你的 flavor 到 main,它是一层层合并的,合并的规则上面已经提到了。
  • 最后,如果我有一个 Activity,或者 Service,或者 Receiver,真的要用另一份 AndroiManifest.xml 里的怎么办?

关于这个问题,官方文档给出了我们答案:

tools:node markers

没错,我们可以使用 tools:node replace 来解决我们的问题。现在来修改一下 src/meizu/AndroidManifest.xml,在 SecondActivity 的声明里加上,如下图所示:

再编译一下这个 flavor,点击按钮,可以看到报错了。

现在已经没有对应的 Activity 来解析这个 action 了,也就是说我们为 meizu 这个 flavor 指定的 AndroidManifest.xml 总算“生效”了。

Demo 地址:https://github.com/shawnlinboy/Android-MultiFlavors

参考文章:

https://www.zhihu.com/question/22842123/answer/55675046

http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger#TOC-tools:node-markers

http://my.oschina.net/fallenpanda/blog/373183

Copyright © 2014 - 2017 - linshen - @ . +