消息传递

BroadcastReceiver

BroadcastReceiver,广播接收者本质上是一种全局的监听器,它可以接收来自系统和应用的的广播,实现系统不同组件之间的通信。比如Activity与通过startService()方法启动的Service之间通信,就可以借助于BroadcastReceiver来实现。

BroadcastReceiver的onReceive方法中不要有执行超过5秒的代码,否则系统会弹出一个超时对话框。对于耗时的操作,请start service来完成。因为当得到其他异步操作所返回的结果时,BroadcastReceiver 可能已经无效了。如果确实需要做的话,可以用goAsync方法,然后在新开一个线程去执行。

注册BroadcastReceive
(1)在代码中注册 context.registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
(2)在配置中注册
注销BroadcastReceive:context.unregisterReceiver(BroadcastReceiver receiver)

http://www.2cto.com/kf/201408/324155.html

Android 异步与定时

AsyncTask

Android之所以有Handler和AsyncTask,都是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的。
Android为了降低这个开发难度,提供了AsyncTask。AsyncTask就是一个封装过的后台任务类,顾名思义就是异步任务。
AsyncTask比Handler更轻量级一些,适用于简单的异步处理。
AsyncTask直接继承于Object类,位置为android.os.AsyncTask。要使用AsyncTask工作我们要提供三个泛型参数,并重载几个方法(至少重载一个)。

AsyncTask定义了三种泛型类型 Params,Progress和Result。

Params 启动任务执行的输入参数,比如HTTP请求的URL。
Progress 后台任务执行的百分比。
Result 后台执行任务最终返回的结果,比如String。

最少要重写以下这两个方法:
doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回

以下这三个方法不是必须重写的:
onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
onCancelled() 用户调用取消时,要做的操作
使用AsyncTask类,以下是几条必须遵守的准则:

Task的实例必须在UI thread中创建;
execute方法必须在UI thread中调用;
不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法;
该task只能被执行一次,否则多次调用时将会出现异常;

handler

Handler为Android操作系统中的线程通信工具,它主要由两个作用:(1)安排消息或Runnable 在某个主线程中某个地方执行(2)安排一个动作在另外的线程中执行。每个Handler对象维护两个队列(FIFO),消息队列和Runnable队列, 都是有Android操作系统提供的。Handler可以通过这两个队列来分别:

发送、接受、处理消息–消息队列;
启动、结束、休眠线程–Runnable队列;
Handler的使用方法大体分为3个步骤:1.创建Handler对象。2.创建Runnable和消息。3.调用post以及sendMessage方法将Runnable和消息添加到队列。

Android的线程异步处理机制:Handler对象维护一个线程队列,有新的Runnable送来(post())的时候,把它放在队尾,而处理 Runnable的时候,从队头取出Runnable执行。当向队列发送一个Runnable后,立即就返回,并不理会Runnable是否被执行,执行 是否成功等。而具体的执行则是当排队排到该Runnable后系统拿来执行的。这就好比邮局的例子。寄信者将信写好后放入邮筒就回家了,他并不知道邮件何 时被邮局分发,何时寄到,对方怎样读取这些事。这样,就实现了Android的异步处理机制。

Android中实现定时器的四种方式

第一种方式利用Timer和TimerTask
第二种方式 使用CountDownTimer
第三种方式 AlarmManager

【Android和iOS】快速切换到主线程更新UI

Android
方法一:view.post(Runnable action)
方法二:activity.runOnUiThread(Runnable action)
方法三:new Handler

http://blog.csdn.net/xieyupeng520/article/details/47336941

smali

1.smali

apk文件通过apktool反编译出来的都有一个smali文件夹,里面都是以.smali结尾的文件。
smali语言是Davlik的寄存器语言,语法上和汇编语言相似,Dalvik VM与JVM的最大的区别之一就是Dalvik VM是基于寄存器的。基于寄存器的意思是,在smali里的所有操作都必须经过寄存器来进行。

2.基本数据类型

B—byte
C—char
D—double
F—float
I—int
S—short
V—void
J—long
Z—boolean

注意J、Z两个不是对应类型的首字母;
在dalvik字节码中,寄存器都是32位的,能够支持任何类型,Long和Double类型是64位的,需要2个寄存器;
V 只能用于返回值类型;

