您的位置:首页 > 其它

Binder机制5--- Binder实现进程管理服务示例

2014-08-20 16:51 375 查看
6. 用eclipse实现PMService

PMservice是一个通过Service服务,来实现任务管理的程序。分为客户端PMClient和服务端PMService。

PMService提供一些操作方法:

服务开始的提示方法:getVal();

任务管理器的查询方法:getProcessID() 获取进程号,和getProcessName()获取进程名;
以及终止进程的方法:killProc(String ID),来提供服务给客户端。

PMClient使用PMService所提供的服务,来调用这些方法实现业务逻辑,并提供显示界面。

对于PMService的实现

通过ActivityManager activityManager = (ActivityManager) getSystemService("activity"); 获得activityService
编写aidl文件,eclipse会自动生成相应的Stub和Proxy的接口实现

对于PMClient的实现

复制PMService的aidl文件,eclipse会为PMClient生成相应的接口实现
通过在ServiceConnection:: onServiceConnected()中,PMService = IPMService.Stub.asInterface(service); 获得PMService创建的 PMServiceProxy(new BinderProxy()); 并把这个proxy对象保存在PMService中
在onCreate()方法中,调用bindService(new Intent(IPMService.class.getName()),serConn, Context.BIND_AUTO_CREATE);

其中, serConn 是ServiceConnection类的实例化,传递的是PMService对象,这里是把当前类的PMService与PMService那边的PMService绑定在一起,这样就实现了两个进程的通信了

实现流程分析

调用的时候,客户端首先调用bindService(new Intent (IPMService.class.getName(), serConn,Context.BIND_AUTO_CREATE);激活serviceConnection的onServiceConnected方法,在此方法中获取到一个binder,这个binder是系统给我们的一个与远程进行通信的binder,此binder能够查找系统中注册的service,如果没有查找到该Service,那么可认定该service是从其他apk获得的,就创建一个此service的静态代理类Proxy,否则,就把这个service返回,供客户端调用。
服务端收到这个Intent后,激活PMServiceImpl extends IPMService.Stub的onBind方法,创建一个Binder返回 (return new PMServiceImpl())。之后,这个Binder负责与客户端的Proxy通信。

源码流程:

PMService的源码

在eclipse新建PMServer工程,我用的是android 4.2.2

先列出PMServer工程的文件清单,其中IPMService.java是通过IPMService.aidl自动创建的



下面是各个文件的源码:

IPMService.aidle

[plain]
view plaincopy

package com.example.pmserver;

interface IPMService
{
double getVal(String val);
List<String> getProcessName();
List<String> getProcessID();
String killProc(String PID);
}

把这个文件放入到我们工程的com.example.pmserver包中,系统会自动生成IPMService.java

为了实现进程信息的查询,我们需要CommandHelper.java这个类,通过API执行shell语言的方式来收集我们需要的进程信息。

CommandHelper.java

[java]
view plaincopy

package com.example.pmserver;

import com.example.pmserver.CommandResult;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
*
*
*/
public class CommandHelper {

//default time out, in millseconds
public static int DEFAULT_TIMEOUT;
public static final int DEFAULT_INTERVAL = 1000;
public static long START;

public static CommandResult exec(String command) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(command);
CommandResult commandResult = wait(process);
if (process != null) {
process.destroy();
}
return commandResult;
}

private static boolean isOverTime() {
return System.currentTimeMillis() - START >= DEFAULT_TIMEOUT;
}

private static CommandResult wait(Process process) throws InterruptedException, IOException {
BufferedReader errorStreamReader = null;
BufferedReader inputStreamReader = null;
try {
errorStreamReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
inputStreamReader = new BufferedReader(new InputStreamReader(process.getInputStream()));

//timeout control
START = System.currentTimeMillis();
boolean isFinished = false;

for (;;) {
if (isOverTime()) {
CommandResult result = new CommandResult();
result.setExitValue(CommandResult.EXIT_VALUE_TIMEOUT);
result.setOutput("Command process timeout");
return result;
}

if (isFinished) {
CommandResult result = new CommandResult();
result.setExitValue(process.waitFor());

//parse error info
if (errorStreamReader.ready()) {
StringBuilder buffer = new StringBuilder();
String line;
while ((line = errorStreamReader.readLine()) != null) {
buffer.append(line);
}
result.setError(buffer.toString());
}

//parse info
if (inputStreamReader.ready()) {
StringBuilder buffer = new StringBuilder();
String line;
while ((line = inputStreamReader.readLine()) != null) {
buffer.append(line);
}
result.setOutput(buffer.toString());
}
return result;
}

try {
isFinished = true;
process.exitValue();
} catch (IllegalThreadStateException e) {
// process hasn't finished yet
isFinished = false;
Thread.sleep(DEFAULT_INTERVAL);
}
}

} finally {
if (errorStreamReader != null) {
try {
errorStreamReader.close();
} catch (IOException e) {
}
}

if (inputStreamReader != null) {
try {
inputStreamReader.close();
} catch (IOException e) {
}
}
}
}
}

