首页 > 代码库 > java攻城狮之路(Android篇)--BroadcastReceiver&Service

java攻城狮之路(Android篇)--BroadcastReceiver&Service

四大组件:
activity 显示。
contentProvider 对外暴露自己的数据给其他的应用程序。
BroadcastReceiver 广播接收者,必须指定要接收的广播类型。必须明确的指定action
service 服务,是运行后台,它是没有界面的。对某件事情进行监听。

一、广播:事件。
普通广播: 是异步的。会广播接收者同时接收,不能被中断
sendBroadcast()
有序广播: 是同步的。会根据广播接收的优先级进行接收,是可以中断 短信到来广播
sendOrderBroadcast()
-1000 ~ 1000
如果有序广播明确的指定了广播接收者,他是无法被中断的。
广播接收者的订阅:
1 在清单文件里面指定

<receiver android:name=".SmsReceiver">    <intent-filter android:priority="1000">        <action android:name="android.provider.Telephony.SMS_RECEIVED"/>    </intent-filter></receiver>    

2 在代码里面指定

IntentFilter filter = new IntentFilter();filter.setPriority(1000);filter.addAction("android.provider.Telephony.SMS_RECEIVED");registerReceiver(new SmsReceiver(), filter);

当广播接收者一旦接收到广播,它会执行里面的onReceive()方法,但是里面是不能执行耗时的操作,这个方式如果在10s之内没有执行完毕,就会出现anr异常,也就是应用无响应异常

练习1:利用广播拦截短信、拦截外拨电话、增加IP拨号功能

技术分享

技术分享
package com.shellway.mysmsreceiver;import android.support.v7.app.ActionBarActivity;import android.content.IntentFilter;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;public class MainActivity extends ActionBarActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //代码注册广播接收者        IntentFilter filter = new IntentFilter();        filter.setPriority(1000);        filter.addAction("android.provider.Telephony.SMS_RECEIVED");        registerReceiver(new SmsReceiver(), filter);    }}
MainActivity.java
技术分享
package com.shellway.mysmsreceiver;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.text.BoringLayout;import android.util.Log;public class PhoneReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        //得到外拨电话        String number = getResultData();        Log.i("i", number);      //  abortBroadcast();//因为底层明确了广播接收者,所以这种中断打电话的效果不行      //  setResultData(null);//应该用这种方式来中断打电话      //  给用户设置IP拨号        setResultData("17951"+number);    }}
PhoneReceiver.java
技术分享
package com.shellway.mysmsreceiver;import java.text.SimpleDateFormat;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.telephony.SmsMessage;import android.telephony.gsm.SmsManager;import android.util.Log;public class SmsReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        // TODO Auto-generated method stub      Log.i("i", "已经接收到短信");            Bundle bundle = intent.getExtras();      Object[] objects = (Object[]) bundle.get("pdus");      for (Object object : objects) {        SmsMessage smsMessage = SmsMessage.createFromPdu((byte[])object);        //获取短信内容        String body = smsMessage.getDisplayMessageBody();        //获取来短信的电话号码        String addr = smsMessage.getDisplayOriginatingAddress();        //获取短信到来的时间        Long time = smsMessage.getTimestampMillis();        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        String date = sdf.format(time);        Log.i("i", "电话号码:"+addr);        Log.i("i", "短信内容: "+body);        Log.i("i","时间: "+ date);        if(addr.equals("5558")){            //中断广播,注意在配置文件里要设置好这个接收者的优先级为最高            abortBroadcast();            //拦截消息转发给第三方            android.telephony.SmsManager smsManager = android.telephony.SmsManager.getDefault();            smsManager.sendTextMessage("5554", null, addr + ","+ body +","+ date, null, null);        }    }  }}
SmsReceiver.java
技术分享
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.shellway.mysmsreceiver"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk        android:minSdkVersion="8"        android:targetSdkVersion="21" />    <uses-permission android:name="android.permission.RECEIVE_SMS"/>    <uses-permission android:name="android.permission.SEND_SMS"/>    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <activity            android:name=".MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>                <!--  这里使用了代码来实现广播接收者的注册来代替        <receiver android:name=".SmsReceiver">            <intent-filter android:priority="1000">                <action android:name="android.provider.Telephony.SMS_RECEIVED"></action>            </intent-filter>        </receiver>        -->                <receiver android:name=".PhoneReceiver">            <intent-filter android:priority="1000">                <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>            </intent-filter>        </receiver>    </application></manifest>
AndroidManifest.xml

运行结果截图:

技术分享技术分享

技术分享技术分享

练习2:自定义广播(这里演示自己接收自己发出的广播)

技术分享

