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

 

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

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。