概述
在我经历的所有的面试当中几乎都会遇到这样一道面试题:Activity与Service之间是如何交互的。这个问题看上去很简单,实际上它确实也简单(神转折)。主要是因为面试官问的多了,简单归简单,但我觉得挺重要的。特意写一篇文章记录一下。
交互方式
Acticity与Service传递参数方式有很多种,例如:
- 把变量声明为静态的,通过类名获取参数。
- 利用Intent传递
- 利用广播的方式传递。
- 还有通过存储方式(Shareperferrence,File、sqlie等)来传递。
- 重写ServiceConnetion。(重点)
我想面试官问你这个问题的主要目的就是看你会不会第五个方法。前面四个虽说能传递参数,但其实都不可取。只有第五个才是我们项目中经常使用的方法。
 现在假设一个场景:在Service中下载文件,下载的进度要回调给Activity。
我们先新建一个接口:1
2
3
4
5
6/**
  *回调进度接口
  */
public interface OnProgressListener {  
    void onProgress(int progress);  
}
再新建一个Service: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
71public class MsgService extends Service {  
    /** 
     * 进度条的最大值 
     */  
    public static final int MAX_PROGRESS = 100;  
    /** 
     * 进度条的进度值 
     */  
    private int progress = 0;  
  
    /** 
     * 增加get()方法,供Activity调用 
     * @return 下载进度 
     */  
    public int getProgress() {  
        return progress;  
    }  
	/**
	 *回调进度接口
	 */
	private OnProgressListener onProgressListener;
    /** 
     * 注册回调接口的方法,供外部调用 
     * @param onProgressListener 
     */  
    public void setOnProgressListener(OnProgressListener onProgressListener) {  
        this.onProgressListener = onProgressListener;  
    }  
    /** 
     * 模拟下载文件任务,每秒钟更新一次 
     */  
    public void startDownLoad(){  
        new Thread(new Runnable() {  
              
              
            public void run() {  
                while(progress < MAX_PROGRESS){  
                    progress += 5;  
                    //进度发生变化通知调用方  
                    if(onProgressListener != null){  
                        onProgressListener.onProgress(progress);  
                    }  
                    try {  
                        Thread.sleep(1000);  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                      
                }  
            }  
        }).start();  
    }  
  
    /** 
     * 返回一个Binder对象 
     */  
      
    public IBinder onBind(Intent intent) {  
        return new MsgBinder();  
    }  
      
    public class MsgBinder extends Binder{  
        /** 
         * 获取当前Service的实例 
         * @return 
         */  
        public MsgService getService(){  
            return MsgService.this;  
        }  
    }  
}
在Activity中: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
64public class MainActivity extends Activity {  
    private MsgService msgService;  
    private ProgressBar mProgressBar;  
      
      
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
          
          
        //绑定Service  
        Intent intent = new Intent("com.example.communication.MSG_ACTION");  
        bindService(intent, conn, Context.BIND_AUTO_CREATE);  
          
          
        mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);  
        Button mButton = (Button) findViewById(R.id.button1);  
        mButton.setOnClickListener(new OnClickListener() {  
              
              
            public void onClick(View v) {  
                //开始下载  
                msgService.startDownLoad();  
            }  
        });  
          
    }  
      
    /**
     *创建ServiceConnection
     */
    ServiceConnection conn = new ServiceConnection() {  
          
        public void onServiceDisconnected(ComponentName name) {  
              
        }  
          
          
        public void onServiceConnected(ComponentName name, IBinder service) {  
            //返回一个MsgService对象  
            msgService = ((MsgService.MsgBinder)service).getService();  
              
            //注册回调接口来接收下载进度的变化  
            msgService.setOnProgressListener(new OnProgressListener() {  
                  
                  
                public void onProgress(int progress) {  
                    mProgressBar.setProgress(progress);  
                      
                }  
            });  
              
        }  
    };  
  
    /**
     *解除绑定
     */
      
    protected void onDestroy() {  
        unbindService(conn);  
        super.onDestroy();  
    }  
}
这里需要注意开启Service只能用binService进行绑定,不能用startService。因为调用bindService (Intent service, ServiceConnection conn, int flags)方法,得到Service对象的一个引用,这样Activity可以直接调用到Service中的方法。
这里再顺带说一下利用广播发送消息,其优势稍稍弱于ServiceConnection。
首先要在Activity中注册广播,在定义广播接收器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//动态注册广播接收器  
    private void setBroadcast(){
        msgReceiver = new MsgReceiver();  
        IntentFilter intentFilter = new IntentFilter();  
        intentFilter.addAction("com.example.communication.RECEIVER"); //设置Action 
        registerReceiver(msgReceiver, intentFilter); 
	}	
	/** 
     * 广播接收器 
     * @author len 
     * 
     */  
    public class MsgReceiver extends BroadcastReceiver{  
  
          
        public void onReceive(Context context, Intent intent) {  
            //拿到进度,更新UI  
            int progress = intent.getIntExtra("progress", 0);  
            mProgressBar.setProgress(progress);  
        }  
          
    }
在Service中,每当下载进度改变时都要发送广播,让Acticty接受,Action要对应Activity中的Action1
2
3
4Intent intent = new Intent("com.example.communication.RECEIVER");  
//发送Action为com.example.communication.RECEIVER的广播  
intent.putExtra("progress", progress);  
sendBroadcast(intent);
利用广播适合用于向多个Activity发送相同的消息,单个Activity的话还是用ServiceConnetion更好一些。