前面几篇总结了进程、线程相关的知识。这里总结下关于Android中的多进程、多线程及其使用。

这里总结的Android中的多进程、多线程也是一个基础,可扩展的很多。

Android中多进程

常见的几种使用

Runtime.getRuntime().exec("xxx")

这个方法,调用程序外的 脚本或命令程序,它会生成一个新的进程去调用 返回一个Process对象。

如:windows下,调用记事本。

Runtime.getRuntime().exec("notepad.exe");

linux下(Android)下,调用系统本身的ps命令后,通过返回的Process对象的输入流 读取了调用内容。

try {
String[] commandStr = new String[] {"/bin/sh","-c", "ps -ef"};
//String commandStr = "/bin/sh -c ps";
Process process1 = Runtime.getRuntime().exec( commandStr );
Log.d( TAG, "onCreate: process1=" + process1 );
byte [] data = new byte[1024];
int len = 0;
while( -1 != (len = process1.getInputStream().read(data)) ) {
String str = new String(data, 0, len , "UTF-8");
Log.d( TAG, "onCreate: \n" + str );
}
} catch (IOException e) {
e.printStackTrace();
}

执行后,通过log,很容易看出,Runtime.getRuntime().exec("xxx")调用就是新建的进程。

ProcessBuilder("xxx").start()

这个方法同样可以调用程序外的 脚本或命令程序。

类似Runtime.getRuntime().exec(),如:window下打开计算器。

new ProcessBuilder("calc.exe").start();

llinux:

try {
String[] commandStr = new String[] {"/bin/sh","-c", "ps -ef"};
ProcessBuilder processBuilder = new ProcessBuilder(commandStr);
processBuilder.redirectErrorStream( true );
Process process2 = processBuilder.start();
Log.d( TAG, "onCreate: process2=" + process2 + ";processBuilder.directory="+processBuilder.directory());
byte [] data = new byte[1024];
int len = 0;
while( -1 != (len = process2.getInputStream().read(data)) ) {
String str = new String(data, 0, len , "UTF-8");
Log.d( TAG, "onCreate: \n" + str );
}
} catch (IOException e) {
e.printStackTrace();
}

注:

Runtime.getRuntime().exec(param)和 ProcessBuilder(param).start()关联

通过源码跟踪,很容易看到Runtime.getRuntime().exec()最终也是调用的ProcessBuilder().start()来实现的。

所以它们很多类似,都是创建一个新的进程来执行程序外的脚本或命令程序,并返回Process实例,通过实例可以获取进程信息及控制进程状态。

传递参数有所不同,Runtime.getRuntime().exec()可以是单独字符串(用空格分隔可执行命令程序和参数,如例子中注释的那条),也可以是字符串数组。ProcessBuilder().start()只能是字符串数组或集合,同样第一个参数是可执行命令程序。

android:process标签

应用的AndroidManifest.xml的清单文件中,可以通过设置android:process标签 实现多进程。

默认,同一应用所有组件运行相同进程中,进程名即包名。

支持

四大组件(Activity,Service,ContentProvider,BroadcastReceive)都支持android:process标签,组件元素-(<activity>、<service>、<receiver> 和 <provider>),通过此属性指定组件在哪个进程中运行。

<application>也支持android:process标签,它只设置所有组件默认运行进程的默认值。

进程名(属性值)

如果以冒号(“:”)开头,创建的 则是应用的私有进程。

如配置android:process=":myprocess1",包名是com.android.test,则实际进程名即com.android.test:myprocess1。

如果不是以冒号而是以小写字母开头,则是全局进程,只要有权限即可访问。不同应用的组件可以共享该进程。

其他

进程内存限制

如下两个重要的值,如果超过,则会出现OOM。通过 adb shell getprop | grep xxx(如adb shell getprop | grep dalvik可以看到很多配置,不仅仅下面两个 )可以查询到相应手机中的配置,不同手机可能是不同的。

dalvik.vm.heapsize ---单个dalvik虚拟机最大内存

dalvik.vm.heapgrowthlimit ---单个进程的最大内存

Android中进程间通信

进程间通信总结中提到比较全面,可以参考下。//TODO

系统中也存在很多包括Activity,Service,Broadcast,ContentProvider都有这样的实现。

比较常用,需要了解和掌握的:Bundle(序列化,四大组件常用),AIDL,Messenger,ContentProvider,Socket

Android中多线程

主线程

当应用启动后,系统即会为应用创建一个线程---“main”,即主线程。主线程,也称为UI线程,它负责事件的分派,包括绘制事件。

一般所有组件都在主线程中实例化。

当耗时操作(如网络访问或数据库操作)时就会阻塞主线程,会导致事件无法分派,包括绘制事件,甚至5s ANR。

工作线程

保证应用界面的响应能力,关键是不能阻塞界面线程。如果执行的操作不能即时完成,则应确保它们在单独的线程中运行。这个单独的线程即工作线程。

注意:

1.不要阻塞主线程,即耗时操作不要放在主线程中。

2.只有主线程可以更新UI,其他所有线程都无法更新UI。

从其他线程进入主线

由于第二点,系统提供了从其他线程进入主线程的几种方法:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

例如

public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
// a potentially time consuming task
final Bitmap bitmap =
processBitMap("image.png");
imageView.post(new Runnable() {
public void run() {
imageView.setImageBitmap(bitmap);
}
});
}
}).start();
}

但是,随着操作日趋复杂,这类代码也会变得复杂且难以维护。

解决://TODO

1.若通过工作线程完成复杂交互,考虑在工作线程中使用Handler处理来自主线程的消息

2.扩展AsyncTask类。AsyncTask通过异步通信和消息传递,将工作线程中的结果传递到主线程,更新相关UI操作。

线程安全

先看下面的例子,onCreate中开启了10个线程,调用plusOne方法,对num进行加1处理。

private static final String TAG = "ProcessThread";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
for (int i = 0; i < 5; i++) {
new Thread_thread().start();
new Thread( runnable ).start();
}
} int num = 0;
//继承Thread类
private class Thread_thread extends Thread {
@Override
public void run() {
plusOne();
}
} //实现Runnable接口
private Runnable runnable = new Runnable() {
@Override
public void run() {
plusOne();
}
}; private void plusOne() {
num++;
Log.d( TAG, "num=" + num );
}

打印的log如下:

2020-05-28 10:58:12.731 9531-9567/com.flx.process_thread D/ProcessThread: num=1
2020-05-28 10:58:12.741 9531-9568/com.flx.process_thread D/ProcessThread: num=3
2020-05-28 10:58:12.741 9531-9570/com.flx.process_thread D/ProcessThread: num=3
2020-05-28 10:58:12.744 9531-9569/com.flx.process_thread D/ProcessThread: num=4
2020-05-28 10:58:12.761 9531-9571/com.flx.process_thread D/ProcessThread: num=5
2020-05-28 10:58:12.762 9531-9572/com.flx.process_thread D/ProcessThread: num=6
2020-05-28 10:58:12.787 9531-9574/com.flx.process_thread D/ProcessThread: num=7
2020-05-28 10:58:12.791 9531-9573/com.flx.process_thread D/ProcessThread: num=8
2020-05-28 10:58:12.801 9531-9575/com.flx.process_thread D/ProcessThread: num=9
2020-05-28 10:58:12.802 9531-9576/com.flx.process_thread D/ProcessThread: num=10

从log看到num值存在重复。像上述的情况,可能是两个线程同时操作了num,操作时num都是一样的。这种情况就是线程不安全的。

线程安全就是,多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。

上述例子中,分别使用了创建线程常用的两种方法:

继承Thread类 实现Runnable接口

实现线程安全常用方法

synchronized关键字

如下,应该都很熟悉,不做解释。

private synchronized void plusOne() {
num++;
Log.d( TAG, "num=" + num );
}

Lock锁

ReentrantLock是Lock的一个子类。

如下示例,一般使用,unlock放在finally中,执行方法体放在try中。

private final ReentrantLock lock = new ReentrantLock();
private void plusOne() {
lock.lock();
try {
num++;
Log.d( TAG, "num=" + num );
}finally {
lock.unlock();
}
}

注:

volatile关键字

volatile保证了不同线程对某个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

volatile不能保证原子性,因此不能保证线程安全

常用进程线程信息获取

//当前进程ID,用户ID
Log.d( TAG, "onCreate: currPID=" + android.os.Process.myPid()
  + ";currUID=" + android.os.Process.myUid() );
//当前线程ID。下面两种方法获取的值是不一样的。
//第一个是系统级的,系统分配管理;第二个是java级的,Thread管理的。由于java跨平台
Log.d( TAG, "onCreate: currTid=" + android.os.Process.myTid()
  + ";currTid2=" + Thread.currentThread().getId() );
//主线程ID
Log.d( TAG, "onCreate: mainTid=" + Looper.getMainLooper().getThread().getId() );

打印情况:

2020-05-28 10:58:12.703 9531-9531/com.flx.process_thread D/ProcessThread: onCreate: currPID=9531;currUID=10133
2020-05-28 10:58:12.704 9531-9531/com.flx.process_thread D/ProcessThread: onCreate: currTid=9531;currTid2=2
2020-05-28 10:58:12.704 9531-9531/com.flx.process_thread D/ProcessThread: onCreate: mainTid=2

