Comments

Why Proguard

Proguard 是什么?要清楚这个概念,我们先看看 Proguard 官方是怎么定义的,再看看 Android 官方是怎么定义

Proguard 官方

ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes. It optimizes bytecode and removes unused instructions. It renames the remaining classes, fields, and methods using short meaningless names. Finally, it preverifies the processed code for Java 6 or higher, or for Java Micro Edition.

ProGuard 是一个免费的压缩、优化、混淆,预验证 Java 类的工具。它能在编译期间检测并移除没有用到的类、变量、方法和属性,也能优化字节码并且移除没有用到的指令。ProGuard会把那些类、变量、和方法用一些短小且无意义的名称去重命名。最后,对于 Java 6 或者更高的版本,或者 Java Micro Edition,它还会预校验已处理的类代码,从而利于更快加载。

看起来有点意思,再来看一下 Android 官方的定义

The ProGuard tool shrinks, optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and methods with semantically obscure names. The result is a smaller sized .apk file that is more difficult to reverse engineer. Because ProGuard makes your application harder to reverse engineer, it is important that you use it when your application utilizes features that are sensitive to security like when you are Licensing Your Applications.

ProGuard 通过移除未使用的代码和使用一些语意模糊的名字来重命名类、变量、方法和属性名,从而达到压缩、优化,和混淆代码的目的。最终可以得到一个更小的 .apk 文件,这个文件会增大软件逆向工程(反编译)的难度。正因为 ProGuard 会让你的应用更加难以被逆向工程反编译,所以对于独立应用而言,如果你对你的代码安全很敏感,建议在签名阶段还是“ ProGuard 一下” 。

Read on →
Comments

很长时间以来 Mobile Lin 访问慢这个问题我是知道的,但是一直也没想着去整,主要是因为觉得真正搞技术的人肯定都知道是什么原因导致访问慢,而且一定也知道加速的办法是什么。但这其实都是在为自己的懒找借口。晚上微博上终于有哥们儿跟我说了:

毕竟手机上2G网络挂着VPN来访问你的网站,也不是一件容易的事。

好吧。既然用户有需求,那就开整呗~

Octopress 在国内访问速度的优化主要从两方面进行:

googleapis 相关

Octopress 默认使用了 google fonts 和 googleapis 的 ajax,但因为众所周知的原因它们在国内是被墙的。好在数字公司在这点上做了件好事,它们有一个这玩意儿:

360网站卫士常用前端公共库CDN服务

这么一来就好办了,打开 /Octopress/source/_includes/head.html

1
2
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400italic,400,700' rel='stylesheet' type='text/css'>

换成

1
2
<script src="//ajax.useso.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<link href='http://fonts.useso.com/css?family=Open+Sans:400italic,400,700' rel='stylesheet' type='text/css'>

gravatar 相关

gravatar 是一个全球公认的头像库,跟你的 e-mail 绑定。可惜,这么好的东西在我大天朝也是不存在的。不过,依旧好在国内有 duoshuo,他们提供了一个 gravatar 的缓存。打开 /Octopress/source/_includes/header.html

1
2
3
<script type="text/javascript">
  document.write("<img src='http://www.gravatar.com/avatar/" + MD5("YOUR_EMAIL") + "?s=160' alt='Profile Picture' style='width: 160px;' />");
</script>

换成:

1
2
3
<script type="text/javascript">
  document.write("<img src='http://gravatar.duoshuo.com/avatar/" + MD5("YOUR_EMAIL") + "?s=160' alt='Profile Picture' style='width: 160px;' />");
</script>

That’s all,我们 rake generate 一下之后本地预览一下就可以看到非常显著的效果。

参考

替换Octopress Google 字体库

Comments

不得不承认,长久以来,对于大部分 Android 工程师,分析内存泄露这一问题多少还是显得有些苦巴巴。因为自己去 dump HPROF 文件,再用 MAT 这类工具分析,对于之前没有接触过这方面工作的还是要一定学习成本的。而且因为这些代(da)码(keng)真的是你一行行写(wa)出来的,每个人在查自己代码的内存泄露问题时候多少都会想着“卧槽这里怎么可能有问题?这可是我亲手写的啊!!!”,这往往就让问题更加难以被发现。

