Android基础巩固——Service

我尽量不打错别字,用词准确,不造成阅读障碍。

最近在巩固Android知识点,突然感觉自己对Service的知识并没有形成一个知识网络,所以在此简单总结一下,复杂的东西一是不怎么会,不敢写出来丢人,二是写起来费劲,本文章可能并不适合小白,当然现在小白可能也很少了。

Service一般分为本地服务远程服务,当然也可以按照其他方面分,但是刚开始接触还是分为这两种好理解。
首先是不管本地服务还是远程服务都必须知道的知识点。
你必须知道Service的生命周期,这是基础:
Android基础巩固——Service_第1张图片
生命周期显示Service有两种启动方式,一种是直接startService(左边),一种是使用bindService(右边);

代码很简单,就从郭神那里拿过来了,自己为了验证几个问题也写了一遍。

startService:

public class MyService extends Service {  

    public static final String TAG = "MyService"; 

    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "onCreate() executed");  
    }  

    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d(TAG, "onStartCommand() executed");  
        return super.onStartCommand(intent, flags, startId);  
    }  

    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "onDestroy() executed");  
    }

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

然后只要在Activity里面使用startService就好了:

Intent intent = new Intent(this, MyService.class);
startService(intent);  //启动服务
/*****/
stopService(intent);   //结束服务

最后要在AndroidManifest.xml里面注册Service:

<application  
        android:allowBackup="true"  
        android:icon="@drawable/ic_launcher"  
        android:label="@string/app_name"  
        android:theme="@style/AppTheme" >          
        ……  
        <service android:name="com.example.servicetest.MyService" >  
        service>  
    application> 

但是这种方式启动后,除了事先在生命周期里面可以有一些操作外,之后与Serivice就取不到联系了,就是不能通信!所以一般不会这么写,一般都用第二种方式。

bindService:

bindService的启动方式与startService的差别不大,首先要在MyService里面重写onBind方法,并创建Bind类,最后加上Bind类对象的引用:

public class MyService extends Service {  

    private MyBinder mBinder = new MyBinder();
    public static final String TAG = "MyService"; 

    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "onCreate() executed");  
    }  

    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d(TAG, "onStartCommand() executed");  
        return super.onStartCommand(intent, flags, startId);  
    }  

    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "onDestroy() executed");  
    }  

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

class MyBinder extends Binder {  
     public void startDownload() {  
         Log.d("TAG", "startDownload() executed");  
         // 执行具体的下载任务  
      }  
  } 
}

接下来在Activity里面加上与Service的链接:

private MyService.MyBinder myBinder;  

private ServiceConnection connection = new ServiceConnection() {  

    @Override  
    public void onServiceDisconnected(ComponentName name) { 

    }  

     @Override  
     public void onServiceConnected(ComponentName name, IBinder service) {  
         myBinder = (MyService.MyBinder) service;  
         myBinder.startDownload();  
       }  
 };  

这样我们就可以在onServiceConnected里面获取Bind实例,并调用具体的public方法了;

接下来可以启动Service了:

在Activity里面调用:

Intent bindIntent = new Intent(this, MyService.class);  
bindService(bindIntent, connection, BIND_AUTO_CREATE);  //绑定服务
/******/
unbindService(connection);                              //解绑服务

BIND_AUTO_CREATE:表示自动创建服务;

解除与销毁服务

startService方式启动的服务直接stopService就好了,即销毁服务,没有解除一说,因为没绑定啊。bindService方式启动的服务需要解绑:unbindService(connection); 然后才是销毁:stopService(intent); 没有解绑就直接销毁是不行的,一个Service必须在没有绑定任何Activity的时候才可以销毁,所以startService方式启动的Service可以直接stop,而bindService()不行。

Service的使用场景:

Service一般在后台运行,比如心跳保活、音乐播放等等,但是,Service是运行在主线程的,所以不可以在Service里直接进行耗时操作,一般都会另开线程操作,那为什么要在主线程运行呢,因为可以与Activity关联,好控制啊。所以一般标准的Service这样写:

......
@Override  
public int onStartCommand(Intent intent, int flags, int startId) {  
    new Thread(new Runnable() {  
        @Override  
        public void run() {  
            // 开始执行后台任务  
        }  
    }).start();  
    return super.onStartCommand(intent, flags, startId);  
}  

class MyBinder extends Binder {  

    public void startDownload() {  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                // 执行具体的下载任务  
            }  
        }).start();  
    }  
}  

Service是可以在前台运行的,因为后台Service优先级比较低,容易被回收。前台Service一般与Notification结合使用。

public class MyService extends Service {  

    public static final String TAG = "MyService";  
    private MyBinder mBinder = new MyBinder();  

