ppjun's blog

Thanks for your watching


  • 首頁

  • 歸檔

  • 關於

  • 檢索
close

android面试题

發表於 2017-04-20   |  

1. Activity和fragment的生命周期

  1. Activity从onCreate→onStart→onResume→onPause→onStop→onDestroy,其中oncreate初始化加载布局资源,onStart布局可见但是还在后台不可交互

    onResume布局可见在前台可以交互。onPause正在停止当前activity,onStop表示activity即将停止,做一些回收资源操作,onDestroy销毁activity最终的资源释放。总结:onStart和onPause可见不可见,onResume和onStop可不可以交互

  2. Fragment从onAttach→onCreate→onCreateView→onStart→onResume→onPause→onStop→onDestroyView→onDestroy-onDetach

    ​

2. Layout_gravity和gravity的区别。

比如设置android:layout_gravity=”right”的button会显示在父view的最右边。所以layout_gravity是设置当前view在父view的位置。

比如设置了android:gravity=”left”的LinearLayout会让里面的子View最显示在最左边。所以gravity是设置当前view里面的子view的位置。

3. 当一个activity跳转到另一个activity的生命周期

我们分别用AB代替两个activity

onPause(A)→onCreate(B)→onStart(B)→onResume(B)→onStop(A)

4.简单介绍Handler Message MessageQueue Looper

当Handler调用sendMessage方法会先把调用MessageQueue.enqeueMessage把Message加到消息队列,然后Looper开启循环不断遍历消息队列,调用MessageQueue.next获取message,然后调用目标Handler的dispatchMessage,判断Message有没有Callback即有没有Runnable对象,再判断Handler有没有callback,如果都没最后输出到Handler的handleMessage方法来处理消息。

5. 内存溢出和内存泄露

oom又叫out of memory 就是内存溢出。就是对象需求的内存大于jvm可用内存,避免oom要

减小对象内存的使用

1)使用更加轻量的数据结构,使用ArrayMap/SparseArray代替HashMap

2)避免使用Enum

3)减小bitmap对象的内存占用 使用insampleSize缩放比例,设置decodeFormat,解码格式

4)使用更小的图片,用tinypng压缩图片

内存对象的重复使用。使用对象池

1)StringBuilder减少String对象创建

2)避免在onDraw创建对象,因为onDraw会频繁调用,频繁gc,从而造成内存抖动。

3)复用系统提供的资源,比如字符串 颜色 图片 动画 样式 和布局

4)在listview或者gridview对convertView的复用

5)bitmap对象的复用在api11-18使用inbitmap,确定具有相同的解码模式,第二张图片会使用第一张照片已存在的位置

避免内存泄露

1)注意Activity的泄漏,一般是内部类引用导致activity泄露比如handler

2)Activity Context被传到其他实例中,这可能导致自身引用发生泄漏

3)考虑使用Application Context 而不是Activity Context当然dialog就必须是activity的content

4)注意临时的bitmap对象的回收 调用bitmap.recycle()

5) 监听器的注销要手动unregister 比如realm的list监听器

6)注意缓存容器中的对象泄漏比如2.3版本的drawable会对view强引用,

7)注意webview的泄露

8)还有Cursor游标的关闭。

6. Jvm的堆栈方法区

堆区存放所有的对象,只有一个,每个对象都包含一个与之对应的class信息class的目的是得到操作指令,但是不包括基本类型和对象引用,new一个类就是对象引用。只存放对象本身。

栈区 每个线程都有一个栈区,用来存放对象的引用和基本类型,(怎么区分对象和对象的应用呢 举个例子 Person person=new Person(); =左边是对象的引用,=右边是对象),其他栈不能访问另外一个栈的内容

方法区,就是静态变量static修饰的变量,常量。

设计模式SOLID五大原则

發表於 2017-03-20   |  

设计模式SOLID五大原则

SOLID每一个字母都代表这一种编程原则,其中

  1. S代表着单一职责原则
  2. O代表着开闭原则
  3. L代表着里氏替换原则
  4. I代表着接口隔离原则
  5. D代表着依赖倒置原则

单一职责原则

单一职责原则就是造成一个类改变的原因一个只有一个。再比如手机的电池是一个类,电池只为手机提供电源的职责。

