blog of faywong

love coding, love life


  • 首页

  • 关于

  • 归档

  • 标签

美化终端里的生活

发表于 2017-06-06 |

我的热爱

从 2007 年接触 Linux 开始,便一直习惯在终端里做事情。最开始是简单的 c/c++ 编程,后边遇到了 vim 便一发不可收拾。

Linux 世界里有很多神器——LaTeX, vim, emacs,个中奇技淫巧可能一辈子都难以穷尽,但每个爱他们的人都至爱着他们可能习惯了上十年的若干特性。我主要用着 bash、vim、ctags/cscope 这几件。

zsh 敢情也是极好的 shell,只是我习惯使然,也是惰性使然,不会再去了解它了。

vim 有两个系列,主要是 NeoVim 和 经典 vim,前者是由于经典 vim 在新特性的开发上太慢而分裂出来的版本,在异步化编程的支持、buffer 管理方面有很多新特性,给用户的感知就是做事能多任务,效率会更高,明显地降低前端界面的卡顿感。比如自动补全这种场景需要 clang 一边咀嚼源代码生成 vst 信息一边给 vim 前端提供数据就非常依赖这个特性; 有了异步 job control neovim 里生成 tags 也变得更优雅而安静。最近 vim 8.x 也发布了,朝着 NeoVim 的方向前进了一大步,所以两者也相对比较接近了。我最近也习惯于使用 NeoVim 了。

vim 和 emacs 之争是一个口味问题,并且我见过的人中基本上只能熟悉其中的一种,没办法同时熟悉两种,这就跟你没法频繁变换两种键位差异巨大的键盘打字一样。功能上两者都非常强,有久经考验的插件生态。在能做的事这块可能 emacs 占优,基本上你在其他很多软件里能获得的功能都可以在一个 emacs 里搞定,比如听歌、上网、写博客等等,谁让它是用只要 7 个函数就能构造出全宇宙的 lisp 写的呢。

好了。以上就是我对终端的简单热爱,其中 90% 是对 vim 的热爱。

为了改善我们的 coding 生活,同时号召那些没有在终端里生活习惯的人尝试这样一种新的方式,我谈一谈如何美化终端里的生活。

终端选择

我们这里所说的“终端”不是原始的那个终端,而是终端模拟器软件,在这个的选择上我倾向于选择 iTerm2(对了,我现在是忠实的 Mac 用户),在真彩色方面有优势,其他特性在这里我不多展开。

终端配色

终端的主题配色我推荐 base 16,个人很喜欢 base16-3024。

程序员字体

程序员编程字体我推荐 INCONSOLATA,在过去我使用过微软雅黑 consolas hybrid,source code pro 等那个时代不错的程序员字体。

vim 插件管理器

NeoVim 插件这块我选择了 vim-plug,整个的 NeoVim 配置文件可从我的 coding pack 仓库下载

vim 插件

我常用以下几个经典插件:

  • cscope/ctags (搜索利器)
  • ctrlp
  • NERDTree
  • airline (配合 base16-3024 主题味道更好)
  • youcompleteme (目前用得不多,接下来打算重点用用)
  • goyo (markdown 语法高亮)

网上关于 vim 插件的文章汗牛充栋,我这里链接一篇适合初学者的——打造一个现代化的vim编辑器: neovim + nyaovim + youcompleteme + airline。

vim 对写作的好处

由于 vim 里写作不需要去操作鼠标,这样你的双手就尽情在键盘上挥洒(跳转、创建文件、提交代码、切 tag 都可以无缝完成)就好了。可以得到一种沉浸式写作的体验。

geek 的标志

在计算机这个行业,一个 geek 最明显的标志就是:你会熟练使用 vim 或 emacs

图例

附上几张案发现场图

001
002
003
004

ndk-gdb时提示应用不可调试的解决方法

发表于 2017-06-03 |

在开发包含c/c++本地代码的android项目中,通过gdb来调试代码是必不可少的前提。

android官方为此提供了ndk-gdb,看起来非常之nice。但个人在实践中发现还是有一系列问题需要记载下(ndk版本:r10e):

1) ndk-build NDK_DEBUG=1这个选项编译时要加上,一般将之定制在你的c/c++ builder中

2) 即便你按照1)做了,在项目根目录运行ndk-gdb的时候还是会报以下错误:

ERROR: Package faywong.github.io.mediakit is not debuggable ! You can fix that in two ways:

  • Rebuilt with the NDK_DEBUG=1 option when calling ‘ndk-build’.

  • Modify your manifest to set android:debuggable attribute to “true”,

    then rebuild normally.

After one of these, re-install to the device!

