Java 中实例化一个抽象类对象的方式

在 Java 中抽象类是不能直接被实例化的。但是很多时候抽象类的该特点成为一个比较麻烦的阻碍。例如如果我想使用动态代理来给一个抽象类赋予其执行抽象方法的能力,就会有两个困难:1. 动态代理只能创建实现接口的一个代理对象,而不能是一个继承抽象类的对象。为此标准的 JVM 中有一些实现,例如 javassist 可以使用字节码工具来完成这一目的(ProxyFactory)。

JitPack 发布包含 Annotation Processor 的项目

普通的项目(单个 Library Module)发布到 JitPack,体验不错几乎是零配置的。尝试了一下直接构建了一下一个包含两个 module 的项目——一个 Android Library Module 和一个纯 Java Module 的时候,JitPack 只构建了 Java 的 Module。

关于 Android 背景色 Alpha 值的一个坑

这是公司项目一个留了很久的 bug,尝试找了很多次原因都没有头绪。这个 bug 是这样的:

定义了一个主题色为例如 #FF0000,很多界面控件都用了这个颜色。可是这个颜色在 app 使用过程中用着用着就会出现变成了透明的情况,而且出现透明之后,所有使用这个颜色的控件都会变成透明。

更奇怪的是,重启应用会恢复这个问题,但是操作一会儿又会出现。

Jenkins 中如何让 Android SDK 根据需要自动更新

每次项目中切换了新的 support,总是要登录构建服务器更新一下 SDK,很麻烦。其实 Jenkins 本身是支持自动更新的,稍微留一下会发现,构建时不存在的 SDK 的报错信息为:

You have not accepted the license agreements of the following SDK components: …

原来是 license 问题,需要手动去 agree 才能进行下一步,所以就中断了。但其实是可以跳过的,Stackoverflow 上有人引用了 Jake Wharton 给出了方法:

1
2
3
mkdir "$ANDROID_SDK/licenses" || true
echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_SDK/licenses/android-sdk-license"
echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_SDK/licenses/android-sdk-preview-license"

上面的哈希字符串据说是 license 文本的 sha1,所以如果 license 换掉的话也会失效。暂时这样用吧。

参考:http://stackoverflow.com/questions/38096225/automatically-accept-all-sdk-licences

从 Retrofit 源码学习 Java 的动态代理的使用

Retrofit 是当前 Android 最流行的 HTTP 网络库之一了,其使用方式比较特殊,是通过定义一个接口类,通过给接口中方法和方法参数添加注解的方式来定义网络请求接口。这种风格下定义一个网络接口变得很简单。不过 Retrofit 是如何使用一个接口的 Class 创建出来实现了该接口的对象呢?最近因为工作原因想封装项目中的网络请求部分,在解决获取泛型嵌套问题的时候,一直没有找到比较理想的方案,所以拜读了 Retrofit 的源码看看这个明星网络库是如何实现这一黑科技的。

Android 项目的 Jenkins 参数化构建

需求描述

项目分为三种类型构建:

  1. 测试环境构建 debug
  2. 测试环境构建后上传内测平台 (fir.im)
  3. 发布版构建 release

三种构建类型在完成后都保存构建输出的 apk,只有在类型 2 完成后执行上传到内测平台操作

Android App 开发环境和线上环境共存的 gradle 配置小技巧

前段时间考虑过一个问题:Android 开发者在供职公司开发一个 app,但是又需要在下班后使用这个 app 怎么办?一般公司的服务端都会区分线上和线下环境,在开发时使用开发的环境以免脏数据污染线上的环境,但开发环境收不到真实消息的推送,而下班后每次都卸载重装的话感觉又有些蛋疼。今天想到可以用设置 flavor 的方式解决这一痛点,分享一下这个方案的配置过程。

搭建 jenkins 构建 Android 项目

之前用过 Flow.ci、Travis 等公共的 CI 工具感觉很方便,但毕竟源码在别人那里多少有点别扭。Jenkins 是一个免费的高度可定制的 CI 开源项目,现在的公司使用它来构建 Android 项目,所以也来试一试,记录下搭建的过程以及遇到的问题,供自己和需要的人参考。

