Android 面试题 线程间通信 六

🔥 主线程向子线程发送消息 Thread+handler🔥

子线程中定义Handler,Handler定义在哪个线程中,就跟那个线程绑定,在线程中绑定Handler需要调用Looper.prepare(); 方法,主线程中不调用是因为主线程默认帮你调用了 :

Dart 复制代码
public class LoopThread implements Runnable {  
  
    public Handler mHandler = null;  
  
    @Override  
    public void run() {  
        Looper.prepare();  
        mHandler = new Handler() {  
            public void handleMessage(Message msg) {  
                String result = NetUtil.getJsonContent("北京");  
                //完成了获取北京天气的操作;  
                Log.i("test", "handler"+result);  
            }  
        };  
        Looper.loop();  
    }  
  
} 

其中Looper.prepare();和Looper.loop();维护了一个消息队列,等待消息注入并在子线程中执行;
主线程中这样调用

Dart 复制代码
lThread.mHandler.sendEmptyMessage(0);   

主线程向子线程发消息,让子线程执行指定的操作,在Android中还有一种方法,即:HandlerThread,看下面的例子:

Dart 复制代码
HandlerThread handlerThread = new HandlerThread("jerome");  
handlerThread.start();  
  
/** 
 * 这里要将HandlerThread创建的looper传递给threadHandler,即完成绑定; 
 */  
threadHandler = new Handler(handlerThread.getLooper()) {  
  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        switch (msg.what) {  
        case 0:  
这儿可以做耗时的操作;  
            Log.i("jerome", "hello,I am sub thread");  
            break;  
        default:  
            break;  
        }  
    }  
};  

🔥 子线程向主线程发送消息 Thread+handler 🔥

主线程中定义Handler:

Dart 复制代码
Handler mHandler = new Handler(){  
  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        switch (msg.what) {  
        case 0:  
            //do something,refresh UI;  
            break;  
        default:  
            break;  
        }  
    }  
      
};

子线程处理完耗时操作之后发消息给主线程,更新UI:

Dart 复制代码
mHandler.sendEmptyMessage(0);  

这样在子线程与主线程任务分工的条件下完成了消息交互;

🔥 子线程向子线程发送消息 🔥

创建线程 ThreadA 用来接受线程 ThreadB 传递的消息

Dart 复制代码
class ThreadA implements Runnable{
        private Handler mHandler;
        //run运行后才不为null在main里判断
        public Handler getHandler(){
            return mHandler;
        }
 
        @SuppressLint("HandlerLeak")
        @Override
        public void run() {
            Looper.prepare();
            mHandler=new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what){
                        case 1:
                            Log.e("线程A","线程B发过来消息了--"+msg.obj);
                            break;
                    }
                }
            };
            Looper.loop();
        }
    }

创建线程 ThreadB 发送消息到线程 ThreadA

Dart 复制代码
class ThreadB implements Runnable{
 
        @Override
        public void run() {
                Message mess=Message.obtain();
                mess.what=1;
                mess.obj= "线程B"+System.currentTimeMillis();
                handler.sendMessage(mess);
        }
    }

在activity onCreate方法里 , 分别调用线程 ThreadA 和 ThreadB

Dart 复制代码
        ThreadA threadA = new ThreadA();
        ThreadB threadb = new ThreadB();
        new Thread(threadA).start();
        if(threadA.getHandler() == null) {
            try {
                Thread.sleep(1000);
                handler = threadA.getHandler();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        new Thread(threadb).start();

🔥 runOnUiTherd() 🔥

主线程主要来完成UI绘制和响应用户 的操作 , 大多数情况都习惯在**onCreate()、onResume()、onCreateView()**中启动我们的逻辑 , 导致代码运行在主线程中,容易导致ANR(Application Not Responding),这些逻辑包括文件读写, 数据库读写, 网络查询等。
开启一个子线程来完成一个耗时操作,以避免阻塞主线程而出现卡顿甚至ANR导致闪退。


子线程执行完要更新UI的时候,我们又必须回到主线程来更新,实现这一功能常用的方法是执行
**Activity的runOnUiThread()**方法:

Dart 复制代码
runOnUiThread(new Runnable() {
     void run() {
         // do something
     }
});

Fragment/Presentation/Dialog中使用:

Dart 复制代码
((MainActivity)getActivity()).runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //在此进行更新UI的操作
            }
        });

