android之service

来源:互联网 发布:火狐浏览器优化版 编辑:程序博客网 时间:2024/05/16 23:48

刚开始学service的时候就在想,service于thread的区别。后来发现service根本就不是thread!!!原来service是直接运行在主线程里面的,也就是UI主线程。明白了service于thread的区别之后又有新问题,为什么要用service?直接用thread不行吗?反正都是执行长时间任务。后来发现的确是有原因的。这么说吧,thread是独立运行在程序中的,也就是和activity没有半毛钱关系,当activity被销毁的时候thread可能还在运行。而service不一样,当activity销毁的时候你可以选择连同销毁service。而且你没有办法在不同的activity操作同一thread。但service可以。所以这时候就需要service,一般的方法在service里创建thread,让service来控制thread。这样就形成了可能你的activity还没启动的时候thread已经start了,也可以让不同的activity来控制同一个service,进而来获取thread中处理的数据。

不过这里我有个问题,我在做小程序的时候,在service里创建了一个thread,当service销毁的时候thread还在运行,thread是个死循环,这个该怎么关闭,感觉这个是thread里的问题。虽然我用了其他办法解决了,但这块不知道有没有什么直接的办法可以杀死service里的正在运行的thread。

还有一点,service必须运行在UI主线程,不能在子线程运行,但可以在service里创建子线程。切记!

service的表现方式也分为两种:

1.startService。

生命周期:onCreat -- onStartCommand -- Service Runing -- stop -- onDestory

使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。

最重要的是startService开启的service不能和activity交互,也就是说不能进行数据传递,除非使用异步消息处理机制。

下面是代码,首先是service里的

