Android Service基本使用

参考:
https://blog.csdn.net/guolin_blog/article/details/11952435

bindService+startService Demo

清单文件

注意注册service

        <service android:name=".MyService" >
            <intent-filter>
                <action android:name="com.test.chj" />
            </intent-filter>
        </service>

布局文件

非常简单,四个按钮。最外面是个相对布局

    <Button
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="start service" />

    <Button
        android:id="@+id/stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/start"
        android:text="stop service" />

    <Button
        android:id="@+id/bind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/stop"
        android:text="bind service" />

    <Button
        android:id="@+id/unbind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/bind"
        android:text="unbind service" />

Java文件

先创建Service类,onBind和MyBinder可以先不看后面会说,所以主要就是三个方法:onCreate onStartCommand onDestroy

public class MyService extends Service {

    private static final String TAG = "MyService";

    @Override
    public void onCreate() {
        Log.v(TAG, "onCreate");
        Log.d(TAG, "MyService thread id is " + Thread.currentThread().getId());
        super.onCreate();
    }

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

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

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

    class MyBinder extends Binder {
        public void startDownload() {
            Log.d("TAG", "startDownload() executed");
        }
    }
}

主界面内容也很简单,就是4个按钮,一个标志位。在onCreate初始化,设置点击事件。注意内部类ServiceConnection只服务于bind的service

public class MainActivity extends Activity implements OnClickListener {
    protected static final String TAG = "MainActivity";
    Button start, stop, bind, unbind;
    boolean isBound =false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "MainActivity thread id is " + Thread.currentThread().getId());
        setContentView(R.layout.activity_main);
        start = (Button) findViewById(R.id.start);
        stop = (Button) findViewById(R.id.stop);
        bind = (Button) findViewById(R.id.bind);
        unbind = (Button) findViewById(R.id.unbind);

        start.setOnClickListener(this);
        stop.setOnClickListener(this);
        bind.setOnClickListener(this);
        unbind.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent i = new Intent();
        i.setAction("com.test.chj");
        switch (v.getId()) {
        case R.id.start:
            startService(i);
            break;
        case R.id.stop:
            stopService(i);
            break;
        case R.id.bind:
            bindService(i, conn, Context.BIND_AUTO_CREATE);
            isBound = true;
            break;
        case R.id.unbind:
            if(isBound){
                unbindService(conn);
                isBound= false;
            }
            break;
        default:
            break;
        }
    }

    ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.v(TAG, "onServiceDisconnected");
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.v(TAG, "onServiceConnected");
        }
    };

}

Service 启动方式startService

依次点击start service和stop service:
点击start service触发Service的 onCreate和 onStartCommand
点击stop service触发Service的 onDestroy
多次点击start service
onCreate只会触发一次,onStartCommand会触发多次
多次点击start service后多次点击stop service
onDestroy只会触发一次

Service启动方式bindService

依次点击bind service和unbind service:
点击bind service会触发Service的 onCreate和内部类ServiceConnection的onServiceDisconnected方法
点击unbind service触发Service的 onDestroy
多次点击bind service后多次点击unbind service
onCreate onServiceDisconnected onDestroy都只会调用一次。
注意:如果没有bindService就unbind会报错:

java.lang.IllegalArgumentException: Service not registered:

所以需要加个bool变量,确保解绑前确实调用过bind

Service混合启动

实验:
点击bind Service后点击stop Service不会触发Service的onDestroy
点击start Service后点击unbind Service也不会触发Service的onDestroy
无论先点击的bind Service还是start Service,需要stop Service和unbind Service都点击之后onDestroy才会触发
结论:
如果一个Service既通过start方法启动,又通过bind启动,则必须调用两个方法对应的接触Service方法,Service才会销毁。

Start和Bind service比较

Android 4.4验证如下
bind service与Activity紧密结合。当Activity销毁时(比如按了返回键),bind方式启动的activity会直接销毁(调用onDestroy),如果从最近列表删除应用程序,Service连onDestroy都不会调用就被销毁了。
而startService和Activity没有关系,除非Activity主动调用stopService,否则该Service与启动它的Activity无关,即使那个Activity已经关闭
另一个区别就是bindService可以和启动它的Activity通信,就是通过上面没有说的onBind方法和MyBinder对象

利用binder进行Service与Activity的进行通信(限bind启动)

其实之前bind启动时,触发的方法应该有三:
Service的onCreate onBind和Activity内部类的onServiceConnected
具体通信方式如下:
给MyBinder添加返回MyService对象的方法,添加MyService和MyBinder的测试方法
在onServiceConnected中既可以获得MyBinder的实例,又可以通过MyBinder获得MyService的实例,所以两者的方法都可以调用,这样就实现了Activity和Service的通信(通过Binder对象)
MyService:

    public void test(){
        //do sth
        Log.v(TAG, "test");
    }
    class MyBinder extends Binder {
        public MyService getService(){  
            return MyService.this;  
        }
        public void startDownload() {
            Log.d(TAG, "startDownload");
            new Thread(new Runnable() {  
                @Override  
                public void run() {  
                    // 执行具体的下载任务  
                }  
            }).start();
        }
    }

内部类ServiceConnection onServiceConnected的实现

        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.v(TAG, "onServiceConnected");
            MyBinder binder = (MyBinder) service;
            binder.startDownload();
            MyService serv =binder.getService();
            serv.test();
        }

前台Service与后台Service

之前的几种启动方法创建的Service都是后台Service,即没有界面,只能通过开发者选项查看正在运行的服务可以看到这些Service。
那么前台Service区别于后台Service,它有一个通知栏界面
要想把一个后台Service变成一个前台Service很简单,只要在Service的onCreate方法添加如下代码(给Service添加一个通知)

        Notification notification = new Notification(R.drawable.ic_launcher,  
                "有通知到来", System.currentTimeMillis());  
        Intent notificationIntent = new Intent(this, MainActivity.class);  
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,  
                notificationIntent, 0);  
        notification.setLatestEventInfo(this, "这是通知的标题", "这是通知的内容",  
                pendingIntent);  
        startForeground(1, notification);  
        Log.d(TAG, "onCreate() executed"); 

前台service除了比后台service多了一个通知栏以外,其实最大的区别是前台service优先级高于后台service,不会因为内存不足而被操作系统轻易的kill掉

Service与Thread的关系?

Service的启动给我们一种幻觉,让我们觉得Service是Activity另起的一个线程。其实不然,本地Service仍然是运行在主线程的,我们可以通过在Activity的onCreate和Service的onCreate打印如下log:

        Log.d(TAG, " thread id is " + Thread.currentThread().getId());
        Log.d(TAG, " process id is " +Process.myPid());

来查看进程号和线程号。打印会发现Service的进程号和线程号都和Activity一致。也就是说,Service同样是运行在主线程的,因此不难得知,在Service中同样是不能直接执行耗时操作的,要另起线程。否则会导致主线程阻塞,发生ANR。
那么,既然如此,为什么不直接在Activity中创建Thread,做耗时操作呢?
原因是:

这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

猜你喜欢

转载自blog.csdn.net/u011109881/article/details/80232863