Android Scroller

0x81 Scroller 是什么

Scroller是Android提供的一个滚动计算器,我们通常用它在自定义View时形成动画,更是做滚性滚动提高用户体验的利器。Scroller跟踪整个View的内容位移变化,但是它不会主动做些什么,它只是计算了对应的数值用于返回给开发者使用。

0x82 startScroll 方法

该方法是触发滚动的最简单方法,它共接受5个参数,前四个分别为起始位置坐标和结束位置坐标,第五个参数是滚动计算时长,默认为250ms。

看一下startScroll的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime =AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}

我们可以看到实际上这个方法仅仅是保存了一下你传进去的值,并没有进行动画的相关代码。

Read More

Material Design绚丽动画:transition framework

0x81 Transition Framework

Transition Framework是Android Kitkat开始添加的动画框架,在Lolipop才得到完美的支持,所以支持源码里会对当前版本进行判断,采用不同的实现,API 19和21是两个分水岭。Transition其实就是动效转场的意思,Google官网既推出PropertyAnimator之后,为充分利用RenderThread并解放Android开发者,官方实现了一些专长动画,而这些动画是根据View状态来动态计算的,而Scene就保存了这些状态,动画实际上描述的就是从一种Scene到另一种Scene变化的过程。

0x82 Scene

Scene中文意为场景,就是将当前页面(或者说Activity,亦或者可以视为一个场景的View)以及其子View的状态抽象为一个场景,而它们的位置大小可视状态就是场景里的元素。TransitionManager就是负责将View从一个场景转变为另一个场景,而变换的方式就是Transition。

Read More

Android DI利器:Dagger2应用

0x81 开始使用Dagger2

昨天我们说过了Dagger2的一些基本概念,比如Module、Component、Inject各自的职责,今天我们就根据之前的介绍动手试一试,看用Dagger2如何管理类对象实例。

0x82 全局唯一——AppModule

在我们平时进行的应用开发中,有很多工具类是以全局唯一的单例对象存在的,比如网络请求OkHttpClient和Retrofit、数据库管理的Realm等,对于这种global的对象,我们便可以将其与Application绑定,我们只在Application里将他们实例化一次,之后的每次调用都是从该处获取的。

Dagger2要实例化并缓存一个对象很容易,Dagger2有Scope的概念,在同一Scope内的对象可以认为是单例的,一经创建Dagger2便会将其缓存起来,以后有需要将缓存返回给Component。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Module
public class AppModule {

private final Application application;

public AppModule(Application application) {
this.application = application;
}

@Provides
@Singleton
Application provideApplication(){
return application;
}

@Provides
@Singleton
OkHttpClient provideOkHttpClient(){
File cacheFile = new File(BaseApplication.getInstance().getCacheDir(), "HttpCache");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 10);

OkHttpClient.Builder builder = new OkHttpClient.Builder().connectTimeout(10,
TimeUnit.SECONDS).addInterceptor(new LogInterceptor()).cache(cache);

return builder.build();
}

@Provides
@Singleton
Gson provideGson(){
GsonBuilder builder = new GsonBuilder();
return builder.create();
}

@Provides
@Singleton
Retrofit provideRetrofit(OkHttpClient okHttpClient, Gson gson) {
Retrofit.Builder builder = new Retrofit.Builder().client(okHttpClient).baseUrl(
HttpConstants.BASE_URL).addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create());

return builder.build();
}

@Provides
@Singleton
ApiService provideApiService(Retrofit retrofit){
return retrofit.create(ApiService.class);
}
}

其中@Module注解指明了该类是一个工厂,就是它返回实例对象给Component然在再被注入到@Inject成员上。@Provides注解修饰的方法通常用provide开头(不强制),Dagger会主动寻找对应的方法获取对象。@Singleton注解是Dagger提供的默认的一个Scope注解,并不是说加了这个注解它就单例了,实际上它规约了一种Scope,如前面所说的同一Scope下它是单例的,因此如果我们只在一个地方进行初始化,那它就是单例的,而这个注解名也起到一定的标记作用。从OkHttpClient->Retrofit->ApiService,当我们注入ApiService时,Dagger会自动根据依赖图为我们创建好对象并注入。

Module的最简单用法就是这样,当然它本身就是这么简单,当你理解了Scope,你就能了解到Dagger2的奇妙之处了。

Read More

Android DI利器:Dagger2初探

0x81 DI

