android service

Service 是一个可以在后台执行长时间运行操作而不使用用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务并与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

服务在其托管进程的主线程中运行,它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。

应使用服务还是线程?

服务是一种即使用户未与应用交互,但它仍可以在后台运行的组件。 因此,应仅在必要时才创建服务。如果您确实要使用服务,则默认情况下,它仍会在应用的主线程中运行,因此,如果服务执行的是密集型或阻止性操作(例如 MP3 播放或联网),则仍应在服务内创建新线程。

如需在主线程外部执行工作,不过只是在用户正在与应用交互时才有此需要,则应创建新线程而非服务。 例如,如果只是想在 Activity 运行的同时播放一些音乐,则可在 onCreate() 中创建线程,在 onStart() 中启动线程,然后在 onStop() 中停止线程。还可以考虑使用 AsyncTask 或 HandlerThread,而非传统的 Thread 类。

Service的种类

  • 按运行类型分:
类别 区别 应用
前台服务 会在通知栏显示onGoing的 Notification 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务。
后台服务 默认的服务即为后台服务,即不会在通知一栏显示 onGoing的 Notification。 当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。
  • 按使用方式分:
类别 区别
startService启动的服务 主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService。
bindService启动的服务 方法启动的服务要进行通信。停止服务使用unbindService。
同时使用startService、bindService 启动的服务 停止服务应同时使用stopService与unbindService。

使用service

  • 使用startService启动服务
    1.定义一个类继承service
public class MyService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
            //TODO do something useful
            return Service.START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
          //TODO for communication return IBinder implementation
      return null;
    }
}

onstartCommad方法返回一个int类型用来定义服务在被android系统终止之后的重启方式。最常用的三种常量返回值



2.在Manifest.xml文件中配置该Service

<service
        android:name="MyService"
        android:icon="@drawable/icon"
        android:label="@string/service_name"
        >
</service>

3.使用Context的startService(Intent)方法启动该Service

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

4.不再使用时,调用stopService(Intent)方法停止该服务
.
使用这种方式创建启动服务的生命周期如下图:


注意:
1.如果服务已经开启,不会重复的执行onCreate(),而是会调用onStartCommand()。
2.服务停止的时候调用 onDestory()。服务只会被停止一次。

  • 使用bindService方式启动服务
    如果您的服务仅供本地应用使用,不需要跨进程工作,则可以实现自有 Binder 类,让您的客户端通过该类直接访问服务中的公共方法。

注:此方法只有在客户端和服务位于同一应用和进程内这一最常见的情况下方才有效。 例如,对于需要将 Activity 绑定到在后台播放音乐的自有服务的音乐应用,此方法非常有效。

具体的创建服务的方法:

① 在服务中,创建一个可满足下列任一要求的 Binder 实例:
 - 包含客户端可调用的公共方法
 - 返回当前 Service实例,其中包含客户端可调用的公共方法
 - 返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法

② 从 onBind()回调方法返回此 Binder实例。

③ 在客户端中,从 [onServiceConnected()](https://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder))回调方法接收 Binder,并使用提供的方法调用绑定服务。

例如,以下这个服务可让客户端通过 Binder 实现访问服务中的方法:

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

    //Binder中包含返回该实例的方法
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService 
            //so clients can call public methods
            return LocalService.this;
        }
    }
    // onBind()回调方法返回此 Binder实例。
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

LocalBinder 为客户端提供 getService() 方法,以检索 LocalService 的当前实例。这样,客户端便可调用服务中的公共方法。 例如,客户端可调用服务中的 getRandomNumber()。

点击按钮时,以下这个 Activity 会绑定到 LocalService 并调用 getRandomNumber():

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService,建议在onstart方法中进行绑定
        Intent intent = new Intent(this, LocalService.class);
        //利用bindService(Intent, ServiceConnection, int)方法启动该Service
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service,建议在onstop方法中进行解绑
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute) */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call were something that might hang, 
            //then this request should occur in a separate thread 
            //to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder 
            //and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

特点:bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。绑定者可以调用服务里面的方法。

