当前位置: 首页 > news >正文

[Java][Android][Process] 暴力的服务能够解决一切,暴力的方式运行命令行语句

不管是在Java或者Android中运行命令行语句殊途同归都是创建一个子进程运行调用可运行文件运行命令。类似于Windows中的CMD一样。

此时你有两种方式运行:ProcessBuilder与Runtime;两种创建方式各有千秋,至于差别详见:[Java][Android][Process] ProcessBuilder与Runtime差别

在Android中创建子进程运行命令的时候有着一定的限制:

1.JVM提供的内存有限。

2.底层缓冲区间大小有限。

3.在高并发情况下easy造成堵塞。

基于上几点在运行命令行时我们不得不慎重操作。不能随便创建。

在上一篇文章中我提到:[Java][Android][Process] Process 创建+控制+分析 经验浅谈 了一些我的管理方式已经对实现的分析;其归根结底为:创建一个子进程的时候同一时候创建一个线程用于读取输出的流信息。在返回后及时退出。图示:



通过以上的控制方式能有效的解决掉底层ProcessManager线程死掉情况(出现等待IO缓冲区情况),当此线程死掉后子进程也将进入等待且永不退出。通过这种情况能达到运行上万条命令不出现故障。可是经过我半个月的观察发现其并非最稳定的方式,当程序中还有非常多其它IO操作如(文件读写。网络请求等)出现的时候;我运行到接近2万条命令行后出现了相同的问题。

查询后得出。尽管我们启动了线程来读取多余的流数据。可是当线程非常多或者请求非常多的时候线程可能来不及马上启动,而此时IO却已经满了,那么将会进入IO临界区等待,也就是说线程事实上不是万能的。庆幸的是在Android中有这样一种机制:服务

Android服务是什么?我不多说百度一下就知道!

如今来说说我的新思路:

首先咱们创建一个服务,并设置服务为独立进程:android:process=".command.CommandService"

然后把实现部分放在我们开启的服务中。使用一个类来控制服务启动以及调用服务做任务,其任务就是把上面的部分放在服务中控制实现,如图:


这样看来是不是没有差别?事实上不然,差别已经来了,第一因为是独立进程的服务。所以会有独立的JVM空间,同一时候该进程的IO与主线程IO不是同一个(逻辑上);所以假设在主进程中操作其它的流并不影响独立进程的流。

而且有一个特别重要的情况:当独立服务进程出现死掉(IO)等待情况,这时服务中的守护进程将会自己主动杀掉自己。然后等待又一次启动后继续运行任务。


实现代码:

public CommandServiceImpl() {
            //线程初始化
            thread = new Thread(CommandServiceImpl.class.getName()) {
                @Override
                public void run() {
                    while (thread == this && !this.isInterrupted()) {
                        if (commandExecutors != null && commandExecutors.size() > 0) {
                            lock.lock();
                            LogUtil.i(TAG, "Executors Size:" + commandExecutors.size());
                            for (CommandExecutor executor : commandExecutors) {
                                if (executor.isTimeOut())
                                    try {
                                        killSelf();
                                    } catch (RemoteException e) {
                                        e.printStackTrace();
                                    }
                                if (thread != this && this.isInterrupted())
                                    break;
                            }
                            lock.unlock();
                        }
                        try {
                            Thread.sleep(10000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            thread.setDaemon(true);
            thread.start();
        }

<span style="white-space:pre">	</span>/**
         * 杀掉自己
         *
         * @throws RemoteException
         */
        @Override
        public void killSelf() throws RemoteException {
            android.os.Process.killProcess(android.os.Process.myPid());
        }
        /**
         * 运行命令
         *
         * @param params 命令
         * @return 结果
         * @throws RemoteException
         */
        @Override
        public String command(String params) throws RemoteException {
            CommandExecutor executor = CommandExecutor.create(params);
            lock.lock();
            commandExecutors.add(executor);
            lock.unlock();
            String result = executor.getResult();
            lock.lock();
            commandExecutors.remove(executor);
            lock.unlock();
            return result;
        }

此时因为服务杀掉自己没法在内存中保存当前队列任务,那任务是否就丢弃掉呢?这肯定是不同意的,我们没法在服务中控制可是能够在控制任务的:


代码例如以下:

package net.qiujuer.libraries.genius.command;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;

import net.qiujuer.libraries.genius.journal.LogUtil;
import net.qiujuer.libraries.genius.utils.GlobalValue;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by Genius on 2014/8/13.
 * 命令运行Model
 */
public class CommandModel {
    private static final String TAG = CommandModel.class.getName();
    //调用服务接口
    private static ICommandInterface iService = null;
    //服务链接类,用于实例化服务接口
    private static ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iLock.lock();
            iService = ICommandInterface.Stub.asInterface(service);
            if (iService != null) {
                try {
                    iCondition.signalAll();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else
                bindService();
            iLock.unlock();
            LogUtil.i(TAG, "onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iService = null;
            LogUtil.i(TAG, "onServiceDisconnected");
        }
    };
    //锁
    private static Lock iLock = new ReentrantLock();
    //等待与唤醒
    private static Condition iCondition = iLock.newCondition();

    //运行參数
    private String parameter;
    //是否取消測试
    private boolean isCancel;

    /**
     * 实例化
     *
     * @param params @param params 命令參数 eg: "/system/bin/ping", "-c", "4", "-s", "100","www.qiujuer.net"
     */
    public CommandModel(String... params) {
        //check params
        if (params == null)
            throw new NullPointerException();
        //run
        StringBuilder sb = new StringBuilder();
        for (String str : params) {
            sb.append(str);
            sb.append(" ");
        }
        this.parameter = sb.toString();
    }

    /**
     * 运行測试
     *
     * @param model ProcessModel
     * @return 结果
     */
    public static String command(CommandModel model) {
        //检測是否取消測试
        if (model.isCancel)
            return null;
        //check Service
        if (iService == null) {
            iLock.lock();
            try {
                iCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            iLock.unlock();
        }

        String result;
        try {
            result = iService.command(model.parameter);
        } catch (Exception e) {
            e.printStackTrace();
            bindService();
            result = command(model);
        }
        return result;
    }

    /**
     * 启动并绑定服务
     */
    private static void bindService() {
        Context context = GlobalValue.getContext();
        Intent intent = new Intent(context, CommandService.class);
        context.startService(intent);
        context.bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    /**
     * 取消測试
     */
    public void cancel() {
        isCancel = true;
    }

    /**
     * 静态初始化
     */
    static {
        bindService();
    }

}
当中:

    public static String command(CommandModel model) {
        //检測是否取消測试
        if (model.isCancel)
            return null;
        //check Service
        if (iService == null) {
            iLock.lock();
            try {
                iCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            iLock.unlock();
        }

        String result;
        try {
            result = iService.command(model.parameter);
        } catch (Exception e) {
            e.printStackTrace();
            bindService();
            result = command(model);
        }
        return result;
    }
採用回调,就是为了完毕任务运行的方法!



独立进程服务接口:ICommandInterface.aidl

interface ICommandInterface {
     void killSelf();
     String command(String params);
}
命令运行者(服务中的实际功能实现):CommandExecutor.java

package net.qiujuer.libraries.genius.command;

import net.qiujuer.libraries.genius.journal.LogUtil;
import net.qiujuer.libraries.genius.utils.ToolUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by Genius on 2014/8/13.
 * 命令行运行命令
 */
class CommandExecutor {
    private static final String TAG = CommandExecutor.class.getName();
    //换行符
    private static final String BREAK_LINE;
    //错误缓冲
    private static final byte[] BUFFER;
    //缓冲区大小
    private static final int BUFFER_LENGTH;
    //创建进程时须要相互排斥进行
    private static final Lock LOCK = new ReentrantLock();
    //不能超过1分钟
    private static final long TIMEOUT = 60000;
    //ProcessBuilder
    private static ProcessBuilder PRC;

    final private Process process;
    final private InputStream in;
    final private InputStream err;
    final private OutputStream out;
    final private StringBuilder sbReader;

    private BufferedReader bInReader = null;
    private InputStreamReader isInReader = null;
    private boolean isDone;
    private long startTime;

    /**
     * 静态变量初始化
     */
    static {
        BREAK_LINE = "\n";
        BUFFER_LENGTH = 128;
        BUFFER = new byte[BUFFER_LENGTH];

        LOCK.lock();
        PRC = new ProcessBuilder();
        LOCK.unlock();
    }


    /**
     * 实例化一个CommandExecutor
     *
     * @param process Process
     */
    private CommandExecutor(Process process) {
        //init
        this.startTime = System.currentTimeMillis();
        this.process = process;
        //get
        out = process.getOutputStream();
        in = process.getInputStream();
        err = process.getErrorStream();

        //in
        if (in != null) {
            isInReader = new InputStreamReader(in);
            bInReader = new BufferedReader(isInReader, BUFFER_LENGTH);
        }

        sbReader = new StringBuilder();

        //start read thread
        Thread processThread = new Thread(TAG) {
            @Override
            public void run() {
                startRead();
            }
        };
        processThread.setDaemon(true);
        processThread.start();
    }

    /**
     * 运行命令
     *
     * @param param 命令參数 eg: "/system/bin/ping -c 4 -s 100 www.qiujuer.net"
     */
    protected static CommandExecutor create(String param) {
        String[] params = param.split(" ");
        CommandExecutor processModel = null;
        try {
            LOCK.lock();
            Process process = PRC.command(params)
                    .redirectErrorStream(true)
                    .start();
            processModel = new CommandExecutor(process);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //sleep 100
            ToolUtil.sleepIgnoreInterrupt(100);
            LOCK.unlock();
        }
        return processModel;
    }


    /**
     * 获取是否超时
     *
     * @return 是否超时
     */
    protected boolean isTimeOut() {
        return ((System.currentTimeMillis() - startTime) >= TIMEOUT);
    }

    //读取结果
    private void read() {
        String str;
        //read In
        try {
            while ((str = bInReader.readLine()) != null) {
                sbReader.append(str);
                sbReader.append(BREAK_LINE);
            }
        } catch (Exception e) {
            String err = e.getMessage();
            if (err != null && err.length() > 0) {
                LogUtil.e(TAG, "Read Exception:" + err);
            }
        }
    }

    /**
     * 启动线程进行异步读取结果
     */
    private void startRead() {
        //while to end
        while (true) {
            try {
                process.exitValue();
                //read last
                read();
                break;
            } catch (IllegalThreadStateException e) {
                read();
            }
            ToolUtil.sleepIgnoreInterrupt(50);
        }

        //read end
        int len;
        if (in != null) {
            try {
                while ((len = in.read(BUFFER)) > 0) {
                    LogUtil.d(TAG, "Read End:" + len);
                }
            } catch (IOException e) {
                String err = e.getMessage();
                if (err != null && err.length() > 0)
                    LogUtil.e(TAG, "Read Thread IOException:" + err);
            }
        }

        //close
        close();
        //destroy
        destroy();
        //done
        isDone = true;

    }

    /**
     * 获取运行结果
     *
     * @return 结果
     */
    protected String getResult() {
        //until startRead en
        while (!isDone) {
            ToolUtil.sleepIgnoreInterrupt(200);
        }

        //return
        if (sbReader.length() == 0)
            return null;
        else
            return sbReader.toString();
    }

    /**
     * 关闭全部流
     */
    private void close() {
        //close out
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //err
        if (err != null) {
            try {
                err.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //in
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (isInReader != null) {
            try {
                isInReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bInReader != null) {
            try {
                bInReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 销毁
     */
    private void destroy() {
        String str = process.toString();
        try {
            int i = str.indexOf("=") + 1;
            int j = str.indexOf("]");
            str = str.substring(i, j);
            int pid = Integer.parseInt(str);
            try {
                android.os.Process.killProcess(pid);
            } catch (Exception e) {
                try {
                    process.destroy();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

好了本次採用暴力方式快要结束了,对于运行命令參数能够说是比較完美的运行方式了,採用这种方式我运行Ping測试达到10万次,并发达到500个任务同一时候运行没有问题。測试的设备为:魅族M9.

本次的代码我已经打包到自己的类库中。并开源到GitHub。地址:Genius-Android
希望大家多多迁移我的项目。该类库中还带有一个自己开发的完整的日志系统,以后还会增加其它东西,比方图片处理相关(模糊等)



相关文章:

  • 【Service专题】
  • C#套接字和windowsAPI套接字
  • 【ContentProvider专题】
  • 2.4-saltstack pillar
  • 【BroadcastReceiver专题】
  • 计算机存储形式与进制转换
  • 【hadler机制】
  • 【Mongo】uploadify插件帮助实现批量上传
  • 【view的绘制流程】
  • AOS V0.9 发布,JavaEE 应用基础平台
  • 【View事件分发机制 】
  • 闭包--循序学习
  • 【MVCMVPMVVM】
  • 【内存泄露】
  • 默认input=file样式美化的bug及解决
  • python3.6+scrapy+mysql 爬虫实战
  • [NodeJS] 关于Buffer
  • Angular 2 DI - IoC DI - 1
  • C++类的相互关联
  • canvas绘制圆角头像
  • chrome扩展demo1-小时钟
  • ES6核心特性
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • PAT A1120
  • windows下mongoDB的环境配置
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 收藏好这篇,别再只说“数据劫持”了
  • 一道闭包题引发的思考
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • 阿里云API、SDK和CLI应用实践方案
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • ​渐进式Web应用PWA的未来
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • (1)Nginx简介和安装教程
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (AngularJS)Angular 控制器之间通信初探
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (Redis使用系列) SpringBoot 中对应2.0.x版本的Redis配置 一
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (算法)Game
  • (未解决)macOS matplotlib 中文是方框
  • *Django中的Ajax 纯js的书写样式1
  • .NET MVC之AOP
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 WeakDictionary)
  • .Net6使用WebSocket与前端进行通信
  • .net下的富文本编辑器FCKeditor的配置方法
  • :中兴通讯为何成功
  • [ vulhub漏洞复现篇 ] Django SQL注入漏洞复现 CVE-2021-35042
  • [@Controller]4 详解@ModelAttribute
  • [20150904]exp slow.txt
  • [C++]priority_queue的介绍及模拟实现
  • [Excel] vlookup函数