【攻克Android (28)】Local Service 本地服务

[b][size=large]本文围绕以下三个部分展开: [/size][/b]

[b][size=large]案例一:监听手机通话状态(日志输出)【启动方式】[/size][/b]
[b][size=large]案例二:Activity调用Service换歌方法进行换歌【绑定方式】[/size][/b]
[b][size=medium]代码补充[/size][/b]
[b][size=large]案例三:Find Name【绑定方式】[/size][/b]


[b][size=large]案例一:监听手机通话状态(日志输出)[/size][/b]

[size=medium][b]1. 创建并在功能清单中注册服务:MobileStateService[/b][/size]

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


[size=medium][b]2. 启动服务 (MainActivity中启动)[/b][/size]

        // 启动服务
Intent intent = new Intent(this, MobileStateService.class);
startService(intent);

// 停止服务
// stopService(intent);


[size=medium][b]3. MobileStateService。将onBind方法中的异常删掉,返回null。(否则运行时直接退出)[/b][/size]

    @Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");

// 改为返回null
return null;
}


[size=medium][b]4. MobileStateService。重写 onCreate() 和 onDestroy() 方法[/b][/size]

    @Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "创建服务.");
}

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


[size=medium][b]5. 监听手机来电状态,需要在功能清单中授予读取手机状态权限。[/b][/size]

    <!--  读取手机状态权限
<uses-permission android:name="android.permission.read_phone_state" />
<uses-permission android:name="ANDROID.PERMISSION.READ_PHONE_STATE" />
这两种写法,都不会报错。但是,在按住ctrl键并点击的时候,弹不出来。
而下面这种正确写法,按住ctrl键并点击,可以弹出来。
因此可用这种方法检验写的是否正确。
-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />


[size=medium][b]6. MobileStateService。通过电话管理器来监听手机状态。[/b][/size]

    @Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "创建服务.");

// 获得电话管理器(电话管理的服务)
// telephony:电话(学);电话制造
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
// 电话管理器监听手机状态
// LISTEN_CALL_STATE 三个状态:响铃状态,接通状态,空闲状态
tm.listen(new MyPhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE);
}


[size=medium][b]7. MobileStateService。创建手机状态监听器类:MyPhoneStateListener。[/b][/size]

    private class MyPhoneStateListener extends PhoneStateListener {
/**
* @param state 状态
* @param incomingNumber 来电号码
*/
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
// idle:闲置的
case TelephonyManager.CALL_STATE_IDLE:
Log.v(TAG, "空闲状态");
break;
// ring:响铃
case TelephonyManager.CALL_STATE_RINGING:
Log.v(TAG, "响铃状态:" + incomingNumber);
if ("13888888888".equals(incomingNumber)) {
// 日志输出,并不是真的挂断
Log.v(TAG, "挂断情敌号码:" + incomingNumber);
}
break;
// offhook:摘机
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.v(TAG, "接听状态");
break;
}
}
}


