总结Android App的一些技巧以及注意事项

我做Android App开发其实也就半年多时间,而且我是从Python转到Java,之前也没有系统学习过Java,下面内容是总结这半年多的经验,但难免理解不到位的地方,看到的各位请大胆直接在评论中指出。

1. App中可以直接写一个继承自Application的自定义类用作一个单例,可以存放一些全局的方法和变量。但请记住单例不能跨进程(我犯过这个错误)!在不同进程中Android会为每个进程创建一个Application对象,进程间通信还请使用AIDL或者Handler/Messenger模式。创建一个自定义的Application类很简单,只要继承自Application就可以了:

1
2
3
4
5
6
7
public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // your code here
    }
}

然后在AndroidManifest.xml中将android:name指定为该自定义Application:

1
2
3
4
5
6
7
8
9
10
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.example" >
 
    <application android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" 
        android:name="BaseApplication">
 
     </application>
</manifest>

这样就大功告成了。在Activity中通过(BaseApplication) getApplication();的方法来访问。

2. 自定义一个Activity基类是一个很好的方法,通过把一些通用方法写入Activity可以减少那些真正展现在用户面前的Activity代码。通用方法往往是界面布局上的方法,比如设定ActionBar,设定FullScreen等。之前我写的几个App都没有使用这个方法,导致部分Activity类的代码特别长,看起来吃力。结合上面的Application自定义类,我们可以创建一个不错的BaseActivity:

1
2
3
4
5
6
7
8
9
public class BaseActivity extends ActionBarActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        application = (BaseApplication) getApplication();
    }
 
    protected BaseApplication application;
}

3. Activity与Service通信有好几种方法,不同的方法适合不同情况。如果Activity与Service在同进程内,那么直接使用Binder绑定的方法进行通信;如果Activity与Service在不同进程中,但Activity只需要向Service发送信息而不需要从Service中获取信息,那么使用Handler/Messenger的方法进行通信会比较方便;如果Activity需要从另一进程中的Service获取信息,那么必须使用AIDL的方法进行通信。

a. 同进程内使用Binder方法非常简单,只需要创建一个自定义的Binder类然后创建一个getService方法即可:

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
// Binder example: MService.java
public class MService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
 
    private LocalBinder binder = new LocalBinder();
    public class LocalBinder extends Binder {
        public MService getService() {
            return MService.this;
        }
    }
}
 
 
// Binder example: MActivity.java
public class MActivity extends BaseActivity {
    @Override
    public void onStart() {
        super.onStart();
        bindService(new Intent(this, MService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
    }
 
    @Override
    public void onStop() {
        super.onStop();
        if (isMServiceBound) {
            unbindService(mServiceConnection);
            isMServiceBound = false;
        }
    }
 
    private boolean isMServiceBound = false;
    private MService mService;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = ((MService.LocalBinder) service).getService();
            isMServiceBound = true;
        }
 
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
            isMServiceBound = false;
        }
    }
}

b. 不同进程中使用Handler/Messenger模式。这种方法适用于由Activity向Service发送消息。当然也是可以实现反向发送消息,不过需要两边都实现一套Handler/Messenger,对于这种情况我更倾向使用AIDL的方法去实现。

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
// Handler/Messenger example: MService.java
public class MService extends Service {
    @Override
    public IBinder onBind() {
        return messenger.getBinder();
    }
 
    private void sayHello(int name) {
        Log.i("MService", "hello " + Integer.toString(name));
    }
 
    private final Messenger messenger = new Messenger(new IncomingHandler());
    public final static int MESSAGE_SAY_HELLO = 0;
    private class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            swtich (msg.what) {
                case MESSAGE_SAY_HELLO:
                    sayHello(msg.arg1);
                    break;
                defaule:
                    super.handleMessage(msg);
            }
        }
    }
}
 
 
// Handler/Messenger example: MActivity.java
public class MainActivity extends BaseActivity {
    @Override
    public void onStart() {
        super.onStart();
        bindService(new Intent(this, MService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
    }
 
    @Override
    publicvoid onStop() {
        if (isMServiceBound) {
            unbindService(mServiceConnection);
            isMServiceBound = false;
        }
    }
 
    private void sayHello() {
        try {
            // 这里偷懒了,关于使用Message传递其他类型数据的方法请参考官方文档
            mService.send(Message.obtain(null, MService.MESSAGE_SAY_HELLO, 123, 0));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
 
    private boolean isMServiceBound = false;
    private Messenger mService;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);
            isMServiceBound = true;
            sayHello();
        }
 
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
            isMServiceBound = false;
        }
    }
}

Handler/Messenger还可以在很多情形下使用,我想我应该避免使用Broadcast而更多的使用Handler/Messenger模式才对,因为这种模式相比广播更加安全而且效率更加高。

c. AIDL是Android中实现跨线程访问的推荐方法,AIDL的全程是Android Interface Definition Language,它的语法就是一些Java的声明语法,所以很容易理解。在Android Studio中AIDL文件都是放在app/src/main/aidl/中相对应于java代码目录结构的文件夹下。比如我有一个app/src/main/java/com/example/example/service/MService.java文件,那么这个文件对应的AIDL文件就在app/src/main/aidl/com/example/example/service/MService.aidl。需要注意的是在Android Studio中AIDL的文件在编译后如果再进行修改的话,修改部分不会再次被编译,所以只能删除原有文件后重新创建才可以,这点可能在后续的Studio版本中改进,至于Eclipse的情况因为我不用就不清楚了。

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
// AIDL example: MServiceAidl.aidl
interface MServiceAidl {
    // 定义相应的接口
    void sayHello(String name);
}
 
// AIDL example: MService.java
public class MService extends Service {
    @Override
    public IBinder onBind() {
        return binder;
    }
 
    private void doSayHello(String name) {
        Log.i("MService", "hello " + name);
    }
 
    private final MServiceAidl.Stub binder = new MServiceAidl.Stub() {
        public sayHello(String name) {
            doSayHello(name);
        }
    }
}
 
// AIDL example: MActivity.java
public class MActivity extends BaseActivity {
    @Override
    public void onStart() {
        super.onStart();
        bindService(new Intent(this, MService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
    }
 
    @Override
    public void onStop() {
        super.onStop();
        if (isMServiceBound) {
            unbindService(mServiceConnection);
            isMServiceBound = false;
        }
    }
 
    private boolean isMServiceBound = false;
    private MServiceAidl mService;
    privaet ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);
            isMServiceBound = true;
            mService.sayHello("world");
        }
 
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
            isMServiceBound = false;
        }
    }
}