3.数组和对象是引用类型

数组的表示方式是在基本类型前加上前中括号“[”,例如int数组和float数组分别表示为:[I、[F;
对象类型以L作为开头来表示,格式是Lpackage/ClassName;(用分号表示对象结束是必须的),示例:
String对象在smali中为:Ljava/lang/String;
Class1对象的一个boolean成员表示为:Lcom/disney/Class1;->isRunning:Z
Class1对象的一个String对象成员表示为:Lcom/disney/Class1;->name:Ljava/lang/String;
可以总结为格式为对象类型->成员名:成员类型,->表示所属关系,类型尾部必须包括一个分号。
内部类表示为:Lpackage/ClassName$innerObjectName;,也就是在内部类前加“$”符号。

4.函数

格式:Func-Name (Para-Type1Para-Type2Para-Type3…)Return-Type

返回类型在最后,参数之间没有任何分隔符,示例:

void fun()
fun()V

boolean fun(int, int, int)
fun(III)Z

String fun(boolean, int[], int[], String, long)
fun(Z[I[ILjava/lang/String;J)Ljava/lang/String;

5.语法

#标记,构造函数的返回类型为V,名字为<init>

# static fields        定义静态变量的标记
# instance fields      定义实例变量的标记
# direct methods       定义静态方法的标记??
# virtual methods      定义非静态方法的标记??
 .class public Lcom/disney/WMW/WMWActivity;
 .super Lcom/disney/common/BaseActivity;
 .source "WMWActivity.java"
 .implements Lcom/burstly/lib/ui/IBurstlyAdListener;

上面这几行代码表示类名,父类名,源文件名,实现了接口。

.annotation
 内部类
.end annotation

6.局部变量

本地寄存器(local register,非参寄存器)用v开头数字结尾的符号来表示,如v0、v1、v2、…,
参数寄存器(parameter register)用p开头数字结尾的符号来表示,如p0、p1、p2、…,
.registers 用来标明方法中寄存器的总数,即参数寄存器和非参寄存器的总数。
.local 0,标明在这个函数中最少要用到的本地寄存器的个数,出现在方法中的第一行。在这里,由于只需要调用一个父类的onDestroy()处理,所以只需要用到p0,所以使用到的本地寄存器数为0,在植入代码后不要忘记可能要修改.local的值。
如 .local 4,则可以使用的寄存器是v0-v3。
当一个方法被调用的时候,方法的参数被置于最后N个寄存器中。
在实例函数中,p0代指“this”,p1表示函数的第一个参数,p2代表函数中的第二个参数…,
在static函数中,p1表示函数的第一个参数,p2代表函数中的第二个参数…,因为Java的static方法中没有this方法。
示例:

const/4 v0, 0x0
iput-boolean v0, p0, Lcom/disney/Class1;->isRunning:Z

上面第一句中把值0x0存到v0本地寄存器中,
第二句用iput-boolean指令把v0中的值存放到this.isRunning这个成员变量中,即this.isRunning = false; 因为在实例函数中p0代表的是“this”,Lcom/disney/Class1;是类名,对应实例是p0。

7.成员变量和指令

# static fields
.field private static final PREFS_INSTALLATION_ID java/lang/String; = "installationId"
# instance fields
.field private _activityPackageName java/lang/String;

获取和操作静态成员变量和实例成员变量有不同的指令。
读取的指令有:iget、sget、iget-boolean、sget-boolean、iget-object、sget-object等,
赋值的指令有:iput、sput、iput-boolean、sput-boolean、iput-object、sput-object等。
带“-object”表示操作的成员变量是对象类型,没有“-object”后缀的表示操作的成员变量对象是基本数据类型,特别地boolean类型则使用带“-boolean”的指令操作。

获取static fields的指令示例:

sget-object v0, Lcom/disney/Class1;->PREFS_INSTALLATION_ID:Ljava/lang/String;

上句中sget-object指令把PREFS_INSTALLATION_ID这个String成员变量获取并放到v0寄存器中。

获取instance fields的指令与static fields的类似,需要指明对象所属的实例。示例:

iget-object v0, p0, Lcom/disney/Class1;->_view:Lcom/disney/Class2;

上句iget-object指令比sget-object多了一个参数p0,就是该变量所在类的实例,在这里就是p0即“this”。

put指令的使用和get指令是统一的,示例:

const/4 v3, 0x0
sput-object v3, Lcom/disney/Class1;->globalIapHandler:Lcom/disney/config/GlobalPurchaseHandler;

上句相当于Class1.globalIapHandler = null;

8.函数调用

smali中的函数调用也分为direct和virtual两种类型,direct method就是private函数,public和protected函数都属于virtual method。在调用函数时,有invoke-direct,invoke-virtual,invoke-static、invoke-super以及invoke-interface等几种不同的指令。还有invoke-XXX/range 指令的,这是参数多于4个的时候调用的指令,比较少见。

invoke-static:就是调用static函数的,示例:

invoke-static {}, Lcom/disney/Class1;->fun()Z

上句invoke-static后面有一对大括号“{}”,内部是调用该方法的实例和参数列表,由于这是static方法也不需要参数,所以{}内为空。

invoke-super:调用父类方法,在onCreate、onDestroy等方法都能看到。
invoke-direct:调用private函数,示例:

invoke-direct {p0}, Lcom/disney/Class1;->getGlobalIapHandler()Lcom/disney/config/GlobalPurchaseHandler;

上句即this->getGlobalIapHandler(),函数GlobalPurchaseHandler getGlobalIapHandler()是定义在Class1中的一个private函数。

invoke-virtual:用于调用protected或public函数,示例:

sget-object v0, Lcom/disney/Class1;->shareHandler:Landroid/os/Handler;
invoke-virtual {v0, v3}, Landroid/os/Handler;->removeCallbacksAndMessages(Ljava/lang/Object;)V

上句v0是shareHandler android/os/Handler,v3是传递给removeCallbackAndMessage方法的Ljava/lang/Object参数。

9.获取函数调用结果

在smali里调用函数和返回函数结果需要分开来完成,在调用的函数返回非void后,用move-result(返回基本数据类型)和move-result-object(返回对象)指令获取返回结果。

示例:

const/4 v2, 0x0
invoke-virtual {p0, v2}, Lcom/disney/Class1;->getPreferences(I)Landroid/content/SharedPreferences;
move-result-object v1

上句v1保存的就是调用this.getPreferences(int)方法返回的SharedPreferences实例。

10.函数体

.method   和  .end method之间。

示例:

.method protected onDestroy()V
.locals 0

.prologue
.line 277
invoke-super {p0}, Lcom/disney/common/BaseActivity;->onDestroy()V

.line 279
return-void
.end method

上段是onDestroy()函数。
.line 277,标注了该代码在原Java文件中的行数,它不是必须的,去掉没有编译问题。它在出错时可以指出错误位置,jd-gui工具即是通过分析这些信息将smali代码还原成Java代码的。

11.条件语法

if-eq p1, v0, :cond_8 
    :cond_8
    invoke-direct {p0}, Lcom/paul/test/a;->d()V

上段表示如果p1和v0相等,则执行cond_8的流程:调用com.paul.test.a的d()方法

if-ne p1, v0, :cond_b 
    :cond_b
    const/4 v0, 0x0
    invoke-virtual {p0, v0}, Lcom/paul/test/a;->setPressed(Z)V
    invoke-super {p0, p1, p2}, Landroid/view/View;->onKeyUp(ILandroid/view/KeyEvent;)Z
    move-result v0

上段表示不相等则执行cond_b的流程。

 

读取meta-data

示例

<application android:label="@string/app_name"
                 android:icon="@drawable/icon">
        <meta-data
            android:name="channel"
            android:value="0" >
        </meta-data>
</application>

读取时需要注意当meta-data值为数字时,使用getString的陷阱,参考http://my.oschina.net/xesam/blog/135333

private void initChannel() 
    {
        try 
        {
            ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPackageName(),PackageManager.GET_META_DATA);

            int intChannel=appInfo.metaData.getInt("channel");
            channel=intChannel+"";
        } 
        catch (NameNotFoundException e) 
        {
            Log.i("ApplicationInfo NameNotFoundException ",e.getMessage());
        }
    }

Activity

Activity的四种launchMode

launchMode在多个Activity跳转的过程中起到决定是生成新的还是重用已有的Activity实例,是否和其它Activity实例共存于一个task。
task是一个栈结构,一个task可以管理多个Activity,启动一个应用也就创建了一个与之对应的task。
Activity有以下四种launchMode:

  • standard
  • singleTop
  • singleTask
  • singleInstance

standard模式

standard是默认的启动模式

<activity android:launchMode="standard">

每次跳转到这个Activity都会生成一个新的实例,并且放于Task栈顶。

singleTop模式

这个模式的要点是看栈顶

<activity android:launchMode="singleTop">

跳转到这个Activity前,会判断位于当前Task栈顶的Activity是否是它的实例,如果是则直接使用这个实例;如果不是,则生成一个新的实例,并且放于Task栈顶。

singleTask模式

这个模式要点是看整个栈,且会有退栈操作。

<activity android:launchMode="singleTask">

跳转到这个Activity前,会判断当前Task中是否已存在它的实例,如果已存在,则把这个实例之上的其它Activity实例退出栈,使它成为栈顶实例;如果没有,则生成一个新的实例,并且放于Task栈顶。

 

反编译apk文件

下载apktool,用来反编译和重编译apk文件

到这里下载apktool-windows-1.5.3-mod-.zip

反编译apk文件

apktool d xxx.apk

会生成xxx目录,里面有res,smali,AndroidManifest.xml,apktool.yml。

重新编译apk目录

apktool b -f xxx

xxx是修改好的反编译目录,执行成功后,会在xxx下生成build、dist目录,dist目录下的apk文件即是重新编译生成的未签名的apk文件。

 

apk文件签名

APK签名的作用

  • 使用特殊的key签名可以获取到一些不同的权限
  • 不同的key签名过的相同apk文件无法安装覆盖老的版本,这样可以防止已安装的应用被修改过的apk覆盖替换掉

使用命令行方式进行签名需要JDK中的两个命令行工具:keytool.exe和jarsigner.exe,它们的作用分别是生成证书和签名。

使用keytool生成专用密钥(Private Key)文件

keytool -genkey -v -keystore 密钥名.keystore -alias 密钥别名 -keyalg RSA -validity 有效天数

RSA表示密钥算法,天数可以是30000天,将近100年了。下面要输入一些密钥需要的信息:

输入keystore密码:
再次输入新密码:
您的名字与姓氏是什么?
 [Unknown]: ztdx
您的组织单位名称是什么?
 [Unknown]: ztdx
您的组织名称是什么?
 [Unknown]: ztdx
您所在的城市或区域名称是什么?
 [Unknown]: ztdx
您所在的州或省份名称是什么?
 [Unknown]: ztdx
该单位的两字母国家代码是什么
 [Unknown]: cn
CN=ztdx, OU=ztdx, O=ztdx, L=ztdx, ST=ztdx, C=cn 正确吗?
 [否]: Y
正在为以下对象生成 1,024 位 RSA 密钥对和自签名证书 (SHA1withRSA)(有效期为 30,00
0 天):
 CN=ztdx, OU=ztdx, O=ztdx, L=ztdx, ST=ztdx, C=cn
输入<ZhiTongDianXun>的主密码
 (如果和 keystore 密码相同,按回车):
[正在存储 ZhiTongDianXun.keystore]

使用 jarsigner 命令对apk文件签名

jarsigner -verbose -keystore 密钥名.keystore ztdx.apk 密钥别名

输入密码正确后,开始进行签名操作,完成后不生成新的apk,会发现比原apk体积大了几十K。

android导出apk

1.在r19之后,Android SDK Tool Export的时候遇到is not translated in的问题

这是因为Android SDK Tool 將 ANDROID_LINT_COMPLETE_REGIONS 改为了默认需要检查。
临时解决方法:
Eclipse > Preference > Android > Lint Error Checking的Correctness: Messages > MissingTranslate
将 Severity 值由 Fetal 改为 Warning。

2.>proguard.cfg 混淆文件出错, Obsolete proguard file;

修改 proguard.cfg
把有错行的 “keepclasseswithmembernames” 改为 “keepclasseswithmembers”
重编绎 (点Lint Warnings view里的refresh灯泡左边的按钮)

3.导出apk完成前报unresolved references to program class members.

在proguard.cfg文件中加入-ignorewarnings就可以跳过这个错误了。