AIDL

Book.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
package com.honszeal.processdemo;

import android.os.Parcel;
import android.os.Parcelable;

/**
* date:2018/3/13 on 16:07
* desc:
* author: liuch
*/
public class Book implements Parcelable{

private int bookId;
private String bookName;

protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}

public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}

@Override
public Book[] newArray(int size) {
return new Book[size];
}
};

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
}

IDE 自动完成

Book.aidl

1
2
3
package com.honszeal.processdemo;

parcelable Book;

ILibraryManager.aidl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.honszeal.processdemo;

import com.honszeal.processdemo.Book;


interface ILibraryManager {

List<Book> getNewBookList();

void donateBook(in Book book);

int add(int a, int b);

void log(String message);

}

编译一下

切换到 project 选项卡,在 app -> build -> generated -> source -> aidl -> debug -> 包名 下 找到系统生成的ILibraryManager.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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package com.honszeal.processdemo;

public interface ILibraryManager extends android.os.IInterface {

public static abstract class Stub extends android.os.Binder implements com.honszeal.processdemo.ILibraryManager {

private static final java.lang.String DESCRIPTOR = "com.honszeal.processdemo.ILibraryManager";

public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

public static com.honszeal.processdemo.ILibraryManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.honszeal.processdemo.ILibraryManager))) {
return ((com.honszeal.processdemo.ILibraryManager) iin);
}
return new com.honszeal.processdemo.ILibraryManager.Stub.Proxy(obj);
}

@Override
public android.os.IBinder asBinder() {
return this;
}

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getNewBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.honszeal.processdemo.Book> _result = this.getNewBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_donateBook: {
data.enforceInterface(DESCRIPTOR);
com.honszeal.processdemo.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.honszeal.processdemo.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.donateBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.honszeal.processdemo.ILibraryManager {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

@Override
public java.util.List<com.honszeal.processdemo.Book> getNewBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.honszeal.processdemo.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getNewBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.honszeal.processdemo.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

@Override
public void donateBook(com.honszeal.processdemo.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_donateBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}

static final int TRANSACTION_getNewBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_donateBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}

public java.util.List<com.honszeal.processdemo.Book> getNewBookList() throws android.os.RemoteException;

public void donateBook(com.honszeal.processdemo.Book book) throws android.os.RemoteException;

}

BookService.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
public class BookService extends Service {

private Binder binder = new ILibraryManager.Stub() {
@Override
public List<Book> getNewBookList() throws RemoteException {
return null;
}

@Override
public void donateBook(Book book) throws RemoteException {

}
};

@Override
public void onCreate() {
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
return binder;
}
}

手写

IBookManager.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.honszeal.processdemo;

import android.os.IInterface;
import android.os.RemoteException;

import java.util.List;

public interface IBookManager extends IInterface {

String DESCRIPTOR = "com.honszeal.processdemo.ILibraryManager";
int TRANSACTION_getNewBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
int TRANSACTION_donateBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

List<Book> getNewBookList() throws RemoteException;

void donateBook(Book book) throws RemoteException;
}

BookManagerImpl

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package com.honszeal.processdemo;

import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;

import java.util.List;

public class BookManagerImpl extends Binder implements IBookManager {

public BookManagerImpl() {
this.attachInterface(this, DESCRIPTOR);
}

public static IBookManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}

IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

if (iin != null && iin instanceof IBookManager) {
return ((IBookManager) iin);
}
return new BookManagerImpl.Proxy(obj);
}

@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_getNewBookList:
data.enforceInterface(DESCRIPTOR);
List<Book> bookList = this.getNewBookList();
reply.writeNoException();
reply.writeTypedList(bookList);
return true;
case TRANSACTION_donateBook:
data.enforceInterface(DESCRIPTOR);
Book arg0;
if (0 != data.readInt()) {
arg0 = Book.CREATOR.createFromParcel(data);
} else {
arg0 = null;
}
this.donateBook(arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}

@Override
public List<Book> getNewBookList() throws RemoteException {
return null;
}

@Override
public void donateBook(Book book) throws RemoteException {

}