今天,哦不,凌晨了。。。昨天!昨天,Android 开源界最伟(jian)大(zhi)高(kai)效(gua)的公司 Square 又向业界投下一颗重磅炸弹。推出了一个叫 LeakCanary 的玩意儿,可以通过简单粗暴的方式来让开发者获取自己应用的内存泄露情况。而且得益于 gradle 强大的可配置性,可以确保只在编译 debug 版本时才会检查内存泄露,而编译 release 等版本的时候则会自动跳过检查,避免影响性能。当然,理论上在 debug 阶段所有发现的问题也都该在 release 之前解决掉,否则就没有办法显得逼(ku)格(bi)满满了。

这货真的有这么好用?机智的我还是决定写个 demo 跑一下试试:

接入步骤

build.gradle

因为不想让这样的检查在正式给用户的 release 版本中也进行,所以在 dependencies 里添加

1
2
3
4
5
6
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:support-v13:+'
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}

接下来,在你的应用里写一个自定义 Application ,并在其中“安装” RefWatcher

1
2
3
4
5
6
7
8
9
10
public class AppApplication extends Application {

    private RefWatcher mRefWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        mRefWatcher = LeakCanary.install(this);
    }
}

记得把它作为 android:name 配到 AndroidManifest.xmlApplication 节点下。

大功告成,就是这么简单。。。

Read on →
Comments

本文由 林申 译自 Getting Java Event Notification Right,并由 唐尤华 校稿,首次发布在 ImportNew转载请务必注明出处

很多情况下你会定义一类事件,然后对其进行管理。然而,处理不当就会遇到 ConcurrentModificationException。本文通过示例介绍了使用 Java 事件通知(Event Notification)需要注意的一些细节。

通过实现观察者模式来提供 Java 事件通知(Java event notification)似乎不是件什么难事儿,但这过程中也很容易就掉进一些陷阱。本文介绍了我自己在各种情形下,不小心制造的一些常见错误。

Java 事件通知

让我们从一个最简单的 Java Bean 开始,它叫 StateHolder,里面封装了一个私有的 int 型属性 state 和常见的访问方法:

1
2
3
4
5
6
7
8
9
10
11
12
public class StateHolder {

  private int state;

  public int getState() {
    return state;
  }

  public void setState( int state ) {
    this.state = state;
  }
}

现在假设我们决定要 Java bean 给已注册的观察者广播一条 状态已改变 事件。小菜一碟!!!定义一个最简单的事件和监听器简直撸起袖子就来……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// change event to broadcast
public class StateEvent {

  public final int oldState;
  public final int newState;

  StateEvent( int oldState, int newState ) {
    this.oldState = oldState;
    this.newState = newState;
  }
}

// observer interface
public interface StateListener {
  void stateChanged( StateEvent event );
}

…接下来我们需要在 StateHolder 的实例里注册 StatListeners

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StateHolder {

  private final Set<StateListener> listeners = new HashSet<>();

  [...]

  public void addStateListener( StateListener listener ) {
    listeners.add( listener );
  }

  public void removeStateListener( StateListener listener ) {
    listeners.remove( listener );
  }
}

…最后一个要点,需要调整一下 StateHolder#setState 这个方法,来确保每次状态有变时发出的通知,都代表这个状态真的相对于上次产生变化了:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void setState( int state ) {
  int oldState = this.state;
  this.state = state;
  if( oldState != state ) {
    broadcast( new StateEvent( oldState, state ) );
  }
}

private void broadcast( StateEvent stateEvent ) {
  for( StateListener listener : listeners ) {
    listener.stateChanged( stateEvent );
  }
}

搞定了!要的就是这些。为了显得专(zhuang)业(bi)一点,我们可能还甚至为此实现了测试驱动,并为严密的代码覆盖率和那根表示测试通过的小绿条而洋洋自得。而且不管怎么样,这不就是我从网上那些教程里面学来的写法吗?

那么问题来了:这个解决办法是有缺陷的。。。

