android异步任务AsyncTask(转)

android异步任务AsyncTask(转)

android的异步任务体系中还有一个非常重要的操作类:AsyncTask,其内部主要使用的是java的线程池和Handler来实现异步任务以及与UI线程的交互。本文主要解析AsyncTask的的使用与源码。

首先我们来看一下AsyncTask的基本使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MAsyncTask extends AsyncTask<Integer, Integer, Integer> {
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.i(TAG, "onPreExecute...(开始执行后台任务之前)");
}

@Override
protected void onPostExecute(Integer i) {
super.onPostExecute(i);
Log.i("TAG", "onPostExecute...(开始执行后台任务之后)");
}

@Override
protected Integer doInBackground(Integer... params) {
Log.i(TAG, "doInBackground...(开始执行后台任务)");
return 0;
}
}

我们定义了自己的MAsyncTask并继承自AsyncTask;并重写了其中的是哪个回调方法:onPreExecute(),onPostExecute(),doInBackground();
然后开始调用异步任务:

1
new MAsyncTask().execute();

好了,下面我们开始分析异步任务的执行过程,首先查看一下异步任务的构造方法:

android异步消息机制(转)

android异步消息机制(转)

知乎上看了一篇非常不错的博文:有没有必要阅读ANDROID源码
痛定思过,为了更好的深入android体系,决定学习android framework层源码,就从最简单的android异步消息机制开始吧。

(一)Handler的常规使用方式

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
public class MainActivity extends AppCompatActivity {

public static final String TAG = MainActivity.class.getSimpleName();
private TextView texttitle = null;

/**
* 在主线程中定义Handler,并实现对应的handleMessage方法
*/
public static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 101) {
Log.i(TAG, "接收到handler消息...");
}
}
};

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

texttitle = (TextView) findViewById(R.id.texttitle);
texttitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
// 在子线程中发送异步消息
mHandler.sendEmptyMessage(101);
}
}.start();
}
});
}
}

可以看出,一般handler的使用方式都是在主线程中定义Handler,然后在子线程中调用mHandler.sendEmptyMessage();方法,然么这里有一个疑问了,我们可以在子线程中定义Handler么?

android项目构建流程(转)

android项目构建流程(转)

平时开发过程中我们通过android studio编写完成android项目之后直接点击 Run ‘app’就可以在build/outputs/apk生成可以在android设备中安装的apk文件了,那么整个android源码的构建过程是怎么样的呢?

我们可以根据Google官方提供的流程图来具体了解构建的过程:
image

通常的构建过程就是如上图所示,下面是具体描述:

1.AAPT(Android Asset Packaging Tool)工具会打包应用中的资源文件,如AndroidManifest.xml、layout布局中的xml等,并将xml文件编译为二进制形式,当然assets文件夹中的文件不会被编译,图片及raw文件夹中的资源也会保持原来的形态,需要注意的是raw文件夹中的资源也会生成资源id。AAPT编译完成之后会生成R.java文件。

2.AIDL工具会将所有的aidl接口转化为java接口。

3.所有的java代码,包括R.java与aidl文件都会被Java编译器编译成.class文件。

4.Dex工具会将上述产生的.class文件及第三库及其他.class文件编译成.dex文件(dex文件是Dalvik虚拟机可以执行的格式),dex文件最终会被打包进APK文件。

5.ApkBuilder工具会将编译过的资源及未编译过的资源(如图片等)以及.dex文件打包成APK文件。

6.生成APK文件后,需要对其签名才可安装到设备,平时测试时会使用debug keystore,当正式发布应用时必须使用release版的keystore对应用进行签名。

7.如果对APK正式签名,还需要使用zipalign工具对APK进行对齐操作,这样做的好处是当应用运行时会减少内存的开销。

注解相关知识

注解相关知识