使用这种方式创建启动服务的生命周期如下图:


Service 元素的常见属性

属性 描述
android:name 服务类名
android:label 服务的名字,如果此项不设置,那么默认显示的服务名则为类名
android:icon 服务的图标
android:permission 申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务
android:process 表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字
android:enabled 如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false
android:exported 表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false

IntentService

IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。
另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
那么,用IntentService有什么好处呢?首先,我们省去了在Service中手动开线程的麻烦,第二,当操作完成时,我们不用手动停止Service

下面来写一个Demo来模拟耗时操作在IntentService中的运行过程


自定义intentService

public class MyIntentService extends IntentService {

    public static final String REUSLT = "reuslt";

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String msg = intent.getStringExtra(MainActivity.COM_KEVINWANGY_TESTINTENTSERVICE_MSG);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        msg = "get result \"" + msg + "_result\" at "
                + android.text.format.DateFormat.format("dd/MM/yy h:mm:ss aa", System.currentTimeMillis());
        Log.i("onHandleIntent", msg);

        Intent resultIntent = new Intent();
        resultIntent.setAction(MainActivity.ResultReceiver.RESULT_ACTION);
        resultIntent.putExtra(REUSLT, msg);
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this); //Service本身就是Context
        localBroadcastManager.sendBroadcast(resultIntent);
    }
}

定义MainActiviy , 其中包含一个BroadCastReceiver

public class MainActivity extends AppCompatActivity {

    public static final String COM_KEVINWANGY_TESTINTENTSERVICE_MSG = "com.kevinwangy.testintentservice.msg";
    private Button mButton;
    private TextView mStart_text;
    private TextView mFinish_text;
    private EditText mEditText;
    private String mStart_msg;
    private ResultReceiver mResultReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final StringBuilder stringBuilder = new StringBuilder();

        mStart_text = (TextView)findViewById(R.id.start_text);
        mFinish_text = (TextView)findViewById(R.id.finish_text);

        mEditText = (EditText)findViewById(R.id.input_edit);

        mButton = (Button)findViewById(R.id.input_btn);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mStart_msg = mEditText.getText().toString();
                stringBuilder.append("send message \"" + mStart_msg + "\" at "
                        + android.text.format.DateFormat.format("dd/MM/yy h:mm:ss aa", System.currentTimeMillis()) + "\n");
                mStart_text.setText(stringBuilder.toString());

                Intent intent = new Intent(MainActivity.this, MyIntentService.class);
                intent.putExtra(COM_KEVINWANGY_TESTINTENTSERVICE_MSG, mStart_msg);
                startService(intent);
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    public class ResultReceiver extends BroadcastReceiver {
    }
}

ResultReceiver 的定义如下

    public class ResultReceiver extends BroadcastReceiver {
        public static final String RESULT_ACTION = "com.kevinwang.testintentservice.resultReceiver";

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null && TextUtils.equals(intent.getAction(), RESULT_ACTION)) {
                String result = intent.getStringExtra(MyIntentService.REUSLT);
                Log.i("onReceive", result);
                Log.i("onReceive", "context is " + context.toString());
                mFinish_text.setText(result);
            }
        }
    }

在onstart中注册BroadCastReceiver

    protected void onStart() {
        super.onStart();
        IntentFilter filter = new IntentFilter(ResultReceiver.RESULT_ACTION);
        mResultReceiver = new ResultReceiver();
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localBroadcastManager.registerReceiver(mResultReceiver, filter);
    }

在onstop中解除注册

    protected void onStop() {
        super.onStop();
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localBroadcastManager.unregisterReceiver(mResultReceiver);
    }

在androidManifest中声明

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name=".MyIntentService"/>

关于service、intentService、thread和AsyncTask的对比

参考文档及博客服务绑定服务Service那点事儿

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,999评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,102评论 1 302
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,709评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,439评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,846评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,881评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,062评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,783评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,517评论 1 248
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,762评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,241评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,236评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,145评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,941评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,965评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,802评论 2 275

推荐阅读更多精彩内容