    @Override  
    public void onCreate() {  
        super.onCreate();  
        //添加下列代码将后台Service变成前台Service
        //构建"点击通知后打开MainActivity"的Intent对象
        Intent notificationIntent = new Intent(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notificationIntent,0);

        //新建Builer对象
        Notification.Builder builer = new Notification.Builder(this);
        builer.setContentTitle("前台服务通知的标题");          //设置通知的标题
        builer.setContentText("前台服务通知的内容");            //设置通知的内容
        builer.setSmallIcon(R.mipmap.ic_launcher);            //设置通知的图标
        builer.setContentIntent(pendingIntent);               //设置点击通知后的操作
        Notification notification = builer.getNotification(); //将Builder对象转变成普通的notification
        startForeground(1, notification);//让Service变成前台Service,并在系统的状态栏显示出来
    }  
    .........  
}

如果通知栏方式比较老旧,请使用新的方式。

本地服务与远程服务

本地服务其实就是上面讲的方式,远程服务与本地服务不同的地方在于AndroidManifest.xml文件的设置:

<service  
        android:name="com.example.servicetest.MyService"  
        android:process=":remote" >  
service> 

远程服务已经是另一个进程了,所以在MyService的onCreate方法里面有耗时操作时当前进程是不会ANR的。

那么我们为什么一般不用远程服务呢?因为无法绑定,前面说了bindService()方法,远程服务时Service与Activity不在一个进程,所以无法绑定,那么岂不是不好控制了,不能通信了?所以远程服务尽量不要用,但是如果必须用怎么办?只能使用进程间通信技术(IPC)了,进程间通信有很多技术,在这里举例AIDL。

AIDL

新建xxxx.aidl文件,右键java文件夹-new-AIDL,就会新建aidl文件夹,并有aidl文件,内容如下:

package com.example.servicetest;  
interface MyAIDLService {  
    int plus(int a, int b);  
    String toUpperCase(String str);  
}  

之后保存并编译,然后在MyService中实现刚定义的aidl文件接口:

public class MyService extends Service {  
    ......  
    @Override  
    public IBinder onBind(Intent intent) {  
        return mBinder;  
    }  

    MyAIDLService.Stub mBinder = new MyAIDLService.Stub() {  
        @Override  
        public String toUpperCase(String str) throws RemoteException {  
            if (str != null) {  
                return str.toUpperCase();  
            }  
            return null;  
        }  

        @Override  
        public int plus(int a, int b) throws RemoteException {  
            return a + b;  
        }  
    };  
} 

此时onBind返回的就是MyAIDLService.Stub的实现了。然后在Activity里面修改代码:

private MyAIDLService myAIDLService;  

private ServiceConnection connection = new ServiceConnection() {  

   @Override  
       public void onServiceDisconnected(ComponentName name) {  
       }  

        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            myAIDLService = MyAIDLService.Stub.asInterface(service);  
            try {  
                int result = myAIDLService.plus(3, 5);  
                String upperStr = myAIDLService.toUpperCase("hello world");  
                Log.d("TAG", "result is " + result);  
                Log.d("TAG", "upperStr is " + upperStr);  
            } catch (RemoteException e) {  
                e.printStackTrace();  
            }  
        }  
    }; 

onServiceConnected方法中调用MyAIDLService.Stub.asInterface()将service装换为MyAIDLService对象;

接下来bindService();时就可以绑定Service并运行方法了。但这只是在程序内部链接远程服务,真正情况下是外部程序链接远程服务;由于Android5.0后已经禁止隐式声明Intent来启动Service了,所以我们显示的方式,首先是AndroidManifest.xml:

<service  
        android:name="com.example.servicetest.MyService"  
        android:process=":remote" >  
        <intent-filter>  
            <action android:name="com.example.servicetest.MyAIDLService"/>  
        intent-filter>  
    service> 

编译并运行程序。新建项目AndroidTestClient,将之前项目的aidl文件拷过来,注意加上包路径一起拷过来,最后就是在项目main目录下多了个aidl文件夹(与java平级),其下有导过来的包名的文件夹,再往下有aidl文件,然后打开新项目的MainActivity,加上:

    private MyAIDLService myAIDLService;  

    private ServiceConnection connection = new ServiceConnection() {  

        @Override  
        public void onServiceDisconnected(ComponentName name) {  
        }  

        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            myAIDLService = MyAIDLService.Stub.asInterface(service);  
            try {  
                int result = myAIDLService.plus(50, 50);  
                String upperStr = myAIDLService.toUpperCase("comes from ClientTest");  
                Log.d("TAG", "result is " + result);  
                Log.d("TAG", "upperStr is " + upperStr);  
            } catch (RemoteException e) {  
                e.printStackTrace();  
            }  
        }  
    };  

 @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        Button bindService = (Button) findViewById(R.id.bind_service);  
        bindService.setOnClickListener(new OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                Intent intent = new Intent("com.example.servicetest.MyAIDLService"); 
                intent.setPackage("com.example.servicetest");
                bindService(intent, connection, BIND_AUTO_CREATE);  
            }  
        });  
    } 

结果:
Android基础巩固——Service_第2张图片
点击onBind按钮运行;不过还有一点需要说明的是,由于这是在不同的进程之间传递数据,Android对这类数据的格式支持是非常有限的,基本上只能传递Java的基本数据类型、字符串、List或Map等。

参考文章(感谢):
https://blog.csdn.net/guolin_blog/article/details/9797169

你可能感兴趣的:(Android日常开发知识点)