然后我们乖乖地跑到AndroidManifest.xml里边去修改Application标签的debuggable属性,eclipse会提示你不能hardcode,可以通过如下方式设置下:

QQ20151012-1

3) 接下来还有可能会遇到如下问题:

ERROR: Could not find gdb.setup under ./libs/

这是由于ndk-gdb命令的bug带来的,它没有去参照ABI的不同设置去不同的目录下找gdb.setup文件(是一个脚本文件,帮你做一些繁琐的gdb server的启动,gdb client的启动和设置等任务)。

简单绕过这个错误的方式是将libs/{your abi, e.g. armeabi-v7a}里的gdb.setup直接拷贝至libs目录下

4) 在你跋山涉水,翻山越岭走了这么久之后,再次运行ndk-gdb,会出现以下惊喜:

ERROR: Non-debuggable application installed on the target device.

Please re-install the debuggable version!

更新

现在可以结合gradle-experimental插件和ndk中搭载的lldb + android studio 2.0断点native代码了,虽然还不那么完善,bug多多,但是相比过去的ndk-gdb时代还是进步一点了。

调用WebView中onPause方法需要好好权衡

发表于 2017-06-03 |

最近踩了一个坑,有个非全屏的dialog遮住我的WebApp容器时,WebApp容器中JS代码操纵dom对象显示文本(input框中)巨慢(俗话描述就是“软键盘中输入了字,但是上屏很慢”)。

debug了一天之后发现。在WebView的onPause方法调用后,其内部JS引擎执行JS代码会慢好几倍。所以是否需要跟随Activity的生命周期调用onPause方法需要根据应用场景来区分:

若是应用中严格依赖JS做一些比较紧要的事情,则不应该onPause WebView。否则应该onPause WebView以释放一部分系统资源。

三星GS4(android 4.3)上webview crash问题

发表于 2017-06-03 |

最近发现客户端中android4.3上GS4手机上的WebApp应用特别容易crash。分析了源代码之后发现,在ActivityThread中回收内存时会调用EGLImpl里边去,回收RenderThread,进而调用到计算CPU FPS的逻辑,进而crash:

java.lang.Error: signal 11 (Address not mapped to object) at address 0xbe59dff0 [at libPowerStretch.so:0x2d4c (_ZN11LucidConfig13calcTargetFPSEi+0x1b)]
at system.lib.libPowerStretch_so.0x2d4c(LucidConfig::calcTargetFPS(int):0x1b:0)
at system.lib.libPowerStretch_so.0x2f23(LucidConfig::isLucidActive(bool):0x86:0)

因为在问题出在系统层而android应用回收内存这个message是ActivityManager发出,为正常且必要的行为,无法规避。最终选择如下方式将其绕过:

public class H5WebViewRenderPolicy {
public static boolean shouldDisableHardwareRenderInLayer() {
// case 1: samsung GS4 on android 4.3 is know to cause crashes at libPowerStretch.so:0x2d4c
// use GT-I95xx to match more GS4 series devices though GT-I9500 is the typical device
final boolean isSamsungGs4 = android.os.Build.MODEL != null && android.os.Build.MODEL.contains("GT-I95") && android.os.Build.MANUFACTURER != null && android.os.Build.MANUFACTURER.equals("samsung");
final boolean isJbMr2 = Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR2;
if (isSamsungGs4 && isJbMr2) {
return true;
}
return false;
}
}

以上定义一个渲染策略类(方便以后维护),针对GS4 + android 4.3这种组合在WebView layer层面关闭硬件加速(这样就不会存在RenderThread,自然也就没法触发上文的crash)。

之后在自定义WebView中利用上以上渲染策略类:

final boolean meetApiLevel11 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
if (H5WebViewRenderPolicy.shouldDisableHardwareRenderInLayer() && meetApiLevel11) {
final View underlyingWebView = webView.getUnderlyingWebView();
if (underlyingWebView != null && webView.getType().equals(WebViewType.SYSTEM_BUILD_IN)) {
try {
underlyingWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
} catch (Exception globalException) {
globalException.printStackTrace();
}
}
}

更新 2015/2/1

该现象表现在多款三星制造的搭载android 4.3系统的手机上,不仅限于GS4

WebView getUserAgentString的副作用分析

发表于 2017-06-03 |

源代码如下(以android 4.0.4为例):