[size=medium][b]效果如下:[/b][/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6261/923fba44-9830-3300-8d3c-1e6c8648c8a9.png[/img][/align]


[b][size=large]案例二:Activity调用Service换歌方法进行换歌【绑定方式】[/size][/b]

[size=medium]主界面如下:[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6287/cd04b970-41c8-30c3-b91e-77b312136618.png[/img][/align]

[size=medium]当点击“Play Music”的时候,跳转到 SingerActivity 页面。该页面有3个按钮。[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6289/ece007f9-ed2f-306c-bc07-fea1d039ab84.png[/img][/align]

[size=medium]当点击“绑定服务”按钮之后,再点击“换歌”按钮,Toast输出:“更改歌曲:七里香”。[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6291/88d315ea-d31f-35d3-8963-daf1dbaf9e30.png[/img][/align]

[size=medium]当点击“解绑服务”按钮之后,再点击“换歌”按钮,Toast输出:“未绑定服务”。[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6293/0012f4b0-559e-39b0-bf33-e3f4630afe0e.png[/img][/align]

[size=medium][b]1. activity_main.xml。写 Play Music 按钮。[/b][/size]

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="playMusic"
android:text="@string/play_music" />

</RelativeLayout>


[size=medium][b]2. MainActivity。写按钮点击事件。[/b][/size]

    public void playMusic(View view) {
// 跳转到 播放歌 的活动
startActivity(new Intent(this, SingerActivity.class));
}


[size=medium][b]3. 创建 SingerActivity。并在功能清单中写返回主页面的按钮。[/b][/size]

        <activity
android:name=".SingerActivity"
android:label="@string/title_activity_singer"
android:parentActivityName=".MainActivity" />


[size=medium][b]4. activity_singer.xml。写3个按钮。[/b][/size]

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.android.localservice.SingerActivity">

<Button
android:id="@+id/btnBindService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/btn_bind_service" />

<Button
android:id="@+id/btnUnbindService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/btnBindService"
android:onClick="onClick"
android:text="@string/btn_unbind_service" />

<Button
android:id="@+id/btnChangeSong"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/btnUnbindService"
android:onClick="onClick"
android:text="@string/btn_change_song" />

</RelativeLayout>


[size=medium][b]5. SingerActivity。写3个按钮的点击事件。[/b][/size]

    public void onClick(View view) {
switch (view.getId()) {
case R.id.btnBindService:

break;
case R.id.btnUnbindService:

break;
case R.id.btnChangeSong:

break;
}
}


[size=medium][b]6. 创建并注册 SingerService。[/b][/size]

[size=medium][b]7. SingerService。将onBind方法中的异常删掉,返回null。(否则运行时直接退出)[/b][/size]

    @Override
public IBinder onBind(Intent intent) {
Log.v(TAG, "服务被成功绑定了");

return null;
}


[size=medium][b]8. SingerService。重写 onCreate 和 onDestroy 方法[/b][/size]

    @Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "创建服务,开始唱歌.");
}

@Override
public void onDestroy() {
super.onDestroy();
Log.v(TAG, "销毁服务,停止唱歌.");
}


[size=medium][b]9. SingerService。创建 换歌的方法:changeSong。[/b][/size]

    public void changeSong(String songName) {
Log.v(TAG, "更改歌曲:" + songName);
Toast.makeText(getApplicationContext(), "更改歌曲:" + songName,
Toast.LENGTH_LONG).show();
}


[size=medium][b]10. SingerActivity。“绑定服务”按钮事件。[/b][/size]

    // 是否绑定服务(默认值:false)
private boolean isBind;
public void onClick(View view) {
// 创建 激活服务的意图
Intent intent = new Intent(this, SingerService.class);
switch (view.getId()) {
case R.id.btnBindService:
// 绑定服务
// 如果没有绑定服务
if (!isBind) {
// bindService:绑定服务
// 参数一:intent 激活服务的意图
// 参数二:conn 代理人中间对象,用来与服务建立联系(不能为空)
// 参数三:BIND_AUTO_CREATE 在绑定服务时,若服务不存在则自动创建
bindService(intent, conn, BIND_AUTO_CREATE);
// 设置为:已绑定服务
isBind = true;
}
break;
case R.id.btnUnbindService:

break;
case R.id.btnChangeSong:

break;
}
}


[size=medium][b]11. SingerActivity。创建 服务连接 的方法。[/b][/size]

    /**
* 创建 服务连接 的方法
*/
private ServiceConnection conn = new ServiceConnection() {
/**
* 在服务被成功绑定时调用
*
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v(TAG, "歌手返回代理人对象");
}

/**
* 在服务失去绑定时调用(只有程序异常终止)
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {

}
};


[size=medium][b]12. 创建接口类 ISingerService,暴露出来,供 SingerActivity 中 中间代理人的实例 调用 换歌 的方法。[/b][/size]

package com.android.localservice;

/**
* 接口类:用于 Binder中的方法
*
* 暴露出来,供 SingerActivity 中 中间代理人的实例 调用 换歌 的方法,
* 这样,因为 中间代理人 MyBinder 实现了该接口,
* 因此, SingerActivity 中 中间代理人的实例 调用接口中的 换歌 方法,
* 相当于调用了 私有的 MyBinder 中的 换歌 的方法。
*/
public interface ISingerService {
void callChangeSong(String songName);
}


[size=medium][b]13. SingerService。创建中间代理人、中间代理人的实例,并在onBind方法中返回该实例。[/b][/size]

    /**
* 当Service被绑定时,系统会调用onBind()函数,
* 通过该函数的返回值, 将Service对象返回给调用者。
*
* 注意:onBind()的返回值必须符合IBinder接口,
* IBinder接口是用于进程内部和进程间过程调用的轻量级接口,
* 定义了与远程对象交互的抽象协议,使用时需要继承Binder类。
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
Log.v(TAG, "服务被成功绑定了");

// 在 onBind() 方法中, 返回 中间代理人的实例
return binder;
}

// 创建 中间代理人的实例
private IBinder binder = new MyBinder();
/**
* 创建 中间代理人(私有类)
*/
private final class MyBinder extends Binder implements ISingerService {
// 重写 ISingerService 接口 中的 换歌 方法
@Override
public void callChangeSong(String songName) {
// 内部类 MyBinder 中,可以调用外部类 SingerService 中的 换歌 的方法
// 这样,通过在 SingerActivity 中 中间代理人的实例 调用接口中的 换歌 方法,
// 就相当于 调用了 SingerService 类 中的 换歌 的方法。
changeSong(songName);
}
}


[size=medium][b]14. SingerService。解绑的方法:onUnbind[/b][/size]

    /**
* 解绑
*
* 如果返回为 true,则代表在新调用者绑定服务时,onRebind()函数才被调用
*
* @param intent
* @return
*/
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "取消本地绑定");
return false;
}


[size=medium][b]15. SingerActivity。onBind方法中将中间代理人的实例,返回至SingerActivity中的“服务连接”的方法中。[/b][/size]

    private ISingerService singerService;

/**
* 创建 服务连接 的方法
*/
private ServiceConnection conn = new ServiceConnection() {
/**
* 在服务被成功绑定时调用
*
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v(TAG, "歌手返回代理人对象");

// service 是 MyBinder 类的实例,是 IBinder接口类型。
// 因为 MyBinder 类 实现了 ISingerService 接口,
// 因此,service 也可由 IBinder接口类型 强转为 SingerService 接口类型。
singerService = (ISingerService) service;
}

/**
* 在服务失去绑定时调用(只有程序异常终止)
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
singerService = null;
}
};


[size=medium][b]补充:SingerService中,返回的中间代理人的实例 binder,就是 SingerActivity 的“服务连接”方法中的 service,二者指向同样的地址。[/b][/size]

    @Override
public IBinder onBind(Intent intent) {
Log.v(TAG, "服务被成功绑定了");
/*
Test 结果:
此处的 binder 与 SingerActivity 中的 onServiceConnected 中的 service
指向同样的地址。
二者是一样的。
*/
// Test:打印 binder.toString()
Log.v(TAG, "...." + binder.toString());
// 在 onBind() 方法中, 返回 中间代理人的实例
return binder;
}


        @Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v(TAG, "歌手返回代理人对象");
// 打印 service.toString()
Log.v(TAG, "...." + service.toString());

}


[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6313/9724b10c-c829-31d4-bdbd-60e0674cb2fb.png[/img][/align]

[size=medium][b]16. SingerActivity。“解绑服务”按钮事件。[/b][/size]

            case R.id.btnUnbindService:
// 解绑
// 如果已绑定服务
if (isBind) {
// 设置为:未绑定服务
isBind = false;
// 解绑
unbindService(conn);
singerService = null;
}
break;


[size=medium][b]17. SingerActivity。“换歌”按钮事件。[/b][/size]

            case R.id.btnChangeSong:

// 换歌
if (singerService == null) {
Toast.makeText(this, "未绑定服务", Toast.LENGTH_LONG).show();
} else {
singerService.callChangeSong("七里香");
}
break;


[size=medium][b]补充:不能在 Activity中直接调用 Service 中的方法:[/b][/size]

                SingerService service = new SingerService();
service.changeSong("七里香");


[size=medium]由于系统框架在创建服务时会创建与之对应的上下文,而下面的代码是直接 new 对象,直接 new 出来后,与上下文就没有关系了,所以抛 空指针异常。[/size]


[size=medium][b][color=red]整个过程的示意图如下:[/color][/b][/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6315/4a8fa7e5-02ca-3eb2-8d6c-8e4a46c8d0d8.png[/img][/align]

[size=medium][b]Activity与服务进行通信,开发人员通常把通信方法定义在接口里,然后让Ibinder对象实现该接口,而Activity通过该接口引用服务onBind()方法返回的Ibinder对象,然后调用Ibinder对象里自定义的通信方法。[/b][/size]


[b][size=medium]代码补充[/size][/b]

[size=medium]项目目录结构如下:[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6317/95d8523a-0bce-3f1b-abc0-52b99e2219f6.png[/img][/align]

[size=medium][b]1. strings.xml[/b][/size]

<resources>
<string name="app_name">LocalService</string>

<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>

<string name="play_music">Play Music</string>
<string name="btn_change_song">换歌</string>
<string name="btn_bind_service">绑定服务</string>
<string name="btn_unbind_service">解绑服务</string>
<string name="title_activity_singer">SingerActivity</string>
</resources>


[size=medium][b]2. MainActivity[/b][/size]

package com.android.localservice;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;


public class MainActivity extends Activity {

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

/**
* 按钮点击事件
* @param view
*/
public void playMusic(View view) {
// 跳转到 播放歌 的活动
startActivity(new Intent(this, SingerActivity.class));
}

// ------------------------------------------------------------------
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}


[size=medium][b]3. SingerActivity[/b][/size]

package com.android.localservice;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

/**
* 创建 SingerActivity
*/
public class SingerActivity extends Activity {
private static final String TAG = "LocalService";
private ISingerService singerService;
// 是否绑定服务(默认值:false)
private boolean isBind;

/**
* 创建 服务连接 的方法
*/
private ServiceConnection conn = new ServiceConnection() {
/**
* 在服务被成功绑定时调用
*
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v(TAG, "歌手返回代理人对象");
// 打印 service.toString()
Log.v(TAG, "...." + service.toString());

// service 是 MyBinder 类的实例,是 IBinder接口类型。
// 因为 MyBinder 类 实现了 ISingerService 接口,
// 因此,service 也可由 IBinder接口类型 强转为 SingerService 接口类型。
singerService = (ISingerService) service;
}

/**
* 在服务失去绑定时调用(只有程序异常终止)
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
singerService = null;
}
};

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

/**
* 写 3个按钮的点击事件
*
* @param view
*/
public void onClick(View view) {
// 创建 激活服务的意图
Intent intent = new Intent(this, SingerService.class);
switch (view.getId()) {
case R.id.btnBindService:
// 绑定服务
// 如果没有绑定服务
if (!isBind) {
// bindService:绑定服务
// 参数一:intent 激活服务的意图
// 参数二:conn 代理人中间对象,用来与服务建立联系(不能为空)
// 参数三:BIND_AUTO_CREATE 在绑定服务时,若服务不存在则自动创建
bindService(intent, conn, BIND_AUTO_CREATE);
// 设置为:已绑定服务
isBind = true;
}
break;
case R.id.btnUnbindService:
// 解绑
// 如果已绑定服务
if (isBind) {
// 设置为:未绑定服务
isBind = false;
// 解绑
unbindService(conn);
singerService = null;
}
break;
case R.id.btnChangeSong:
if (singerService == null) {
Toast.makeText(this, "未绑定服务", Toast.LENGTH_LONG).show();
} else {
singerService.callChangeSong("月亮之上");
}
break;
}
}

// --------------------------------------------------------------------
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_singer, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}


[size=medium][b]4. SingerService[/b][/size]

package com.android.localservice;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

/**
* 创建服务:SingerService
*/
public class SingerService extends Service {
private static final String TAG = "LocalService";

public SingerService() {
}

/**
* 创建 onCreate 方法
*/
@Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "创建服务,开始唱歌.");
}

/**
* 创建 onDestroy 方法
*/
@Override
public void onDestroy() {
super.onDestroy();
Log.v(TAG, "销毁服务,停止唱歌.");
}

/**
* 换歌 的方法
* @param songName
*/
public void changeSong(String songName) {
Log.v(TAG, "更改歌曲:" + songName);
Toast.makeText(getApplicationContext(), "更改歌曲:" + songName,
Toast.LENGTH_LONG).show();
}

/**
* 当Service被绑定时,系统会调用onBind()函数,
* 通过该函数的返回值, 将Service对象返回给调用者。
*
* 注意:onBind()的返回值必须符合IBinder接口,
* IBinder接口是用于进程内部和进程间过程调用的轻量级接口,
* 定义了与远程对象交互的抽象协议,使用时需要继承Binder类。
*
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
Log.v(TAG, "服务被成功绑定了");
/*
Test 结果:
此处的 binder 与 SingerActivity 中的 onServiceConnected 中的 service
指向同样的地址。
二者是一样的。
*/
// Test:打印 binder.toString()
Log.v(TAG, "...." + binder.toString());

// 在 onBind() 方法中, 返回 中间代理人的实例
return binder;
}

// 创建 中间代理人的实例
private IBinder binder = new MyBinder();
/**
* 创建 中间代理人(私有类)
*/
private final class MyBinder extends Binder implements ISingerService {
// 重写 ISingerService 接口 中的 换歌 方法
@Override
public void callChangeSong(String songName) {
// 内部类 MyBinder 中,可以调用外部类 SingerService 中的 换歌 的方法
// 这样,通过在 SingerActivity 中 中间代理人的实例 调用接口中的 换歌 方法,
// 就相当于 调用了 SingerService 类 中的 换歌 的方法。
changeSong(songName);
}
}

/**
* 解绑
*
* 如果返回为 true,则代表在新调用者绑定服务时,onRebind()函数才被调用
*
* @param intent
* @return
*/
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "取消本地绑定");
return false;
}
}