@Override
public IBinder asBinder() {
return this;
}

private static class Proxy implements IBookManager {

private IBinder mRemote;

public Proxy(IBinder mRemote) {
this.mRemote = mRemote;
}

public String getInterfaceDescriptor() {
return DESCRIPTOR;
}

@Override
public List<Book> getNewBookList() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();

List<Book> result;

data.writeInterfaceToken(getInterfaceDescriptor());
mRemote.transact(TRANSACTION_getNewBookList, data, reply, 0);
reply.readException();
result = reply.createTypedArrayList(Book.CREATOR);
return result;
}

@Override
public void donateBook(Book book) throws RemoteException {
//
}

@Override
public IBinder asBinder() {
return mRemote;
}
}
}

感觉,就是具体的实现在服务端写,在客户端调用。实际上就是调用了 Proxy 的对应方法,然后 Bindertransact 方法 再就是 onTransact 方法。

权限验证

  1. onBind 中进行验证,验证不通过就直接返回 null, 这样验证失败的客户端直接无法绑定服务。
  2. onTransact 中进行验证,验证不通过就直接返回 false

Android 性能优化

布局优化

  • 删除布局中无用的控件和层级,其次有选择的使用性能较低的 ViewGroup,比如 RelativeLayout, ConstraintLayout 。同样层级的话更优先选择使用 LinearLayout,因为RelativeLayout 的布局过程需要花费更多的 CPU 时间。如果需要嵌套的方式来做,更建议采用 RelativeLayout ,因为 ViewGroup 的嵌套就相当于增加了布局的层级,同样会降低程序的性能。
  • 采用 <include> 标签,如果多个布局中需要一个相同的 layout ,可以使用<include> 来避免写重复的布局文件。
  • 采用 <merge> 标签。一般和 <include> 标签一起使用而减少布局的层级。
  • ViewStub。它非常轻量级,而且高/宽都是 0,因此它本身不参与任何的布局和绘制过程。ViewStub 的意义在于按需加载所需的布局文件,在实际开发中,有很多布局文件在正常情况下不会显示,这个时候就没有必要在整个界面初始化的时候将其加载进来,通过 ViewStub 就可以做到在使用的时候再加载。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <ViewStub
    android:id="@+id/stub_import"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:inflatedId="@+id/id_login"
    android:layout="@layout/refresh_layout" />

    其中 stub_import 是 ViewStub 的 id,id_login 是 refresh_layout 这个布局根元素的 id, 需要加载 ViewStub 的布局时,有两种方式:

    ((ViewStub)findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);

    或者

    View layout = ((ViewStub)findViewById(R.id.stub_import)).inflate();

绘制优化

绘制优化是指 ViewonDraw 方法要避免执行大量的操作。

onDraw 里不要创建新的局部对象 不要做耗时的操作。

内存泄露优化

内存泄露优化分为两个方面,一方面是在开发过程中避免写出有内存泄漏的代码,另一方面是通过一些分析工具找出潜在的内存泄漏继而解决。

场景1 静态变量导致的内存泄漏

下面的代码将导致 Activity 无法正常销毁,因为静态变量 sContext 引用了它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainActivity extends Activity{

private static final String TAG = "MainActivity";

private static Context sContext;

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

sContext = this;
}
}

解决方案:

  1. onDestroy 中释放 sContext = null
  2. 变成虚引用 private static WeakReference<Context> context

场景2 单例模式导致的内存泄漏

如下所示,提供一个单例模式的 TestManagerTestManager可以接受外部的注册并将外部的监听器存储起来。

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 TestManager{
private List<OnDataArrivedListener> mOnDataArrivedListeners = new ArrayList<>();

private static class SingletonHolder {
public static final TestManager INSTANCE = new TestManager();
}

private TestManager(){

}

public static TestManager getInstance(){
return SingletonHolder.INSTANCE;
}

public synchronized void registerListener(OnDataArrivedListener listener){
if(!mOnDataArrivedListeners.contains(listener)){
mOnDataArrivedListeners.add(listener);
}
}

public synchronized void unregisterListener(OnDataArrivedListener listener){
mOnDataArrivedListeners.remove(listener);
}

public interface OnDataArrivedListener{
void onDataArrived(Object data);
}
}