Android中的多进程、多线程的更多相关文章

  1. Android学习记录(6)—将java中的多线程下载移植到Android中(即多线程下载在Android中的使用)③

    在这一节中,我们就来讲多线程下载以及断点续传在android中怎么使用,前两节是为本节做准备的,没有看前两节的同学,最好看完前面的两篇文章再来看这篇.其实在android端的应用和java基本上是差不 ...

  2. Android中Sqlite数据库多线程并发问题

    最近在做一个Android项目, 为了改善用户体验,把原先必须让用户“等待”的过程改成在新线程中异步执行.但是这样做遇到了多个线程同时需要写Sqlite数据库,导致操作数据库失败. 本人对Java并不 ...

  3. 解决Android中AsyncTask的多线程阻塞问题

    Android开发中执行耗时操作并更新UI时,通常有三种方式:1.直接调用runOnUiThread(new Runnable(){}),使用简单,但不能在Activity之外的环境使用,如View. ...

  4. Android DevArt5:如何在Android中创建多线程?

    本篇内容: 如何在Android中创建多进程?查看进程的三种方式有哪些? 多进程模式的运行机制?- 演示了多进程出现问题中的两种情况: 静态成员失效 Application多次创建 IPC基础概念介绍 ...

  5. Android中多线程下载列表的封装实现(含进度反馈)

    来源:http://blog.csdn.net/u011638883/article/details/17347015 实现了一下Android中的文件多线程下载模块,支持自定义线程数.断点续传.下载 ...

  6. Python中的多进程与多线程(一)

    一.背景 最近在Azkaban的测试工作中,需要在测试环境下模拟线上的调度场景进行稳定性测试.故而重操python旧业,通过python编写脚本来构造类似线上的调度场景.在脚本编写过程中,碰到这样一个 ...

  7. Android中的多线程断点下载

    首先来看一下多线程下载的原理.多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序"拼接"起来就 ...

  8. 深入解析PHP中的(伪)多线程与多进程

    本篇文章是对PHP中的(伪)多线程与多进程进行了详细的分析介绍,需要的朋友参考下 (伪)多线程:借助外力利用WEB服务器本身的多线程来处理,从WEB服务器多次调用我们需要实现多线程的程序.QUOTE: ...

  9. [Android学习笔记]Android中多线程开发的一些概念

    线程安全: 在多线程的情况下,不会因为线程之间的操作而导致数据错误. 线程同步: 同一个资源,可能在同一时间被多个线程操作,这样会导致数据错误.这是一个现象,也是一个问题,而研究如何解决此类问题的相关 ...

随机推荐

  1. 无向图求割(找桥)tarjan

    本博客参考了李煜东的<算法竞赛进阶指南>,大家要是觉得这篇文章写的不错请大家支持正版.豆瓣图书 我在之前的博客中讲解了搜索序时间戳,这次我们讲讲追溯值的概念. 追溯值: 设subtree( ...

  2. OSG程序设计之Hello World1.0

    对于从未接触过OSG的我来说,首先需要一个入门教程.在OSG论坛逛了半天,再加上google,最终决定使用<OSG程序设计>这本书. 下面就贴出书中的第一个例子:Hello World. ...

  3. java读源码 之 list源码分析(ArrayList)---JDK1.8

    java基础 之 list源码分析(ArrayList) ArrayList: 继承关系分析: public class ArrayList<E> extends AbstractList ...

  4. spring学习笔记(八)webSocket

    知识储备 什么是stomp? 我们可以类比TCP与Http协议,我们知道Http协议是基于TCP协议的,Http协议解决了 web 浏览器发起请求以及 web 服务器响应请求的细节,我们在编码时候只要 ...

  5. 完了!CPU一味求快出事儿了!

    自我介绍 我叫阿Q,是CPU一号车间里的员工,我所在的这个CPU足足有8个核,就有8个车间,干起活来杠杠滴. 我所在的一号车间里,除了负责执行指令的我,还有负责取指令的小A,负责分析指令的小胖和负责结 ...

  6. 多线程高并发编程(8) -- Fork/Join源码分析

    一.概念 Fork/Join就是将一个大任务分解(fork)成许多个独立的小任务,然后多线程并行去处理这些小任务,每个小任务处理完得到结果再进行合并(join)得到最终的结果. 流程:任务继承Recu ...

  7. 构建自己的专用OpenCV----记录一次由applyColorMap()引发的探索

    在编写实际项目的过程中,我需要实现绿色主题的"伪彩色"变换.在目前提供的模板中,只有summer最为接近,但是它的颜色太浅了,看上去不是很清晰.所以我结合ocean和summer两 ...

  8. spring-boot+spring-cloud+maven-module 一个 maven多模块的微服务架构模版

    spring-boot-cloud-module spring-boot+spring-cloud+maven-module 一个 maven多模块的微服务架构模版. 新手上路的绝佳模版,只有必要的配 ...

  9. HDU 2006 (水)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2006 题目大意:给你几个数,求奇数的乘积和 解题思路: 很水,不需要数组的,一个变量 x 就行 代码: ...

  10. Winform GDI+绘图二:绘制旋转太极图

    大家好,今天有时间给大家带来Winform自绘控件的第二部分,也是比较有意思的一个控件:旋转太极图. 大家可以停下思考一下,如果让你来绘制旋转的太极图,大家有什么样的思路呢?我今天跟大家展示一下,我平 ...