public class MyService extends Service {    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {        throw new UnsupportedOperationException("Not yet implemented");    }    @Override    public void onCreate() {        //这里可以初始化一些数据        super.onCreate();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        /**         * 这里放进行的任务,比如下载网络数据等等。         * 一定要记得记得创建一个子线程,一是为了方便用户体验,二是有些任务需要在子线程里进行,例如下载图片。         * 任务完成后记得在这个方法里调用stopSelf()来关闭service服务。         * 也可以在activity中调用stopService(intent),但是startService不能和activity交互,         * 并不知道什么时候service里的任务已经完成,所以最好还是让service本身来关闭自己。         * 同时stopService(intent)是外部调用,不利于代码结构。         * 最后记住,关闭service也要在主线程进行,可以在主线程创建一个关闭service的handler,         * 让子线程来发送关闭请求到handler。         */        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        //这里可以进行一些资源的释放        super.onDestroy();    }}
然后是activity里的
                MyService myService = new MyService();                Intent intent = new Intent(MainActivity.this, MyService.class);                MainActivity.this.startService(intent);

这样一个service就启动成功了。这种方式启动的service比较简单。

2.bindService

生命周期:onCreat -- onBind -- Client Bound Service -- onUnbind -- onDestory

使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。

最重要的是bindService绑定的service可以和activity交互,能进行数据传递。

下面是代码,首先是service里的

public class MyService extends Service {    //上转型对象的方式来实例化,MyBinder不行吗?    private final Binder myBinder = new MyBinder();    public MyService() {    }    /**     * 一定要在onBind方法中返回当前Binder对象,这样client根据返回的Binder对象来获取当前service     * 不过这里为什么返回的是IBinder类型?而不是Binder类型?     * Binder是一个具体类,具有其自己的功能,继承Binder可以使用Binder原有功能,只对你需要的方法做修改     * IBinder是个接口,可以让你的类当作Binder来用,但是所有的具体功能都需要你自己编写逻辑。     * 意思就是ServiceConnection里传回去的是一个IBinder接口对象,我们需要回调成MyBinder对象,进而获取service     * 可是为什么要设计的这么麻烦,难道有什么好处我还不知道?有待挖掘。。。     */    @Override    public IBinder onBind(Intent intent) {        return myBinder;    }    /**     * 这个类里必须有一个共有的方法要返回当前service本身的实例,给client调用,     * 这就是问什么多个activity可以访问同一个bindService实例的原因     * MyBinder类和上面的onBind方法是用来给调用者获取service实例的     */    class MyBinder extends Binder {        //获取当前Service实例        public MyService getService() {            return MyService.this;        }        //Binder里的onTransact方法用于和调用组件进行交互        @Override        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {            //接受activity传进来的值            String string1 = data.readString();            //将传进来的data进行操作            String string2 = string1 + "和";            //这里可以将service的后台任务处理的数据返回哦            reply.writeString(string2 + getRandom() + "");            return super.onTransact(code, data, reply, flags);        }    }    /**     * client获取了当前service对象后,就可以操作这些执行任务的方法,     * 可以是获取网络信息等,记得最好要在方法里创建子线程?????     * 这里是返回一个随机数     */    int getRandom() {        Random random = new Random();        return random.nextInt(30);    }    @Override    public void onCreate() {        //这里可以初始化一些数据        super.onCreate();    }    @Override    public void onDestroy() {        //这里可以进行一些资源的释放        super.onDestroy();    }}
然后是调用者activity里面的

public class MainActivity extends AppCompatActivity {    Button button1, button2;    TextView textView1, textview2;    //service实例    MyService myService;    //服务对象    MyService.MyBinder myBinder;    //默认是不绑定service的    boolean isBind = false;    //官方推荐我们在这里绑定service,当然也可以动态绑定service,具体参见activity生命周期    @Override    protected void onStart() {        super.onStart();        Intent intent = new Intent(MainActivity.this, MyService.class);        //绑定service,参数是intent,serviceConnection,和flag        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        textView1 = (TextView) findViewById(R.id.textview1);        textview2 = (TextView) findViewById(R.id.textview2);        button1 = (Button) findViewById(R.id.button1);        button2 = (Button) findViewById(R.id.button2);        //按钮一调用service方法,获取随机数        button1.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                if (isBind) {                    int result = myService.getRandom();                    textView1.setText(result + "");                }            }        });        //按钮二获得service的回传值        button2.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //将textView1中的内容传进去                String string = textView1.getText().toString();                //往service中传递值的对象,必须是序列化的对象                Parcel data = Parcel.obtain();                //在传递对象里放入数据                data.writeString(string);                //用来接受传回来的序列化对象                Parcel replay = Parcel.obtain();                try {                    /**                     * transact是一个可以往service里传递数据并返回结果的方法,用于service和调用者交互数据                     * 第一个参数只有两个,一个是FIRST一个是LAST                     * 第二个参数是要传递的Parcel对象                     * 第三个参数是传回来的parcel对象                     * 第四个参数是int flag,通常都是0                     */                    myBinder.transact(IBinder.LAST_CALL_TRANSACTION, data, replay, 0);                } catch (RemoteException e) {                    e.printStackTrace();                }                //将返回的值取出                String string2 = replay.readString();                textview2.setText(string2);            }        });    }    //在这里销毁service,也是官方推荐    @Override    protected void onStop() {        super.onStop();        if (isBind) {            unbindService(serviceConnection);            isBind = false;        }    }    /**     * 这个方法充当着client和service的桥梁,要想操作service不能new一个service,     * 因为new出来的不是同一个实例,不能保证数据同步等,这时候我们就需要一个方法来获取当前service的实例,     * ServiceConnection类就能解决这个问题,还能被多个context调用来获取相同的service实例。     */    ServiceConnection serviceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            //根据传进来的IBinder参数获取MyService.MyBinder对象            myBinder = (MyService.MyBinder) service;            //根据myBinder获取MyService对象            myService = myBinder.getService();            //告诉是上面的变量已经绑定了            isBind = true;        }        @Override        public void onServiceDisconnected(ComponentName name) {            isBind = false;        }    };}
这样就是bindservice的运行模式了。

除了这两个service的表现方式外,service有五个已知的子类AbstractInputMethodService, AccessibilityService, IntentService, RecognitionService, WallpaperService

intentService貌似很常用,感觉就是普通的service加上handler,thread和stopSelf。意思就是这个service不用在创建线程,不用创建handler机制,不用手动停止service,通常用于单线程的操作。

还有一个是service里的AIDL进程间通信,学的有点稀里糊涂,里面涉及到一个服务器的概念,主要是web没学过,html也不太懂,就知道AIDL貌似和android里的四大组件之contentProvider有相似之处,感觉应用领域比较窄,就没深入研究,等以后用到的时候再回来看看吧。




0 0
原创粉丝点击