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

四大組件:
activity 顯示。
contentProvider 對外暴露本身的數據給其餘的應用程序。
BroadcastReceiver 廣播接收者,必須指定要接收的廣播類型。必須明確的指定action
service 服務,是運行後臺,它是沒有界面的。對某件事情進行監聽。java

1、廣播:事件。
普通廣播: 是異步的。會廣播接收者同時接收,不能被中斷
sendBroadcast()
有序廣播: 是同步的。會根據廣播接收的優先級進行接收,是能夠中斷 短信到來廣播
sendOrderBroadcast()
-1000 ~ 1000
若是有序廣播明確的指定了廣播接收者,他是沒法被中斷的。
廣播接收者的訂閱:
1 在清單文件裏面指定android

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

2 在代碼裏面指定shell

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

當廣播接收者一旦接收到廣播,它會執行裏面的onReceive()方法,可是裏面是不能執行耗時的操做,這個方式若是在10s以內沒有執行完畢,就會出現anr異常,也就是應用無響應異常數據庫

練習1:利用廣播攔截短信、攔截外撥電話、增長IP撥號功能express

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

運行結果截圖:apache

練習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

運行結果截圖:app

2、service:服務
運行於後臺,沒有界面,對某件事情進行監聽。它不能本身運行,必須手動激活。less

當服務被建立的時候會調用一個onCreate()方法。dom

練習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();
                //一、設置刻錄的音頻來源,來自麥克風
                mr.setAudioSource(MediaRecorder.AudioSource.MIC);
                //二、設置所刻錄的音頻輸出格式
                mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
                //三、設置輸出數據的編碼
                mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                File file = new File(Environment.getExternalStorageDirectory(),
                                      System.currentTimeMillis()+".3gp");
                //獲取文件名,若是在刻錄完成後能夠把它上傳到服務器,就要用到 httpClient 3.0
                 fileName = file.getName();
                //四、設置輸出的路徑
                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
相關文章
相關標籤/搜索