首先,定义一下本文中的几个概念:
远程调用:跨进程的方法调用
客户进程:发起方法调用的进程
服务进程:实际实现方法的进程
如果需要设计一个“远程调用”的机制,我们需要考虑以下几个问题:
- 客户进程如何发起方法调用
- 服务进程如何知道要调用的方法名,如何接收方法参数
- 客户进程与服务进程如何通信
基础模式如下:
系统:Ubuntu
java本版:1.7
解决办法:将java的安装位置更改到/usr/java或者/opt/java就行了,注意同时修改原本的JAVA_HOME系统变量。
解决办法:在SDK Manager中将tools(包括sdk tools,platform tools,build tools)更新到最新的版本。然后
Configure -> Project Defaults -> Project Structure
在Project 设定Project SDK。在SDKs设定Buildtarget。 保存之后就可以“New Project”了
问题描述:
修改了 applicationId 之后,无法从 As 界面 launcher Activity 。提示错误:
Activity class {package/xxxxx} does not exist
这个应该是 As 自身的问题,没有刷新缓存,把 .idea *.iml 什么都删除一下,重新打开基本就可以了。
File -> Settings (ctrl+alt+s) -> Appearance & Behavior -> System Settings -> Android SDK.
点击 edit,无论有没有下载过对应的源码,都执行一遍,主要是让 Android studio 刷新自身的信息。
最新有的库是使用 java8 编译的,有可能报字节码解析错误,所以可以都改成 java8 编译。
要求 as 2.1 以上, jack 支持:
1 | android { |
解决方法:在签名时,添加参数
-digestalg SHA1 -sigalg MD5withRSA
示例如下:
jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore my_keystore -signedjar $signed_apk $unsign_apk my_alias_name
最新的Surpport里面的主题检查好像更严格了。以前使用
ActionBarActivity + Toolbar
的时候,我是这么定义的
1 | <style name="AppTheme.Base" parent="Theme.AppCompat.Light"> |
升级之后就出问题了。应该使用
1 | <style name="AppTheme.Base" parent="Theme.AppCompat.Light"> |
其实最好的方法还是,不管在哪里,都使用sdk预置的主题:
1 | <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar"> |
其他问题类似
比较常见的情况是 Activity 发起一个异步调用,然后 Activity 退到后台,异步调用返回之后,想弹出一个 Dialog 或者 DialogFragment,这个时候就会报
Can not perform this action after onSaveInstanceState
原因就是 Activity 调用 onStop (有的版本是 onPause) 之前,先触发了 onSaveInstanceState。
解决方案:
如果是在 Fragment 里面弹出 Dialog,可以判断宿主 Fragment 的当前状态,比如:
1 | if(fragment.isResumed()){ |
如果是 Activity,需要自己来实现当前状态检测,比如:
1 | class BaseActivity extends Activity{ |
这里有个详细的说明:
http://www.cnblogs.com/kissazi2/p/4181093.html
注意 : onBackPressed() 与 finish() 的区别
finish() 这个方法比较纯粹,触发 Activity 生命周期中的 onDestroy 方法。
onBackPressed() 是在 Fragment 引入之后才新增加的方法,所以,onBackPressed() 相比 finish() 会先处理各种 Fragment 的状态。
这样问题就来了,假如按下 Home 键将当前 Activity 放到后台,这个时候某个调用触发 onBackPressed(),会进行状态清理,但是由于此时已经调用了 onSaveInstanceState,
所有涉及到 Fragment 的操作都会导致崩溃。由于 onBackPressed() 是默认方法,除非重写,不然也办法使用 commitAllowingStateLoss() 方法。
这个错误出现在用 gradle 命令行打包的时候。
我遇到的一个原因就是在 Mac 上修改了类的位置之后,各个模块下的 build 文件没有 clean。
当用命令行来编译的时候,就会报 IOException 的错误。
解决方案:
先在工程根目录调用一下
1 | gradle clean |
然后再正常编译就行。
一个真实的错误,错误原因:
res
|
|----values :
| |
| |----<null> //这里丢失了 cll_update_strings.xml
| |
|----values-zh :
| |
| |----cll_update_strings.xml
如果将系统调成英文,那么会直接报错。
如何预防:
当前手机屏幕分辨率为 normal, 如果只在 drawable-normal 放有 xxx.png 资源文件,那么会崩溃。
因为 Android 如果找不到最佳匹配的资源图片,只会向更低级的进行查找,而不会向更高级的进行查找。
一般是因为 Proguard 配置错误或者不全
低版本开启硬件加速导致的 2D 绘图问题,参见官网文档:https://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported
不过虽然官网说从 API 18 开始就恢复支持了,但是我在 4.4 以及 5.0 版本上都看到过此类崩溃,待调查。。
####Android分享 Q群:315658668
首先,要明确一点,Promise 是用来处理回调嵌套的问题,而不是用来处理回调本身的问题。
通常,我们的程序有三个要素:
执行器按照语义规则在上下文中执行表达式,而表达式的最终效果都是要修改上下文,不修改上下文的表达式除了浪费时间之外,是没有意义的。 方法是表达式的集合,方法对上下文的修改可以表现在两个方面:
所以,执行一个方法,最终的意义就是观察这个方法对上下文的修改结果,然后做出相应的动作。
同步方法的特征:
异步方法的特征:
对比下面的例子:
同步:
var value = fn_1();
var c = value_1 + 20;
var value_2 = fn_2(c);
console.log(value_2);
执行器 A 发起 fn_1 的调用, 执行器 A 执行并等待 fn_1 的执行完成,执行器 A 观察 fn_1 的结果。
执行器 A 发起 fn_2 的调用, 执行器 A 执行并等待 fn_2 的执行完成,执行器 A 观察 fn_2 的结果。
由于 fn_1 和 fn_2 都是同步方法,这个过程会很完美的工作。 但是,如果 fn_1 和 fn_2 都是异步方法,而异步调用根本就没有返回值,所以,这段代码是得不到预期效果的。 那我们就只能这么写了:
异步:
fn_1(function(value_1){
var c = value_1 + 20;
fn_2(c, function(value_2){
console.log(value_2);
})
})
虽然写法不一样,但是本质都是一样的,注意其中执行器的变化:
执行器 A 发起 fn_1 的调用,执行器 B 执行并等待 fn_1 的执行完成,执行器 A 观察 fn_1 的结果。
执行器 A 发起 fn_2 的调用,执行器 C 执行并等待 fn_2 的执行完成,执行器 A 观察 fn_2 的结果。
既然本质都是一样的,那么我们就可以将 “执行并等待 -> 观察”封装为一个概念——Promise,当异步方法的结果返回时,根据不同的结果,触发不同的动作。 形式如下:
var promise_1 = create_promise(fn_1);
var promise_2 = promise_1.then(function(value_1){
var c = value_1 + 20;
return create_promise(c, fn_2);
});
promise_2.then(function(value_2){
console.log(value_2);
});
如此一来,就将异步的嵌套回调,转化为扁平的同步调用形式。
C++ | Java | |
---|---|---|
类默认权限 | 私有 | 包访问 |
私有 | private | private |
受保护 | protected | protected |
公开 | public | public |
友元 | friend | 不支持 |
C++ | Java | |
---|---|---|
多态方法 | virtual(虚函数) | 非静态,非私有方法 |
要求子类实现的方法 | pure virtual(纯虚函数) | abstract(抽象方法) |
接口 | 完全纯虚函数 | interface |
多重继承 | 支持 | 不支持 |
安装
1 | sudo apt-get install python-pip |
安装
1 | sudo pip install virtualenv |
创建虚拟环境
1 | virtualenv test_env |
默认情况下,虚拟环境会依赖系统环境中的site packages,就是说系统中已经安装好的第三方package也会安装在虚拟环境中,
如果不想依赖这些package,那么可以加上参数
--no-site-packages
进入虚拟环境
source ./bin/activate
退出虚拟环境
deactivate
pip install supervisor
pip install tornado
autoreload
1 | ioloop = tornado.ioloop.IOLoop.instance() |
Ubuntu 14.04
1 |
|
环境 Ubuntu 14.04
ubuntu 下 lua 的安装包,binary和dev是分开装的。
1 | sudo apt-get install lua5.2 |
c 调用 lua
1 |
|
第一个错误:
lua.h: No such file or directory
没有找到 h 头文件,需要指定:
gcc lua_test.c -I/usr/include/lua5.2
第二个错误:
undefined reference to `luaL_newstate'
没有找到 so 文件,需要指定:
gcc lua_test.c -I/usr/include/lua5.2 -llua5.2
找到文件位置:
locate lua.h
locate liblua
Cupboard 是针对 Android 的一个简单的持久化存储方案,简单而且容易与现有代码集成。
更准确的说, Cupboard 是一个存取对象的方式。并不是一个真正的ORM,因为为了保持简单,它并不会去维护对象之间的关系。
设计 Cupboard 是因为现有的持久化框架并不能满足实际的需求:
引入 Cupboard 依赖,然后静态导入 cupboard():
build.gradle:
compile 'nl.qbusict:cupboard:(insert latest version)'
最新是 2.1.4 所以可以这么写: compile 'nl.qbusict:cupboard:2.1.4'
java 类:
import static nl.qbusict.cupboard.CupboardFactory.cupboard;
在代码中可以这么调用:
public long storeBook(SQLiteDatabase database, Book book) {
return cupboard().withDatabase(database).put(book);
}
上面的代码将一个 Book entity 存入数据库中,然后返回记录的 id, 就这么简单。
Cupboard 中的 entity 就是一个POJO,Cupboard使用反射来操作字段,没有使用注解,因为 Android 中的注解反射实在是太低效了。
entity 在使用之前,需要先使用 Cupboard.register() 进行注册。一个示例:
public class Book {
public Long _id;
public String title;
public Author author;
public Date publishDate;
}
entity 的字段名对应 SQLite 数据库中的 列名,表名根据 entity 名得来,本例中,表名就是 book。
每个 entity 都应该有一个 Long 类型的 _id 字段,采用 Long 类型是因为这样 _id 就可以取 null了。
而且,Android 原生的 Cursor 本身就期望一个 _id。
entity 的一个字段可以是任何基本类型,相应的包装类,java.util.Date,或者另一个 entity 。
你可以:
当使用 withDatabase() 【见后文】的时候,也可以像使用 SQL 一样来执行 update 操作。
在许多通常的应用中,你需要从不同的组件访问 model,比如 ContentProvider 可能就持有 SQLiteDatabase 的连接,然后你从 Activity 或者 Service 来访问。
如果你只能在 ContentProvider 使用持久化库,而在其他组件里面只能使用原始的 ContentValues, 这不就是一个笑话吗。
Cupboard 以及预料到这一点:
SQliteDataBase db = getDatabase();
// 将一个 entity 保存到数据库中
cupboard().withDatabase(db).put(book);
Cursor cursor = getCursor();
// 从 cursor 获取第一个条记录
Book book = cupboard().withCursor(cursor).get(Book.class);
// 遍历所有的记录
Iterable<Book> itr = cupboard().withCursor(cursor).iterate();
for (Book book : itr) {
// do something with book
}
// 使用 content provider 来保存一条记录
Uri bookUri = ...
cupboard().withContext(this).put(bookUri, book);
//将 Book entity 传递给另一个需要 ContentValues 对象参数的方法
ContentValues values = cupboard().withEntity(Book.class).toContentValues(book);
// 我们可能正在构建一组 ContentProviderOperation
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(10);
Book newBook = new Book();
...
cupboard().withOperations(ops).put(bookUri, book).put(bookUri, newBook);
参见工程 app
注意每个 model 里面定义的 _id,Cupboard 要求使用 Long 类型,这样就可以根据 _id 是否为 null 来判断是否需要自动分配 _id。
当然,你也可以定义为 long 或者 int 等等任意其他类型的数值,但是,由于数值的默认值都是 0,因此,Cupboard 无法判断这个值到底是默认值还是用户赋值。
从而需要用户自己来为维护 _id 的分配。比如,定义如下:
public class SimpleBook {
public long _id;
public String title;
}
如果你使用下面的调用:
SimpleBook simpleBook = new SimpleBook();
simpleBook.title = "title_single_" + System.currentTimeMillis();
cupboard().withDatabase(cupboardSQLiteOpenHelper.getWritableDatabase()).put(simpleBook);
那么,不论你调用多少次,数据库里面只会得到一条数据,而且 _id = 0;所以除非你手动赋值 _id:
SimpleBook simpleBook = new SimpleBook();
simpleBook._id = System.currentTimeMillis();
simpleBook.title = "title_single_" + System.currentTimeMillis();
cupboard().withDatabase(cupboardSQLiteOpenHelper.getWritableDatabase()).put(simpleBook);
所以,如果 _id 没有特别的需求,还是按照 Cupboard 默认要求。
RoundedBitmapDrawable 是 android.support.v4.graphics.drawable 里面的一个类,用来创建简单的圆角图片。
如果只是简单的圆角展示,比如展示一个圆角头像,这个类完全可以胜任。
获取 RoundedBitmapDrawable。RoundedBitmapDrawable 是一个抽象类,无法直接获取。所以提供了 RoundedBitmapDrawableFactory 来操作:
RoundedBitmapDrawableFactory的静态签名:
static RoundedBitmapDrawable create(Resources res, InputStream is)
static RoundedBitmapDrawable create(Resources res, String filepath)
static RoundedBitmapDrawable create(Resources res, Bitmap bitmap)
没有提供直接从 resId 获取的方法,但是是一样的:
RoundedBitmapDrawable roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(getResources(), BitmapFactory.decodeResource(getResources(), R.drawable.xxx));
设置属性。几个重要的属性:
如果想得到一个圆形,那么可以直接调用 RoundedBitmapDrawable#setCircular(true),
不过这样需要注意点是,如果原始的图形不是圆形,那么图形会变形。
当然,结果还与 scaleType 有关,这里有点复杂,暂无需关心。所以,如果你想要一个圆形,你就给一个正方形。
不要同时设置 cornerRadius 和 setCircular(true),因为两者是冲突的。
设置 Drawable。 ImageView#setImageDrawable(roundedBitmapDrawable);
RoundedBitmapDrawable 内部使用 BitmapShader 来处理图形渲染,无他。
RoundedBitmapDrawable 的局限性还是比较大,如果想要实现一写些自由度更大的圆角,边框等等,可以考虑使用第三方空间,比如:
各种工具参考 androiddevtools
腾讯 Bugly 使用比较流畅,推荐。
还是 中国科学院开源协会镜像站 的比较靠谱。。
编译环境:
操作系统 Ubuntu 14.04.1 x86_64
替换源(/etc/apt/source-list)为:
deb http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.163.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.163.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.163.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.163.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ trusty-backports main restricted universe multiverse
更新一下:
1 | sudo apt-get updata |
1 | sudo apt-get install git-core gnupg flex bison gperf build-essential \ |
各种安装依赖冲突的情况,都可以通过更换源解决。
这个需要根据编译的源码版本而定,比如编译 Android 6.0 需要 OpenJDK 8。但是编译 Android 4.0 需要 Oracle Java 6.
具体安装过程略过。
这里使用清华大学的镜像。
参考文章 清华大学 TUNA 镜像源,Android 镜像使用帮助
参考文章 同步、更新、下载Android Source & SDK from 国内镜像站
安装 Repo,
1, 添加 PATH
1 | mkdir ~/bin |
2, 下载 Repo
1 | curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo |
3., 修改repo
修改环境变量或者直接修改 /git-repo/repo 文件。
将 google 的地址
REPO_URL= 'https://gerrit.googlesource.com/git-repo'
改为清华大学的地址
REPO_URL= 'https://gerrit-google.tuna.tsinghua.edu.cn/git-repo'
4, 下载 manifest
1 | repo init -u git://aosp.tuna.tsinghua.edu.cn/android/platform/manifest |
或者选择版本
1 | repo init -u git://aosp.tuna.tsinghua.edu.cn/android/platform/manifest -b android-4.0.1_r1 |
5, 同步
1 | repo sync |
建立环境
1 | source build/envsetup.sh |
这个时候就可以使用 lunch,mmm等命令了
1 | lunch |
lunch负责设置一些环境变量,比如 TARGET_PRODUCT 等等。
结果中 full 表示完全编译,eng表示工程版本,full-eng就是模拟器版本。
开始编译,指定4线程。
1 | make -j4 |
注意编译过程中的 java 版本问题。
暂时没有 jcenter 镜像,也别瞎折腾了,还是搞个 vpn 吧,毕竟党国无耻。