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
- package com.example.pmserver;
- interface IPMService
- {
- double getVal(String val);
- List<String> getProcessName();
- List<String> getProcessID();
- String killProc(String PID);
- }
爲了實現進程信息的查詢,我們需要CommandHelper.java這個類,通過API執行shell語言的方式來收集我們需要的進程信息。
CommandHelper.java
- 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
- 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
- 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配置文件
- <?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
- 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
- <?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
- <?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
- <?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自己,界面會自刷新)。
原文:http://blog.csdn.net/bathinbreeze/article/details/8994825