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

运行结果截图:

练习4:在通话的同时刻录音频

package com.shellway.myservice;

import java.io.File;
import java.io.IOException;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;

public class MyService extends Service {

    //定义一个系统默认的电话服务管理者
    private TelephonyManager tm = null;
    private MyPhoneStateListener listener;
    //声明一个音频刻录机
    private MediaRecorder mr;
    
    //当服务被创建的时候调用该方法
    @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{

        String fileName ;
        @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);
                if (mr!=null) {
                    mr.stop();
                    mr.reset();
                    mr.release();
                    mr = null;
                }
                if (fileName!=null) {
                    //这里可以写一些上传录制的音频到服务器
                    //默认只能刻录自己的声音,若要刻录对方声音就要对底层进行修改
                    Log.i("i", fileName);
                }
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK://接听状态
                Log.i("i", "接听状态");
                mr = new MediaRecorder();
                //1、设置刻录的音频来源,来自麦克风
                mr.setAudioSource(MediaRecorder.AudioSource.MIC);
                //2、设置所刻录的音频输出格式
                mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
                //3、设置输出数据的编码
                mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                File file = new File(Environment.getExternalStorageDirectory(),
                                      System.currentTimeMillis()+".3gp");
                //获取文件名,如果在刻录完成后可以把它上传到服务器,就要用到 httpClient 3.0
                 fileName = file.getName();
                //4、设置输出的路径
                mr.setOutputFile(file.getAbsolutePath());
                
                try {
                    //准备刻录
                    mr.prepare();
                    //开始刻录
                    mr.start();
                } catch (IllegalStateException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                break;
            case TelephonyManager.CALL_STATE_RINGING://响铃状态
                Log.i("i", "响铃状态");
                break;
            default:
                break;
            }
        }
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();

    }
}
在练习3项目的MyService类中添加录音的代码

同时添加录音权限和外存储权限:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

运行结果截图:

Service的生命周期:

如果用startService()去启动服务。只能通过stopService()的方式去停止,且:
onCreate() 只会被调用一次
onStart() 只要启动就会多次调用
onDestory() 只会被调用一次

如果用bindService()去启动(绑定)服务:
onCreate() 只会被调用一次
onBind() 只会被调用一次
onUnbind() 只会被调用一次
onDestroy() 只会被调用一次
注意:
如果访问者退出,需要把连接释放。
可以多次绑定,但是解绑服务只能执行一次,多次解绑就会出错
用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

服务里面能否执行耗时的操作?
答:服务里面是不能执行耗时的操作。如果需要执行耗时的操作:可以开启子线程来执行。
在开发的时候往往都需要和服务进行通信,就需要使用bindService()来启动服务。

Service的生命周期练习:

package com.shellway.servicelifecycle;

import android.support.v7.app.ActionBarActivity;
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;

public class MainActivity extends ActionBarActivity {
    private MyServiceConnection conn = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    //用startService方式开启一个服务
    public void startService(View view){
        Intent intent = new Intent(this,ServiceLifeCycle.class);
        startService(intent);
    }
    //用startService方式开启一个服务,只能用stopService()去停止这个服务
    public void stopService(View view){
        Intent intent = new Intent(this,ServiceLifeCycle.class);
        stopService(intent);
    }
    
    //绑定服务,即用bindService()开启一个服务
    public void bindService(View view){
        Intent intent = new Intent(this,ServiceLifeCycle.class);
        bindService(intent, conn,  BIND_AUTO_CREATE);
    }
    
    public void unbindService(View view){
        //解绑服务
        unbindService(conn);
        conn = null;
    }
    
    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub
        }
    }
    
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        if (conn!=null) {
            //解绑服务
            unbindService(conn);
            conn = null;
        }
    }
}
MainActivity.java
package com.shellway.servicelifecycle;

import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;

public class ServiceLifeCycle extends Service {

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        Log.i("i", " onCreate ");
    }
    
    @Override
    public void onStart(Intent intent, int startId) {
        // TODO Auto-generated method stub
        super.onStart(intent, startId);
        Log.i("i", " onStart ");
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        Log.i("i", " onBind ");
        return null;
    }
    
    @Override
    public void unbindService(ServiceConnection conn) {
        // TODO Auto-generated method stub
        super.unbindService(conn);
        Log.i("i", " unbindService ");
    }
    
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        Log.i("i", " onDestroy ");
    }
}
ServiceLifeCycle.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:text="启动服务startService"
        android:onClick="startService"
        />
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止服务stopService"
        android:onClick="stopService"
        />
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务bindService"
        android:onClick="bindService"
        />
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解绑服务unbindService"
        android:onClick="unbindService"
        />

