Simple...

view visibility

View的hide埋点缺失 通常针对一个View进行埋点时,一般会有show/hide两个埋点,分别对应于View的展现和消失。 而在View的生命周期中,其onAttachedToWindow()回调方法表示View即将展现;与之相对的onDetachedFromWindow()回调方法则表示View即将消失。所以,一般针对show/hide两个埋点,直接在onAttachedToWindow()和onDetachedFromWindow()中埋点即可。 然而当我们打开一个新的Activity页面时,我们会发现此时View已经看不到了,但是由于其onDetachedFromWindow()方法没有被系统回调,所以我们只有show埋点,而没有hide埋点,那么问题就来了,如果让show/hide这两个埋点就对应上呢? View的生命周期 对于一个View来说,其本身涉及的回调函数有很多,在此我们只关注与View的展现/消失相关的回调函数,为了方便描述,我们将该类函数统称为View的生命周期函数。其主要包括如下函数: // View被添加到Window时被调用,View开始绘制。 // View是可见的。 protected void onAttachedToWindow() {} // View所属于的Window展现/消失时被调用。 // View的展现/消失由visibility决定。 protected void onWindowVisibilityChanged(int visibility) {} // View或其祖先View的visibility发生改变时被调用。 // View的展现/消失由visibility决定。 // 注意: 滑动RecyclerView,当一个item view消失时该方法不会被调用。 protected void onVisibilityChanged(@NonNull View changedView, int visibility) {} // View从Window移除时被调用,View不再绘制。 // View不可见。 protected void onDetachedFromWindow() {} 通过以上方法,我们就可以知道一个View的可见状态是显示还是隐藏了。 获取View的可见状态 获取View的可见状态整体流程如下: LifecycleView -> ViewLifecycle -> ViewVisibilityObserver -> OnViewVisibilityChangedListener 首先通过重写View的上述生命周期函数来监听View的状态并将其分发出去。 public class LifecycleView extends View implements ViewLifecycleOwner { @Override protected void onWindowVisibilityChanged(int visibility) { super.

How to write sqlites extension

0x00 SQLite sqlite是一个使用c语音编写的库,其实现了一个的小巧、快速、自含、高可靠性、全功能的SQL数据库引擎。它仅依赖少数的几个系统调用。它是一个非常流行的数据库,几乎打包到了所有的移动设备中,同时有无数的应用程序内嵌了它。 0x01 SQLite扩展 sqlite extension是可以在运行时被sqlite加载的库,其主要有以下作用: 扩展sqlite的功能,如提供sqlite本身不支持的函数。 运行时加载扩展,可以动态更新,也可以保证只在需要时加载扩展,减少内存消耗。 扩展可以独立于sqlite本身单独开发,测试。 0x02 编写扩展 编写扩展时,我们可以从sqlite的官网复制一份模板代码,整个模板代码如下所示: /* Add your header comment here */ #include <sqlite3ext.h> /* Do not use <sqlite3.h>! */ SQLITE_EXTENSION_INIT1 /* Insert your extension code here */ #ifdef _WIN32 __declspec(dllexport) #endif /* TODO: Change the entry point name so that "extension" is replaced by ** text derived from the shared library filename as follows: Copy every ** ASCII alphabetic character from the filename after the last "/" through ** the next following ".

auto_create_wx_acitivty_by_annotation

如何使用注解生成WXEntryActivity? 在Android中使用注解时,需要创建java模块,否则无法导入javax.annotation.processing.AbstractProcessor;相关依赖。 1. android注解配置 1. 使用annotationProcessor设置参数 a) /gradle/wrapper/gradle-wrapper.properties distributionUrl = https\://services.gradle.org/distributions/gradle-4.4-all.zip b) 配置annotationProcessor buildscript { repositories { // Gradle 4.1 and higher include support for Google's Maven repo using // the google() method. And you need to include this repo to download // Android plugin 3.0.0 or higher. google() ... } dependencies { classpath 'com.android.tools.build:gradle:3.1.0' } } android { defaultConfig { javaCompileOptions { annotationProcessorOptions { arguments = [ key1 : 'key1Value', key2 : 'key2Value' ] } } } } dependencies { annotationProcessor 'xxx:xx:xxx' } 2.

golang dev

