`
XiangdongLee
  • 浏览: 91196 次
  • 性别: 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  
写这么长,毅力不错~

相关推荐

    攻克Data动态获取网页评论,保存数据库

    【攻克Data动态获取网页评论,保存数据库】是一个关于利用特定工具——攻克Data,来抓取网页上的评论数据并存储到数据库的过程。这个过程涉及到网络爬虫技术、JSON解析以及数据库管理等多个IT领域的知识点。 1. **...

    大学生攻克Linux系统教程

    【大学生攻克Linux系统教程】 本教程专为对Linux操作系统感兴趣的初学者设计,旨在提供一个从零开始学习Linux的全面指南。教程内容涵盖了Linux系统的安装、基本操作、文本编辑器VI的使用、调试工具GDB的基础知识,...

    24学时攻克c++

    根据提供的信息,“24学时攻克C++”这本书旨在通过一系列高效的学习计划帮助读者在较短的时间内掌握C++编程语言。尽管仅上传了23页的内容,我们仍然可以从书名、描述以及部分可见的内容中推断出一些关键知识点。 ##...

    如何深入学习Android Framework.pdf

    - **系统服务**:如Window Manager Service (WMS)、Activity Manager Service (AMS)等。 4. **系统启动流程**:了解从Boot Loader到最终启动用户界面的整个过程,这对于理解各个组件之间的交互非常重要。 5. **...

    24小时攻克c++代码

    《24小时攻克C++代码》是一门针对C++编程语言深入浅出的学习教程,旨在帮助初学者在短时间内掌握C++编程基础,并逐步提升至进阶水平。C++是一种广泛应用的面向对象编程语言,它的强大功能和高效性能使得它在系统软件...

    大学生攻克Linux系统教程.rar

    大学生攻克Linux系统教程(又名天下没有难学的Linux)。一位在校大学生整理的教程,献给每一位要征服Linux的学习者-五星级的Linux教程。 本书汇集众多Linux学习者的心血与贡献,有很多作者在默默的为你呼喊,感谢...

    8天攻克8000英语词汇

    8天攻克8000词汇,word版,方便学习,背单词的好东东

    大学生攻克Linux系统教程(又名天下没有难学的Linux)

    本教程“大学生攻克Linux系统教程”旨在帮助初学者轻松理解并熟练运用Linux,让看似复杂的Linux变得易学易用。 首先,让我们从基础开始。Linux系统基于Unix,其命令行界面(CLI)是核心操作方式。学习Linux,首先要...

    Flash Development for Android Cookbook

    - **核心概念理解:** 掌握Activity、Intent、Service、Broadcast Receiver等基本组件的概念对于Android开发至关重要。这些组件构成了Android应用的基本结构。 **5. 使用Flash、Flex与AIR开发Android应用** - **...

    新东方8天攻克8000单词魔鬼训练营

    标题中的“新东方8天攻克8000单词魔鬼训练营”揭示了一个高效学习英语单词的课程项目。新东方作为知名的教育机构,以其在英语教学领域的专业性而著称,这个训练营可能是一个针对英语学习者设计的高强度、短期记忆...

    24学时攻克c++_笔记

    从给定的文件信息来看,这是一份关于学习C++编程的笔记,涵盖了从环境搭建到高级主题的多个方面。下面将对这份笔记中的关键知识点进行详细的解释和扩展。 ### 1.... 笔记中提到了几种推荐使用的编译器,包括BCB5、VS...

    Android自动来电录音

    在Android平台上实现自动来电录音是一项技术挑战,但并非无法攻克。Android系统提供了丰富的API和工具,使得开发者可以创建各种功能丰富的应用,包括来电录音。在本文中,我们将深入探讨如何利用Android的...

    Android开心词场app

    - **Android四大组件**:Activity、Service、BroadcastReceiver和ContentProvider,这些组件构成了Android应用的基础结构,开发者需要熟练掌握它们之间的交互逻辑。 - **UI设计**:采用Material Design设计风格,...

    需要攻克的模块

    【标题】:“需要攻克的模块”通常指的是在IT项目开发或个人学习过程中遇到的特定技术难题或功能模块,这些模块可能涉及到系统设计的关键部分,或者是技术栈中的难点。在这个特定的场景下,"蓝牙模块"可能就是我们...

    攻克Linux系统教程

    "攻克Linux系统教程"旨在帮助初学者和有一定基础的学习者深入理解Linux系统,掌握其核心概念和操作技能。 首先,我们需要了解Linux的基础知识。Linux并非单一的操作系统,而是基于Linux内核的各种发行版的总称,如...

    24学时攻克C++源码及习题答案

    在学习编程语言C++的过程中,24学时的课程安排是一个相当常见的学习进度,这个压缩包文件名为"24学时攻克C++源码及习题答案",显然是为初学者设计的一套完整教程。C++是面向对象编程的重要语言,它的强大功能和广泛...

    Android应用源码之Fanfoudroid(饭否网开源项目)-IT计算机-毕业设计.zip

    此外,可能还会使用SharedPreferences存储用户的偏好设置,这些都是Android本地数据存储的重要方式。 UI设计是提升用户体验的关键。“Fanfoudroid”展示了如何利用Android的布局组件、自定义View以及动画效果来构建...

    通信与网络中的思科攻克视频服务难题 增强观看IPTV体验

     VQE允许基于网络快速切换频道并修复视频错误,帮助运营商攻克了数字有线电视以及IPTV服务领域开发和交付内容的两大难题。 意大利电信负责创新工程和服务 技术的副总裁Stefano Nocenti ni说:“无缝的频道切换...

    40天攻克大学英语四级

    本文将重点解析“40天攻克大学英语四级”中的核心知识点,尤其是关于作文和翻译部分的倒装句技巧。 倒装句是英语写作中的一种高级表达方式,它能够使句子更具表现力,提升文章的层次感。倒装句分为完全倒装和部分倒...

Global site tag (gtag.js) - Google Analytics