下面,需要提供一些操作的接口,以便调用
CommandResult.java

[java]
view plaincopy

package com.example.pmserver;

public class CommandResult {
public static final int EXIT_VALUE_TIMEOUT=-1;

private String output;

void setOutput(String error) {
output=error;
}

String getOutput(){
return output;
}

int exitValue;

void setExitValue(int value) {
exitValue=value;
}

int getExitValue(){
return exitValue;
}

private String error;

/**
* @return the error
*/
public String getError() {
return error;
}

/**
* @param error the error to set
*/
public void setError(String error) {
this.error = error;
}
}

接下来,就是我们Service的核心文件了,实现了业务逻辑。

PMService.java

[java]
view plaincopy

package com.example.pmserver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.example.pmserver.CommandHelper;
import com.example.pmserver.CommandResult;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Service;
import android.content.Intent;
import android.util.Log;

public class PMService extends Service {

private static final String TAG = "PMService";

List<String> ProcName = new ArrayList<String>();
List<String> ProcID = new ArrayList<String>();

public class PMServiceImpl extends IPMService.Stub {

@Override
public double getVal(String val) throws RemoteException {
Log.v(TAG, "getVal() called for " + val);
return 1.0; //test the binder transaction is ok between c/s
}

public List<String> getProcessName() {

List<RunningAppProcessInfo> procList = this.getProcessInfo(); //get process info
int j = 0;
Iterator<RunningAppProcessInfo> iterator = procList.iterator();

if(iterator.hasNext()) {
do {
RunningAppProcessInfo procInfo = iterator.next();

Log.v("ProcInfo", "ProcName = " + procInfo.processName);

ProcName.add(procInfo.processName);
//ProcID.add(Integer.toString(procInfo.pid));

Log.v("ProcName", "ProcName = " + ProcName.get(j++));

}while(iterator.hasNext());
}

return ProcName;
}

public List<String> getProcessID() {

List<RunningAppProcessInfo> procList = this.getProcessInfo();
int i = 0;
Iterator<RunningAppProcessInfo> iterator = procList.iterator();

if(iterator.hasNext()) {
do {
RunningAppProcessInfo procInfo = iterator.next();

Log.v("ProcInfo","ProcID = " + procInfo.pid);

ProcID.add(String.valueOf(procInfo.pid));
//ProcID.add(Integer.toString(procInfo.pid));

Log.v("ProcName", "ProcID = " + ProcID.get(i++));

}while(iterator.hasNext());
}

return ProcID;
}

@Override
public String killProc(String PID) throws RemoteException {
// TODO Auto-generated method stub
String cmd = "kill -9 "+PID;
String reply = "";

Log.v("cmd",cmd);
try {

CommandHelper.DEFAULT_TIMEOUT = 5000;
CommandResult result = CommandHelper.exec(cmd);
if (result != null) {
if(result.getError()!=null)
{
Log.v("Output","Error:" + result.getError());
reply = result.getError();
}
if(result.getOutput()!=null)
{
Log.v("Output","Output:" + result.getOutput());
reply = result.getOutput();
}
}

} catch (IOException ex) {
Log.v("Output","IOException:" + ex.getLocalizedMessage());
} catch (InterruptedException ex) {
Log.v("Output","InterruptedException:" + ex.getLocalizedMessage());
}
return reply;

}

public void exec(String command) throws IOException, InterruptedException {
// Process process = Runtime.getRuntime().exec(command);
Runtime.getRuntime().exec(command);
return ;
}

public List<RunningAppProcessInfo> getProcessInfo() {
List<RunningAppProcessInfo> procList = new ArrayList<RunningAppProcessInfo>();

// ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
ActivityManager activityManager = (ActivityManager) getSystemService("activity");
procList = activityManager.getRunningAppProcesses();

return procList;
}
}

@Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "onCreate called");
}

@Override
public void onDestroy() {
super.onDestroy();
Log.v(TAG, "onDestory() called");
}

@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.v(TAG, "onStart() called");
}

@Override
public IBinder onBind(Intent intent) {
Log.v(TAG, "onBind() called");
return new PMServiceImpl();
}

}

AndroidManifest.xml配置文件

[html]
view plaincopy

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

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<service
android:name="com.example.pmserver.PMService"
android:label="@string/app_name" >
<intent-filter>
<action android:name="com.example.pmserver.IPMService" />
</intent-filter>
</service>
</application>

</manifest>

Service这边不需要显示操作,界面的配置就不需要了。这样,Service这边就OK了。

PMClient的源码

这个是PMClient工程的文件清单,