</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shellway.servicelifecycle"
    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>
        <service android:name=".ServiceLifeCycle"></service>
    </application>

</manifest>
AndroidMainfest.xml

运行结果截图:

如何调用服务里面的方法?步骤:
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()返回的对象调用服务里面的方法。

练习:访问本地服务的方法

package com.shellway.callservicemethods;

import com.shellway.callservicemethods.domain.Student;
import com.shellway.callservicemethods.imp.IStudentService;

import android.support.v7.app.ActionBarActivity;
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.EditText;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {
    private MyServiceConnection conn;
    private EditText et_id;
    private IStudentService istu;
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_id = (EditText) findViewById(R.id.et_id);
        tv = (TextView) findViewById(R.id.tv_show);
        //创建一个绑定服务的连接
        conn = new MyServiceConnection();
        //应用一启动就绑定一个服务
        Intent intent = new Intent(this,CallServiceMethods.class);
        bindService(intent, conn,  BIND_AUTO_CREATE);
    }
    
    //点击查询按钮操作
    public void query(View view){
        //获得用户输入的数据
        String id = et_id.getText().toString();
        //调用本地服务里面的方法
        Student stu = istu.getStudent(Integer.valueOf(id));
        //把返回数据设置到界面
        tv.setText(stu.toString());
    }
    
    private class MyServiceConnection implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            //因为在服务中接口的实现类继承了IBinder,所以可以把它通过Ibinder这个通道返回
            istu = (IStudentService) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub
            conn = null;
            istu = null;
        }
    }
    
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        //当界面退出时记得解绑
        if (conn!=null) {
            unbindService(conn);
            conn=null;
        }
    }
}
MainActivity.java
package com.shellway.callservicemethods;

import com.shellway.callservicemethods.domain.Student;
import com.shellway.callservicemethods.imp.IStudentService;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class CallServiceMethods extends Service {
    //定义一个我们自定义的接口的实现类
    private MyStudentService iBinder = new MyStudentService();
    //模拟一个学生信息数据库
    Student[] students = new Student[]{new Student(),new Student(1,"韩信",67),
            new Student(2,"蒙恬",58),new Student(3,"颜路",25),new Student(4,"张良",28)};
    
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return iBinder;
    }
    
    private class MyStudentService extends Binder implements IStudentService{

        @Override
        public Student getStudent(int id) {
            // TODO Auto-generated method stub
            return reStudent(id);
        }
    }
    //通过传过来的学生ID获得学生信息,这里是被调用的服务里面的方法
    public Student reStudent(int id){
        return students[id];
    }
}
CallServiceMethods.java
package com.shellway.callservicemethods.domain;

public class Student {
    private int id;
    private String name;
    private int age;
    
    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }
    
    public Student(int id, String name, int age) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
    }
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
    }
}
Student.java
package com.shellway.callservicemethods.imp;

import com.shellway.callservicemethods.domain.Student;

public interface IStudentService {
    public Student getStudent(int id);
}
IStudentService.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_id"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="query"
        android:text="查询"
        />
    <TextView 
        android:id="@+id/tv_show"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        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.callservicemethods"
    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>
        <service android:name=".CallServiceMethods"></service>
    </application>

</manifest>
AndroidManifest.xml

运行结果截图:

上面的操作是一个本地的服务。在开发的时候有可能还会去调用别人应用里面提供好的服务。

远程绑定服务,调用服务里面的方法。这里一两个名词:IPC(Inter-Process Communication,进程间通信)和AIDL
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);
}

 练习:远程服务的绑定,即访问远程服务的方法。

服务端:注意运行的时候先启动服务端

package com.shellway.callremoteservice;

import android.support.v7.app.ActionBarActivity;
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);
    }

}
MainActivity.java
package com.shellway.callremoteservice;

