课程背景:
Service 是 Android 四大基本组件之一,是无界面的应用程序,可以长期在后台运行,在实际工作中非常重要。

核心内容:
绑定Service并与之通信

 

启动 Service 并传递数据

启动Service,传数据

//定义一个文本框,获取文本
private EditText etData;
etData = (EditText) findViewById(R.id.etData);

//通过Intent,传递给Service
Intent i = new Intent(MainActivity.this, MyService.class);
i.putExtra("data", etData.getText().toString());
startService(i);

Service中接收数据,处理输出数据

private boolean running = false;
private String data = "这是默认信息";

//Service中通过Command获取传来的Intent
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    data = intent.getStringExtra("data");
    return super.onStartCommand(intent, flags, startId);
}

//生命周期:第一次创建Service时,先onCreate(),再onStartCommand()。之后如果没有destroy,那么每次停止后重新启动,则只执行onCommand
@Override
public void onCreate() {
    super.onCreate();
    System.out.println("启动了");

    running = true;
    //创建Service后,创建一个线程用来输出
    new Thread(){
        @Override
        public void run() {
            super.run();

            while (running) {

                System.out.println(data);

                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start()
}

 

绑定 Service 进行通信(上)

Service绑定详细说明见上一篇笔记:《Android笔记3.7》 认识 Android Service()

Service:MyService.java

public class MyService extends Service {
    private boolean running = false;
    private String data = "这是默认信息";

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return new Binder();
    }

    //这个类的继承是重点,将Serivice的引用binder传回给Activity,可以使用的所有操作,都在这个类下边的public方法中定义
    public class Binder extends android.os.Binder {
        //定义了public的方法,来供Activity操作Service中的变量数据
        public void setData(String data) {
            MyService.this.data = data;
        }
   
        //回调所必须的,用于返回Service实例的引用给Aty   
        public MyService getService(){
            return MyService.this;
        }

    }

    //Service中通过Command获取传来的Intent
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("执行onStartCommand");
        data = intent.getStringExtra("data");

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("启动了");

        running = true;
        new Thread(){
            @Override
            public void run() {
                super.run();

                while (running) {

                    System.out.println(data);

                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("结束了");

        running = false;
    }
}

 

Activity:MainActivity.java

//定义Service的Binder
private MyService.Binder binder=null;

//重载连接时的方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    //绑定连接成功后,得到Service实例的引用
    binder = (MyService.Binder) service;
}

//用事先在Service中设置的public方法,进行对Serivce的数据操作
binder.setData(etData.getText().toString());

 

绑定 Service 进行通信(下)

本课时讲解如何侦听被绑定的 Service 的内部状态。

 

关于动态侦听Service中的变化,并显示到MainActivity,使用Callback回调。

原理:

onServiceConnected的时候会获取一个Service实例的引用,然后在Aty中传一个匿名Callback对象给Service,其中重载了Service里Callback接口的一个方法,这个Aty自定的方法实现的就是让Service自己执行修改Aty中内容

如onChangeData(String data){/*这里就可以定义Service回调接口方法的执行代码*/}

 

Aty中:

//Aty中获取Service实例,并重载设置接口的onDataChange方法,用于让Service自身具有回调修改Aty内容的功能
@Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        System.out.println("ServiceConnected");

        binder = (MyService.Binder) service;
        binder.getService().setCallback(new MyService.Callback() {
            @Override
            public void onDataChange(String data) {//方法在Service中先声明过,这里重载并写具体功能代码,最后是由Service来调用执行的,Service本事并不会写具体功能代码,只提供一个可以传递String的接口,或其他自定义借口i
                Message msg = new Message();
                Bundle b = new Bundle();
                b.putString("data", data);
                msg.setData(b);
                handler.sendMessage(msg);//通过handler来修改Aty的Ui内容,修改代码是写在Aty中
            }
        });
    }

Service中:

private Callback callback = null;

public void setCallback(Callback callback) {
    this.callback = callback;
}

public Callback getCallback() {
    return callback;
}

public static interface Callback {
    //提供一个接口,具体功能代码由Aty去自己写
    void onDataChange(String data);
}

Aty中:

//Service里的线程,不能直接操作Aty的UI,可通过android.os.Handler,来发送一条消息指令,具体代码写在Aty中,通过向Aty发送指令,让Aty自己实现修改。
public Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            tvOut.setText(msg.getData().getString("data"));
        }
    };
//调用接口,执行由Aty指定的具体功能
if (callback != null) {
    callback.onDataChange(str);
}

 

 

Callback类本事是一个接口,在Service实现接口,自定义一个所需的方法:onChangeData(String data),但是Service本身不去实现这个接口,而是开放给所有的Activity来自己实现。

 

所以,本例中,Service做的事情,只是给出一个带有String参数的接口供Aty定义。

Aty定义这个接口为修改自身的TextView内容(通过Handler,因为Service中的Thread()是不能直接操作UI的,需通过HandlerMessage,来传回一个Bundle给Aty,再在Aty中处理HandlerMessage,来修改自己的UI内容)。

 

 

关于回调函数的思路的基本清晰了,虽然这里描述的有点随意,以后实际项目中再详细体会吧。进入下一章节。

 

贴一个完整的Service通信demo代码:

Service.java

 

1 package com.lanyunwork.l38_connectservice;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.Binder;
 6 import android.os.IBinder;
 7 
 8 public class MyService extends Service {
 9     private boolean running = false;
10     private String data = "这是默认信息";
11 
12     public MyService() {
13     }
14 
15     @Override
16     public IBinder onBind(Intent intent) {
17         // TODO: Return the communication channel to the service.
18         return new Binder();
19     }
20 
21     public class Binder extends android.os.Binder {
22         public void setData(String data) {
23             MyService.this.data = data;
24         }
25 
26         public MyService getService(){
27             return MyService.this;
28         }
29     }
30 
31     //Service中通过Command获取传来的Intent
32     @Override
33     public int onStartCommand(Intent intent, int flags, int startId) {
34         System.out.println("执行onStartCommand");
35         data = intent.getStringExtra("data");
36 
37         return super.onStartCommand(intent, flags, startId);
38     }
39 
40     @Override
41     public void onCreate() {
42         super.onCreate();
43         System.out.println("启动了");
44 
45         running = true;
46         new Thread() {
47             @Override
48             public void run() {
49                 super.run();
50 
51                 int i = 0;
52 
53                 while (running) {
54 
55                     i++;
56 
57                     String str = i + ": " + data;
58                     System.out.println(str);
59 
60                     if (callback != null) {
61                         callback.onDataChange(str);
62                     }
63 
64                     try {
65                         sleep(1000);
66                     } catch (InterruptedException e) {
67                         e.printStackTrace();
68                     }
69                 }
70             }
71         }.start();
72     }
73 
74     @Override
75     public void onDestroy() {
76         super.onDestroy();
77         System.out.println("结束了");
78 
79         running = false;
80     }
81 
82     private Callback callback = null;
83 
84     public void setCallback(Callback callback) {
85         this.callback = callback;
86     }
87 
88     public Callback getCallback() {
89         return callback;
90     }
91 
92     public static interface Callback {
93         void onDataChange(String data);
94     }
95 }

 

 

 

MainActivity.java

 

1 package com.lanyunwork.l38_connectservice;
  2 
  3 import android.content.ComponentName;
  4 import android.content.Context;
  5 import android.content.Intent;
  6 import android.content.ServiceConnection;
  7 import android.os.IBinder;
  8 import android.os.Message;
  9 import android.os.Parcelable;
 10 import android.support.v7.app.ActionBarActivity;
 11 import android.os.Bundle;
 12 import android.os.Handler;
 13 import android.view.Menu;
 14 import android.view.MenuItem;
 15 import android.view.View;
 16 import android.widget.EditText;
 17 import android.widget.TextView;
 18 
 19 
 20 
 21 public class MainActivity extends ActionBarActivity implements View.OnClickListener, ServiceConnection {
 22 
 23     private EditText etData;
 24     private MyService.Binder binder;
 25     private TextView tvOut;
 26 
 27     @Override
 28     protected void onCreate(Bundle savedInstanceState) {
 29         super.onCreate(savedInstanceState);
 30         setContentView(R.layout.activity_main);
 31 
 32         etData = (EditText) findViewById(R.id.etData);
 33         tvOut = (TextView) findViewById(R.id.tvOut);
 34 
 35         findViewById(R.id.btnStartService).setOnClickListener(this);
 36         findViewById(R.id.btnStopService).setOnClickListener(this);
 37         findViewById(R.id.btnBindService).setOnClickListener(this);
 38         findViewById(R.id.btnUnBindService).setOnClickListener(this);
 39         findViewById(R.id.btnSyncData).setOnClickListener(this);
 40     }
 41 
 42     @Override
 43     public boolean onCreateOptionsMenu(Menu menu) {
 44         // Inflate the menu; this adds items to the action bar if it is present.
 45         getMenuInflater().inflate(R.menu.menu_main, menu);
 46         return true;
 47     }
 48 
 49     @Override
 50     public boolean onOptionsItemSelected(MenuItem item) {
 51         // Handle action bar item clicks here. The action bar will
 52         // automatically handle clicks on the Home/Up button, so long
 53         // as you specify a parent activity in AndroidManifest.xml.
 54         int id = item.getItemId();
 55 
 56         //noinspection SimplifiableIfStatement
 57         if (id == R.id.action_settings) {
 58             return true;
 59         }
 60 
 61         return super.onOptionsItemSelected(item);
 62     }
 63 
 64     @Override
 65     public void onClick(View v) {
 66         Intent i = new Intent(MainActivity.this, MyService.class);
 67         i.putExtra("data", etData.getText().toString());
 68         switch (v.getId()){
 69             case R.id.btnStartService:
 70                 startService(i);
 71                 break;
 72             case R.id.btnStopService:
 73                 stopService(i);
 74                 break;
 75             case R.id.btnBindService:
 76                 bindService(new Intent(this, MyService.class), this, Context.BIND_AUTO_CREATE);
 77                 break;
 78             case R.id.btnUnBindService:
 79                 unbindService(this);
 80                 break;
 81             case R.id.btnSyncData:
 82                 if (binder != null){
 83                     binder.setData(etData.getText().toString());
 84                 }
 85                 break;
 86         }
 87     }
 88 
 89     @Override
 90     public void onServiceConnected(ComponentName name, IBinder service) {
 91         System.out.println("ServiceConnected");
 92 
 93         binder = (MyService.Binder) service;
 94         binder.getService().setCallback(new MyService.Callback() {
 95             @Override
 96             public void onDataChange(String data) {
 97                 Message msg = new Message();
 98                 Bundle b = new Bundle();
 99                 b.putString("data", data);
100                 msg.setData(b);
101                 handler.sendMessage(msg);
102             }
103         });
104     }
105 
106     @Override
107     public void onServiceDisconnected(ComponentName name) {
108         System.out.println("ServiceDisconnected");
109     }
110 
111     public Handler handler = new Handler(){
112         @Override
113         public void handleMessage(Message msg) {
114             super.handleMessage(msg);
115 
116             tvOut.setText(msg.getData().getString("data"));
117         }
118     };
119 }