Dagger 2 中 Scope 用法

Dagger 2 简介

Dagger 2 是 Google 对 Square 下的项目 Dagger fork 出来的 Android IoC 框架,替换了原 Dagger 的反射使用代码生成进行注入,并添加了一些新特性。

@Scope

Dagger 1 中对注入对象的的注解只有 Singleton,而 Dagger 2 中可以使用 Scope 自定义注解来指定注入对象的作用域,来实现局部单例的效果。看了很多介绍文档都不是很清楚,使用 find usage 发现并没有生成代码对注解进行监听,推测 Scope 是影响了代码生成的结果。这里举个例子来说明如何使用 Scope 注解。

关于Adapter的一个异常

如果你在 Android BaseAdapter 中看到了这样一个异常:

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getImportantForAccessibility()' on a null object reference

十有八九是你忘了把 getView 传递的 convertView 给 return 了。。。

Activity 接收通知中的发送的 Intent 中的 Extras

在通知中创建一个通知,并且指定通知点击后的 Intent 时,会遇到 put 进 intent 中的 extras 在启动的 Activity 中无法获取到的问题。

查了一下,原因是从通知进入 Activity 时, getIntent() 方法默认返回的是 Activity 的「第一个」Intent,如果从通知点击时,Activity 在后台或者在睡眠,这个 intent 就不是我们想要传入的 intent。

那么怎么获取到新的 intent 呢?当然有办法:重写 Activity onNewIntent(Intent newIntent) 方法,获取这个方法里的 intent 中的 extras,就是我们从通知中 setContentIntent 中 PaddingIntent 里的 Intent 了(通知中发送 Intent 需要有一个 PaddingIntent 包装)。

然后该 Intent(被包装的) 还需要设置 Flag 为:

Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP

Android Studio 升级 El Capitan 之后的两个问题

升级 OS X 11 (El Capitan)后 Android Studio 遇到了两个麻烦的问题,在这里记一下。

1. 升级之后字体发虚

这个主要是低分屏下看起来很不舒服,同事的高分屏就没有这个。字体发虚是 JDK 的问题,因为 Jetbrains 全家桶都是基于 Java 开发的,所以把 JDK 的这一不良特性也带了进来,下载直接去苹果官网即可:https://support.apple.com/kb/DL1572

不过也不急解决这个问题,因为下面这个问题可以一起解决

2. 外接显示器+全屏模式会导致一定几率崩溃

这个问题是最让人蛋疼的。因为自己平时习惯两个显示器低头看文档抬头写代码,升级之后莫名其妙就会有一定几率崩溃,而且是随机的完全摸不清头脑,在 V2EX 上连发两个问题也没能得到满意的解决方案(不过第一个问题是很快就有人告诉我是 JDK 的问题,还是十分感谢 V2EX 的)。直到今天 IDEA 15 发布了正式版,下载之后发现切换屏幕焦点之后不像我电脑上装的其他 Jetbrains 产品那样闪屏,仔细看了一下它安装包自带了一个 JDK。所以就尝试了把这个 JDK 放到了 Android Studio 下,果然不出所料,Android Studio 也不闪屏了,而且多显示器全屏的问题似乎也解决了。IDEA 15 下载地址:https://www.jetbrains.com/idea/download/ 社区版就可以了,然后从 .app 文件下把 jre 这个文件夹复制到 Android Studio 的对应目录即可。

更令人高兴的是,用 Intellij 提供的 JDK 同样也可以解决第一个问题。

otto 在 Service 中发布事件

otto 是 square 开发的一个 event bus 类库,项目地址:https://github.com/square/otto

在使用 otto 在 service 中发布 event 时,会抛出没有在主线程调用的异常,对于这个问题 StackOverflow 上给出解决的代码,解决思路是定义一个主线程的 handler,如果当前不在主线程,则通过这个 handler post 一个事件。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainThreadBus extends Bus {
private final Handler mHandler = new Handler(Looper.getMainLooper());
@Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
mHandler.post(new Runnable() {
@Override
public void run() {
MainThreadBus.super.post(event);
}
});
}
}