技术分享
package com.shellway.customreceiver;import android.support.v7.app.ActionBarActivity;import android.content.Intent;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.EditText;public class MainActivity extends ActionBarActivity {    private EditText et_data;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //获取用户输入的数据        et_data =http://www.mamicode.com/ (EditText) findViewById(R.id.et_data);    }        public void send(View view){        String data = et_data.getText().toString();        //创建一个意图        Intent intent = new Intent(this,CustomReceiver.class);        //给我这个广播自定义一个动作名称        intent.setAction("com.shellway.CustomReceiver");        //往意图里封装数据        intent.putExtra("data", data);        //发送的是普通广播,不是有序广播        sendBroadcast(intent);    }}
MainActivity.java
技术分享
package com.shellway.customreceiver;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;public class CustomReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {                //接收广播过来的数据        String data = http://www.mamicode.com/intent.getStringExtra("data");        Log.i("i", data);    }}
CustomReceiver.java
技术分享
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    >    <TextView        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="要广播的数据:" />    <EditText         android:id="@+id/et_data"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:hint="请输入要广播发送的数据"        />    <Button         android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:onClick="send"        android:text="开始广播"        /></LinearLayout>
activity_main.xml
技术分享
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.shellway.customreceiver"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk        android:minSdkVersion="8"        android:targetSdkVersion="21" />        <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <activity            android:name=".MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <receiver android:name=".CustomReceiver">            <intent-filter>                <action android:name="com.shellway.CustomReceiver"></action>            </intent-filter>        </receiver>    </application></manifest>
AndroidManifest.xml

运行结果截图:

技术分享

二、service:服务
运行于后台,没有界面,对某件事情进行监听。它不能自己运行,必须手动激活。

当服务被创建的时候会调用一个onCreate()方法。

练习3:通过服务监听电话的呼叫状态

技术分享

技术分享
package com.shellway.myservice;import android.support.v7.app.ActionBarActivity;import android.telephony.TelephonyManager;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.view.View;public class MainActivity extends ActionBarActivity {        @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }        public void startService(View view){         //创建一个意图         Intent intent = new Intent(this,MyService.class);         //启动一个服务         startService(intent);    }}
MainActivity.java
技术分享
package com.shellway.myservice;import android.app.Service;import android.content.Context;import android.content.Intent;import android.os.IBinder;import android.telephony.PhoneStateListener;import android.telephony.TelephonyManager;import android.util.Log;public class MyService extends Service {    //定义一个系统默认的电话服务管理者    private TelephonyManager tm;    private MyPhoneStateListener listener;        //当服务被创建的时候调用该方法    @Override    public void onCreate() {        super.onCreate();        listener = new MyPhoneStateListener();        tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);        //监听电话的呼叫状态        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);    }    private final class MyPhoneStateListener extends PhoneStateListener{        @Override        public void onCallStateChanged(int state, String incomingNumber) {            // TODO Auto-generated method stub            super.onCallStateChanged(state, incomingNumber);                        switch (state) {            case TelephonyManager.CALL_STATE_IDLE://闲置状态                Log.i("i", "闲置状态");                Log.i("i", incomingNumber);                break;            case TelephonyManager.CALL_STATE_OFFHOOK://接听状态                Log.i("i", "接听状态");                break;            case TelephonyManager.CALL_STATE_RINGING://响铃状态                Log.i("i", "响铃状态");                break;            default:                break;            }        }    }        @Override    public IBinder onBind(Intent intent) {        return null;    }}
MyService.java
技术分享
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    >    <Button         android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:onClick="startService"        android:text="启动一个监听电话服务"        /></LinearLayout>
activity_main.xml
技术分享
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.shellway.myservice"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk        android:minSdkVersion="8"        android:targetSdkVersion="21" />    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <activity            android:name=".MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <service android:name=".MyService"></service>    </application></manifest>
AndroidManifest.xml

运行结果截图:

技术分享

技术分享

技术分享

startService()去启动服务。只能通过stopService()的方式去停止

onCreate() 只会被调用一次
onStart() 只要启动就会多次调用
onDestory() 只会被调用一次

bindService().
09-01 04:10:53.014: E/ActivityThread(5415):
Activity cn.itcast.service.MainActivity has leaked ServiceConnection cn.itcast.service.MainActivity$MyServiceConnection@44ee6248 that was
originally bound here

onCreate()只会被调用一次
onBind()只会被调用一次
onUnbind()只会被调用一次
onDestroy()只会被调用一次

如果访问者退出,需要把连接释放。
可以多次绑定,但是解绑服务只能执行一次,多次解绑就会出错。

服务里面能否执行耗时的操作?
服务里面是不能执行耗时的操作。如果需要执行耗时的操作:可以开启子线程来执行。

在开发的时候往往都需要和服务进行通信,就需要使用bindService()来启动服务。

调用服务里面的方法:
1 设计一个接口:IStudentQueryService Student queryStudent(int no);
2 在activity里面进行绑定操作:
bindService(intent,conn,flag);
3 写一个连接实现类:

private final class MyServiceConnection implements ServiceConnection{    public void onServiceConnected(ComponentName name, IBinder service) {        // TODO Auto-generated method stub        ibinder = (IStudnetQueryService) service;    }    public void onServiceDisconnected(ComponentName name) {        // TODO Auto-generated method stub        conn = null;        ibinder = null;    }}            

4 在activity里面用IStudentQueryService 来接收onServiceConnected(ComponentName name, IBinder service)
返回过来的IBinder对象。

5 写一个StudentService extends Service ,重写onBind()
编译一个内部类StudentBinder extends Binder implements IStudnetQueryService

6 创建StudentBinder的对象,通过onBind()方法返回。
7 在activity通过onBind()返回的对象调用服务里面的方法。

上面的操作是一个本地的服务。在开发的时候有可能还会去调用别人应用里面提供好的服务。
远程绑定服务,调用服务里面的方法。
1 编写一个接口,再把接口文件修改为aidl,不能有修饰符。
如果我们使用了自定义对象需要实现Parcelable接口,还需要定义个一个aidl文件对这个类进行一个描述(Student.aidl).
编译器就会在gen目录下面生成对应的xx.java文件
2 在服务里面创建一个内部类:extends Stub 实现未实现的方法。
生成这个类的对象通过onBind()方法返回。

3 在客户端绑定服务。
注意:

public void onServiceConnected(ComponentName name, IBinder service) {    // TODO Auto-generated method stub    ibinder = Stub.asInterface(service);}

 

注意啦:本章还没完善,后面会继续更新的。。。

 

java攻城狮之路(Android篇)--BroadcastReceiver&Service