在Android开发中,adapter类职责只负责视图的显示,这时候你会说adapter里面还有很多方法比如创建视图,显示视图,提供视图数量等。但是根据Martin的话来说变化的指针只在变化真正发生时起作用,如果没有任何征兆,应用单一职责原则或者其他原则是不明智的。就是说adapter创建和显示视图是这个类的职责,而不应该包括视图显示内容的计算逻辑,我只要知道结果就行了。下面看例子:

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
51
52
53
54
55
56
//UserOrder.java
public class UserOrder {
private double createTime;

public double getCreateTime() {
return createTime;
}

public void setCreateTime(double createTime) {
this.createTime = createTime;
}

}
//UserOrderAdapter.java
public class UserOrderAdapter extends RecyclerView.Adapter<UserOrderAdapter.UserOrderHolder> {
List<UserOrder> mList;
public UserOrderAdapter(List<UserOrder> list) {
this.mList = list;
}

@Override
public UserOrderHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_order_rv_item, parent, false);
return new UserOrderHolder(view);
}

@Override
public void onBindViewHolder(final UserOrderHolder holder, int position) {

holder.mPayBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
UserOrder userOrder=mList.get(position);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
holder.mCreatetime.setText(sdf.format(userOrder.getCreateTime()));
}
});

}
@Override
public int getItemCount() {
return mList.size();
}

public class UserOrderHolder extends RecyclerView.ViewHolder {

TextView mCreatetime;

public UserOrderHolder(View itemView) {
super(itemView);
mCreatetime = (TextView) itemView.findViewById(R.id.user_order_createtime);

}
}
}

↑这里的onBindViewHolder处理了视图的逻辑,应该把时间格式化的操作放在UserOrder类中。如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//UserOrder.java
public class UserOrder {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private double createTime;
public String getCreateTime() {
return sdf.format(createTime);
}
public void setCreateTime(double createTime) {
this.createTime = createTime;
}
}
//UserOrderAdapter.java
@Override
public void onBindViewHolder(final UserOrderHolder holder, int position) {

holder.mPayBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
UserOrder userOrder=mList.get(position);
holder.mCreatetime.setText(userOrder.getCreateTime());//格式化时间的逻辑放在UserOrder类中
}
});

}

总结:一个类只包含一个职责,其他的逻辑放在对应的类上处理。

开闭原则

开闭原则就是在每次有新需求都不能在原来的代码中做修改。你可以一开始的时候就用多态和接口来实现架构,让你的代码更容易扩展,而不是修改。

比如你要计算一个三角形的面积

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Triangle.java
public class Triangle{
private double width;
private double height;
private double getArea(){
return width*height/2;
}
//setter&getter
}

//AreaManager
public class AreaManager{
private double calculateArea(ArrayList<Triangle> triangles){
double area=0;
for(Triangle triangle:triagnles)
area+=triangle.getArea();
return area;
}

}

一开始代码是这样子设计的,但是后续要继续加上圆形,矩形的面积,就要在AreaManager类里面加上计算圆形,矩形的面积方法,不符合我们的开闭原则。所以要写一个shape接口,让三角形矩形都实现这个接口,在calculateArea方法里面传入ArrayList。不管以后要计算什么边形的面积只要实现shape接口即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface Shape{
double getArea();
}

public class Circle implements Shape{
public static final double pi=3.14;
double radius;
public double getArea(){
return radius*radius*pi;
}
//setter&getter
}

public class AreaManager{
public double calculateArea(ArrayList<Shape> shapes){
double area=0;
for(Shape shape:shapes){
area+=shape.getArea();

}
return area;
}

}

总结:在写计算方法时,考虑到以后有多个方案要怎么设计。通常是多个方案都实现了一个接口,接口方法就是该方案的逻辑。然后在方法被调用时传入接口类作为参数,调用接口方法。

里氏替换原则

里氏替换原则就是用接口类或者父类来替代子类,而不改变程序的正确性。

举个例子,下面adapter的构造函数就用了List类型的参数来替换ArrayList类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
//MainActivity.java
List<String> mList=new ArrayList<>();
mList.add("str");
UserOrderAdapter adapter=new UserOrderAdapter(mList);

//UserOrderAdapter.java
public class UserOrderAapter extends RecyclerView.Adapter<UserorderHolder>{
List<String> list;
public UserOrderAdapter(List<String> list){
this.list=list;
}

}