深入理解runOnUiThread()

🔥 AsyncTask 🔥

一个 Android 已封装好的轻量级异步类。属于抽象类,即使用时需实现子类。

Dart 复制代码
public abstract class AsyncTask<Params, Progress, Result> { 
 ... 
 }

作用

  • 实现多线程:在工作线程中执行任务,如 耗时任务

  • 异步通信、消息传递:实现工作线程 & 主线程(UI线程)之间的通信 ,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作,保证线程安全。
    优点

  • 方便实现异步通信
    不需使用 "任务线程(如继承Thread类) + Handler"的复杂组合

  • 节省资源
    采用线程池的缓存线程 + 复用线程,避免了频繁创建 & 销毁线程所带来的系统资源开销
    类定义

Dart 复制代码
public abstract class AsyncTask<Params, Progress, Result> { 
 ... 
}

// 类中参数为3种泛型类型
// 整体作用:控制AsyncTask子类执行线程任务时各个阶段的返回类型
// 具体说明:
    // a. Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数
    // b. Progress:异步任务执行过程中,返回下载进度值的类型
    // c. Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致
// 注:
    // a. 使用时并不是所有类型都被使用
    // b. 若无被使用,可用java.lang.Void类型代替
    // c. 若有不同业务,需额外再写1个AsyncTask的子类
}

核心方法 execute()

作用 : 触发执行异步线程任务

调用时刻 :手动调用

使用场景 : 必须在UI线程调用 , 运行在主线程
核心方法 onPreExecute()

作用 : 执行 线程任务前的任务操作

调用时刻 : 执行 线程任务前自动调用 , 即 execute() 执行前调用

使用场景 : 用于界面的初始化操作 , 如 : 显示进度条的对话框
核心方法 doInBackground()

作用 : 接收输入参数 , 执行任务中的耗时操作 , 返回任务中执行的结果

调用时刻 : 执行 线程任务时自动调用 , 即 onPreExecute() 执行后 自动调用

使用场景 : 不能更改UI组件的信息 , 执行过程中可以调用 publishProgress() 更新进度信息
核心方法 onProgressUpdate()

作用 : 在主线程中显示线程任务执行的进度

调用时刻 : 调用publishProgress(Progress ... values) 时 自动调用
核心方法 onPostExecute()

作用 : 接受线程任务执行的结果 , 将执行结果显示到UI组件上

调用时刻 : 线程任务结束时 自动调用
核心方法 onCancelled()

作用 : 将异步任务设置为取消状态

调用时刻 : 异步任务被取消时 即自动调用

使用场景 : 该方法调用时 onPostExecute() 就不会被调用

🔥 View.post 🔥

View.post方法可以在UI线程上安排一个Runnable,确保在某些情况下,如视图大小改变后,执行特定操作。然而,使用View.post时,可能会遇到一些问题。以下是一些可能遇到的坑及其解决方法:
生命周期问题:在使用View.post时,如果Activity或Fragment的生命周期发生变化,如onDestroy或onDetach,可能导致内存泄漏。解决方法是在生命周期方法中移除所有已post的Runnable。

Dart 复制代码
@Override
protected void onDestroy() {
    super.onDestroy();
    yourView.removeCallbacks(yourRunnable);
}
  1. 延迟执行:View.post可能会在稍后执行Runnable,这意味着如果您希望立即执行操作,可能会遇到问题。解决方法是使用View.post()之外的其他方法,如在onLayout或onSizeChanged中执行操作。
  2. 视图不可见:如果使用View.post时视图不可见(例如,它已被隐藏或从窗口分离),Runnable可能无法执行。解决方法是在执行操作前检查视图的可见性和附加状态。
  3. 多次执行:如果使用View.post多次调度相同的Runnable,可能会导致多次执行。为避免这种情况,请在调度新Runnable之前移除旧的Runnable。
  4. 视图未初始化:View.post在视图初始化之前可能无法正常工作。解决方法是确保在视图完全初始化之后再调用View.post。

总之,在使用View.post时要注意生命周期问题、视图可见性、多次执行以及视图初始化等潜在问题。了解这些问题并采取相应的解决方法,可以帮助您更有效地使用View.post
view.post()主要有两个作用:更新UI、获取view的实际宽高。

🔥 View.postDelayed() 🔥

UI控件延迟显示 View.postDelayed()