Dependency Injection,简称DI,中文昵称依赖注入,一作IoC(控制反转)。这种模式很容易理解,就是将原本有我们控制的实例化对象,改为又容器动态注入,所以也有人说这是一种控制反转,但我觉得这还是有一些差别的,毕竟他们的陈述对象不算一样。

0x82 DI带来的好处

最早在接触SSH的时候,Spring等框架便对JavaBean采用依赖注入的方式管理对象,所有的对象都是容器管理的,框架在我们需要某些实例对象的时候动态的注入以提供我们使用。

DI最明显的特点就是不再是我们自己手动管理类对象的创建,这也将我们从可能很繁杂的类组合中解救出来,解除编写代码时类与类之间的耦合,就好像响应式编程将我们带离了回调地狱,让我们能更优雅的编写代码,把更多的精力放到业务逻辑的实现上去。

Read More

Vim的Python配置——YouCompleteMe

0x81 YouCompleteMe

YouCompleteMe: a code-completion engine for Vim

正如它的README所说的,YCM是一个用于Vim自动补全的插件。它的功能十分强大,支持非常多的编程语言补全。

  • Clang 为C/C++/OC/OC++提供补全支持
  • Jedi 为Python2/Python3提供补全支持
  • OmniSharp 为C#提供补全支持
  • Gocode、Godef 为Golang提供支持
  • TSServer、Tern 分别为TypeScript和JavaScript提供支持
  • Racer 支持Rust
  • 其他补全支持

0x82 ycm_core的编译使用

YCM提供了基于python的编译脚本,一般情况下,安装好依赖直接执行install.py脚本文件就可以了,用户还可以出入不同的参数以提供不同的补全支持。

对于我的DST-Fedora 25,需要先使用dnf包管理工具安装基本的构建工具sudo dnf install automake gcc gcc-c++ kernel-devel cmake,还需要安装python的头文件sudo dnf install python-devel python3-devel,处理完这些依赖便可以进行编译工作了。

PS:根据官方README的描述,如果YCM更新了,要使用新的特性,需要重新编译。

Read More

Openstack快速安装:packstack

0x80 前言

我曾经在差不多两年前安装过Openstack,当时是按照某个论坛的大牛的分享,一步一个脚印,用了很长时间才进入Openstack DashBoard并进行自己劳动成果的体验,感受自己为自己提供的服务。但是那次血泪安装史也并不完美,虽然核心组件成功运行,但是仍旧有部分功能存在问题,迫于精力有限,我便没有再碰过Openstack。
最近又心血来潮想要玩一玩这个曾经的玩物,恰巧了解到Openstack可以近乎一键安装,于是便再次操弄起来。

这里要特别感谢《Linux就该这么学》的主题网站,两年前的论坛我是记不住了,但这个网站我个人觉得是非常适合参照学习来提升个人能力的。第22章 使用openstack部署云计算服务环境。 | 《Linux就该这么学》

0x81 安装准备

处于学习目的,我使用的资源都是这篇教程的网站提供的。再次感谢让我有一次轻松学习部署Openstack的机会。软件资源库 | 《Linux就该这么学》

0x82 RHEL 7 安装

和安装一般的虚拟机一样,但是我们需要添加两块虚拟硬盘,其中一块提供给Openstack的Cinder使用。内存至少4GB(这里坑了我好久,第一次是horizon配置失败,后来不定时的发生错误,经过监视CPU和内存的使用才发现根本原因是内存不够,我分配4G只有3.8可用,最高占用达到过3.77G),虚拟网卡至少要有一个方便主机直接控制虚拟机的,比如Host-Only模式,当然要链接外网下载Package的话可以再分一块NAT,我直接使用的桥接(我是在Virtual Box中完成的)。

Read More

Hexo Image Asset

0x81 官方推荐做法

source目录下默认有一个图片目录提供给模板引擎,这个目录就是Images,放在该目录下的文件可以直接被Hexo使用。

0x82 自定义管理

config.yml配置文件,这里面有Hexo的相关配置,其中post_asset_folder属性决定是否导出资源文件夹。
这个导出的资源文件夹就不仅仅局限于图片了,音乐视频都可以,它会在你使用hexo n [template] name创建一个新的Post时同时生成资源文件夹,并在生成时导出。

Wine 解决中文字体方框

0x81 Wine

Wine的全称Wine Is not An Emulator,意思是Wine并不是一个模拟器,的确是这样。
Wine和hyperv这种虚拟机的实现原理不一样,它并没有模拟整个OS逻辑,而是采用有点类似Arm Translator的解释器的方式,动态的将Windows的系统调用转换成Posix调用,从而使用Windows软件。
Wine 2.2 于2月末发布,并且官方将默认系统兼容曾提升到Windows 7。而由于我有使用IDA的需要,Linux版的Decompiler只有Arm支持,因此决定采用Wine的方式模拟运行Windows版IDAPro6.8。