注解@Retention可以用来修饰注解,是注解的注解,称为元注解。

Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型,
@Retention 注解指定标记注解的存储方式:
RetentionPolicy.SOURCE - 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
RetentionPolicy.CLASS - 标记的注解在编译时由编译器保留,但Java虚拟机(JVM)会忽略。 这是默认的生命周期;
RetentionPolicy.RUNTIME - 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,因此运行时环境可以使用它。

@Target 注解标记另一个注解,以限制可以应用注解的 Java 元素类型。目标注解指定以下元素类型之一作为其值:

ElementType.ANNOTATION_TYP可以应用于注解类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注解。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。

package com.example.inject;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

在Activity 中增加注解标注,通过注解+反射+动态代理等,来优化Activity中代码。省去 setContentView、findViewById、setOnClickListener等操作。

Layout注解

1
2
3
4
5
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectLayout {
@LayoutRes int value();
}
序列化面试题

序列化面试题

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
面试相关
1. 反序列化后的对象,需要调用构造函数重新构造吗
反序列化调用不会调用构造函数。以存储的二进制数据进行构造

2. 序列前的对象与序列化后的对象是什么关系?是("=="还是equal?是浅复制还是深复制?)
序列化前和序列化后 是两个不同的对象,对象地址发生了改变。调用equal 和 == 返回true。是一个深复制。

3. Android里面为什么要设计出Bundle而不是直接用Map结构
Bundle中是使用的Parcel打包数据。Parcel可以实现跨进程通讯。
<!-- more -->
Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数 据对应下标,一个对象数组
保存key和value,内部使用二分法对key进行排序,所以在添加、删 除、查找数据的时候,都会使用二分法查找,只适合于小数
据量操作,如果在数据量比较大的情况 下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,
HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据 量,我没见过在两个Activity之
间传递10个以上数据的场景,所以相比之下,在这种情况下使用 ArrayMap保存数据,在操作速度和内存占用上都具有优势,
因此使用Bundle来传递数据,可以保 证更快的速度和更少的内存占用。 另外一个原因,则是在Android中如果使用Intent来携带数据的话,
需要数据是基本类型或者是可 序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。
而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为 了更加快速的进行数据的序列化和反序列化,
系统封装了Bundle类,方便我们进行数据的传输。

4. SerialVersionID的作用是什么?
版本控制

5. Android中Intent/Bundle的通信原理及大小限制
大小限制 bundle 在zgote在创建进程的时候,分配了binder的内存大小。binder申请匿名内存有限制。
binder在内核空间创建内存映射时,大小限制在 < 4M
intent 1M限制
Intent 中的 Bundle 是使用 Binder 机制进行数据传送的。能使用的 Binder 的缓冲区是有大小限 制的(有些手机是 2 M),
而一个进程默认有 16 个 Binder 线程,所以一个线程能占用的缓冲区 就更小了( 有人以前做过测试,大约一个线程可以占用 128 KB)。
所以当你看到 The Binder transaction failed because it was too large 这类 TransactionTooLargeException 异常时,
你应 该知道怎么解决了

6. 为何Intent不能直接在组件间传递对象而要通过序列化机制?
startActivity(intent),activity启动流程要和AMS交互,需要跨进程通讯。只有把数据序列化后,传递。

7. 序列化与持久化的关系和区别是什么?
序列化:跨进程传输数据时,需要使用序列化。
持久化:数据的存储。
Intent在启动其他组件时,会离开当前应用程序进程,进入ActivityManagerService进程 (intent.prepareToLeaveProcess()),
这也就意味着,Intent所携带的数据要能够在不同进程间 传输。首先我们知道,Android是基于Linux系统,不同进程之间的java对象是无法传输,
所以我 们此处要对对象进行序列化,从而实现对象在 应用程序进程 和 ActivityManagerService进程 之间 传输。
而Parcel或者Serializable都可以将对象序列化,其中,Serializable使用方便,但性能不如Parcel 容器
,后者也是Android系统专门推出的用于进程间通信等的接口

:D 一言句子获取中...