下面例子用了一个Arraylist的变量作为返回值。

1
2
3
4
5
6
public List<String> getList(String[] str){
ArrayList<String> list=new ArrayList<>();
for(String s:str)
list.add(s);
return list;
}

总结,这个原则相当简单你应该相当熟悉或者你每天都在用了。

接口隔离原则

接口隔离原则有点像单一职责原则,不过目标是接口类应该只有一种职责。

比如自定义一个View需要加上一个点击事件,

1
2
3
public interface onClickListener{
void onClick(View v);
}

然后,你又有需求要加上一个长按事件和一个触摸事件,

1
2
3
4
5
public interface onClickListener{
void onClick(View v);
void onLongClick(View v);
void onTouch(View v,MotionEvent ev);
}

在设置监听这个接口时:你必须重写这三个方法,可能你用不着后面两个方法。

1
2
3
4
5
6
7
8
9
10
11
new CustomeView.setOnClick(new OnClickListener{
public void onClick(View v){

}
public void onLongClick(View v){

}
public void onTouch(View v,MotionEvent ev){

}
});

这个违背了接口隔离原则,一个实现接口的类应该依赖他最小的接口。

所以我们的onClickListener只要写一个onClick方法就行了。其他的方法另外创建接口来写。比如OnTouchListener。

总结:我们写一个接口类里面的方法应该是同一个职责的,不同职责的接口方法创建新的接口类来实现。

依赖倒置原则

依赖倒置就是依赖抽象,而不依赖具体的实例。

就是在项目中我们的架构分为三层,安卓ui→业务逻辑→数据层,业务逻辑具体是判断写入或者获取数据是否符合条件

比如我们现在要做网络可用时将一个字符串写入数据库的操作,这样子activity就持有netmanager和dbmanager的具体实例子,不符合我们的依赖倒置原则。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//MainActivity.java
protected void onCreate(bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NetWorkManager netManager=//...
DbManager dbManager
if(netManager.isNetWorkConnected){

dbManager.insert();
}
}

//NetWorkManager.java
public boolean isNetWorkConnected(){

}
//DbManager.java
public void insert(){}

所以我们要做出如下修改:

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

//MainActivity.java
IDbManager IdbManager;


protected void onCreate(bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IdbManager=new DBManager(this);//实例化BDmanager
IdbManager.insert();

}

//IDbManager.java
public interface IDbManager{
public void insert();
}
//DbManager.java
public class DbManager{
INetManager netManager;
public void setNetManager(INetManager netManager){
this.netManager=netManager;
}
public void insert(){
if(netManager.isNetworkConnected()){
//do
}
}

}

//INetManager.java
public interface INetManager{
boolean isNetworkConnected();
}
//NetManager.java
public class NetManager{
public boolean isNetworkConnected(){
//do
}
}

这样子就可以让activity直接依赖IDbManager的抽象类。

总结:通常我们为了更简单实例化抽象类,我们推荐使用dagger2。

EventBus3.0源码分析

發表於 2017-03-13   |  

EventBus3.0源码分析

从3.0开始用了更多注解事件的订阅方法来代替之前固定onEvent开头的方法。现在你可以是容易方法加上@Subscribe的注解。

更多的eventbus使用方法请看http://www.jianshu.com/p/c47e0900399e

EventBus.java

  1. EventBus类的创建使用了常见的单例模式,如下:
1
2
3
4
5
6
7
8
9
10
11
static volatile EventBus defaultInstance; //volatile 修饰的变量防止被编译器优化,导致执行顺序变化
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
  1. 使用了建造者模式给变量赋值。先在EventBus的构造方法创建EventBusBuilder ,再重载过的EventBus方法传入builder来配置一些变量(比如一些exception)。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
  1. 在register中,获取订阅者的类subscriberClass通常是activity和fragment。然后通过subscribeerMethodFinder获取订阅者上订阅方法返回一个list集合。其中subscriberMethod就是写在activity上拿来接受消息的方法的对象,里面的变量包括Method就可以知道方法名;eventType可以知道是ativity还是fragment;threadMode是哪个线程;sticky是否粘性事件;priority优先级系数多少。如下:
1
2
3
4
5
6
7
8
9
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}

Realm-in-android-1

發表於 2017-02-23   |  