配置Golang开发环境(仅介绍Mac平台) install Homebrew $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" install golang $ brew install go $ go version go version go1.11 darwin/amd64 # 配置GOROOT $ printf "\n\nexport GOROOT=/usr/local/Cellar/go/1.11/libexec \n" >> ~/.bash_profile # 配置GOPATH $ mkdir -p ~/go/gopath $ printf "export GOPATH=~/go/gopath \n" >> ~/.bash_profile # 追加到PATH $ printf "export GOBIN=$GOROOT/bin \n" >> ~/.bash_profile $ printf "export PATH=$PATH:$GOBIN:$GOPATH/bin \n\n" >> ~/.bash_profile $ source ~/.bash_profile install privoxy privoxy可以将http请求代理到socks5,下面将 privoxy + shadowsocks 搭配使用

flutter

[TOC] 0x00 Flutter flutter是google开发的移动端UI框架,支持android和ios。该框架使用dart语言进行开发,在skia的基础上开发了一套公共组件达到android与ios共用代码的目的。 1. Flutter系统架构 2. Platform Channels flutter使用methodChannel/flutterMethodChannel来访问系统原生api。 0x01 使用Flutter开发应用程序 1. 下载配置Flutter SDK 由于众所周知的原因,我们首先需要进行如下配置,让flutter通过国内镜像下载sdk等依赖。 export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn 安装sdk $ git clone -b beta https://github.com/flutter/flutter.git $ export PATH=`pwd`/flutter/bin:$PATH 使用flutter doctor检测本机环境,然后根据提示安装依赖软件。 $ flutter doctor Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel beta, v0.1.5, on Mac OS X 10.11.6 15G19009, locale zh-Hans) [✓] Android toolchain - develop for Android devices (Android SDK 27.

rxjava distinguish user cancel from others

RxJava区分用户主动取消 0x00 取消订阅 首先我们知道使用rxjava执行操作的时候,基本流程大致如下,取消操作时只需要调用subscription.unsubscribe()即可: Subscription subscription = Observable.<String>create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { } println("call..."); subscriber.onNext("aaaaaaa"); subscriber.onCompleted(); } }).subscribeOn(Schedulers.io()) .observeOn(Schedulers.newThread()) .unsubscribeOn(Schedulers.newThread()) .subscribe(new PrintSubscriber<>()); 那么我们在执行操作的过程中,如何知道一个操作被取消了呢?熟悉rxjava的童鞋可能一下子就会想到,直接使用doOnUnsubscribe()不就可以了吗?这有什么好纠结的。 Subscription subscription = Observable.<String>create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { } println("call..."); subscriber.onNext("aaaaaaa"); subscriber.onCompleted(); } }).

rxjava 2.0 head first

RxJava 2.0 简单介绍 一年一年有一年,RxJava也新增了2.0版本,那么为什么是新增版本而不说升级版本呢? 因为2.0版本和1.0版本两者并不兼容,2.0版本是基于Reactive-Streams规范重新设计而来;同时1.x版本和2.x版本两者会并行开发维护,但是1.x版本只维护到2018-03-31。 下面我们简单介绍一下两者的不同。 0x00 依赖&包名不同 使用rxjava 1.x、2.x版本的依赖如下: // rxjava 1.x compile 'io.reactivex:rxjava:1.1.6' // rxjava 2.x compile "io.reactivex.rxjava2:rxjava:2.x.y" 包名修改如下: // 1.x -> 2.x rx.** -> io.reactivex.** 0x01 Observable与Flowable Observable在2.0版本不支持backpressure,它会缓存全部的数据,一一发送给消费者,如果消费不及时,会产生OOM。于此对应,在2.x版本新增了Flowable,支持设置/自定义backpressure,同时在创建时必须制定backpressure。 Flowable.create(new FlowableOnSubscribe<Object>() { @Override public void subscribe(FlowableEmitter<Object> e) throws Exception { for (int i = 0; i < 256; i++) { e.onNext(i); } e.onComplete(); } }, BackpressureStrategy.BUFFER).subscribe(System.out::println, Throwable::printStackTrace); 0x02 Single 当使用Single时,生产者调用onSuccess()通知订阅者,同时终止整个事件流,生产者只能发送一个success事件,订阅者也只能收到一个success事件,适用于网络请求等确定只有单个事件的事件流。对于1.x版本而言,则需要主动调用onComplete()来终止事件流。 注意: Single没有onComplete()方法;只能产生success、error两种事件。 Single.create(s -> s.