Read on →
Comments

明天回公司了,关电脑之前写点东西。博客似乎是好久没更新了,正好也总结一下。

//EVENT 2014

  • 2014.01 考研失败 (意料之中,虽然想来觉得浪费了时间,但是小妞劝我还是当经历吧,要是没有这段经历倒也真的可能会后悔)
  • 2014.02 ~ 2014.03 苦逼做东西找实习找工作
  • 2014.04 很幸运像魅族投出的人生第一份简历有了答复,当然也顺利拿到了实习 Offer
  • 2014.04.13 扬中 –> 珠海,开始了我的魅族工程师生涯
  • 2014.05 半个月多一点时间,在 Flyme 里面留下了第一款产品——系统升级
  • 2014.06 四年大学时光就此告别,我毕业了
  • 2014.06.09 班级聚餐,一班人喝得伶仃大醉,第一次向她说出了藏在心里好久的话
  • 2014.06.10 毕业回家,整理了全部情绪,准备 11 号回公司
  • 2014.06.13 谢谢小妞,好巧,我也喜欢你
  • 2014.06.23 毕业典礼,我在珠海,缺席了学生时代最后这场毕业典礼
  • 2014.07 ~ 2014.09 忙碌+收获最多的 3 个月,第一次完完整整看着一个原生的 Android 4.4.2 系统在大家的努力下,一步步演变成了如今的 Flyme 4。很高兴再次负责了 Flyme 4 中的“系统升级”项目,全新的一级界面和动画效果,最后完成的时候自己也不免感慨
  • 2014.09.02 MX4 正式上市。第一次感受到要把自己做的一个东西交到千万级的用户群手中,就像一个等待检验成果的孩子一般,既紧张又激动
  • 2014.10 ~ 2014.12 系统升级大致进入稳定维护期,抽身进了应用中心 & 游戏中心项目组,开发了其中的部分功能

这一年无论是在技术能力还是在工作交流能力上,自我感觉都有了很大的提升。技术方面本来一直准备抽空用一篇文章总结一下的,没想到也就这么一天天压到了现在都没弄。 还是感觉不管怎么样一定要注重技术的积累与总结,加上我有这么好的一个博客平台,所以今年我会努力把博客重新运作起来。

//TODO 2015

Wish List

  1. 买一部微单,好好学习摄影。之所以不买单反是因为真的觉得单反好重又累赘。另外,把女朋友拍漂亮似乎才是正经事!
  2. 6 月过后重新换一处地方住。最好要能看到海,因为小妞以后来的话会喜欢。要有一面可以用来布置的墙,可以挂一个电视下班以后看看电影。
  3. 如果预算充足,想买一套好一点的桌面音箱,还是好喜欢音乐,没办法。

Tech

  1. 学习更多 Android Lollipop 中新增的 API 用法
  2. 每月分析至少一款开源项目的源代码,或者翻译一篇国外网站的技术文章,后期我打算也开始向一些 IT 技术网站投稿做译者
  3. 好好学习设计模式, 尝试将它们的思想渗透进我的代码
  4. 让我的 Apps 更流畅,更顺滑
  5. 有可能的话学习一门新语言。比如 QML 或者 HTML 5,可以用来开发 Ubuntu Phone 的 App。
  6. 保持对新技术的热情,尽可能多去看一些“轮子”的实现方式,取人之长,补己之短

Life

  1. 把爸妈接过来看我一次
  2. 和小妞去海南旅游
  3. 我知道这一年会有很多操蛋的事情等着我,其实准确说每年都会有。所以但愿我能心平气和地一件件应付过来吧
Mac, OS, X
Comments

折腾了好几天总算搞定了 Mac OS 下 Octopress 的搭建。我使用的版本是 Yosemite。

要在 Mac OS 下搭建 Octopress ,需要以下几个步骤。当前,建议读者还是要有一点 Linux 基础,最好之前用过一款 Linux 发行版。

1.包管理软件

