Android中进程与线程
常说的主线程(UI线程)是什么?
当一个Android程序刚启动的时候,我们的android系统就会启动一个带有一个单一线程的linux进程。默认情况下,所有的组件比如Activity都运行在同样的一个进程和线程当中,这个线程就叫做主线程或者UI线程。也就是说,默认情况下,app启动的时候会创建一个线程,这个线程就叫做主线程。因为大部分功能是进行UI上的操作,所有也叫做UI线程。
关于为什么叫主线程请参考:Android 主线程之旅——PSVM(public static void main。
让你的组件运行在一个新的进程
一般情况下,同一个Android程序里的所有应用都运行在一个进程当中。但是如果你有需要,你可以在manifest文件当中组件入口配置处进行设置,activity, service, receiver, provider都支持用android:process来设置运行的进程。你也在application里设置全局的android:process属性。
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:process="com.example.yangqiangyu.processandthread1"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Main2Activity"
android:label="@string/title_activity_main2"
android:theme="@style/AppTheme.NoActionBar" android:process="com.example.yangqiangyu.processandthread2">
</activity>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this,Main2Activity.class));
}
}).show();
}
});
}
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActivityManager mActivityManager =(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> list = mActivityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo info:list){
Log.d("process", info.processName);
}
}
指定了两个Activity的process属性,Main进入Main2,在Main2中输出了运行的所有进程信息:
11-15 23:35:23.454 22628-22628/com.example.yangqiangyu.processandthread2 D/process: com.example.yangqiangyu.processandthread2
11-15 23:35:23.454 22628-22628/com.example.yangqiangyu.processandthread2 D/process: com.example.yangqiangyu.processandthread1
11-15 23:35:23.454 22628-22628/com.example.yangqiangyu.processandthread2 D/process: com.android.launcher
可以看到的确存在了我们设置的进程。
进程的优先级
我们知道,Android系统会根据需要移除一些进程。比如当系统内存不足的时候,会把运行的某些Android程序干掉。那它又是按什么规则去干掉合适的程序呢?这就是进程的优先级。根据进程中的应用组件的重要性要判断干掉哪些程序,让哪些程序继续运行。
下面是官网列出的5个级别,越往后优先级越低,越容易被干掉:
1、前台进程
一般来说,在同一时刻只有一个前台进程存在,前台进程拥有最高的优先级,所以除非在特殊的情况下,比如内存不足完全不能运行程序的时候才会被干掉。(如果你没有遇到过,说明你不是和我一样用的百元Android机,哈哈)下面的情况都被认为是一个前台进程:
进程中有一个正在和用户交互的Activity时,也就是该Activity调用了onResume方法。
进程中含有一个与正在和用户交互的Activity绑定了的Service
进程中含有一个运行”在前台的” Service —–即该Service调用了startForeground()方法。
进程中含有一个调用了 onCreate(), onStart(), 或者onDestroy()方法中的任意一个方法的Service。
进程中含有一个调用了onReceive()方法的BroadcastReceiver。
2、可见的进程
即使一个进程没有前台组件,但是如果它能够影响到用户所看到的界面,它就是可见的进程。可见的进程依然很重要,它只有在必需干掉它才能维持前台进程存在的情况下才会被干掉。老大需要资源,做小弟的能不让吗?下面的情况都被认为是一个可见进程:
当我们学习生命周期的时候打开一个dialog的情况。此时之前的Activity所在的进程就是可见的进程。
进程中含有一个绑定到一个可见的、或者前台Activity的Service。
3、Service进程
和名字一样,就是一个进程当中有Service在运行的情况,也就是调用了startService() 方法。进程Service没有关联任何东西,用户看不见摸不着。但是它们所做的事情仍然重要(比如在后台放音乐,下数据)。所有系统会在只有内存不足以维持以上两个进程的时候干掉它。那就是老三。
4、后台进程
进程中含有当前不可见的Activity(即调用了onStop()方法),这些进程对用户没太大影响,所以系统可以在任何系统资源不足、前三种需要资源的时候干掉它。但是一般情况下,后台进程都会存在,并且维持着不可见的Activity信息。
5、空进程
一个没有任何活跃的组件的进程就是空进程,它存在的目的是为了缓存。比如让下次启动组件的需要的时间更快一点。最低优先级,没人权,不说了。
当上面的列举的情况存在多个的时候,以情况优先级最高的为准。也就是说,当一个进程中存在前台Activity,又有一个运行在后台的Service时,就是前台进程。
线程
当我们的Android应用启动的时候,系统就会创建一个默认的线程,就叫做主线程。关于为什么叫主线程请参考:Android 主线程之旅——PSVM(public static void main。
它管理着我们用户界面怎么‘画‘出来,当我们点击屏幕的时候,事件如何分发。所以主线程也叫做UI线程(以前虽然这么叫,但是我不知道为啥)。
由于我们的主线程要做那么多事情,如果此时我们又在它当中做耗时任务,比如网络请求或者数据库查询。就可能会导致主线程阻塞。当主线程阻塞之后,就不能进行事件分发等它本来应该做的事情了。如果阻塞超过5秒,就会出现弹ANR(“application not responding” ) dialog的情况了。
Android Ui线程并不是线程安全的,所以不能在其他线程(工作线程)里操作Ui,你只能在主线程是操作你的UI。
总结:
不能阻塞UI线程
不能非主线程的其他外部线程进行UI操作。
工作线程
由于上面描述的原因,我们在Android中耗时任务必需放在工作线程当中,下面参照一个官网的写个类似的例子,关键代码如下
public void loadImage(View view) {
switch (view.getId()){
case R.id.loadImage:
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = loadImageFromNetwork("http://e.hiphotos.baidu.com/image/pic/item/b2de9c82d158ccbf0881c1d01dd8bc3eb135411e.jpg");
imageView.setBitmap(bitmap);
break;
}
}
private Bitmap loadImageFromNetwork(String imageUrl) {
URL imgUrl = null;
Bitmap bitmap = null;
try {
imgUrl = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection)imgUrl.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
is.close();
} catch (MalformedURLException e) {
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
return bitmap;
}
布局文件很简单,一个按钮一个ImageView,在点击按钮的时候在一个新的线程中去执行网络加载,这符合了上面总结的第一点不能阻塞UI线程,我们运行项目后点击按钮发现闪退了,报错信息如下:
Only the original thread that created a view hierarchy can touch its views.
也就是我们之前总结的的第二点,由于主线程不是线程安全的,不能非主线程的其他外部线程进行UI操作。
为了解决上面的问题,Android给我们提供了几种在其他线程中获取主线程的方式:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
我们可以将上面的imageView.setImageBitmap(bitmap)设置图片改成
image.post(Runnable)
image.postDelayed(Runnable, long)
runOnUiThread(Runnable)的任何一种方式。
修改之后运行,你发现可以正在的获取图片并且显示在ui 界面上了。比如:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap bitmap =
loadImageFromNetwork("http://example.com/image.png");
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
}
}).start();
}
这样就实现了网络在其他线程,而UI操作在主线程了。
Handler和AsyncTask的作用
在上面的代码中,用一个ImageView的 View.post(Runnable)方法,虽然实现了功能,但是如果每个View的操作都要这么写的话,那我们的代码不就太多太难维护了。然而Handler却可以让复杂的UI线程与主线程的交互变得简单。你只需要简单的在其他线程当中用handler发送消息。而AsyncTask也让你能够在合适的地方进行耗时操作,在合适的地方进行UI操作。比如我们可以将上面的代码用handler来处理,整个类的代码如下:
public class MainActivity extends AppCompatActivity {
private ImageView imageView;
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
switch (message.what){
case 1000:
imageView.setImageBitmap((Bitmap) message.obj);
break;
}
return true;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
imageView = (ImageView) findViewById(R.id.imageView);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private Bitmap loadImageFromNetwork(String imageUrl) {
URL imgUrl = null;
Bitmap bitmap = null;
try {
imgUrl = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection)imgUrl.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
is.close();
} catch (MalformedURLException e) {
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
return bitmap;
}
public void loadImage(View view) {
switch (view.getId()){
case R.id.loadImage:
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = loadImageFromNetwork("http://e.hiphotos.baidu.com/image/pic/item/b2de9c82d158ccbf0881c1d01dd8bc3eb135411e.jpg");
Message message = new Message();
message.what = 1000;
message.obj = bitmap;
handler.sendMessage(message);
}
}).start();
break;
}
}
}
在执行耗时任务的线程中用handler发送了一条消息,然后在handler的handleMessage方法里面进行了UI操作。
相信你到这里对进程与线程、主线程是什么,为什么不能在其他线程进行UI操作,为什么不能在主线程进行耗时任务等等,关于Handler和AsyncTask的将会在之后具体介绍!
如果觉得对你有用,点个赞或者留个言支持一下,如果有错误请提出,因为我也是一个正在学习的菜鸟。
Android中进程与线程的更多相关文章
- Android中进程与线程及如何在子线程中操作UI线程
1. Android进程 一个应用程序被启动时,系统默认创建执行一个叫做"main"的线程.这个线程也是你的应用与界面工具包(android.widget和android.view ...
- 第11讲- Android中进程及其优先级
第11讲Android中进程及其优先级 进程与线程: 进程:操作系统结构的基础,资源分配的最小单元,一个操作系统包括多个进程: 线程:线程存在于进程当中,是操作系统调试执行的最小单元,一个进程包括多个 ...
- android中进程的优先级
android中进程的优先级
- Android 的进程和线程
进程和线程 如果某个应用程序组件是第一次被启动,且这时应用程序也没有其他组件在运行,则android系统会为应用程序创建一个包含单个线程的linux进程.默认情况下,同一个应用程序的所有组件都运行在同 ...
- Android的进程和线程(转)
进程和线程 当一个应用程序第一次启动的时候,Android会启动一个Linux进程和一个主线程(即UI线程:主要负责处理用户的按键事件.触屏事件及屏幕绘图事件等).默认情况下,所有该程序的组件都将在该 ...
- Android中使用Thread线程与AsyncTask异步任务的区别
最近和几个朋友交流Android开发中的网络下载问题时,谈到了用Thread开启下载线程时会产生的Bug,其实直接用子线程开启下载任务的确是很Low的做法,那么原因究竟如何,而比较高大上的做法是怎样? ...
- 转:Android 的进程与线程总结
当一个Android应用程序组件启动时候,如果此时这个程序的其他组件没有正在运行,那么系统会为这个程序 以单一线程的形式启动一个新的Linux 进程. 默认情况下,同一应用程序下的所有组件都运行再相同 ...
- 关于Java中进程和线程的详解
一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...
- java中进程与线程的三种实现方式
一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是一个线程. 进程:进程是指 ...
随机推荐
- linux常用
一.命令 1.查指令,man, info, /usr/share/doc/: 2.文档,nano lyp.txt: 3.谁在线,who: 4.数据同步写入硬盘,sync: 5.显示档案,ls: 6.目 ...
- Maven进价:Maven构建系列文章
Maven:基于Java平台的项目构建.依赖管理和项目信息管理. 1.构建 Maven标准化了构建过程 构建过程:编译.运行单元测试.生成文档.打包和部署 避免重复:设计.编码.文档.构建 2.依赖管 ...
- java模拟一个简单的QQ
v 项目源码 https://github.com/hjzgg/java_QQ v 标题效果 package testFour; import java.awt.Color; import ...
- 二项堆(二)之 C++的实现
概要 上一章介绍了二项堆的基本概念,并通过C语言实现了二项堆.本章是二项堆的C++实现. 目录1. 二项树的介绍2. 二项堆的介绍3. 二项堆的基本操作4. 二项堆的C++实现(完整源码)5. 二项堆 ...
- 3.12----对potplayer的使用评价
每一台电脑,除了最底层的操作的系统之外,对用户最重要的组成部分应该就是各类应用程序,毕竟人和计算机之间的交互说到底都是通过各类应用的来完成的,人通过利用这些应用程序来把各种问题转化为计算机可以理解的0 ...
- AngularJS 五大特性,加快 Web 应用开发
AngularJS 是谷歌的一个 JavaScript 框架,旨在简化前端应用程序的开发.如果你在开发单页的应用程序,我敢肯定你已经听说过它.我是 AngularJS 的忠实粉丝,在这篇文章中我将概述 ...
- Hya.io – 基于 Web 的数字音频工作站
Hya.io 是基于 Web 的音频应用程序,通过 Web MIDI ,音频合成器,音序以及大量的插件来支持硬件 MIDI .您可以添加插件到工作区,将其连接到路由音频,进行播放和实验. HYA 支持 ...
- hash函数为什么要选择对素数求余?
常用的hash函数是选一个数m取模(余数),这个数在课本中推荐m是素数,但是经常见到选择m=2^n,因为对2^n求余数更快,并认为在key分布均匀的情况下,key%m也是在[0,m-1]区间均匀分布的 ...
- SQL Server里ORDER BY的歧义性
在今天的文章里,我想谈下SQL Server里非常有争议和复杂的话题:ORDER BY子句的歧义性. 视图与ORDER BY 我们用一个非常简单的SELECT语句开始. -- A very simpl ...
- spring AspectJ的Execution表达式
Aspectj切入点语法定义 在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点" 例如定义切入点表达式 execu ...