Gradle plugin 3.0 & Android Studio 3.0

Gradle plugin 3.0 & Android Studio 3.0 我们主要讲一下升级gradle plugin 3.0过程中遇到的问题与解决方案。 Gradle Plugin 3.0 1. 升级gradle plugin插件版本为3.x buildscript { repositories { mavenLocal() jcenter() // You need to add the following repository to download the // for new plugin. google() } dependencies { classpath 'com.android.tools.build:gradle:3.0.0-beta2' } } 2. 升级gradle版本为4.1 修改gradle/wrapper/gradle-wrapper.properties中的distributionUrl为gradle-4.1-all。 distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 3. 升级build-tools为25.0.3 The specified Android SDK Build Tools version (22.0.1) is ignored, as it is below the minimum supported version (25.

android check app running foreground

0x00 使用Application.ActivityLifecycleCallbacks检测App运行在前台/后台 import android.app.Activity; import android.app.ActivityManager; import android.app.Application; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.util.Log; import java.util.List; public class ApplicationLifecycle implements Application.ActivityLifecycleCallbacks { private Handler mHandler = new Handler(); private Runnable mCheck = new Runnable() { @Override public void run() { Log.e("ApplicationLifecycle", "check task exec; mCounter=" + mCounter); if (mCounter == 0) { Log.e("ApplicationLifecycle", "check task exec; get runningAppProcessInfo."); final ActivityManager am = (ActivityManager) mAppContext.getSystemService(Context.ACTIVITY_SERVICE); // NOTE: getRunningAppProcess() ONLY GIVE YOU THE PROCESS OF YOUR OWN PACKAGE IN ANDROID M // BUT THAT'S ENOUGH HERE List<ActivityManager.

sha1-collision & android signature algorithm

SHA1-Collision & Android Sign 参看SHA1-collision我们可以知道,SHA-1签名已经不安全了,签名算法可以考虑升级到SHA-2或者其他算法。 0x01 SHA1-Collision 1. SHA-1是什么? SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)[2]。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数,参考:SHA-1 2. SHA1-Collision是什么? 两个内容不同的数据,SHA-1算法会生成相同的摘要信息,参考:SHA1 collision Two PDF。 3. SHA1-Collision对Android的影响 Android SDK默认对apk使用SHA-1签名,在最坏的情况下,攻击者可以伪造SHA-1值相同的文件替换已签名apk中的文件来达到攻击的目的。 0x02 Android的证书验证机制 我们知道Android的Apk文件是一个压缩文件,文件结构大致如下: . ├── AndroidManifest.xml ├── META-INF │ ├── CERT.RSA │ ├── CERT.SF │ └── MANIFEST.MF ├── assets ├── classes.dex ├── classes2.dex ├── classes3.dex ├── lib ├── res └── resources.arsc apk相关的签名相关的文件在META-INF目录中,其中: MANIFEST.MF 遍历APK包中除了META-INF\文件夹以外的所有文件,利用SHA-1算法生成这些文件的消息摘要,然后转化为对应的base64编码。MANIFEST.MF存储的是文件的摘要值,保证完整性,防止文件被篡改。 .SF xx.SF文件(xx为使用者证书的自定义别名,默认为CERT,即CERT.SF),保存的是MANIFEST.MF的摘要值, 以及MANIFEST.MF中每一个摘要项的摘要值,然后转化成对应的base64编码。虽然该文件的后缀名.sf(SignatureFile)看起来是签名文件,但是并没有私钥参与运算,也不保存任何签名内容。 .RSA/.DSA .RSA/.DSA文件(后缀不同采用的签名算法不同,.RSA使用的是RSA算法,.DSA使用的是数字签名算法DSA,目前APK主要使用的是这两种算法),保存的是第二项.SF文件的数字签名,同时还会包括签名采用的数字证书(公钥)。特别说明,当使用多重证书签名时,每一个.sf文件必须有一个.RSA/.DSA文件与之对应,也就是说使用证书CERT1签名时有CERT1.SF和CERT1.RSA,同时采用证书CERT2签名时又会生成CERT2.SF和CERT2.RSA。 Android 系统不允许安装没有任何数字签名的应用APK程序,所有应用程序必须使用某个证书进行签名(一般为应用开发者自签名证书),