如果你恰好和我一样使用了 RxAndroid 的话,它提供了更为方便的调度器(Scheduler)来完成这个操作,我是这样实现的:

1
2
3
4
public static void post(final Object event) {
Observable.just(event).observeOn(AndroidSchedulers.mainThread())
.subscribe(postEvent -> EventBus.get().post(postEvent));
}

自动生成 ContentProvider 注解框架 Schematic

ContentProvider 是 Android 四大组件之一,主要负责应用内数据库实例的管理,是系统提供的有效管理数据库实例的方式之一。虽然在官方文档中有说过:「如果应用不向外部提供数据,可以不必使用 ContentProvider」,但出于它的一系列好处,我们仍然偏爱于建立 ContentProvider 来管理数据库。不过 ContentProvider 的代码比较繁琐而且格式单一,所以就促使一些类库的产生,今天就发现了一个不错的可以自动完成 ContentProvider 大部分工作的注解框架:Schematic。

项目地址:https://github.com/SimonVT/schematic
本文仅为翻译

Android 绑定 View 终极利器——ButterKnife

Android 中估计最简单枯燥而且无意义的事情就是 findViewById 了,在我们真切期望 Google 爸爸亲自优化这一过程的同时,不少同学也亲自实践做出了一些好用的工具,在我用过的这些工具中,最强大的无疑就是 ButterKnife 了。

Android应用证书签名

在 Android Studio 上给应用签名很简单,首先在菜单中选择 Generate Signed APK,然后会有一个向导,如果之前有证书则需要填写证书路径和密码,如果没有的话可以根据向导创建一个,然后一直 next 直到 finish 就好了。

不过说下遇到的一些小问题:

  1. 之前用 debug 证书签名的应用,如果需要安装 release 版本的应用,有时候在卸载本地应用后仍然安装不上新的应用,这时可以使用 adb uninstall PACKAGE_NAME 完整卸载掉应用。
  2. 在应用存在是 adb install 可能会安装不上版本升级后的应用。这时需要使用 adb install -r 命令即可。

AsyncTask简介

AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate and onPostExecute.


蹩脚翻译 仅供参考


AsyncTask 能使 UI 线程的使用变得恰当和简单。这个类允许 UI 线程在不操纵 Thread 和/或 Handler 的情况下执行后台操作和发布结果。

AsyncTask 被设计成为一个围绕 Thread 和 Handler 工作的帮助类,并且不会形成一个普通的线程框架。AsyncTask 理想中应该被用于短操作(大多是几秒)。如果你需要保持线程运行很长时间,推荐你使用 java.util.concurrent 包提供的许多类比如 Executor、ThreadPoolExecutor 和 FutureTask。

一个 AsyncTask 被定义为一个运行在后台线程并且结果公布给 UI 线程的运算。AsyncTask 定义了三个泛型:Params、Progress 和 Result 以及四个步骤:onPreExecute、doInBackground、onProgressUpdate 和 onPostExecute。


以上摘自 Google 官方文档 。

之前一直使用 Handler+Thread 来完成各种异步操作,昨天参加南京 GDG 这边组织的 Android Study Jam 介绍了一下 AsyncTask 的用法,惭愧于之前一直没有使用正确的姿势来学习 Android,养成了各种不规范的习惯。

在 AsyncTask 中,三个泛型分别代表着:

  • Params:创建时被传入的参数
  • Progress:执行过程中需要及时反馈给 UI 的进度
  • Result:任务执行结束后的结果

四个方法分别表示:

  • onPreExecute:执行前的准备工作。一般用于初始化 UI
  • doInbackground:后台任务
  • onProgressUpdate:进度更新给 UI
  • onPostExecute:返回结果

下面是一个小 demo 来演示一下最基础的 AsyncTask 用法:

MainActivity.java

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
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity {

ProgressBar pb_main;
TextView tv_result;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

pb_main = (ProgressBar) findViewById(R.id.pb_main);
tv_result = (TextView) findViewById(R.id.tv_main);

TestTask task = new TestTask();
task.execute();
}

private class TestTask extends AsyncTask<Void, Integer, String> {

@Override
protected String doInBackground(Void… params) {

int progress = 0;
while (progress < 100) {
SystemClock.sleep(100);
progress++;
publishProgress(progress);
}
return “任务完成” + progress;
}

@Override
protected void onProgressUpdate(Integer… values) {
pb_main.setProgress(values[0]);
tv_result.setText(“任务进度:” + values[0] + “”);
}

@Override
protected void onPostExecute(String s) {
tv_result.setText(s);
}
}
}

activity_main.xml

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
<RelativeLayout xmlns:android=http://schemas.android.com/apk/res/android
xmlns:tools=http://schemas.android.com/tools
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=“.MainActivity”>

<TextView
android:padding=“20dp”
android:id=“@+id/tv_main”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:text=“@string/progress_info”
android:layout_alignParentTop=“true”
android:layout_centerHorizontal=“true” />

<ProgressBar
android:id=“@+id/pb_main”
style=“?android:attr/progressBarStyleHorizontal”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_centerVertical=“true”
android:layout_alignParentLeft=“true”
android:layout_alignParentStart=“true” />

</RelativeLayout>

How to speed up gradle build time

If you use the new Gradle build system with Android (or Android Studio) you might have realized, that even the simplest Gradle call (e.g. gradle project or grade tasks) is pretty slow. On my computer it took around eight seconds for that kind of Gradle calls. You can decrease this startup time of Gradle (on my computer down to two seconds), if you tell Gradle to use a daemon to build. Just create a file named gradle.properties in the following directory:

  • /home//.gradle/ (Linux)
  • /Users//.gradle/ (Mac)
  • C:\Users\.gradle (Windows)

Add this line to the file:

org<span class="class">.gradle</span><span class="class">.daemon</span>=true
`</pre>

From now on Gradle will use a daemon to build, whether you are using Gradle from command line or building in Android Studio. You could also place the gradle.properties file to the root directory of your project and commit it to your SCM system. But you would have to do this, for every project (if you want to use the daemon in every project).

Note: If you don’t build anything with Gradle for some time (currently 3 hours), it will stop the daemon, so that you will experience a long start-up time at the next build.

Note:Performance improvements are one of the great tasks in the Gradle roadmap for 2014 (and reaching into 2015). So I hope, we’ll see the general performance increasing within these years.

Note: This does only affect console builds. Android Studio always uses a Gradle daemon (and depending on your settings some other optimizations).

> 来源:[https://www.timroes.de/2013/09/12/speed-up-gradle/](https://www.timroes.de/2013/09/12/speed-up-gradle/)
(_至今没过英语四级的人的蹩脚翻译仅供参考_)
如果你使用新的 Gradle 构建系统(或 Android Studio)你也许认识到简单的 Gradle 调用(例如 gradle project 或者 gradle tasks)是如此之慢。在我的电脑上那些gradle 调用要花 8 秒左右。如果你告诉 Gradle 使用一个后台驻留进程去构建就可以减少 Gradle 的启动时间。只需要创建一个名为`gradle.properties`在下面的文件夹中:
  • /home//.gradle/ (Linux)
  • /Users//.gradle/ (Mac)
  • C:\Users\.gradle (Windows)

    在该文件中添加这样一行:

    `org.gradle.daemon=true

现在无论你使用 Gradle 命令行还是在 Android Studio 中, Gradle 将使用一个驻留进程去构建项目。你可以把gradle.properties文件放在你项目的根目录并提交到你的 SCM 系统。但是你必须每个项目都这样做(如果你想每个项目都使用后台驻留进程)。

Note:如果你在一段时间(当前是3小时)内不使用 Gradle 构建任何东西,它将会终止这个进程,那么你会在下一次构建时花费很长时间。

Note:不会翻译。