0x82 Wine 中文字体问题

安装完Wine,并启动软件时,我们会发现只有一部分中文字体显示正常,其余的都显示方框(Wine 1.9.2的时候我记得所有的中文字都是方框)。
这是因为Wine使用的字体是Tahoma,而它对中文字体的支持并不好,或者说不提供支持,能显示一部分中文或许是因为个别地方使用了SimSun。

0x83 字体更换

字体的配置主要在注册表当中,HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts键下映射了当前系统中已存在并缓存的所有字体。
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes键下保存了字体的替代方案。
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink键下保存了字体的链接。
所以更换时一般有两种方式:修改FontSubstitutes或SystemLink:

  1. FontSubstitutes
    这里面是字体的替代方案,指定当系统需要这种字体时用哪一种字体替代,它配置的是要替代的字体名,网上很多解决办法就是通过在~/.wine/dosdrive_c/windows/Fonts下放置需要的字体(如果不是用Linux系统已有字体,而想要给特定的Wine Enviroment设定字体,可以放在这),
    然后配置替代方案。

  2. FontLink\SystemLink
    这里配置字体链接,当系统加载某一种字体时实际上读取了哪个字体文件就可以在这里指定。我的配置如下:

    配置结果

    这样重启Wine程序就能看到效果了。

  3. HKCU/S/Wine/Fonts/Replacements
    这里面是字体的替代方案,指定当系统需要这种字体时用哪一种字体替代,它配置的是要替代的字体名,和第一种方法一致,唯一的区别就是这种替换仅仅发生在字体缺失时。

FileProvider 踩坑

0x81 问题

之前简单介绍过FileProvider的使用,由于Android Nougat的出现,我们不得不为7.x带来的安全文件访问策略作出兼容。
在低于Nougat的设备上,我们仍然使用File Uri,而在Nougat及更新平台的设备上,我们需要使用Content Uri共享文件来避免触发UriExposed异常。
问题出现在图片裁剪的文件导出上,原本需求通过return-data返回Bitmap并显示即可,现在需要对该图片按照一定的比例裁剪并上传,考虑放弃手动保存Bitmap的方式,让图库裁剪完直接导出文件即可。
但是导出的Uri使用的是FileProvider,从而导致了图库等三方应用的崩溃。

0x82 是怎样的异常

这个异常很多人应该都见过,也不是什么罕见的异常,就是跨进程组件调用的SecurityException,描述类似如下这样:

1
2
3
java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from 
ProcessRecord{52a99eb0 3493:com.android.gallery3d/u0a57}
(pid=3493, uid=10057) that is not exported from uid 10071

ErrorTrace说得很明白,com.android.gallery3d尝试开启FileProvider但是权限被拒绝从而触发了安全异常。

Read More

Observable的转换

0x81 Observable的转换

Observable 作为RxJava中极为重要的角色,在我们使用的过程中,经常需要对其转换以适应我们的需求。
无论是变换操作,切换Scheduler还是其他对Observable的操作,都有可能出现模板代码,即操作类型都是一样的。
这个时候我们需要对这些操作进行封装,来减少模板代码带来的弊端。

0x82 封装统一的转换方法

最简单直接,也最容易的方法就是将这一系列操作封装成一个函数,在这个函数中进行需要的转换操作。
举个最简单的例子,Retrofit是我们常用的网络请求框架,经由RxJavaAdapter转换后,他的ApiService可以返回一个Observable,
而对于这个操作,我们通常需将线程切换到非主线程进行网络请求,请求完成后再返回主线程进行相应的UI操作,我们便可以封装起来以减少模板代码。

1
2
3
4
5
6
7
8
9
10
public static Observable<List<TestEntity>> test(Map<String, String> options) {
Observable<BaseEntity<List<TestEntity>>> observable = TestApi.getInstance().getApiService()
.test(options);
return wrapObservable(observable);
}

private static <T> Observable<T> wrapObservable(Observable<BaseEntity<T>> observable) {
return observable.map(new CommonFilter<T>()).subscribeOn(Schedulers.io()).observeOn(
AndroidSchedulers.mainThread());
}

该方法将Retrofit返回的Observable经过wrapObservable()方法转换成一个新的Observable,在订阅后Subscriber将接受经过转换的原始数据。

Read More