Realm介绍

  • Realm官网链接https://realm.io/
  • 开源地址https://github.com/realm/realm-java
  • 官方使用文档https://realm.io/docs/java/latest/

Realm是一个开源的ORM概念的(对象关系映射)移动数据库,可以在Android ,ios ,java各个平台上使用,性能秒杀sqlite等数据库比如(greendao)。

快速入门

只需两步

在项目的build.gradle中dependencies输入classpath “io.realm:realm-gradle-plugin:2.3.1”,这里要双引号。这里要双引号。这里要双引号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
buildscript {
repositories {

jcenter()

}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
classpath "io.realm:realm-gradle-plugin:2.3.1"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

在app的bulid.gradle输入apply plugin: ‘realm-android’

1
apply plugin: 'realm-android'

★以上操作完成了realm在as的配置了。

Realm的模型

自定义类要继承RealmObject,注意这里必须要有一个无参数的构造方法。

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
public class Dog extends RealmObject {
private String name;
private int age;

public Dog() {

}

public Dog(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

如果类A中包含着其他类的集合这时候要用到ResultList,比如

1
2
3
4
5
6
public class Person extends RealmObject {
@PrimaryKey
private long id;
private String name;
private RealmList<Dog> mDogRealmList;
}

如果你的自定义类已经继承了其他父类,你也可以实现RealmModel来建造realm对象模型。

1
2
3
4
5
6
7
@RealmClass
public class User implements RealmModel {

private String name;
private int id;
//...
}

Realm注解说明

@RealmClass 当实现RealmModel接口的类要加上这个注解。

@Required 修饰不能为空的成员变量

@Ignore 修饰不写入数据库的变量

@PrimaryKey 设置该成员变量为类的主键

@Index 加快查询速度,不过会让插入数据时变慢

Realm初始化

在自定义Applciation中Realm.init(this);

获取Realm实例

1
realm = Realm.getDefaultInstance();

这是获取默认配置的Realm,默认保存在data/data/packageName/files/default.realm 你也可以自定义RealmConfiguration,通常是自定义文件名,加密的key,数据库版本号和是否删除合并前的数据等。Realm实现了closeable接口,所以每次的getInstance,到最后关闭使用后都要调用close方法。比如在activity的ondestroy调用realm.close();

Realm写入

Realm是一个MVCC架构,同一线程的读写操作不影响获取数据,但是多线程读取写入操作就要用到事务来确保获取数据一致性和线程安全。的是放在事务transcation里面执行,确保整个事务的操作都被提交或者全部取消操作调用realm.cancelTranscation();,确保数据的一致性。

1
2
3
4
5
6
realm = Realm.getDefaultInstance();
realm.beginTransaction();
Dog d=new Dog("a",1);
Dog b=realm.copyToRealm(d);//这里的copyToRealm相当于深拷贝了一个d变量。对原来的d变量没任何影响。
b.setName("b");
realm.commitTransaction(); //最后往数据库写入一个dog变量b

♥copyToRealm会深拷贝一个变量到数据库包括主键,如果主键重复就会抛出异常,拷贝之后主键不能修改。

当两个线程同时进行写入操作,另外一个会造成主线程阻塞,所以要调用异步事务避免主线程阻塞。

1
2
3
4
5
6
7
8
//同步新增数据
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Dog dog = realm.copyToRealm(new Dog(1, "2", 3));
dog.setName("pp");
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//异步修改 查询
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Dog dog = realm.copyToRealm(dogOne);
dog.setName("apple");

}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
RealmResults<Dog> list = realm.where(Dog.class).findAll();
for (int i = 0; i < list.size(); i++) {
Log.i(TAG, "onSuccess: " + list.toString());
}
}
});

realm的主键,在oncreate方法加入设置主键的RealmObject方法,下次调用oncreate没有判断主键是否exist就加入数据库就会报错。而在oncreate方法没加入没设置主键的realmobject类 ,下次调用oncreate方法就会在自启动数据库文件追加。

Realm 查询

查询都是返回一个RealmResults对象支持以下查询条件。

  1. between(),greaterThan(), lessThan(), greaterThanOrEqualTo() 和lessThanOrEqualTo();
  2. equalTo()和notEqualTo()
  3. contains(),beginsWith()和endsWith()
  4. isNull()和isNotNull()
  5. isEmpty()和isNotEmpty()