Dart 复制代码
@SuppressLint("NewApi")
public class TestFragment extends DialogFragment {
    private Button mButton;
    //默认初始值为true
    private boolean isSucess = true;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        //NetworkUtil.requestNet是开启了一个异步线程做请求任务
        //执行网络请求,根据返回成功与否(true or false) 来设定 button上的文字
        //如下为 伪代码
        isSucess = NetworkUtil.requestNet();
    }
 
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View viewLayout = inflater.inflate(R.layout.fragment_test, container);
        mButton = viewLayout.findViewById(R.id.button2);
        return viewLayout;
    }
 
    @Override
    public void onResume() {
        super.onResume();
        //存在bug的代码
       /* if (isSucess) {
            mButton.setText("返回结果成功");
        } else {
            mButton.setText("返回结果失败");
        }*/
 
        //可以用 View.postDelayed(Runnable action, long delayMillis)方法来解决此问题
        mButton.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (isSucess) {
                    mButton.setText("返回结果成功");
                } else {
                    mButton.setText("返回结果失败");
                }
            }
        }, 1000); //这里延时时间根据网络环境的好坏设置
    }
}

自定义view中,postDelayed执行失败

Dart 复制代码
private boolean mAttached;

@Override
protected void onAttachedToWindow() {
      super.onAttachedToWindow();  
      mAttached = true;
}

 @Override
protected void onDetachedFromWindow() {
      mAttached = false;
      super.onDetachedFromWindow();
}

public void show() {
        //....
        if (mAttached) {
              postDelayed(mDelayedShow, MIN_DELAY);    
        } else {
              Handler handler = new Handler();
              handler.post(mDelayedShow);
        }
}

🔥 线程间通信 eventbus 🔥

优点 :

  • 简化组件之间的通信

  • 体积小

  • 将事件的发送者和接受者分离

  • 在activity fragment 线程之间性能优良

  • 避免复杂且容易出错的依赖关系和生命周期问题

  • 代码简单方便

粘性广播

**粘性广播有什么作用?怎么使用?**粘性广播主要为了解决,在发送完广播之后,动态注册的接收者,也能够收到广播。

举个例子首先发送一广播,我的接收者是通过程序中的某个按钮动态注册的。

如果不是粘性广播,我注册完接收者肯定无法收到广播了。

这是通过发送粘性广播就能够在我动态注册接收者后也能收到广播
EeventBus 粘性事件和普通事件的区别?

StickyEvent与普通Event的普通就在于,EventBus会自动维护被作为StickyEvent被post出来 (即在发布事件时使用EventBus.getDefault().postSticky(new MyEvent())方法)的事件的最后一个副本在缓存中。

任何时候在任何一个订阅了该事件的订阅者中的任何地方(可以在任何函数中,而不仅仅是在onEventXXX方法中),都可以通过EventBus.getDefault().getStickyEvent(MyEvent.class)来取得该类型事件的最后一次缓存。

同时,即便事件已经在所有订阅者中传递完成了,如果此时再创建一个新的订阅者(如一个注册了该StickyEvent的Activity),则在订阅者启动后,会自动调用一次该订阅者的noEventXXX方法来处理该StickyEvent。也可以在需要的时候,利用removeStickyEvent方法来移除对某种StickyEvent的缓存。

Android Studio 配置的module的gradle内:

Dart 复制代码
compile 'org.greenrobot:eventbus:3.0.0'

定义一个消息事件 :

Dart 复制代码
public class User {
 
    private String name;
    private String pass;
 
    public User(String name, String pass) {
        this.name = name;
        this.pass = pass;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getPass() {
        return pass;
    }
 
    public void setPass(String pass) {
        this.pass = pass;
    }
 
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", pass='" + pass + '\'' +
                '}';
    }
}

普通事件(a--->b---值-->a),相当于数据回传

1、注册和取消订阅事件(PuTong1Activity .java):

Dart 复制代码
public class PuTong1Activity extends AppCompatActivity implements View.OnClickListener {
 
    private Button putong1_btn;
    private TextView putong1Text;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pu_tong1);
 
        initView();
        //注册事件
        EventBus.getDefault().register(this);
    }
 
    private void initView() {
        putong1_btn = (Button) findViewById(R.id.putong1_btn);
        putong1Text = (TextView) findViewById(R.id.putong1Text);
        putong1_btn.setOnClickListener(this);
    }
 
    @Override
    public void onClick(View v) {
        startActivity(new Intent(PuTong1Activity.this,PuTong2Activity.class));
    }
   //事件订阅者处理事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMoonEvent(User user){
        putong1Text.setText(user.getName()+"---接收到的值----"+user.getPass());
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消注册事件
        EventBus.getDefault().unregister(this);
    }
}