import com.shellway.callremoteservice.domain.Student;
import com.shellway.callremoteservice.imp.IStudentService.Stub;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class CallRemoteService extends Service {
    //自定义一个类继承Stub
    private MyStudentService iBinder = new MyStudentService();
    //模拟虚拟数据
    Student[] students = new Student[]{new Student(1,"荆轲",67),new Student(2,"端慕容",58),
                                       new Student(3,"高渐离",25),new Student(4,"高月",28)};
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        //通过ononBind()返回自定义类的对象
        return iBinder;
    }
    
    private class MyStudentService extends Stub{
        @Override
        public Student getStudent(int id) throws RemoteException {
            // TODO Auto-generated method stub
            return reStudent(id);
        }
    }
    
    //获取数据
    public  Student reStudent(int id){
        return students[id-1];
    }
}
CallRemoteService.java
package com.shellway.callremoteservice.domain;

import android.os.Parcel;
import android.os.Parcelable;

public class Student implements Parcelable {
    private int id;
    private String name;
    private int age;
    
    public Student(int id, String name, int age) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
    }


    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        dest.writeInt(id);
        dest.writeString(name);
        dest.writeInt(age);
    }
    
     public static final Parcelable.Creator<Student> CREATOR
         = new Parcelable.Creator<Student>() {
     public Student createFromParcel(Parcel in) {
         return new Student(in);
     }
    
     public Student[] newArray(int size) {
         return new Student[size];
     }
    };
   
    public Student(Parcel in){
        id = in.readInt();
        name = in.readString();
        age = in.readInt();
    }
    
    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
    }
   
}
Student.java
package com.shellway.callremoteservice.domain;
parcelable Student;
Student.aidl
package com.shellway.callremoteservice.imp;
import com.shellway.callremoteservice.domain.Student;

interface IStudentService {
 Student getStudent(int id);
}
IStudentService.aidl
<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.shellway.callremoteservice.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shellway.callremoteservice"
    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>
        <!-- 这里的service要给它一个action,好让它对外提供接口 -->
        <service android:name=".CallRemoteService">
            <intent-filter >
                <action android:name="com.shellway.callremoteservice.service"/>
            </intent-filter>
        </service>
    </application>

</manifest>
AndroidManifest.xml

客户端:注意这里面的另外两个包中的类都要从服务端拷贝过来,所以客户端就不贴一样的代码了。

package com.shellway.callremoteservicemethods;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import com.shellway.callremoteservice.domain.Student;
import com.shellway.callremoteservice.imp.IStudentService;
import com.shellway.callremoteservice.imp.IStudentService.Stub;

public class MainActivity extends ActionBarActivity {
    private IStudentService iBinder;
    private MyServiceConnection conn;
    private EditText et_id;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_id = (EditText) findViewById(R.id.et_id);
        tv = (TextView) findViewById(R.id.tv_show);
        conn = new MyServiceConnection();
        //程序一开始就要绑定一个远程服务
        Intent intent = new Intent();
        intent.setAction("com.shellway.callremoteservice.service");
        bindService(intent, conn, BIND_AUTO_CREATE);
    }
   
    public void query(View view){
        String id = et_id.getText().toString();
        try {
            //这里是调用远程服务中的方法,即自己先前在服务端定义接口实现类,
            //然后New出一个对象并传过来,通过它来调用里面服务里面的方法
            Student stu = iBinder.getStudent(Integer.valueOf(id));
            tv.setText(stu.toString());
        } catch (NumberFormatException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    private class MyServiceConnection implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            //这里是得到远程服务中,自己先前在服务端定义接口实现类的对象
            iBinder = Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub
            
        }
    }
    
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        unbindService(conn);
    }
}
MainActivity.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_id"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="query"
        android:text="查询"
        />
    <TextView 
        android:id="@+id/tv_show"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        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.callremoteservicemethods"
    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>
    </application>

</manifest>
AndroidManifest.xml

运行效果截图:

练习:调用Android底层已经实现的挂断电话endcall()方法,这里也用到了调用远程服务里方法知识

/* //device/java/android/android/content/Intent.aidl
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

package android.telephony;

parcelable NeighboringCellInfo;
NeighboringCellInfo.aidl
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony;

import android.os.Bundle;
import java.util.List;
import android.telephony.NeighboringCellInfo;

/**
 * Interface used to interact with the phone.  Mostly this is used by the
 * TelephonyManager class.  A few places are still using this directly.
 * Please clean them up if possible and use TelephonyManager insteadl.
 *
 * {@hide}
 */
interface ITelephony {

    /**
     * Dial a number. This doesn't place the call. It displays
     * the Dialer screen.
     * @param number the number to be dialed. If null, this
     * would display the Dialer screen with no number pre-filled.
     */
    void dial(String number);