[size=medium][b]5. ISingerService[/b][/size]

package com.android.localservice;

/**
* 接口类:用于 Binder中的方法
*
* 暴露出来,供 SingerActivity 中 中间代理人的实例 调用 换歌 的方法,
* 这样,因为 中间代理人 MyBinder 实现了该接口,
* 因此, SingerActivity 中 中间代理人的实例 调用接口中的 换歌 方法,
* 相当于调用了 私有的 MyBinder 中的 换歌 的方法。
*/
public interface ISingerService {
void callChangeSong(String songName);
}



[b][size=large]案例三:Find Name【绑定方式】[/size][/b]

[size=medium]主界面如下:[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6319/ed055e1f-a2af-312a-a043-55593d6499e8.png[/img][/align]

[size=medium]当点击“Find Name”按钮后,下面的TextView被“Android L Google / 1”代替,数字按每秒加1的速率在变,但只有当点击“Find Name”按钮时,才可以看到数字变化了。[/size]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6321/cedc75bf-6e2b-3a3e-a3c4-248f69ac45a6.png[/img][/align]

[align=center][img]http://dl2.iteye.com/upload/attachment/0110/6323/06075bd9-aaa5-3842-a525-661a754160b5.png[/img][/align]

[size=medium][b]1. strings.xml[/b][/size]

<resources>
<string name="app_name">ServiceDemo</string>

<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>

<string name="btn_find_name">Find Name</string>
<string name="title_activity_service_demo">ServiceDemo</string>
</resources>


[size=medium][b]2. activity_main.xml[/b][/size]

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<Button
android:id="@+id/btnFindName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/btn_find_name" />

<TextView
android:id="@+id/tvResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/btnFindName"
android:text="@string/hello_world" />

</RelativeLayout>


[size=medium][b]3. MainActivity[/b][/size]

package com.android.servicedemo;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;


public class MainActivity extends Activity {
// 3.1 声明变量(文本)
private TextView tvResult;
// 16.
private INameService nameService;

/**
* 15.
*/
private ServiceConnection conn = new ServiceConnection() {
/**
* 在服务被成功绑定时调用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 17. 对于本地服务,获取的实例 service 和
// 服务 onBind() 返回的实例 binder 是同一个
nameService = (INameService) service;

}

/**
* 服务失去绑定时调用(只有程序异常终止)
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
// 18.
nameService = null;
}
};

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

// 3.2 获得 文本
tvResult = (TextView) findViewById(R.id.tvResult);

// 14. 绑定并启动服务 (写在 onCreate里面,在创建活动的时候,就绑定并启动服务了)
bindService(new Intent(this, NameService.class), conn, BIND_AUTO_CREATE);

/*
当 Activity(调用者) 退出,Service(服务) 也随着调用者退出,就销毁了。
当下次调用者进入,服务也随着创建,再次绑定并启动。
*/
}

/**
* 2. 按钮事件点击事件
*
* @param view
*/
public void onClick(View view) {
// 19. 格式化字符串
String text = String.format("%s / %d",
nameService.getName("Google"),
nameService.getCount());
// 20. TextView 设置字符串,进行显示
tvResult.setText(text); }

// -------------------------------------------------------------------
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}


[size=medium][b]4. NameService[/b][/size]

package com.android.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

/*
* 小结:何时使用 启动方式,何时使用 绑定方式?:
*
* 如果要用服务中自定义的方法,必须用 binder方法。
* 如果启动后,就不管了,就用 onStart方法
*/

/**
* 4. 创建 服务 NameService
*/
public class NameService extends Service {
private static final String TAG = "MainActivity";
// 8.1 退出的条件 (默认为 false)
private boolean exit;
// 8.2
private int count;

public NameService() {
}

/**
* 5.1
*/
@Override
public void onCreate() {
super.onCreate();
// 5.1.1
Log.v(TAG, "onCreate...");

// 8.3 Service 中耗时的操作应使用线程处理
new Thread() {
@Override
public void run() {
/*
如果把 while 直接写入 onCreate,
则 onCreate 一直要做事情,线程可能死掉。
因此,写入一个新的线程中。
*/
while (!exit) {
try {
// 线程休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 每次休眠一秒,count +1
count++;
}
}
}.start();
}

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

/**
* 7. 自定义的方法
* <p/>
* MainActivity中并不能调用该方法,因此必须通过 binder来操作
*/
public String getName(String name) {
return "Android L " + name;
}

@Override
public IBinder onBind(Intent intent) {
// 6.
Log.v(TAG, "onBind...");
// 10.2
return binder;
}

// 10.1
// binder实例是 IBinder 类型,是 NameBinder的实例
// private Binder binder = new NameBinder(); 也可
// private NameBinder binder = new NameBinder(); 也可
// 即:既可以是 NameBinder类型(儿子),也可以是 Binder类型(爸爸),
// 还可以是 IBinder类型(爷爷),一般写成 接口类型。
private IBinder binder = new NameBinder();

/**
* 9.
*/
private final class NameBinder extends Binder implements INameService {

/**
* 12.
* @return
*/
public int getCount() {
// 内部类(NameBinder)可直接访问外部类(NameService)的成员变量 count
return count;
}

/**
* 13.
*
* @param name
* @return
*/
@Override
public String getName(String name) {
// 内部类(NameBinder)访问外部类(NameService)的成员方法
// 若方法名相同时,必须指定所有者(NameService.this.)
// --- 此处,内部类的方法名与外部类的方法名相同。
// 若方法名不同时,无需指定所有者(也可指定)
return NameService.this.getName(name);
}
}
}


[size=medium][b]5. INameService[/b][/size]

package com.android.servicedemo;

/**
* 11. 接口:方便于调用者(Activity)访问 Binder 子类中的方法
*/
public interface INameService {

// 接口中的方法都是 public static的,因此前面可以不写。
int getCount();

String getName(String name);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值