2、事件发布者发布事件PuTong2Activity 类

Dart 复制代码
public class PuTong2Activity extends AppCompatActivity implements View.OnClickListener {
 
    private Button putong2_btn;
    private EditText nameEdit;
    private EditText passEdit;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pu_tong2);
 
        initView();
    }
 
    private void initView() {
        putong2_btn = (Button) findViewById(R.id.putong2_btn);
        nameEdit = (EditText) findViewById(R.id.nameEdit);
        passEdit = (EditText) findViewById(R.id.passEdit);
        putong2_btn.setOnClickListener(this);
    }
 
    @Override
    public void onClick(View v) {
        String nameStr = nameEdit.getText().toString().trim();
        String passStr = passEdit.getText().toString().trim();
        //普通时间发送消息给putong1用post方法
        EventBus.getDefault().post(new User(nameStr,passStr));
        finish();
    }
}

粘性事件(相当于直接从a---值--->b,与intent跳转传值类似)

1.发送黏性事件(MainActivity .java)

Dart 复制代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 
    private EditText nameEdit;
    private EditText passEdit;
    private Button login;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
 
    private void initView() {
        nameEdit = (EditText) findViewById(R.id.name);
        passEdit = (EditText) findViewById(R.id.pass);
        login = (Button) findViewById(R.id.login);
        login.setOnClickListener(this);
    }
 
    @Override
    public void onClick(View v) {
        String nameStr = nameEdit.getText().toString().trim();
        String passStr = passEdit.getText().toString().trim();
        if(null==nameStr&&nameStr.equals("")||null==passStr&&passStr.equals("")){
            Toast.makeText(this,"用户名密码不能为空",Toast.LENGTH_SHORT).show();
        }else{
            //2.发送消息粘性事件用postSticky
            EventBus.getDefault().postSticky(new User(nameStr,passStr));
            //跳转
            Intent intent = new Intent(MainActivity.this, ResultActivity.class);
            startActivity(intent);
        }
    }
 
}

2.订阅粘性事件(ResultActivity.java)

Dart 复制代码
public class ResultActivity extends AppCompatActivity implements View.OnClickListener {
 
    private Button getResult;
    private TextView text;
    private String nameStr;
    private String passStr;
 
    //在接收消息的页面  注册EventBus
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_result);
 
        initView();
    }
 
    private void initView() {
        getResult = (Button) findViewById(R.id.getResult);
        text = (TextView) findViewById(R.id.text);
        getResult.setOnClickListener(this);
    }
 
    @Override
    public void onClick(View v) {
        //注册EventBus
        EventBus.getDefault().register(this);
 
    }
    //订阅者处理粘性事件
    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onEventMainThread(User user) {
 
        String msg = "账号:" + user.getName()+"---密码:"+user.getPass();
        Log.d("ResultActivity", msg);
        text.setText(msg);
        Toast.makeText(ResultActivity.this, msg, Toast.LENGTH_LONG).show();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消注册
        EventBus.getDefault().unregister(this);
    }
}
相关推荐
2401_8979078640 分钟前
10天学会flutter DAY2 玩转dart 类
android·flutter
m0_748233641 小时前
【PHP】部署和发布PHP网站到IIS服务器
android·服务器·php
Yeats_Liao2 小时前
Spring 定时任务:@Scheduled 注解四大参数解析
android·java·spring
雾里看山4 小时前
【MySQL】 库的操作
android·数据库·笔记·mysql
水瓶丫头站住12 小时前
安卓APP如何适配不同的手机分辨率
android·智能手机
xvch13 小时前
Kotlin 2.1.0 入门教程(五)
android·kotlin
xvch17 小时前
Kotlin 2.1.0 入门教程(七)
android·kotlin
望风的懒蜗牛17 小时前
编译Android平台使用的FFmpeg库
android
浩宇软件开发18 小时前
Android开发,待办事项提醒App的设计与实现(个人中心页)
android·android studio·android开发
ac-er888818 小时前
Yii框架中的多语言支持:如何实现国际化
android·开发语言·php