`
XiangdongLee
  • 浏览: 86903 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

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

 
阅读更多
本文围绕以下三个部分展开:

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





案例一:监听手机通话状态(日志输出)

        1. 创建并在功能清单中注册服务:MobileStateService

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


        2. 启动服务 (MainActivity中启动)

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

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


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

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

        // 改为返回null
        return null;
    }


        4. MobileStateService。重写 onCreate() 和 onDestroy() 方法

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

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


        5. 监听手机来电状态,需要在功能清单中授予读取手机状态权限。

    <!--  读取手机状态权限
    <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" />


        6. MobileStateService。通过电话管理器来监听手机状态。

    @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);
    }


        7. MobileStateService。创建手机状态监听器类:MyPhoneStateListener。

    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;
            }
        }
    }


        效果如下:




案例二:Activity调用Service换歌方法进行换歌【绑定方式】

        主界面如下:



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



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



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



        1. activity_main.xml。写 Play Music 按钮。

<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>


        2. MainActivity。写按钮点击事件。

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


        3. 创建 SingerActivity。并在功能清单中写返回主页面的按钮。

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


        4. activity_singer.xml。写3个按钮。

<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>


        5. SingerActivity。写3个按钮的点击事件。

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

                break;
            case R.id.btnUnbindService:

                break;
            case R.id.btnChangeSong:

                break;
        }
    }


        6. 创建并注册 SingerService。

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

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


        8. SingerService。重写 onCreate 和 onDestroy 方法

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


        9. SingerService。创建 换歌的方法:changeSong。

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


        10. SingerActivity。“绑定服务”按钮事件。

    // 是否绑定服务(默认值: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;
        }
    }


        11. SingerActivity。创建 服务连接 的方法。

    /**
     * 创建 服务连接 的方法
     */
    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) {

        }
    };


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

package com.android.localservice;

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


        13. SingerService。创建中间代理人、中间代理人的实例,并在onBind方法中返回该实例。

    /**
     *   当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);
        }
    }


        14. SingerService。解绑的方法:onUnbind

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


        15. SingerActivity。onBind方法中将中间代理人的实例,返回至SingerActivity中的“服务连接”的方法中。

    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;
        }
    };


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

    @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());

        }




        16. SingerActivity。“解绑服务”按钮事件。

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


        17. SingerActivity。“换歌”按钮事件。

            case R.id.btnChangeSong:

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


        补充:不能在 Activity中直接调用 Service 中的方法:

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


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


        整个过程的示意图如下:



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


        代码补充

        项目目录结构如下:



        1. strings.xml

<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>


        2. MainActivity

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);
    }
}


        3. SingerActivity

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);
    }
}


        4. SingerService

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;
    }
}


        5. ISingerService

package com.android.localservice;

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



案例三:Find Name【绑定方式】

        主界面如下:



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





        1. strings.xml

<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>


        2. activity_main.xml

<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>


        3. MainActivity

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);
    }
}


        4. NameService

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);
        }
    }
}


        5. INameService

package com.android.servicedemo;

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

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

    String getName(String name);
}
1
3
分享到:
评论
2 楼 mute_ 2015-08-23  
好腻害啊,
1 楼 toknowme 2015-08-03  
写这么长,毅力不错~

相关推荐

Global site tag (gtag.js) - Google Analytics