频道栏目
首页 > 资讯 > Android > 正文

Android Service个人理解

17-07-07        来源:[db:作者]  
收藏   我要投稿

Android Service个人理解

一、概述

Service 的意思为服务,那这个服务也是由 ContextWrapper 派生出来的,那么他也会有 ContextWrapper 特性,也就是说,他和 Activity 有些地方是相似的,在我的理解,Service 和 Activity 的不同就是一个与用户交互,一个不与用户交互,也就是说 Service 是一个没有界面的 Activity 。那么我们也可以在 Service 也可以去调用 Context 的一些方法。

二、启动 Service

Service 的启动分为两种,一种是 startService 一种是 bindService,那接下来我们一个一个的说。

1、startService

通过这种方式启动的 Service 是一种不可交互的 Service ,这种 service 的生命周期只和自己有关系,和任何组件都没关 系,只有当我们调用 stopService 的时候才会停止。这样启动的service 生命周期比较简单,当我们第一次启动的时候会调 用 onCreate,onStartCommond 方法,重启的时候只会调用 onStartCommond 方法,然后当我们调用 stop 方法的时候,会 去调用 onDestory 方法。下面看一段代码:

Intent intent = new Intent(this,BackService.class);
startService(intent);
stopService(intent);

 

上面代码中写出了 start 和 stop 的方法,通过这两个方法,我们可以开启和关闭服务。


2、bindService

这种方式启动 Service 相当于 Service 和 Activity 进行绑定,当 Activity 被销毁的时候,我们的 Service 也就被销毁了, 这种方式启动 Service 的话需要传递一个 ServiceConnection 对象给我们的 Intent,然后当我们与 Service 绑定之后,我们可 以通过 Service 返回的 Binder 对象来调用 Service 的方法,这样也就完成了 Service 与 Activity 的交互。这种启动 Service 的 生命周期和上一种不太相同,当我们 bind 的时候生命周期是 onCreate 然后是 onBind ,当我们调用 unbindService 的时候先 是执行 unBind 方法解除绑定,然后调用 onDestory。下面看一段程序:

 @Override
    public void onClick(View view) {
        
        final Intent intent1 = new Intent(this,BindBackService.class);
       
        switch (view.getId()) {
            case R.id.btn_bind_service:
                //TODO implement
                bindService(intent1,connection,BIND_AUTO_CREATE);
                break;
            case R.id.btn_unbind_service:
                //TODO implement
                unbindService(connection);
                break;
            
        }
    }

    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            BindBackService.MyBind myBind = (BindBackService.MyBind)service;
            myBind.showTest();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }


    };

这个是启动 Service 的代码片段,下面看一下 Service 的代码片段:
public class BindBackService extends Service{
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("service","onBind");
        return new MyBind();
    }

    public class MyBind extends Binder{
        public void showTest(){
            Log.e("service","showTest");
        }
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.e("service","onDestory");
        super.onDestroy();
    }

    @Override
    public void onCreate() {
        Log.e("service","onCreate");
        super.onCreate();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
    }
}

通过上面两段代码我们可以看到,当我们绑定 Service 的时候在 onBind 方法中返回了一个 Binder 对象,然后在我们的 ServiceConnection 中通过这个对象完成了 Service 中方法的调用,这样也就完成了 Service 和 Activity 的交互。

这段代码中我们可以看到一个 onRebind 方法那这个 onRebind 什么时候会调用呢?其实,这个onRebind 的调用是需要两个 条件的:1、服务中onUnBind方法返回值为true 2、服务没有被销毁。 当符合这两种情况的时候,就会调用这个 onRebind 方法。举个例子,我们先是通过 start 方法启动 Service ,然后通过 bind 方法和我们的 Activity 进行绑定,然后我们再去 unbind,那么下次我们在绑定这个 Service 的时候就会调用 onRebind 这个方法。

在我们调用 bindService 的时候我们的第三个参数是什么意思呢?下面我列举一下几个值:

其中BIND_AUTO_CREATE表示当收到绑定请求时,如果服务尚未创建,则即刻创建,在系统内存不足,需要先销毁优先级 组件来释放内存,且只有驻留该服务的进程成为被销毁对象时,服务才可被销毁;BIND_DEBUG_UNBIND通常用于调试场景中判断绑定的服务是否正确,但其会引起内存泄漏,因此非调试目的不建议使用;BIND_NOT_FOREGROUND表示系统将阻止驻留该服 务的进程具有前台优先级,仅在后台运行,该标志位在Froyo中引入。

那启动 Service 的知识就介绍到这里,下面介绍以下 Service 的声明周期。

三、Service 的生命周期

通过这个图片我们可以很直观的看到 Service 的声明周期,我们可以看到 Service 的声明周期根据启动情况去执行的,所 以我们如果要是打 log 的话需要根据不同情况去打。

四、使用中遇到的问题