熟悉 Ubuntu 的同学可能知道,在 Ubuntu 下面你可以很轻松地通过 apt-get install 来得到你想要的包,如果没有的话还可以通过新立得来查找。而对于 Mac OS X 而言,苹果默认是不带这样一个包管理软件的,不过没关系,我们有 Homebrew 神器,你可以很简单地通过 ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 命令来获取它。安装完成后请记得 vim 一下你的 bash.profile ,增加 export PATH=/usr/local/bin:$PATH,再 source 一下让它生效。

成功安装后,你甚至就可以把它想象成 apt-get 命令,比如你可以执行 brew install wget 来完成你想要的包安装。

2.Gcc 编译环境

gcc 编译环境是为了安装 ruby 时编译作准备的,高大上的 XCode 里面已经带了一套很好的编译环境,所以这一步你需要去装好 Xcode,并运行一次,确保安装了对应的组件。

3. Ruby 1.9.3

Octopress 强制要求了必须用 Ruby 1.9.3 构建,而 Yosemite 自带的 Ruby 版本已经达到了 2.0.0。那么问题就来了——“如何在不破坏系统自带的 ruby 环境的前提下,切换到 Ruby 1.9.3 ?”方法就是使用 rbenv,执行如下命令即可:

1
2
3
4
5
6
7
8
9
*安装 rbenv
brew update
brew install rbenv
brew install ruby-build

*使用 rbenv 安装 1.9.3
rbenv install 1.9.3-p0
rbenv local 1.9.3-p0
rbenv rehash

回过头去看一下,其实正是 rbenv local 1.9.3-p0 命令保证了你在安装 1.9.3 的 ruby 同时不会破坏原有系统变量。此时执行 ruby -v,如果看到版本已经切到了 1.9.3,就证明大功告成。否则请重新执行:

1
2
rbenv local 1.9.3-p0
rbenv rehash`

如果执行完之后还是不行,可能是因为你的 rbenv 压根没生效。请vim一下你的bash.profile,增加export RBENV_ROOT=/usr/local/var/rbenv,再source一下让它生效。

4.最后几步

可能看到这里你已经醉了,但很遗憾,一切已经结束,接下来的照着官方教程走吧,Good Luck!

Comments

如果你的 App 因为权限原因需要设置 android:sharedUserId="android.uid.system" 那么 IDE 编译出的包通常是无法直接安装的,查看控制台会发现报 INSTALL_FAILED_SHARED_USER_INCOMPATIBLE 错误。这是必须的,随随便便一个 App 声明一下就可以和系统用户共享 ID ,岂不乱套了?

解决方法有如下两种:

第一种

如果你 repo sync 了 android 的整个源码,那么可以直接把你的 app 放到 /packages/apps 下面去 mm ,不过要记得在 Android.mk 中增加 LOCAL_CERTIFICATE 属性,这个属性具体有三个值:

系统中所有使用 android.uid.system 作为共享 UID 的 APK ,都会首先在 manifest 节点中增加android:sharedUserId=“android.uid.system”,然后在 Android.mk 中增加 LOCAL_CERTIFICATE := platform。可以参见 Settings 等

系统中所有使用android.uid.shared作为共享 UID 的 APK,都会在 manifest 节点中增加android:sharedUserId=“android.uid.shared”,然后在 Android.mk 中增加 LOCAL_CERTIFICATE := shared。可以参见 Launcher 等

系统中所有使用 android.media 作为共享 UID 的 APK,都会在 manifest 节点中增加android:sharedUserId=“android.media”,然后在 Android.mk 中增加 LOCAL_CERTIFICATE := media。可以参见 Gallery 等。

第二种

当然,毕竟不是每个人都有机会,或者有必要下载整个源码的。 简单地,当你用 IDE 编出 apk 之后,可以去 /build/tools/signapk/ 找到 signapk.jar 文件;再去 /build/target/product/security/ 里找到 platform.pk8platform.x509.pem 这两个文件。把它们连同你的 apk 扔进一个文件夹,然后 cd 到该文件夹下执行 java -jar signapk.jar platform.x509.pem platform.pk8 Origin.apk Signed.apk,得到的 Signed.apk 就可以直接 adb install了。

Copyright © 2014 - 2017 - linshen - @ . +