1
2
3
4
RealmResults<Dog> list=realm.where(Dog.class).lessThan("age",1).findAll();
for (int i = 0; i < list.size(); i++) {
Log.i(TAG, "execute: "+list.get(i).getName());
}

Realm修改

修改操作要在一个事务里面完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dog = new Dog(1, "a", 1);
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
dog = realm.copyToRealm(dog);
dog.setAge(2);
dog.setName("kiki");


RealmResults<Dog> list = realm.where(Dog.class).equalTo("id", 1).findAll();
list.get(0).setName("ab");



}
});

RealmResult的changeListener

监听RealmResult内容变化。

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
RealmResults<Dog> list = realm.where(Dog.class).lessThan("id", 3).findAll();


realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {

Dog dog = realm.createObject(Dog.class);
dog.setId(2);
dog.setName("aaaa");
dog.setAge(3);


}
});
list.addChangeListener(new RealmChangeListener<RealmResults<Dog>>() {
@Override
public void onChange(RealmResults<Dog> element) {
Log.i(TAG, "onChange: " + element.toString());
}
});

//最后需要在activity或者fragment的生命周期比如ondestory调用
list.removeChangeListeners();//删除全部的listener
list。removeChangeListener(callback);//删除一个callback

Realm的删除

继续上面的例子查询id小于3的realmresult集合

1
2
3
4
5
6
7
8
9
10
list.addChangeListener(new RealmChangeListener<RealmResults<Dog>>() {
@Override
public void onChange(RealmResults<Dog> element) {
element.deleteFirstFromRealm();//删除第一条数据
element.deleteAllFromRealm();//删除全部数据
element.deleteFromRealm(0);//删除指定数据
element.deleteLastFromRealm();//删除最后一条数据
element.get(1).deleteFromRealm();//删除指定obejct
}
});

Realm添加一个json字符串

1
2
3
4
5
6
7
8
9
10
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.createObjectFromJson(Dog.class,"{\n" +
" \"id\": 1,\n" +
" \"name\": \"bili\",\n" +
" \"age\": 12\n" +
"}");
}
});

copyFromRealm,copyToRealm和createObject区别

  • copyFromRealm传入一个RealmObject 拷贝realm数据库中的一个变量并拷贝他的所有属性。
  • copyToRealm 传入一个Obeject 拷贝这个Object的所有属性到realm数据库中
  • createObject 传入一个class 并且赋值他的成员变量为默认值null 或者需要后期赋值使用

RealmResult 使用

1
2
3
4
5
RealmResults<Dog> list = realm.where(Dog.class).findAll();
list.sum("age");
list.max("age");
list.min("age");
list.average("age");

比如获取dog类的所有对象,求age的总和,最大值,最小值,平均值

DynamicRealm

DynamicRealm是Realm的变种类,可以操作没继承RealmObject的类,操作类,当然是以字符串的形式操作,而不是RealmObject,他的默认配置少了schema版本号,migration合并信息的检查。

1
2
3
4
5
6
7
8
9
DynamicRealm dynamicRealm = DynamicRealm.getInstance(new RealmConfiguration.Builder().build());
dynamicRealm.executeTransaction(new DynamicRealm.Transaction() {
@Override
public void execute(DynamicRealm realm) {
DynamicRealmObject person = realm.createObject("Person",1);
person.setString("name", "kik");

}
});

Realm的close

Realm实现了closeable接口,所以每次的getInstance,到最后关闭使用后都要调用close方法。比如在activity的ondestroy调用realm.close();

比如在子线程getIntstance一次,需要在子线程结束前调用一次close();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyRunnable implements Runnable{

@Override
public void run() {
try {
Realm realm=Realm.getDefaultInstance();

} catch (Exception e) {
e.printStackTrace();
} finally {
realm.close();

}
}
}

Realm 版本迁移

如果realm版本是1,现在要升级realm版本是2并且数据解构改变了比如新增了一个RealmObejct的成员变量,如果default.realm存在旧数据,会升级失败。需要要设置migration合并规则。

要自定义一个RealmConfiguration变量 ,重写migrate方法判断oldVersion是上一个版本号,要做什么需求。