    /**
     * Place a call to the specified number.
     * @param number the number to be called.
     */
    void call(String number);

    /**
     * If there is currently a call in progress, show the call screen.
     * The DTMF dialpad may or may not be visible initially, depending on
     * whether it was up when the user last exited the InCallScreen.
     *
     * @return true if the call screen was shown.
     */
    boolean showCallScreen();

    /**
     * Variation of showCallScreen() that also specifies whether the
     * DTMF dialpad should be initially visible when the InCallScreen
     * comes up.
     *
     * @param showDialpad if true, make the dialpad visible initially,
     *                    otherwise hide the dialpad initially.
     * @return true if the call screen was shown.
     *
     * @see showCallScreen
     */
    boolean showCallScreenWithDialpad(boolean showDialpad);

    /**
     * End call if there is a call in progress, otherwise does nothing.
     *
     * @return whether it hung up
     */
    boolean endCall();

    /**
     * Answer the currently-ringing call.
     *
     * If there's already a current active call, that call will be
     * automatically put on hold.  If both lines are currently in use, the
     * current active call will be ended.
     *
     * TODO: provide a flag to let the caller specify what policy to use
     * if both lines are in use.  (The current behavior is hardwired to
     * "answer incoming, end ongoing", which is how the CALL button
     * is specced to behave.)
     *
     * TODO: this should be a oneway call (especially since it's called
     * directly from the key queue thread).
     */
    void answerRingingCall();

    /**
     * Silence the ringer if an incoming call is currently ringing.
     * (If vibrating, stop the vibrator also.)
     *
     * It's safe to call this if the ringer has already been silenced, or
     * even if there's no incoming call.  (If so, this method will do nothing.)
     *
     * TODO: this should be a oneway call too (see above).
     *       (Actually *all* the methods here that return void can
     *       probably be oneway.)
     */
    void silenceRinger();

    /**
     * Check if we are in either an active or holding call
     * @return true if the phone state is OFFHOOK.
     */
    boolean isOffhook();

    /**
     * Check if an incoming phone call is ringing or call waiting.
     * @return true if the phone state is RINGING.
     */
    boolean isRinging();

    /**
     * Check if the phone is idle.
     * @return true if the phone state is IDLE.
     */
    boolean isIdle();

    /**
     * Check to see if the radio is on or not.
     * @return returns true if the radio is on.
     */
    boolean isRadioOn();

    /**
     * Check if the SIM pin lock is enabled.
     * @return true if the SIM pin lock is enabled.
     */
    boolean isSimPinEnabled();

    /**
     * Cancels the missed calls notification.
     */
    void cancelMissedCallsNotification();

    /**
     * Supply a pin to unlock the SIM.  Blocks until a result is determined.
     * @param pin The pin to check.
     * @return whether the operation was a success.
     */
    boolean supplyPin(String pin);

    /**
     * Supply puk to unlock the SIM and set SIM pin to new pin.
     *  Blocks until a result is determined.
     * @param puk The puk to check.
     *        pin The new pin to be set in SIM
     * @return whether the operation was a success.
     */
    boolean supplyPuk(String puk, String pin);

    /**
     * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
     * without SEND (so <code>dial</code> is not appropriate).
     *
     * @param dialString the MMI command to be executed.
     * @return true if MMI command is executed.
     */
    boolean handlePinMmi(String dialString);

    /**
     * Toggles the radio on or off.
     */
    void toggleRadioOnOff();

    /**
     * Set the radio to on or off
     */
    boolean setRadio(boolean turnOn);

    /**
     * Request to update location information in service state
     */
    void updateServiceLocation();

    /**
     * Enable location update notifications.
     */
    void enableLocationUpdates();

    /**
     * Disable location update notifications.
     */
    void disableLocationUpdates();

    /**
     * Enable a specific APN type.
     */
    int enableApnType(String type);

    /**
     * Disable a specific APN type.
     */
    int disableApnType(String type);

    /**
     * Allow mobile data connections.
     */
    boolean enableDataConnectivity();

    /**
     * Disallow mobile data connections.
     */
    boolean disableDataConnectivity();

    /**
     * Report whether data connectivity is possible.
     */
    boolean isDataConnectivityPossible();

    Bundle getCellLocation();

    /**
     * Returns the neighboring cell information of the device.
     */
    List<NeighboringCellInfo> getNeighboringCellInfo();

