如果把煤这个主角当作一个物种来看待,整本书就像在讲一个物种的复仇一样~
煤给人带来了气候,带来了温暖,也带来了死亡。人类为了挖煤,深入几百几千米的地下,终日不见阳光,却是为了挖掘亿万年前的太阳能源。
地址 : https://github.com/xesam/AndroidLogTools
现在包含两个类:
compile 'dev.xesam.android:AndroidLogTools:0.1.2'
对 android.util.Log 的简单封装,支持 d(Object… content) 的调用形式,避免对 String 的硬性要求,使用示例:
开启日志(默认不打印):
L.enable(true);
打印日志:
L.d();
L.d(null);
L.d(null, null);
L.d(this);
L.d(this, this);
L.d(1);
L.d(1, 2);
L.d("a");
L.d("a", "b");
结果如下:
D/L[empty_tag]: L[empty_content]
D/L[null]: L[null]
D/L[null]: L[null]
D/MainActivity: L[empty_content]
D/MainActivity: dev.xesam.android.logtools.demo.MainActivity@32dd3da6
D/1: L[empty_content]
D/1: 2
D/a: L[empty_content]
D/a: b
将崩溃记录写入外部文件中,便于检查。(注意,只在测试的时候才使用),使用示例:
在 Application 中注册:
CrashLog.register(this);
即可。
记录日志保存在 XXX/sdcard/Android/data/#{package_name}/files/目录之下,比如:
crash.2015-10-06T12:03:24.txt
Java 与 Js 互调封装 AndroidJavascriptBridge
这个比较常见,适用于同步,没有回调的情况
示例:
java :
@JavascriptInterface
public void java_fn() {
}
webView.addJavascriptInterface(this, "Java");
js:
Java.java_fn();
两种形式
js.call({
data : {},
fn:function(){}
})
js.call(data, function(){
})
主要问题 :
所以,一种处理方式就是在 js 上下文种保持整个请求,在 java 执行完毕之后,从 java 去主动调用 js 方法,找到原始的请求,并执行正确的回调。
示意图如下:
如此可以嵌套调用下去。一个完整的封装实现:AndroidJavascriptBridge
java:
final JavascriptBridge javascriptBridge = new JavascriptBridge(webView);
javascriptBridge.registerLocalRequestHandler("java_fn1", new LocalCallRequest.RequestHandler<Person>() {
@Override
public Person formJson(String requestString) {
Gson gson = new Gson();
return gson.fromJson(requestString, Person.class);
}
@Override
public void handle(LocalCallRequest localCallRequest, Person data) {
Toast.makeText(getApplicationContext(), data.name, Toast.LENGTH_SHORT).show();
if (localCallRequest.hasCallback()) {
javascriptBridge.deliveryRemoteCallback(localCallRequest, "succ", new Person().getJSONObject());
}
}
});
js:
document.getElementById('call_java_2').addEventListener('click', function () {
bridge.invoke_remote_call('java_fn2',
{
succ: function (resp) {
log("call_java_2:" + resp.name);
}
},
true);
}, false);
KitKat 之后可以使用 evaluateJavascript。其他择可以使用传统的 WebView.loadUrl
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mWebView.evaluateJavascript(script, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
}
});
} else {
mWebView.loadUrl("javascript:" + script);
}
见上图
js:
bridge.register_local_request_handler('js_fn1', function (data, callback_id) {
log("js_fn1:java call js[js_fn1]");
});
java:
findViewById(R.id.js_fn1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RemoteCallRequest remoteCallRequest = new RemoteCallRequest("js_fn1", new Person().getJSONObject());
javascriptBridge.invokeRemoteCall(remoteCallRequest);
}
});
github 地址InfiniteImageView
xml形式:
<dev.xesam.android.widgets.InfiniteImageView
android:id="@+id/ifi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
InfiniteImageView:speed="-2dp"
InfiniteImageView:src="@drawable/a_2" />
java代码:
1 | InfiniteImageView.setDrawable(new AzDrawable()); |
LinearLayoutCompat 所在位置 android.support.v7.widget.LinearLayoutCompat
2.3 中使用xml定义drawable的时候有bug,所以在2.3 下最好还是使用图片作为分隔符
定义分割线 /drawable/linearlayout_compat_divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#000000" />
<size android:width="5dp" />
</shape>
定义layout
<android.support.v7.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
app:divider="@drawable/linearlayout_compat_divider"
app:dividerPadding="10dp"
app:showDividers="middle"
tools:context="dev.xesam.android.support.v7.widget.LinearLayoutCompatDemo">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.0"
android:background="#ff0000"
android:gravity="center"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.0"
android:background="#00ff00"
android:gravity="center"
android:text="@string/hello_blank_fragment" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.0"
android:background="#0000ff"
android:gravity="center"
android:text="@string/hello_blank_fragment" />
</android.support.v7.widget.LinearLayoutCompat>
整理版本.原文地址: http://bbs.pediy.com/showthread.php?t=184592
public class Hello{
public static void main(String[] argc){
System.out.println("Hello, Android!");
}
}
javac Hello.java
生成 Hello.class 文件
dx --dex --output=Hello.dex Hello.class
编译正常会生成 Hello.dex 文件 。
adb root
adb push Hello.dex /sdcard/
adb shell
dalvikvm -cp /sdcard/Hello.dex Hello
得到输出如下:
Hello, Android!
在我们写一些 demo 的时候,经常需要针对每种情况写一个用户示例,新建 Activity 的过程太麻烦,所以这个库的作用就是自动帮你创建索引式的导航列表,一行代码搞定所有的示例。
compile 'dev.xesam.android:quick-demo-creator:0.1.0'
用法:
1 | QuickDemo.inflateActivity(activity, R.id.listview); |
用法:
将 dev.xesam.android.quickdemo.QuickDemoActivity 设置为 LAUNCHER Activity 即可
如果觉得不想使用demo,sample之类的名称,可以自定义多虑规则 参见 SimpleFilter:
1 | public class SimpleFilter implements QuickDemoFilter { |
基于Handler的Android定时器与倒计时器
源码地址:Github AndroidTimer
支持操作 :
以上方法是同步方法,请不要在回调方法里面调用以上方法。
1 | new CountTimer(100) { |
1 | new CountDownTimer(100) { |
创建多个任务:
1 | MultiCountTimer multiCountTimer = new MultiCountTimer(10); |
取消任务:
1 | multiCountTimer.cancel(2); |
参考自 Android SDK 中的 CountDownTimer
post的数据在 request Payload 内, 因此,Request Headers 里面带有一个 boundary 属性,服务器收到 post 请求之后,通过 boundary 将参数从 Payload 中分离出来。
即
params = body.split(boundary)
唯一需要注意的是,CRLF(回车换行符)的使用。HTTP协议对CRLF的内容分割要求还是略严的。
Content-Type:multipart/form-data; boundary=----WebKitFormBoundarydKY7YrWhI3bdiNYz
--{boundary}
Content-Disposition: form-data; name="{name1}"
{value1}
--{boundary}
Content-Disposition: form-data; name="{name2}"
{value2}
--{boundary}
Content-Disposition: form-data; name="{file_name1}"; filename="xxx.png"
Content-Type: image/png
0x120x12...
--{boundary}--
本文的目标是用最简代码实现 ButterKnife 的核心功能。示例代码在github上: ButterKnifeProcedure
注解处理器
Java5 中叫APT(Annotation Processing Tool),在Java6开始,规范化为 Pluggable Annotation Processing。
找到所有被注解的属性或者方法,将所有的信息收集到对应的“类数据集”中。
根据每一个“类数据集”,生成对应的java源文件。由于这些文件并不是在运行时生成的,因此也无需动态编译,注解处理器运行完成之后,
编译器会处理所有的编译流程。
运行时动态注入,即用户常规调用的 ButterKnife.bind(activity)
这一步为了避免蹩脚的调用,使用了运行时反射,但是作者对每一个类进行了缓存,因此,不会对执行效率产生多大影响。
在最新的 ButterKnife 源码(2015.06.08)中,ButterKnife已经重构了部分方法:
ButterKnife#inject -> ButterKnife#bind
@InjectView -> @FindView
等等,具体变化可以去看官方文档,本文档后续代码使用最新版本代码演示。
1 |
|
得到:
1 | new FieldViewBinding(vView1, android.view.View, 100) |
1 | MainActivity: |
为了便于在反射时容易实例化生成的类,每一个生成的类都实现了一个 ActivityBinder
1 | package sample; |
我们在 MainActivity 中调用 ButterKnife#bind,第一件事就是找到对应生成的 Bind 工具类,这里遵循命名规则(在对应类后增加 $$ViewBinder 后缀),直接使用动态加载并实例化:
1 | Class<?> activityBindingClass = Class.forName(targetClass.getName() + ButterKnifeProcessor.SUFFIX); |
获得相应的 ActivityBinder 之后,使用 ActivityBinder#bind 进行绑定,与手动调用 findViewById 效果相同
运行:
ButterKnifeProcedure/src$ ./run.sh
结果:
mainActivity.vView1.id = 100
mainActivity.vView2.id = 200