1
2
3
4
5
6
7
8
9
10
//你还可以做以下操作
addField("key",long.class);//加一个成员变量key
removeField("key");//去掉一个成员变量key
addRealmListField("dogs",schema.get("Dog"));//加上Realmlist变量dogs
addRealmObjectField("dog",schema.get("Dog"));//加上realmobject变量dog
transform(new RealmObjectSchema.Function(){
public void apply(DynamicRealmObject obj){
obj.set("fullname",obj.getString("firstName")+" "+obj.getString("lastName"))
}
});//把firstname lastname赋值给fullname

比如版本升级加上key

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

RealmMigration mRealmMigration=new RealmMigration() {
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema=realm.getSchema();
/*
//version 0
class Dog

private long id;
private String name;
private int age;

//version 1
class Dog
private long key;
private long id;
private String name;
private int age;

*/




//version 0 to version 1
if(oldVersion==1){
schema.get("Dog")
.addField("key",long.class);
/* .addRealmObjectField("dogs",schema.get("Dog"))
.addRealmObjectField("dog",schema.get("Dog"));*/

oldVersion++;
}
}
};


//最后
realm = Realm.getInstance(PPApplicaion.mMigrationConfiguration);

总结

  1. Realm很多种情况修改数据要配合事务使用。
  2. Realm以一个RealmObject作为一个表的功能。
  3. realm的getInstance和close要结对使用
  4. 需要注意RealmResult移除listener

java线程中的Runnable,Callable,Future,FutureTask

發表於 2017-02-15   |  

前言,在java线程中最常见的是Thread 和Runnable,很少见到或者用到callable等类。但是,你接触过android源码就会经常看到这些类,比如AsyncTask的源码在execute方法源码可以看到了这些类。在面试时也有可能会问到两者的区别,所以现在容许鄙人来介绍一下java中这些类的用法。

Runnable

Runnable一个接口类,包括一个run的接口方法:通常我们会自定义一个类去实现Runnable,这时候还可以实现其他接口方法,如果类要是继承Thread就不能再继承其他类了。

1
2
3
public interface Runnable{
public abstract void run();
}

Runnable 通常配合Thread使用,在run方法里面写耗时的操作:

1
2
3
4
5
new Thread(new Runnable(){
public void run(){
//
}
}).start();

Callable

Callable是一个泛型接口,要比Runnable强些 ,因为接口方法call有返回值,并且返回值是传入的泛型类型,还能call的过程中抛出异常。

1
2
3
public interface Callable<V>{
V call() throws Exception;
}

示例要配合Future或者FutureTask来执行。

Future

Future是一个泛型接口类,是Runnable和Callable的调度容器,就是对Runnable和Callable的结果进行操作,比如:

  1. isCancelled()取消操作,call方法任务完成前取消,返回true。
  2. isDone()判断是否操作完成,是则返回true。
  3. get()获取操作结果,会导致程序阻塞,必须等到子线程结束才会得到返回值。
  4. get(long timeout TimeUnit unit)在某时间后获取操作结果,如果在规定时间内获取不到返回值将会抛出超时异常)
1
2
3
4
5
6
7
public interface Future<V>{
boolean isCancelled();
boolean isDone();
V get() throws InterputeredException,ExecutionExeception;
V get(long timeout ,TimeUnit unit)throws InterputeredException,ExecutionExeception,TimeoutExeception;

}

比如

1
2
3
4
5
6
7
8
9
ExecutorService threadPool=Executors.newSingleThreadExecutor();
Future future=threadPool.submit(new Callable<String>(){
public String call()throws Exception{
return "result";
}

});

Futurn.get();//返回操作结果

FutureTask

FutureTask类同时实现了Runnable和Future 两个接口,具有了两个接口的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class FutureTask<V> implements RunnableFuture<V>{
public FutureTask(Callable<V> callable){
this.callable=callable;
...
}
public FutureTask(Runnable runnable,V result){
this.callable=Executors.callable(runnable,result);
...
}
boolean isCancelled();
boolean isDone();
V get() throws InterputeredException,ExecutionExeception;
V get(long timeout ,TimeUnit unit)throws InterputeredException,ExecutionExeception,TimeoutExeception;
}
public interface RunnableFuture<V> implements Runnable,Callable<V>{
void run();
}

这里的FutureTask间接实现了两个接口,在FutureTask的构造方法传入Callable或者是Runnable都会转为callable,runnable通过runnableadapter转为callable。同时FutureTask还具备Future的所有方法。

举个例子

1
2
3
4
5
6
7
8
9
Callable<Integer> callable=new Callable<Integer>(){
public Ingeter call() throws Exception{
return 100;
}
};
FutureTask<Integer> task=new FutureTask<Integer>(callable);
new Thread(task).start();

task.get();//当然要先启动线程才能得到结果;

这里的callable当成runnable用了。

总结

  1. Callable比Runnable高级能返回结果值和抛出异常。

  2. 可以有上述例子看到Callable来产生结果,Futuretask来获取结果。

  3. 在获取结果期间还可以控制是否取消thread 判断thread是否完成。

    ​

AsyncTask-Analyze

發表於 2017-02-13   |  

我们在初学Android都用过AsyncTask 一个很方便用来请求网络改变UI控件的类。面试官不多不少也会问到AsyncTask的内容,下面来分析一下这个类内部的逻辑。

AsyncTask说明

AsyncTask内部封装了Handler和Thread分别原来改变ui线程和在子线程做耗时操作。同时AsyncTask是一个抽象的泛型类

1
public abstract class AsyncTask<Params,Progress,Result>{}
  1. Params代表参数类型(doInBackground的参数)通常是请求的url
  2. Progress代表进度类型(onProgressUpdate的参数)通常是Integer
  3. Result代表结果返回类型(onPostExecute的参数),如果asynctask不需要传递参数就用Void来代替上述三个参数类型

AsyncTask 4个核心方法

  1. onPreExecute() 异步开始前会执行该方法,用于显示dialog
  2. doInBackground(Params… params) 在线程池执行的异步任务,在此方法内调用publishProgress 传入int进度值来更新进度,返回结果给onPostExecute,这里的省略号是可以传入多个相同类型的参数。
  3. onPorgressUpdate(Progress… progress) 此方法在主线程执行,当任务发生进度改变就会调用此方法,比如显示下载进度
  4. onPostExecute(Result… result) 主线程执行,但异步方法执行完就会调用的方法。传入参数为doInBackground的返回值.

ps: 当doInBackground的线程被取消就会回调给onCancelled这个方法,此方法被调用onPostExecute就不会调用了。

总结:

  1. asynctask对象要在主线程创建,并调用execute方法
  2. 不能直接调用doinbackground方法
  3. 一个asynctask对象只执行一次
  4. 3.0之前asynctask用并行线程池执行,3.0后asynctask改用串行线程池,当然你可以通过asynctask.executeOnExecutor来并行执行任务

源码入口execute

AsyncTask开始执行的方法是在new AsyncTask().execute(url1),execute又调用了executeOnExetutor方法 传入一个串行的线程池SDefaultExecutor和url参数。以下代码:

1
2
3
4
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}

上面的sDefaultExecute就是new一个SerialExecute类

1
2
3
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

进入SerialExecutor类,我们看到这个类有个mTask 里面的Runnable会排队执行。并且判断执行完开始执行下一个任务。

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
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;

public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}

protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}

接来下,回到executeOnExetutor 这个方法里面 我们可以看到一个mStatus来记录当前任务的状态,期间不断改变状态。

每个AsyncTask最先执行onPreExecute方法,然后线程池exec.execute(mFuture);开始执行。这里的mFuture就是一个Runnable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}

mStatus = Status.RUNNING;

onPreExecute();

mWorker.mParams = params;
exec.execute(mFuture);

return this;
}

接着将mFuture传入SerialExecutor的execute方法执行,将之前的mFuture对象插入队列mTask里面,判断当前有没有任务在进行,没活动就调用schedulNext方法执行下一个asynctask任务。

Asynctask里面有2个线程池(一个用于排队的serialexecutor和用于真正执行任务的THREAD_POOL_EXECUTOR )和(负责线程调度的)internalHandler

然后在Asynctask的构造方法 mWork会调用call方法将mTaskInvoked设置为true,再调用doinbackground 得到返回值再调用onpostexecute方法,

在postResult方法里面会发送message到sHandle来执行finish task还是更新进度条,这里的finish有2种情况分别是调用onCancelled 和onPostexecute.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
123…6
区汇君

区汇君

34 文章
26 標籤
GitHub Weibo
© 2015 - 2017 区汇君
由 Hexo 強力驅動
主題 - NexT.Pisces