Activity 实现 OnDataArrivedListener 并向 TestManager 注册监听,如下所示。下面的代码由于缺少解注册的操作而引起内存泄漏,泄露的原因是 Activity 的对象被单例模式的 TestManager 所持有,而单例模式的特点是其生命周期和 Application 保持一致,因此 Activity 无法被及时释放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainActivity extends Activity{

private static final String TAG = "MainActivity";

private static Context sContext;

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

TestManager.getInstance().registerListener(this);
}
}

解决方案:

onDestroyTestManager.getInstance().unregisterListener(this);

场景3 属性动画导致的内存泄漏

属性动画中有一类无限循环的动画,如果在 Activity 中播放此类动画并且没有在 onDestroy 中去停止动画,那么动画会一直播放下去,尽管已经无法在页面上看到动画效果了,并且这个时候 ActivityView 会被动画持有,而 View 又持有了 Activity,最终 Activity 无法释放。

解决方法

是在 onDestroy 中停止动画。

场景4 非静态内部类导致的内存泄漏

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

private MyHandler handler;

private static class MyHandler extends Handler {

private WeakReference<MainActivity> reference;

public MyHandler(MainActivity activity) {
reference = new WeakReference<>(activity);
}

@Override
public void handleMessage(Message msg) {
//TODO: do something
reference.get().doSomeThing();
}
}

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

handler = new MyHandler(this);
}

@Override
protected void onDestroy() {
handler.removeMessages(1);
super.onDestroy();
}
}

响应速度优化和 ANR 日志分析

响应速度优化的核心思想是避免在主线程中做耗时操作。响应速度过慢更多的体现在 Activity 的启动速度上,如果在主线程中做太多事情,会导致 Activity 启动时出现黑屏现象,甚至出现 ANR(Application not response)Android 规定,Activity 如果 5s 内无法响应屏幕触摸事件或者键盘输入事件就会出现 ANRBroadcastReceiver 如果 10s 之内还未执行完操作也会出现 ANRService 如果 20s 之内还未执行完操作也会出现 ANR

当一个进程发生了 ANR 以后,系统会在data/anr 目录下创建一个 traces.txt,通过分析这个文件就能定位出 ANR 的原因。

ListView 优化 和 Bitmap 优化

ListView 优化方式

  • 布局复用
  • 使用 ViewHolder,减小 findViewById 的使用,避免在 getView 中执行耗时操作
  • 在列表快速滑动时不适合开启大量的异步任务的。
  • 数据可以分页加载。
  • 可以尝试开启硬件加速来使 ListView 的滑动更加流畅。
  • 尽量让 ItemView 的 Layout 层次结构简单。
  • ItemView 元素避免半透明。
  • 尽量能保证 Adapter 的 hasStableIds() 返回 true,这样在 notifyDataSetChanged() 的时候,如果 id 不变,ListView 将不会重新绘制这个 View,达到优化的目的。
  • 每个 ItemView 不能太高,特别是不要超过屏幕的高度。

Bitmap 优化方式

  • 加载合适尺寸的图片(二次采样,inSampleSize)。

线程优化

线程优化的思想是使用线程池,避免程序中存在大量的 Thread。线程池可以重用内部的线程,从而避免了线程的创建和销毁所带来的性能开销,同时线程池还可以有效的控制线程池的最大并发数,避免大量的线程因互相抢占系统资源从而导致堵塞现象的发生。

一些性能优化建议

  • 避免创建过多的对象。
  • 不要过多使用枚举,枚举占用的内存空间要比整形大。(不过枚举要是能影响性能的话,就太差了)
  • 常量请使用 static final 来修饰。
  • 使用 Android 特有的数据结构,比如 SparseArrayPair 等。
  • 适当使用软引用和弱引用。
  • 尽量采用静态内部类,这样可以避免潜在的由于内部类而导致的内存泄漏。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×

keyboard_arrow_up 回到顶端