public synchronized String getUserAgentString() {
if (DESKTOP_USERAGENT.equals(mUserAgent) ||
IPHONE_USERAGENT.equals(mUserAgent) ||
!mUseDefaultUserAgent) {
return mUserAgent;
}
boolean doPostSync = false;
synchronized(sLockForLocaleSettings) {
Locale currentLocale = Locale.getDefault();
if (!sLocale.equals(currentLocale)) {
sLocale = currentLocale;
mUserAgent = getCurrentUserAgent();
mAcceptLanguage = getCurrentAcceptLanguage();
doPostSync = true;
}
}
if (doPostSync) {
postSync();
}
return mUserAgent;
}

其中后半段代码用于将当前locale设定同步到生成的UA中。所以该方法是有副作用的。

在一个使用了WebView的Activity里实例化完成和系统Configuration改变后都需要调用一次getUserAgentString()来让WebView的UA(前提是不要设置自己的UA,即没有针对此WebView调用过setUserAgentString)中locale分量与系统的设定保持一致。以免在访问多语言网站时出现适配问题。

android4.1上WebView页面加载成功时出现IllegalArgumentException

发表于 2017-06-03 |

异常的堆栈如下:

java.lang.IllegalArgumentException: bad parameter
at org.apache.http.client.utils.URLEncodedUtils.parse(URLEncodedUtils.java:139)
at org.apache.http.client.utils.URLEncodedUtils.parse(URLEncodedUtils.java:76)
at android.webkit.AccessibilityInjector.getAxsUrlParameterValue(AccessibilityInjector.java:412)
at android.webkit.AccessibilityInjector.shouldInjectJavaScript(AccessibilityInjector.java:327)
at android.webkit.AccessibilityInjector.onPageFinished(AccessibilityInjector.java:286)
at android.webkit.WebViewClassic.onPageFinished(WebViewClassic.java:4088)
at android.webkit.CallbackProxy.handleMessage(CallbackProxy.java:332)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4829)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
at dalvik.system.NativeStart.main(Native Method)

相关的google code android project里边的issue中论述如下:

“
A customer of mine running 4.1.2 just informed me that the issue is still
present! After this issue crashes my app, I have to pop up a dialog
telling them to disable all accessibility services.
Worst user experience ever, but what else can I do.
……
Fixed in an internal build, to be released in the next major release.
……
The bug doesn’t occur here in 4.2.1 (Galaxy Nexus, JOP40D).
“

开源的崩溃统计项目Socorro

发表于 2017-06-03 |

由Mozilla开源的崩溃统计项目Socorro非常适合针对客户端的崩溃闪退、日志/堆栈上报,服务端进行采集、处理、分析、报告。

客户端的工作由类库Breakpad完成。

服务端的工作由Socorro完成。

代码托管在github.

看这个提交和发布的数目,迭代程度还是挺深的。

这样针对移动开发,就不必重复造轮子了。

通过PackageManager query应用信息时出现java.lang.RuntimeException: Package manager has died

发表于 2017-06-03 |

这属于android中最常见的TransactionTooLargeException的后果表现之一。

该问题较多出现在使用PackageManager查询系统的应用的信息,通过Intent来过滤匹配目标应用组件(android四大组件)时,典型堆栈如下:

java.lang.RuntimeException: Package manager has died
at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:78)
......
Caused by: android.os.TransactionTooLargeException
at android.os.BinderProxy.transact(Native Method)
at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:1393)
at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:73)
... 17 more
android.os.TransactionTooLargeException
at android.os.BinderProxy.transact(Native Method)
at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:1393)
at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:73)

这是Java层的堆栈,原始抛出异常的地方在于文件frameworks/base/core/jni/android_util_Binder.cpp:

case FAILED_TRANSACTION:
LOGE("!!! FAILED BINDER TRANSACTION !!!");
// TransactionTooLargeException is a checked exception, only throw from certain methods.
// FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
// for other reasons also, such as if the transaction is malformed or
// refers to an FD that has been closed. We should change the driver
// to enable us to distinguish these cases in the future.
jniThrowException(env, canThrowRemoteException
? "android/os/TransactionTooLargeException"
: "java/lang/RuntimeException", NULL);
break;

该FAILED_TRANSACTION错误码定义于:
/bionic/libc/kernel/common/linux/binder.h中的BR_FAILED_REPLY

