Android开发之线程间通信
<h1 id="android开发之线程间通信">Android开发之线程间通信</h1><p>当我们的软件启动的时候,计算机会分配进程给到我们运行的程序,在进程中包含多个线程用于提高软件运行速度。</p>
<p>在android网络请求中,我们知道在日常开发中不能在子线程中跟新ui,否则报错Only the original thread that created a view hierarchy can touch its views.,那么我们怎么判断是否是在子线程呢,可以通过log打印在控制台中找到打印信息,这里面就有线程信息。</p>
<p>在MainActivity类onCreate方法中通过</p>
<pre><code class="language-java">new Thread(new Runnable() {
@Override
public void run() {
Log.d("TAG", "run: ");
}
}).start();
</code></pre>
<p>可以在AndroidStudio下面的Logcat中看到打印信息,这其中就包含了线程id,每次启动软件所拿到的线程和进程id是可能不同的。</p>
<p><img src="https://img2022.cnblogs.com/blog/3034026/202211/3034026-20221124140039442-621655586.png" alt="" loading="lazy"></p>
<p>11372是系统分配给我们的进程id,-后面的数字就是线程id,每次启动都会重新分配。除此之外还有个uid,是软件安装时系统分配给我们的,卸载软件重装会重新分配,跟新软件覆盖是不会重新分配的。</p>
<p>也可以通过android.os.Process的方式调出查看</p>
<pre><code class="language-java">android.os.Process.myPid();//进程id
android.os.Process.myUid();//用户id
android.os.Process.myTid();//线程id,在哪个线程中调用就是哪个线程的id
</code></pre>
<h2 id="线程间通信的作用">线程间通信的作用</h2>
<p>线程通信是为了不同线程互相传递信息,能够在将子线程的数据传递到主线程中,方便调用。</p>
<h2 id="线程通信的方式">线程通信的方式</h2>
<p>目前android主流的线程通信的方式有</p>
<p>1、调用Handler类</p>
<p>2、调用Activity类的runOnUiThread方法</p>
<p>3、调用View类中的post方法</p>
<p>4、通过新建一个继承AsyncTask父类的子类来实现</p>
<p>5、使用EventBus等工具</p>
<h2 id="调用handler类">调用Handler类</h2>
<p>创建Handler类,当他被创建的时候他就会开始一直监听是否有消息传递过来,我们通过在子线程中调用该Handler的消息传递方法sendMessage可以向主线程的Handler的消息监听方法handleMessage发送消息,实现线程通信。</p>
<h3 id="示例代码">示例代码</h3>
<pre><code class="language-java"> Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
System.out.println(message.obj+"线程id"+android.os.Process.myTid());
return true;
}
});
new Thread(new Runnable() {
@Override
public void run() {
Message msg = new Message();
System.out.println("线程id"+android.os.Process.myTid());
msg.obj = "子线程发送的消息Message";
handler.sendMessage(msg);
}
}).start();
</code></pre>
<p>打印结果:</p>
<p><img src="https://img2022.cnblogs.com/blog/3034026/202211/3034026-20221124140057020-1910589360.png" alt="" loading="lazy"></p>
<p>可以看到子线程的id是12028,主线程id是11977,而且子线程在Handler下方执行并且当子线程发送消息时,主线程的Handler执行了handleMessage监听方法,这样就可以实现在主线程handleMessage方法中进行ui操作等无法在子线程中执行的操作了。</p>
<h2 id="调用activity类的runonuithread方法">调用Activity类的runOnUiThread方法</h2>
<p>在网络请求(一中说过用法)Android网络请求(1) - 高同学,你好 - 博客园 (cnblogs.com)</p>
<h3 id="示例代码-1">示例代码</h3>
<pre><code class="language-java">new Thread(new Runnable() {
@Override
public void run() {
String name = "android";
System.out.println(android.os.Process.myTid());
runOnUiThread(new Runnable() {
@Override
public void run() {
System.out.println(android.os.Process.myTid());
//TODO 执行ui操作
}
});
}
}).start();
</code></pre>
<p>打印线程id可以看到,在子线程调用了runOnUiThread方法后,成功切换到了主线程</p>
<p>打印结果</p>
<p><img src="https://img2022.cnblogs.com/blog/3034026/202211/3034026-20221124140113811-1838884026.png" alt="" loading="lazy"></p>
<h2 id="调用view类中的post方法">调用View类中的post方法</h2>
<p>它其实和调用Activity类的runOnUiThread方法很像,都是但是一个是调用activity的方法,另一个时调用View的方法,使用方式也是一样的。但是要通过对应的View调用post方法。</p>
<h3 id="示例代码-2">示例代码</h3>
<pre><code class="language-java">new Thread(new Runnable() {
@Override
public void run() {
String name = "android";
System.out.println(Process.myTid());
textView.post(new Runnable() {
@Override
public void run() {
System.out.println(Process.myTid()+name);
textView.setText(name);
}
});
}
}).start();
</code></pre>
<p>打印截图</p>
<p><img src="https://img2022.cnblogs.com/blog/3034026/202211/3034026-20221124140126242-1576316339.png" alt="" loading="lazy"></p>
<h2 id="通过新建一个继承asynctask父类的子类来实现">通过新建一个继承AsyncTask父类的子类来实现</h2>
<p>AsyncTask时通过重写doInBackground和onPostExecute方法来实现线程的通信,onPostExecute可以直接使用参数,参数时doInBackground时的返回值。</p>
<h3 id="示例代码-3">示例代码</h3>
<p>新建子类</p>
<pre><code class="language-java">private class MyAsyncTask extends AsyncTask {
private TextView tv;
public MyAsyncTask(TextView tv) {
this.tv = tv;
}
//子线程进行请求返回数据
@Override
protected Object doInBackground(Object[] objects) {
System.out.println(Process.myTid()+"doInBackground打印id");
return "name";
}
//直接调用子线程返回的o
//切换到主线程进行操作
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
System.out.println(Process.myTid()+"onPostExecute打印id");
tv.setText(String.valueOf(o));
}
}
</code></pre>
<p>MainActivity调用</p>
<pre><code class="language-java">TextView tv = findViewById(R.id.text);
System.out.println(Process.myTid()+"主线程打印id");
new MyAsyncTask(tv).execute("aaaaa");
</code></pre>
<p>打印结果</p>
<p><img src="https://img2022.cnblogs.com/blog/3034026/202211/3034026-20221124140200101-1221981144.png" alt="" loading="lazy"></p>
<h2 id="使用eventbus等工具">使用EventBus等工具</h2>
<p>EventBus是一个消息总线,以观察者模式实现,用于简化程序的组件、线程通信,可以轻易切换线程、开辟线程,包括后台线程、UI线程、异步线程。</p>
<h3 id="示例代码-4">示例代码</h3>
<p>先导入EventBus在项目文件下build.gradle(app)的dependencies中导入所需要的库</p>
<pre><code class="language-gradle">implementation group: 'org.greenrobot', name: 'eventbus', version: '3.0.0'
</code></pre>
<p>新建EventBus所需要接受的实体类,也可以使用String直接发送消息。不知到为什么好像使用Integer、int会报错,其他的我也没具体测试过。</p>
<pre><code class="language-java">public class Event{
private int code;
private String msg;
public Event(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
</code></pre>
<p>创建一个监听方法,方法名自定义,参数类型为你希望接收到的参数类型。假如有两个监听,我发送的是String类型的消息,那么就只有接受值为String类型的监听方法才会触发监听。在监听方法上面加上注解@Subscribe,也可以设置注解的模式,不设置就是使用的默认模式,默认模式就是你在子线程发送的数据,那么监听方法也是在子线程内,同样不能设置ui,默认模式根据你发送数据时所在的线程决定。ThreadMode.MAIN是在主线程执行</p>
<pre><code class="language-java"> @Subscribe(threadMode = ThreadMode.MAIN)
public void msg(Event event){
System.out.println(Process.myTid()+"msg打印id");
System.out.println(event.msg);
}
</code></pre>
<p>使用EventBus时要在OnCreate方法中注册,在onDestroy方法中销毁</p>
<pre><code class="language-java"> @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
EventBus.getDefault().register(this);
}
</code></pre>
<pre><code class="language-java">@Override
protected void onDestroy() {
EventBus.getDefault().unregister(this);
super.onDestroy();
}
</code></pre>
<p>子线程中就可以直接通过EventBus发送消息了。</p>
<pre><code class="language-java">new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Process.myTid()+"Thread打印id");
Event event = new Event(200,"成功");
EventBus.getDefault().post(event);
}
}).start();
</code></pre>
<p>打印结果</p>
<p><img src="https://img2022.cnblogs.com/blog/3034026/202211/3034026-20221124140215141-592525559.png" alt="" loading="lazy"></p>
<h2 id="总结">总结</h2>
<p>线程间通信是Android开发中较为重要的知识点,如果不牢记,很容易出现在子线程中直接操作ui报错却不知道哪里错了的事情。身边的老有人问我这种错误。希望大家能够牢记知识点。高同学祝你步步高升!</p><br><br>
来源:https://www.cnblogs.com/nhgtx/p/16921667.html
頁:
[1]