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

相关推荐

    停车场管理系统c语言.docx

    问题描述: 停车场内只有一个可停放n辆汽车的狭长通道,且只有一个大门可供汽车进出。汽车在停车场内按车辆到达时间的先后顺序,依次由北向南排列(大门在最南端,最先到达的第一辆车停放在停车场的最北端),若车场内已停满n辆汽车,则后来的汽车只能在门外的便道上等候,一旦有车开走,则排在便道上的第一辆车即可开入; 当停车场内某辆车要离开时,在它之后开入的车辆必须先退出车场为它让路,待该辆车开出大门外,其它车辆再按原次序进入车场,每辆停放在车场的车在它离开停车场时必须按它停留的时间长短交纳费用。试为停车场编制按上述要求进行管理的模拟程序。 1.基本要求 (1)以栈模拟停车场,以队列模拟车场外的便道,按照从终端读入的输入数据序列进行模拟管理。 (2)每一组输入数据包括三个数据项:汽车“到达”或“离去”信息、汽车牌照号码及到达或离去的时刻,对每一组输入数据进行操作后的输出数据为:若是车辆到达,则输出汽车在停车场内或便道上的停车位置;若是车离去;则输出汽车在停车场内停留的时间和应交纳的费用(在便道上停留的时间不收费)。 (3)栈以顺序结构实现,队列以链表实现。 2.重点难点 重点:针对停车场问题的特点,利

    精选毕设项目-人民好公仆小程序(生活+便民+政务).zip

    精选毕设项目-人民好公仆小程序(生活+便民+政务)

    精选毕设项目-相册;处理用户信息.zip

    精选毕设项目-相册;处理用户信息

    精选毕设项目-喵喵小说.zip

    精选毕设项目-喵喵小说

    精选毕设项目-图片预览带后端.zip

    精选毕设项目-图片预览带后端

    精选项目-爱靓女带后台.zip

    精选项目-爱靓女带后台

    法院综合安全监管平台解决方案PPT(53页).pptx

    在科技与司法的交响曲中,智慧法院应运而生,成为新时代司法服务的新篇章。它不仅仅是一个概念,更是对法院传统工作模式的一次深刻变革。智慧法院通过移动信息化技术,为法院系统注入了强大的生命力,有效缓解了案多人少的矛盾,让司法服务更加高效、便捷。 立案、调解、审判,每一个阶段都融入了科技的智慧。在立案阶段,智慧法院利用区块链技术实现可信存证,确保了电子合同的合法性和安全性,让交易双方的身份真实性、交易安全性得到了有力见证。这不仅极大地缩短了立案时间,还为后续审判工作奠定了坚实的基础。在调解阶段,多元调解服务平台借助人工智能、自然语言处理等前沿技术,实现了矛盾纠纷的快速化解。无论是矛盾类型的多元化,还是化解主体的多元化,智慧法院都能提供一站式、全方位的服务,让纠纷解决更加高效、和谐。而在审判阶段,智能立案、智能送达、智能庭审、智能判决等一系列智能化手段的应用,更是让审判活动变得更加智能化、集约化。这不仅提高了审判效率,还确保了审判质量的稳步提升。 更为引人注目的是,智慧法院还构建了一套完善的执行体系。移动执行指挥云平台的建设,让执行工作变得更加精准、高效。执行指挥中心和信息管理中心的一体化应用,实现了信息的实时传输和交换,为执行工作提供了强有力的支撑。而执行指挥车的配备,更是让执行现场通讯信号得到了有力保障,应急通讯能力得到了显著提升。这一系列创新举措的实施,不仅让执行难问题得到了有效解决,还为构建诚信社会、保障金融法治化营商环境提供了有力支撑。智慧法院的出现,让司法服务更加贴近民心,让公平正义的阳光更加温暖人心。

    西门子1200与3台台达DTK温控器通讯程序 功能:实现西门子1200 PLC对3台台达DTK温控器进行485通讯控制,在触摸屏上设定温度,读取温度 器件:西门子12

    西门子1200与3台台达DTK温控器通讯程序 功能:实现西门子1200 PLC对3台台达DTK温控器进行485通讯控制,在触摸屏上设定温度,读取温度 器件:西门子1200 1214DC DC DC.昆仑通态TPC7062Ti ,西门子KTP700 Basic PN,台达DTK 4848V12温控器。 说明:的是程序,带详细注释程序,西门子触摸屏程序,PLC设置和温控器设置,接线说明书。 #SIEMENS 西门子

    机械设计电阻绕线焊线一体机sw18全套技术资料100%好用.zip

    机械设计电阻绕线焊线一体机sw18全套技术资料100%好用.zip

    VB6编写的上位机采集2路温度 并形成曲线图 还可查看历史数据

    VB6编写的上位机源码,可实时显示曲线图,带有数据库,可以进行历史数据的保存 及 查看历史采集数据。

    精选毕设项目-新浪读书.zip

    精选毕设项目-新浪读书

    jQuery+Slick插件实现游戏人物轮播展示切换特效源码.zip

    jQuery+Slick插件实现游戏人物轮播展示切换特效源码是一款通过背景图片的切换来显示不同的人物效果,轮播效果通过slick幻灯片插件来制作。效果非常棒,有需要的朋友可以直接下载使用,适应各大网站

    精选毕设项目-地图查找附件.zip

    精选毕设项目-地图查找附件

    (蛐蛐voc数据)农作物病虫害识别目标检测数据集,VOC格式,蛐蛐数据集,纯手动标注,用来进行目标检测代码训练的数据

    (蛐蛐voc数据)农作物病虫害识别目标检测数据集,VOC格式,蛐蛐数据集,纯手动标注,用来进行目标检测代码训练的数据。

    MATLAB Simulink仿真模型 双馈风机并网频率控制仿真模型,利用下垂控制与惯性控制结合的综合惯性控制,实现电力系统的频率稳定,两台同步发电机组,具体参数可自行调节,频率波形比较可利用matl

    MATLAB Simulink仿真模型 双馈风机并网频率控制仿真模型,利用下垂控制与惯性控制结合的综合惯性控制,实现电力系统的频率稳定,两台同步发电机组,具体参数可自行调节,频率波形比较可利用matlab工作区画出。

    科研项目结题报告的撰写指南:结构、内容与注意事项

    一、结题报告的类型及主要结构 结题报告是一种专门用于科研课题结题验收的实用性报告类文体,也叫研究报告。它是研究者在课题研究结束后对科研课题研究过程和研究成果进行客观、全面、实事求是的描述,是课题研究所有材料中最主要的材料,也是科研课题结题验收的主要依据。   一篇规范、合格的结题报告,需要回答好3个问题:一是“为什么要选择这项课题进行研究?”二是“这项课题是怎样进行研究的?”三是“课题研究取得哪些研究成果?”  基本结构大致包括以下部分: 第一个问题 “为什么要选择这项课题进行研究?”  1.课题提出的背景;2.课题研究的意义(包括理论意义和现实意义,这个部分也可以合并归入“课题提出的背景”部分);第二个问题“这项课题是怎样进行研究的?”3.文献综述;4.课题研究的理论依据;5.课题研究的目标;6.课题研究的主要内容;7.课题研究的对象;8.课题研究的方法;9. 课题研究的主要过程(研究的步骤);   除了第9部分外,从第1到第8部分在填报课题立项申报表、在制定课题研究方案、在开题报告中,都有要求,内容基本相同。到了撰写结题报告时,只须稍作适当修改就可以了。而第9部分,则需要通过对

    1+X网络安全应急响应之应急准备:构建高效安全的应急响应体系

    内容概要:本文档重点讲述了网络安全应急响应的各项准备工作,涵盖了‘1+X’网络安全应急响应的职业技能等级证书概述、应急响应的基础知识、应急响应组织的建立、风险评估与改进、应急响应预案的制定以及详细的应急响应计划处置样例。文中详细介绍了各级职业技能的要求和任务,尤其关注如何未雨绸缪,制定完善的应急预案以应对潜在的网络安全风险;同时也探讨了如何在网络安全事件发生时,采取及时有效的应急处置措施。 适合人群:从事或有兴趣进入网络安全领域的从业人员,尤其是准备考取‘1+X’网络安全应急响应职业技能等级证书的相关人员。 使用场景及目标:帮助读者了解网络安全应急响应的基本概念及其在整个国家安全框架中的重要地位;指导读者学会如何建立健全高效的应急响应组织结构,如何进行全面的风险评估以及如何编制切实可行的应急预案;通过实例剖析,增强读者应对突发网络安全事件的能力。文档的目标在于提升读者在不同层面的专业技能,包括但不限于系统备份、日志分析、安全漏洞修复等方面的能力。 阅读建议:此文档结构清晰,内容详尽,非常适合有一定基础的技术从业者参考学习。建议读者逐章节深入了解,特别是关注自身岗位对应的技能细分类别。此外,结合实例深入理解和练习如何进行应急处置是非常有价值的,有助于提升自身的实战能力。

    电动汽车动力系统匹配计算模型:输入整车参数及性能要求,一键生成驱动系统的扭矩功率峰值转速等参数 2、整车动力经济性计算模型:包含NEDC WLTC CLTC工况,输入整车参数可生成工况电耗、百公里电

    电动汽车动力系统匹配计算模型:输入整车参数及性能要求,一键生成驱动系统的扭矩功率峰值转速等参数。 2、整车动力经济性计算模型:包含NEDC WLTC CLTC工况,输入整车参数可生成工况电耗、百公里电耗、匀速工况续航、百公里电耗等信息。 实际项目中使用的计算仿真模型. 两个模型打包

    chromedriver-linux64_122.0.6254.0.zip

    chromedriver-linux64_122.0.6254.0

    SRS构型七自由度冗余机械臂运动学建模全套matlab代码 代码主要功能: 1. 基于臂角参数化方法求解机械臂在给定末端位姿和臂角下的关节角度; 2. 求解机械臂在给定末端位姿下的有效臂角范围

    SRS构型七自由度冗余机械臂运动学建模全套matlab代码 代码主要功能: [1]. 基于臂角参数化方法求解机械臂在给定末端位姿和臂角下的关节角度; [2]. 求解机械臂在给定末端位姿下的有效臂角范围,有效即在该区间内机械臂关节角度不会超出关节限位; [3]. 以避关节限位为目标在有效臂角区间内进行最优臂角的选取,进而获取机械臂在给定末端位姿下的最优关节角度。 购前须知: 1. 代码均为个人手写,主要包含运动学建模全套代码; 2. 代码已经包含必要的注释; 包含原理推导文档,不包含绘图脚本以及urdf;

Global site tag (gtag.js) - Google Analytics