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 = (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 = 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 [email protected] 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);
}

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

时间: 2024-09-30 14:11:01

java攻城狮之路(Android篇)--BroadcastReceiver&Service的相关文章

java攻城狮之路(Android篇)--Activity生命

1 写一个类 extends Activity Activity是android的四大组件之一. Activity的激活分为显式意图激活和隐式意图激活. 如果一个activity希望别人隐式意图激活:则要配置过滤器1 action = ""2 有一个缺省的category,(可以有多个category),若不配置该类型会发生这样的一个错误:Caused by: android.content.ActivityNotFoundException: No Activity found to

java攻城狮之路(Android篇)--ListView与ContentProvider

一.ListView 1.三种Adapter构建ListView ListView添加条目的时候, 可以使用setAdapter(ListAdapter)方法, 常用的ListAdapter有三种 BaseAdapter: 定义一个类继承BaseAdapter, 重写4个抽象方法, ListView的条目是由getView()方法构建出来的 SimpleAdapter: 创建SimpleAdapter对象时, 传入数据(List<Map<String, ?>>), 并指定数据的绑定

java攻城狮之路(Android篇)--与服务器交互

一.图片查看器和网页源码查看器 在输入地址的是不能输入127.0.0.1 或者是 localhost.ScrollView :可以看成一个滚轴 可以去包裹很多的控件在里面 练习1(图片查看器): package com.shellway.imagelooker; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedUR

java攻城狮之路(Android篇)--MP3、MP4、拍照、国际化、样式主题、图片移动和缩放

一.MP3播放器 查看Android API文档可以看到MediaPlayer状态转换图: 练习: package com.shellway.mp3player; import java.io.File; import java.io.IOException; import android.support.v7.app.ActionBarActivity; import android.telephony.PhoneStateListener; import android.telephony.

java攻城狮之路(Android篇)--SQLite

一.Junit    1.怎么使用        在AndroidManifest.xml文件中进行配置, 在manifest借点下配置instrumentation, 在application借点下配置uses-library        定义类继承AndroidTestCast        定义测试方法, Run As JunitTest        如果需要判断测试结果, 可以使用Assert.assertEquals()方法. 下面是利用独立的测试工程JunitTest来测试工程J

java攻城师之路(Android篇)--搭建开发环境、拨打电话、发送短信、布局例子

一.搭建开发环境 1.所需资源 JDK6以上 Eclipse3.6以上 SDK17, 2.3.3 ADT17 2.安装注意事项 不要使用中文路径 如果模拟器默认路径包含中文, 可以设置android_sdk_home环境变量解决.效果如下: 二.拨打电话 1.步骤 在Button节点中添加onClick属性, 指定一个方法名 在Activity中定义一个public void 方法名 (View view) 获取文本框中的号码 创建意图, 设置动作, 设置数据 使用意图开启Activity 2.

java攻城狮之路--复习JDBC

1.JDBC中如何获取数据库链接Connection? Driver 是一个接口: 数据库厂商必须提供实现的接口. 能从其中获取数据库连接. 可以通过 Driver 的实现类对象获取数据库连接. 1. 加入 mysql 驱动 1). 解压 mysql-connector-java-5.1.7.zip 2). 在当前项目下新建 lib 目录 3). 把 mysql-connector-java-5.1.7-bin.jar 复制到 lib 目录下 4). 右键 build-path , add to

java攻城狮之路--复习xml&amp;dom_pull编程续

本章节我们要学习XML三种解析方式: 1.JAXP DOM 解析2.JAXP SAX 解析3.XML PULL 进行 STAX 解析 XML 技术主要企业应用1.存储和传输数据 2.作为框架的配置文件 使用xml 存储和传输数据1.通过程序生成xml2.读取xml 中数据 ---- xml 解析 什么是DOM和SAX ?DOM Document Object Model ----- 文档对象模型 DOM思想: 将整个xml 加载内存中,形成文档对象,所有对xml操作都对内存中文档对象进行DOM

java攻城狮之路--复习xml&amp;dom_pull编程

xml&dom_pull编程: 1.去掉欢迎弹窗界面:在window项的preferences选项中输入“configuration center” 找到这一项然后     把复选框勾去即可. 2.去掉打开Myeclipse时弹出的:Please allow Subclipse team to receive......办法: Windows-->Preferences-->General-->Startup and Shutdown-->取消Subclipse Usage