常说的主线程(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机,哈哈)下面的情况都被认为是一个前台进程:

  1. 进程中有一个正在和用户交互的Activity时,也就是该Activity调用了onResume方法。

  2. 进程中含有一个与正在和用户交互的Activity绑定了的Service

  3. 进程中含有一个运行”在前台的” Service —–即该Service调用了startForeground()方法。

  4. 进程中含有一个调用了 onCreate(), onStart(), 或者onDestroy()方法中的任意一个方法的Service。

  5. 进程中含有一个调用了onReceive()方法的BroadcastReceiver。

2、可见的进程

即使一个进程没有前台组件,但是如果它能够影响到用户所看到的界面,它就是可见的进程。可见的进程依然很重要,它只有在必需干掉它才能维持前台进程存在的情况下才会被干掉。老大需要资源,做小弟的能不让吗?下面的情况都被认为是一个可见进程:

  1. 当我们学习生命周期的时候打开一个dialog的情况。此时之前的Activity所在的进程就是可见的进程。

  2. 进程中含有一个绑定到一个可见的、或者前台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中进程与线程的更多相关文章

  1. Android中进程与线程及如何在子线程中操作UI线程

    1. Android进程 一个应用程序被启动时,系统默认创建执行一个叫做"main"的线程.这个线程也是你的应用与界面工具包(android.widget和android.view ...

  2. 第11讲- Android中进程及其优先级

    第11讲Android中进程及其优先级 进程与线程: 进程:操作系统结构的基础,资源分配的最小单元,一个操作系统包括多个进程: 线程:线程存在于进程当中,是操作系统调试执行的最小单元,一个进程包括多个 ...

  3. android中进程的优先级

    android中进程的优先级

  4. Android 的进程和线程

    进程和线程 如果某个应用程序组件是第一次被启动,且这时应用程序也没有其他组件在运行,则android系统会为应用程序创建一个包含单个线程的linux进程.默认情况下,同一个应用程序的所有组件都运行在同 ...

  5. Android的进程和线程(转)

    进程和线程 当一个应用程序第一次启动的时候,Android会启动一个Linux进程和一个主线程(即UI线程:主要负责处理用户的按键事件.触屏事件及屏幕绘图事件等).默认情况下,所有该程序的组件都将在该 ...

  6. Android中使用Thread线程与AsyncTask异步任务的区别

    最近和几个朋友交流Android开发中的网络下载问题时,谈到了用Thread开启下载线程时会产生的Bug,其实直接用子线程开启下载任务的确是很Low的做法,那么原因究竟如何,而比较高大上的做法是怎样? ...

  7. 转:Android 的进程与线程总结

    当一个Android应用程序组件启动时候,如果此时这个程序的其他组件没有正在运行,那么系统会为这个程序 以单一线程的形式启动一个新的Linux 进程. 默认情况下,同一应用程序下的所有组件都运行再相同 ...

  8. 关于Java中进程和线程的详解

    一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...

  9. java中进程与线程的三种实现方式

    一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是一个线程. 进程:进程是指 ...

随机推荐

  1. `cocos2dx非完整` 开始自己的FW模块

    上一篇的文章中说到了一些个人习惯的东西以及一些简单的项目配置,这一篇文章我们来进一步完善一些东西.首先,打开编译以后的客户端执行,会看到一大堆的fileutils加载luac文件的提示,在终端显示一大 ...

  2. android 隐藏标题栏的方法

    1:单个activity里 onCreate() { super.onCreate(); requestWindowFeature(Window.FEATURE_NO_TITLE); setConte ...

  3. 如何查看distirbution agent的执行进度

    在transactional replication troubleshooting的过程中,经常会遇到下面的场景: 客户在发布端执行了一个几百万行的更新,结果导致性能下降. 客户很想知道目前dist ...

  4. Transact-SQL 示例 - UPDATE中使用INNER JOIN

    一般情况下博主已经对在SELECT语句当中使用INNER JOIN非常娴熟,但在UPDATE当中使用INNER JOIN的场景就为数不多了.以下博主将为你介绍在UPDATE场景当中使用INNER JO ...

  5. Android View 之进度条+拖动条+星级评论条....

    PS:将来的你会感谢现在奋斗的自己.... 学习内容: 1.进度条 2.拖动条 3.星级评论条 1.进度条...       进图条这东西想必大家是很熟悉的...为了使用户不会觉得应用程序死掉了,因此 ...

  6. Action接收页面传来的参数方法

    接收页面传来的参数方法 1.第一种:在action中设置相应的变量 在相应的action中设置与将要传进来的参数名相同的变量 eg: 页面传给后台两个参数 name=chance & age ...

  7. Enum Helper

    public static class EnumHelper { #region get /// <summary> /// 获得枚举类型所包含的全部项的列表 /// </summa ...

  8. MagicalRecord,一个简化CoreData操作的工具库

    简介 项目主页:https://github.com/magicalpanda/MagicalRecord 实例下载:https://github.com/ios122/MagicalRecord 在 ...

  9. Java集合框架源码剖析:LinkedHashSet 和 LinkedHashMap

    Java LinkedHashMap和HashMap有什么区别和联系?为什么LinkedHashMap会有着更快的迭代速度?LinkedHashSet跟LinkedHashMap有着怎样的内在联系?本 ...

  10. IOS高级编程之一:多点触摸与手势验证

    前段时间学习了IOS基础的一些控件的使用作为基础,现在开始学习一些高级编程的东西,手势处理器.文件I/O.定位.网络通信.多线程这些,分享一些学习的重点,还是很实用的. 今天就先介绍个简单点得,手势处 ...