IPMService.aidl直接从PMService中拷贝过来就好,这里就不列出了。同样,会自动生成IPMService.java

CommandHelper.java和CommandResult.java这里不需要(这里,我只是做测试之用),不需要添加,在com.example.pmclient中,只需添加PMClientActivity.java即可。

PMClientActivity.java

[java]
view plaincopy

package com.example.pmclient;

import com.example.pmserver.IPMService;
import com.example.pmclient.CommandHelper;
import com.example.pmclient.CommandResult;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.os.Bundle;
import android.os.IBinder;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class PMClientActivity extends Activity {

protected static final String TAG = "TestaidlClient";
private IPMService PMService = null;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.list_main);
bindService(new Intent(IPMService.class.getName()),serConn, Context.BIND_AUTO_CREATE);
}

public void onDestroy(){
super.onDestroy();
Log.v(TAG, "onDestory() called");
unbindService(serConn);
}

private void callService() {
try {
double val = PMService.getVal("Liang");
if(val == 1.0) {
Toast.makeText(this, "Service is ready!",
Toast.LENGTH_LONG).show();
}
setNameID(PMService.getProcessID(), PMService.getProcessName());

} catch (RemoteException e) {
Log.e("MainActivity", e.getMessage(), e);
}
}

private ServiceConnection serConn = new ServiceConnection() {
// 此方法在系统建立服务连接时调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v(TAG, "onServiceConnected() called");
PMService = IPMService.Stub.asInterface(service);
callService();
}

// 此方法在销毁服务连接时调用
@Override
public void onServiceDisconnected(ComponentName name) {
Log.v(TAG, "onServiceDisconnected()");
PMService = null;
}
};

public void setNameID(final List<String> ProcID, final List<String> ProcName) {
//绑定Layout里面的ListView
ListView list = (ListView) findViewById(R.id.ListView01);

//每个list里面放的都是MAP,map里面放的是键值对
ArrayList<Map<String, String>> Items = new ArrayList<Map<String, String>>();

//把该显示的内容放到list中
for (int i = 0; i < ProcID.size(); i++)
{
Map<String, String> item = new HashMap<String, String>();
String PIDbuf = "PID: "+ProcID.get(i);
item.put("ProcID", PIDbuf);
String PNamebuf = "PName: "+ProcName.get(i);
item.put("ProcName", PNamebuf);
Items.add(item);
}

//构建适配器Adapter,将数据与显示数据的布局页面绑定
final SimpleAdapter simpleAdapter = new SimpleAdapter(this, Items,
R.layout.list_proc_info, new String[]{ "ProcID", "ProcName" },
new int[]{ R.id.ProcID, R.id.ProcName});

//通过setAdapter()方法把适配器设置给ListView
list.setAdapter(simpleAdapter);

list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
String result = "";

Log.v("ClickInfo", "arg0 = " + arg0 + "arg1 = " + arg1 + " arg2 = " + arg2 +" arg3 = " + arg3);

//arg2 放的是process的name
try {
result = PMService.killProc(ProcID.get(arg2));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(result == null)
{
result = "success!";
}

ToastMSG(result);

//刷新页面
simpleAdapter.notifyDataSetChanged();
}
});

}

public void ToastMSG(String info)
{
Toast.makeText(this, "Info: " + info,
Toast.LENGTH_LONG).show();
}

}

再来看布局的设置,一共有2个布局文件,在res/layout/中:
list_main.xml

[html]
view plaincopy

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/LinearLayout01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<ListView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ListView01"
android:background="#A9A9A9"/>
</LinearLayout>

list_proc_info.xml

[html]
view plaincopy

<?xml version="1.0" encoding="utf-8"?>
<!-- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/ProcID"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:textSize="16dp"
android:gravity="center_vertical"
android:paddingLeft="10dp" />

<TextView android:id="@+id/ProcName"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:textSize="16dp"
android:gravity="center_vertical"
android:paddingLeft="10dp" />

</LinearLayout>
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>

<ImageView android:id="@+id/Procimg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5px"
android:src="@drawable/ic_launcher"/>

<LinearLayout android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<TextView android:id="@+id/ProcID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="40px" />
<TextView android:id="@+id/ProcName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="30px" />

</LinearLayout>

</LinearLayout>

最后,是配置文件:
AndroidManifest.xml

[html]
view plaincopy

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.pmclient"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17"
android:sharedUserId="android.uid.system" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.pmclient.PMClientActivity"
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>

关于布局文件的字段名(@XXX),这里没有给出对应的配置文件,因为太简单了!请大家自行设置。

大功告成了!上效果图!





界面比较简单,单击对应的进程即可杀掉进程。这里由于Service的权限不够,导致进程不能结束。另外,杀掉进程后(可以尝试杀掉PMService自己,界面会自刷新)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: