Service笔记
作为Android的四大控件的之一,是每个Android开发者都需要了解的控件,因此特意在这里写下自己的学习笔记。
博客地址 http://blog.csdn.net/sinat_17314503/article/details/53871294
Service是什么,可以干什么
大家都知道Activity是负责前台显示的而Service却恰好相反在后台显示并没有什么界面。例如舞台上的表演,表演者在舞台上表演而幕后工作者,在舞台后面给表演者调灯光、音乐等等。在这里表演者相当于Activity,因为显示在前面观众,观众可以直观的看到,但是幕后工作者观众是看不到的,Service就类似于幕后工作者。既然我们知道Service类似于幕后工作者那么它的能力可以干什么,聪明的你就可以猜出一二分。没错,就是在不阻碍前台的显示的前提下,在后台计算辅助前台更好的显示,还有可以是后台人员发微博,与其他人互动并不一定要一直依附在前台。
Service如何建立和运行
其实Service的建立是十分简单的,只要新建一个类继承Service就好了。
基本上继承以下的方法就可以了
然后在Activity里面点击事件里面写上
Intent i = new Intent(this, EchoService.class);
startService(i);
就可以启动了一个后台Service了,但是但是千万不要忘记在注册表里面注册这个Service
<application
...>
...
<service android:name=".EchoService"/>
</application>
当然又开始就有停止 停止Service的方法
stopService(i);
但是上述的代码并没有运行什么东西。于是我就加一点东西,在后台运行一个计时器,代码如下:
private Timer timer=null;
private TimerTask task=null;
private int i = 0;
public void startTimer(){
if(timer==null){
timer = new Timer();
task = new TimerTask() {
@Override
public void run() {
//---------------------------
//这里注意 现在这个Service没有和前台绑定
//这里可以先注释掉 下面会说Activity和
//Service互动
echoServiceBind.setData(i);
i++;
if(mDataListener!=null){
mDataListener.Show(i);
}
//---------------------------
System.out.println(i);
}
};
timer.schedule(task, 1000,1000);
}
}
public void stopTmer(){
if(timer!=null){
task.cancel();
timer.cancel();
task=null;
timer=null;
}
}
调用的位置
@Override
public void onCreate() {
startTimer();
Log.e("showme","onCreate");
super.onCreate();
}
@Override
public void onDestroy() {
stopTmer();
Log.e("showme","onDestroy");
super.onDestroy();
}
运行后你就会发现Logcat在不断打断的打印:
12-25 16:39:33.249 20674-28409/com.example.administrator.servicedemo I/System.out: 1
12-25 16:39:34.246 20674-28409/com.example.administrator.servicedemo I/System.out: 2
12-25 16:39:35.247 20674-28409/com.example.administrator.servicedemo I/System.out: 3
12-25 16:39:36.246 20674-28409/com.example.administrator.servicedemo I/System.out: 4
12-25 16:39:37.246 20674-28409/com.example.administrator.servicedemo I/System.out: 5
12-25 16:39:38.246 20674-28409/com.example.administrator.servicedemo I/System.out: 6
12-25 16:39:39.246 20674-28409/com.example.administrator.servicedemo I/System.out: 7
12-25 16:39:40.246 20674-28409/com.example.administrator.servicedemo I/System.out: 8
12-25 16:39:41.246 20674-28409/com.example.administrator.servicedemo I/System.out: 9
12-25 16:39:42.247 20674-28409/com.example.administrator.servicedemo I/System.out: 10
然后你按home键返回桌面它还是会继续运行,这就是Service。在后台默默的运行。
Service和Activity的交互
到这个时候你会说:这样有卵用啊,Service运行之后和Activity都没有交互。
上面我不是继承了4个方法吗,那么剩下两个就是和Activity的交互有关系的。猜得不错,但是真正有关系的只有
@Override
public IBinder onBind(Intent intent) {
Log.e("showme","onBind");
return echoServiceBind;
}
看它的返回值就知道(IBinder是关于信息底层传递,现在我不懂,不要问我,你只要知道它可以传递数据就可以了)我们需要IBinder对象,现在的状况是 我没有这个对象啊。身为(面向对象的)程序员,当听到没有没有对象那怎么办,那就new啊。没有对象就new一个出来啊。
于是就有下面这个类
public final EchoServiceBind echoServiceBind = new EchoServiceBind();
public class EchoServiceBind extends Binder{
private int data = 0;
public EchoService getService(){
return EchoService.this;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
然后返回就可以了。
Service那里解决了那么Activity那里怎么办呢。欲知后事如何,且听下会分解。我好想这样说的,越写越多字。
那么我们需要在Activity里面继承ServiceConnection类重写
private EchoService.EchoServiceBind mBind;
private EchoService echoService = null;
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.e("showme","onServiceConnected");
mBind = (EchoService.EchoServiceBind) binder;
echoService = ((EchoService.EchoServiceBind) binder).getService();
echoService.addDataListener(new EchoService.DataListener() {
@Override
public void Show(int t) {
Message message = mHandler.obtainMessage();
message.arg1 = t;
mHandler.sendMessage(message);
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("showme","onServiceDisconnected");
}
通过
echoService = ((EchoService.EchoServiceBind) binder).getService();
mBind = (EchoService.EchoServiceBind) binder;
我们可以获取到EchoService
本体 和 EchoService.EchoServiceBind
内部类的本体。
于是我们就可以愉快的调用本体里,但是这里我会告诉你,你都没有绑定,你调用什么。
先说一下这两个方法的作用onServiceConnected
是当Service和Activity绑定成功之后会调用的而onServiceDisconnected
只有在Service发生异常的情况下断掉连接的时候调用。
现在说一下如何绑定Service和Activity。
bindService(i, this, Context.BIND_AUTO_CREATE);
讲完了是不是很快,对就是这么简单就可以绑定Activity和Service。
bindService()方法接收三个参数,第一个参数就是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三个参数是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service。
于是我们可以愉快的调用EchoService本体和EchoServiceBind本体的方法了。
注意
bindService Service中的onCreate()方法得到执行,但onStartCommand()方法不会执行
当Startservice和bindService都运行了需要stopService和 unbindService(this);都运行了对应的关闭了Service才停下来。
Service是否可以进行耗时操作
很遗憾的告诉你,不行,因为Service也是运行在UI线程,超过3秒之后就会ANR。那么有什么方法可以让它进行耗时操作,在Service里面开启一个线程进行耗时操作,或者为其开启一个进程
<!--普通的Service转换成远程Service其实非常简单,只需要在注册Service的时候将它的android:process属性指定成:remote就可以-->
<!--使用了远程Service后,MyService已经在另外一个进程当中运行了,所以只会阻塞该进程中的主线程,并不会影响到当前的应用程序。-->
<service android:name=".MyService2"
android:process=":remote"/>
但是有会面临一个问题,卧槽那我怎么和Activity进行交互啊。这就要使用AIDL来进行跨进程通信了(IPC)。
在AS里面步骤
aidl的文件代码如下
package com.example.administrator.servicedemo2;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int plus(int a, int b);
String toUpperCase(String str);
}
调用
/**
* 想和Activity進行交互 这就要使用AIDL来进行跨进程通信了(IPC)
* AIDL(Android Interface Definition Language)是Android接口定义语言的意思,
* 它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,
* 从而可以实现多个应用程序共享同一个Service的功能。
*/
public class MyService2 extends Service{
public static final String TAG = "MyService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG,"onStartCommand() executed");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
super.onCreate();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG,"MyService2 onStartCommand() executed");
Log.e("ThreadID","Service="+Thread.currentThread().getId());
Log.d("TAG", "process id is " + Process.myPid());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG,"onDestroy() executed");
}
public IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int plus(int a, int b) throws RemoteException {
return a+b;
}
@Override
public String toUpperCase(String str) throws RemoteException {
if(str!=null){
return str.toUpperCase();
}
return null;
}
};
}
上面的代码做了以下的步骤 一.初始化IMyAidlInterface.Stub
二.将其返回给Activity
Activity调用
private ServiceConnection mConnection2 = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
myBinder2 = IMyAidlInterface.Stub.asInterface(iBinder);
try {
int a = myBinder2.plus(10,15);
String b = myBinder2.toUpperCase("abc");
Toast.makeText(MainActivity.this,b+"顯示一下"+a,Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
// 只有在service因异常而断开连接的时候,这个方法才会用到。
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.e("showme","onServiceDisconnected excuted");
}
};
上面的代码做了以下的步骤 一.获取IMyAidlInterface.Stub
二.调用里面的方法.
IntentService的使用
原来还有一种方法在Service建线程,我还是太年轻了。在Service里面我们肯定不能直接进行耗时操作,一般都需要去开启子线程去做一些事情,自己去管理Service的生命周期以及子线程并非是个优雅的做法;好在Android给我们提供了一个类,叫做IntentService
使用了IntentService最起码有两个好处,一方面不需要自己去new Thread了;另一方面不需要考虑在什么时候关闭该Service了
使用这个方法我们需要重写
@Override
protected void onHandleIntent(Intent intent) {
}
在这里面实现你的逻辑然后和Activity的交互可以选择用广播来实现。这个没什么好说的。
参考http://blog.csdn.net/lmj623565791/article/details/47143563
好了需要将的都讲完了。最后 纸上得来终觉浅,绝知此事要躬行
。
参考 http://blog.csdn.net/guolin_blog/article/details/9797169
http://blog.csdn.net/guolin_blog/article/details/11952435