     int getCallState();
     int getDataActivity();
     int getDataState();

    /**
     * Returns the current active phone type as integer.
     * Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE
     * and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE
     */
    int getActivePhoneType();

    /**
     * Returns the CDMA ERI icon index to display
     */
    int getCdmaEriIconIndex();

    /**
     * Returns the CDMA ERI icon mode,
     * 0 - ON
     * 1 - FLASHING
     */
    int getCdmaEriIconMode();

    /**
     * Returns the CDMA ERI text,
     */
    String getCdmaEriText();

    /**
     * Returns true if OTA service provisioning needs to run.
     * Only relevant on some technologies, others will always
     * return false.
     */
    boolean needsOtaServiceProvisioning();

    /**
      * Returns the unread count of voicemails
      */
    int getVoiceMessageCount();

    /**
      * Returns the network type
      */
    int getNetworkType();

    /**
     * Return true if an ICC card is present
     */
    boolean hasIccCard();

    /**
     * Return if the current radio is LTE on CDMA. This
     * is a tri-state return value as for a period of time
     * the mode may be unknown.
     *
     * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
     * or {@link PHone#LTE_ON_CDMA_TRUE}
     */
    int getLteOnCdmaMode();
}
ITelephony.aidl
package com.shellway.endcall;

import android.support.v7.app.ActionBarActivity;
import android.content.Intent;
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);
        //这里我们让应用一启动就开始一个服务
        Intent intent = new Intent(this,EndCallService.class);
        startService(intent);
    }
}
MainActivity.java
package com.shellway.endcall;

import java.lang.reflect.Method;
import com.android.internal.telephony.ITelephony;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.provider.CallLog.Calls;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

public class EndCallService extends Service {
    private TelephonyManager tm;
    private MyPhoneStateListener listener;
    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        listener = new MyPhoneStateListener();
        tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
    
    private 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://闲置状态
                
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK://通话状态
                
                break;
            case TelephonyManager.CALL_STATE_RINGING://响铃状态
            /**
             * Android里面所有的底层服务都是交给了一个类ServiceManager来管理,我们可以用Everything工具来找到这个类
             * 这个类里面有IBinder getService(String name),void addService(String name,IBinder service) 
             * 这里我们要想得到Android给我们提供好的挂断电话方法endCall(),就得先得到它给我们返回的IBinder对象
             * 然后把它转成ITelephoneService
             */
                endcall(incomingNumber);
                
                break;

            default:
                break;
            }
        }
        
        public void endcall(String incomingNumber){
            if(incomingNumber.equals("5558")){
                try {
                    //这里为了搞到ServiceManager里的getService返回给我们一个IBinder对象,用了反射实现
                    Class clazz = Class.forName("android.os.ServiceManager");
                    Method method = clazz.getMethod("getService", String.class);
                    //这个是代理对象,我们还要对它进行转化
                    IBinder service = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);
                    ITelephony ibinder = ITelephony.Stub.asInterface(service);
                    ibinder.endCall();
                    //注册一个内容提供者
                    getContentResolver().registerContentObserver(Calls.CONTENT_URI, true, 
                                                      new MyContentObserver(incomingNumber));
                    
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        
        private class MyContentObserver extends ContentObserver{
            private String incomingNumber;
            public MyContentObserver(String incomingNumber) {
                super(new Handler());
                // TODO Auto-generated constructor stub
                this.incomingNumber = incomingNumber;
            }
            
            @Override
            public void onChange(boolean selfChange) {
                // TODO Auto-generated method stub
                super.onChange(selfChange);
                //删除通话记录    通过记录的存储是一个异步的操作
                Uri uri = Calls.CONTENT_URI;  
                getContentResolver().delete(uri, "number = ?", new String[]{"5558"});
                //取消内容观察者
                getContentResolver().unregisterContentObserver(this);
            }
        }
    }
}
EndCallService.java
<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.shellway.endcall.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shellway.endcall"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />
    <!-- 监听电话状态的权限 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <!-- 挂断电话需要同打电话一样的权限 -->
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <!-- 读、写通话记录的权限 -->
    <uses-permission android:name="android.permission.READ_CALL_LOG"/>
    <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
    <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=".EndCallService"></service>
    </application>

</manifest>
AndroidManifest.xml

 

转载于:https://www.cnblogs.com/shellway/p/4175715.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值