enum BinderDriverReturnProtocol {
BR_ERROR = _IOR_BAD('r', 0, int),
BR_OK = _IO('r', 1),
BR_TRANSACTION = _IOR_BAD('r', 2, struct binder_transaction_data),
BR_REPLY = _IOR_BAD('r', 3, struct binder_transaction_data),
BR_ACQUIRE_RESULT = _IOR_BAD('r', 4, int),
BR_DEAD_REPLY = _IO('r', 5),
BR_TRANSACTION_COMPLETE = _IO('r', 6),
BR_INCREFS = _IOR_BAD('r', 7, struct binder_ptr_cookie),
BR_ACQUIRE = _IOR_BAD('r', 8, struct binder_ptr_cookie),
BR_RELEASE = _IOR_BAD('r', 9, struct binder_ptr_cookie),
BR_DECREFS = _IOR_BAD('r', 10, struct binder_ptr_cookie),
BR_ATTEMPT_ACQUIRE = _IOR_BAD('r', 11, struct binder_pri_ptr_cookie),
BR_NOOP = _IO('r', 12),
BR_SPAWN_LOOPER = _IO('r', 13),
BR_FINISHED = _IO('r', 14),
BR_DEAD_BINDER = _IOR_BAD('r', 15, void *),
BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR_BAD('r', 16, void *),
BR_FAILED_REPLY = _IO('r', 17),
};

其原因总结如下,首先Android系统对于Binder IPC每进程的事务buffer上限是1M(为所有Binder事务共享,且不区分目标系统的ram配置等因素),在通过PackageManager中获取系统内应用时,比如应用的Icon是个bitmap,且不同应用所制作Icon的品质(像素数)上差异很大。会占用大量内存导致Binder事务内存超过上限。

Binder事务buffer的尺寸定义在ProcessState.cpp:

#define BINDER_VM_SIZE((1 * 1024 * 1024) - (4096 * 2))
......
ProcessState::ProcessState(): mDriverFD(open_driver()), mVMStart(MAP_FAILED), mManagesContexts(false), mBinderContextCheckFunc(NULL), mBinderContextUserData(NULL), mThreadPoolStarted(false), mThreadPoolSeq(1) {
if (mDriverFD >= 0) {
// XXX Ideally, there should be a specific define for whether we
// have mmap (or whether we could possibly have the kernel module
// availabla).
#
if !defined(HAVE_WIN32_IPC)
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
}#
else
mDriverFD = -1;#
endif
}
LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating.");
}

App icon对应的数据模型如下:

class AppInfo {
......
private BitmapDrawable icon
......
}

该问题是android系统的限制,只能通过上层来捕获该异常来绕过。当然也不仅限于PackageManager,其他系统服务,应用间的Binder调用也可以带来这一问题。

最后的最后,由于在c++向java层抛出这个异常的场景有多种(见上边c++代码中“FAILED_TRANSACTION”处的注释),所以当你收到了TransactionTooLargeException的时候,不意味着真正的Transaction is Too Large。

应用压入后台时由WebView带来的耗电问题

发表于 2017-06-03 |

最近遇到一个应用打开WebApp后,将应用压入后台,出现比较耗电的问题。集合众多同学的智慧之后定位到原因:

在WebKit的内核中会引用从各个平台(android, linux pc, mac)注入的自己的服务比如:

  • DeviceOrientationService
  • DeviceMotionService

而这两个Service在android平台上的实现皆通过注册侦听器到SensorManager来获知来自于加速度器,磁力计的方位、手机姿势数据。

Sensor的使用是极其浪费电力的一件事情,所以就出现了上文所述问题。

解决方法:

1) 在Activity的onPause(), onResume()时调用WebView.onPause(), WebView.onResume()以尽量将JS执行线程以及其他WebCore中的线程给停住

2) 在onStop()时将WebView中当前load的url记住,然后让WebView.loadUrl(“about:blank”),在onStart()时WebView.loadUrl({your_previous_url})

更新:
[2015/11]在最近的工作中还发现一种前端页面带来的耗电问题:当页面加载一些gif或者通过其他方式创建的动画效果,当webview变得不可见后这些动画如果没有被显式关掉,就会导致很严重的耗电问题。同时在桌面浏览器里边我也注意到页面里边持续运行的动画也会导致内存占用增大。

android库ShowcaseView

发表于 2017-06-03 |

一个非常适合用于对用户进行第一次使用进行指导的库ShowcaseView,截图示例如下:

showcase view 1

showcase view 2

下面给出一个实例,需要瞄定一个特殊的View(id为R.id.answers),在其上显示一个ShowCaseView:

new ShowcaseView.Builder(getActivity()).setTarget(new ActionItemTarget(getActivity(), R.id.answers))
//Here is where you supply the id of the action bar item you want to display
.setContentTitle("Title").setContentText("Description").hideOnTouchOutside().build();

项目主页位于:https://github.com/amlcurran/ShowcaseView,从其commit数和release数来看,应该非常靠谱。

接下来打算在项目中使用起来,后续补上使用感受。

12…5
faywong

faywong

blog of faywong, faywong

43 日志
16 标签
© 2017 faywong
由 Hexo 强力驱动
主题 - NexT.Muse