我们知道 Service 是运行在 UI 线程中的,如果在 UI 线程中运行一个耗时的操作,会出现 ANR ,在 Service 中也不例 外,那么我们如何在 Service 中运行耗时操作,而不 ANR 呢,方法有几种,我先说一种最简单的,下面看代码:

 
通过这段代码我们可以看到,我们在配置 Service 的时候加了一个 android:process=“remote” 的东西,那这行代码有什么 作用呢,作用就是让 Service 运行在 romote 这个进程中,那如果我们的 service 运行在另外一个进程中,那一定不会出现 ANR 的,但是这种方式值适合 start 启动的 Service ,因为我们 bind 启动的时候需要与我们的 Activity 进行绑定,因为两个 组件不能进行绑定,所有就会出现错误。这种方法也存在局限性,比如说我们跨进程是很麻烦的,因为很麻烦,所以出现了 下一节我们讲到的 IntentService

五、IntentService

我们先来看一下 IntentService 的一些属性,通过这些属性我们就可以知道为什么 IntentService 可以解决我们的问题:

1、每次创建 IntentService 都会启动一个 worker 线程来单独处理每次 Intent 的请求。

2、每次启动 Intent 的时候都会吧这个 Intent 加入 worker 的队列。

3、在处理 onHandleIntent 的时候也是会新启动一个 worker 线程来处理请求

4、当每次执行完 IntentService 的时候都会自动停止。

5、默认实现 onbind 方法并返回 null 。

6、默认实现 onStartCommand 方法,会把 Intent 加入队列中

下面再来看一段代码:

public class TestIntentService extends IntentService{
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public TestIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            wait(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
可以看到代码中我们去调用了 wait 方法,正常的 Service 肯定会是 ANR了,但是我们的 Intent 会去启动一个新的线程去 执行我们 onHandleIntent 中的代码,这样就不会造成 ANR 了。
 

七、前台 Service

由于我们的服务的优先级比较低,所以单内存不足的时候,我们的服务会被回收,所以我们可以用前台服务来弥补这个缺 点,它可以一直保持运行状态而不被系统回收。例如:墨迹天气在状态栏中的天气预报,启动前台服务很简单,只是创建一个 Notification,然后用 startForeground 方法启动就可以了,下面看代码:

/**
 * Created by andya on 2017/6/13.
 */

public class ForegroundService extends Service{
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        showNotifition();
    }

    public void showNotifition(){
        Notification.Builder builder = new Notification.Builder(this);
        builder.setSmallIcon(R.drawable.actual_time)
                .setContentTitle("启动前台服务了")
                .setContentText("好开心哟");
        Intent intent = new Intent(this, MainActivity.class);
        TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(this);
        taskStackBuilder.addParentStack(MainActivity.class);
        taskStackBuilder.addNextIntent(intent);
        PendingIntent pendingIntent = taskStackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(pendingIntent);
        NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
        Notification notification = builder.build();
        nm.notify(0,notification);
        startForeground(0,notification);
    }
}

当我们启动这个服务的时候,我们会看到通知栏的消息,然后当我们点击的时候,可以跳转到我们的 MainActivity 界面,完成 我们的 APP 拉起。


 

六、系统的一些 Service

1. 判断Wifi是否开启

WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);boolean enabled = wm.isWifiEnabled();

2. 获取系统最大音量

AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);int max =

am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);

3. 获取当前音量

AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);int current =

am.getStreamMaxVolume(AudioManager.STREAM_RING);

4. 判断网络是否有连接

ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);NetworkInfo info =

cm.getActiveNetworkInfo();boolean isAvailable = info.isAvailable();


这里面只是列举了几个常用的,其实还有很多,读者可以自行百度。

七、Service 的保活

国内重启 Service 的一些不太提倡的方法:

1、用定时任务启动 Service

2、用两个 Service 相互 bind 保活。

3、在 Service 的 onDestory 中重新启动 Service

4、息屏的时候将后台 Service 提升为 Foreground Service

5、从 JNI 中通过 shell 命令回复 Service。

其实保活的方法有很多,上面都是不提倡的,下面说几个可以用的不过效果不太好

1、在我们配置 Service 的时候加上android:priority = "1000"这个属性设置最高优先级,1000是最高值提高优先级

2、StartCommond几个常量参数简介:

①、START_STICKY

在运行onStartCommand后service进程被kill后,那将保留在开始状态,但是不保留那些传入的intent。不久后service就会

再次尝试重新创建,因为保留在开始状态,在创建 service后将保证调用onstartCommand。如果没有传递任何开始命

令给service,那将获取到null的intent。

②、START_NOT_STICKY

在运行onStartCommand后service进程被kill后,并且没有新的intent传递给它。Service将移出开始状态,并且直到新的明

显的方法(startService)调用才重新创建。因为如果没有传递任何未决定的intent那么service是不会启动,也就是期间

onstartCommand不会接收到任何null的intent。

③、START_REDELIVER_INTENT

在运行onStartCommand后service进程被kill后,系统将会再次启动service,并传入最后一个intent给onstartCommand。

直到调用stopSelf(int)才停止传递intent。如果在被kill后还有未处理好的intent,那被kill后服务还是会自动启动。因此

onstartCommand不会接收到任何null的intent。

相关TAG标签
上一篇:RecyclerAdapter 使用
下一篇:BaseAdapter 使用
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站