Note:这个只会影响控制台的构建。Android Studio 总是会使用一个 gradle 后台驻留进程(并且依靠于你设置上的其他优化)

Android SDK 国内的镜像站

国内更新Android SDK不通畅,可以尝试使用设置镜像地址。

大连东软的 Android SDK 镜像地址:mirrors.neusoft.edu.cn:80

UPDATE:最近国内有服务器了,是北京的,所以不需要镜像了大家可以使用 ping.chinaz.com 来看一下哪个 IP 访问最快来修改自己的 hosts。不过大部分地区应该可以直接访问了。

使用CyanDelta自动升级失败问题

from 官方FAQ

####CyanDelta failed to install a zip in recovery on my device!

You probably have the official closed-source ClockworkMod recovery. It’s not supported by CyanDelta because his author has locked it to only work with his proprietary app “ROM Manager”, so we are unable to support it.

If you have that recovery you can replace them with the TWRP recovery (download from here: http://teamw.in/project/twrp2 ) or a CWM-based recovery. Otherwise you can just manually install the zip files generated by CyanDelta.

原因是我使用了闭源的 ClockworkMod recovery,并不支持 CyanDelta 自动升级时需要的 「Open Recovery Script」,按照官方推荐的下载一个TWRP Recovery就可以了。

Android网络调试开启方法

最近卖了nexus 4换了nexus7(2013)玩,发现之前一直在CM上使用的网络调试在原生Android 5.0上并没有开启的选项,Google了一下,貌似原生的Android都没有。

不过Android本身还是提供这个功能,开启有两种方法:

AndroidStudio中so文件存放路径问题

最近要做一个关于地图导航的模块,用到了百度地图的 API,在 Hello World 的时候就出问题了:

文档中要求 so 文件放在 libs/armeabi/ 下,这样做之后编译通过,运行时报错:

1
java.lang.UnsatisfiedLinkError: Couldn't load BaiduMapSDK_v3_0_0 from loader dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.twiceyuan.baidumaptest-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.twiceyuan.baidumaptest-1, /vendor/lib, /system/lib]]]: findLibrary returned null

查了一下,原来是 so 文件存放路径问题,Android Studio 下 so 文件的存放路径为:src/main/jniLibs/armeabi/

之后运行成功了。

在Android Studio中删除Module

先前在项目设置中选择 Module 之后上面的减号不见了,所以原来删除 Module 的方法不再适用。

现在版本想要删除一个 Module(应该是0.5.2以后的版本),需要先在项目根目录下 setting.gradle 中删除所要删除 Module 的名称,然后点击工具栏上 Sync Project with Gradle Files 按钮,完成后右击所要删除的 Module 的文件夹就会发现原来不在的 Delete 键出现了,然后点击删除即可

Android应用中短信的访问

首先在 AndroidManifest.xml 文件中添加代码

<uses-permission android:name="android.permission.READ_SMS"/>

然后在程序代码中添加

private static final Uri SMS_ALL = Uri.parse("content://sms")

在 Mac中离线升级 Android Studio

Android Studio 在线升级一直是个蛋疼问题,挂代理速度比较慢而且不稳定,但是所有蛋疼的在线升级问题都可以用一个方法解决——离线升级包。Mac 用离线升级包和Windows上基本相同只不过路径和资源的url不一样。

  1. http://dl.google.com/android/studio/patches/updates.xml 找最新版本号如 0.2.10 (Build number: 132.843336)

  2. 下载补丁 查找到版本号之后,直接从,http://dl.google.com/android/studio/patches/AI-[旧的版本号]-[新的版本号]-patch-mac.jar 例如从 130.737825 升级到 132.843336 就要下载 http://dl.google.com/android/studio/patches/AI-130.737825-132.843336-patch-mac.jar

  3. 下载之后可以把jar文件拷贝到 /Applications/Android Studio/ 然后终端进入到 Android Studio 目录终端输入

    java -classpath XXXX.jar com.intellij.updater.Runner install . (. 不可忽略,代表当前目录)

    然后会弹出一个升级框,即可完成升级。完成后即可删除jar文件