2022-12-19 465 0
java

1、线程相关概念1)相关概念:程序:为了完成特定任务,用某种语言编写的一组指令集合(一组静态代码)进程:运行中的程序,系统调度与资源分配的一个独立单位,操作系统会为每个进程分配一段内存空间!程序的依次动态执行,经历代码的加载,执行,执行完毕的完整过程!线程:比进程更小的执行单元,每个进程可能有多条线程,线程需要放在一个进程中才能执行,线程由程序负责管理,而进程则由系统进行调度!多线程的理解:并行执行多个条指令,将CPU时间片按照调度算法分配给各个线程,实际上是分时执行的,只是这个切换的时间很短,用户感觉到"同时"而已!2)线程的生命周期:3)创建线程的三种方式:继承Thread类实现Runnable接口实现Callable接口如果:使用的是2创建的线程的话,可以直接这样启动:newThread(myThread).start();当更多的时候我们喜欢使用匿名类,即下面这种写法:newThread(newRunnable(){publicvoidrun();}).start();2.Service的生命周期图3.生命周期解析好的,从上图的生命周期,我们可以知道,Android中使用Service的方式有两种:1)StartService()启动Service2)BindService()启动ServicePS:还有一种,就是启动Service后,绑定Service!1)相关方法详解:onCreate():当Service第一次被创建后立即回调该方法,该方法在整个生命周期中只会调用一次!onDestory():当Service被关闭时会回调该方法,该方法只会回调一次!onStartCommand(intent,flag,startId):早期版本是onStart(intent,startId),当客户端调用startService(Intent)方法时会回调,可多次调用StartService方法,但不会再创建新的Service对象,而是继续复用前面产生的Service对象,但会继续回调onStartCommand()方法!IBinderonOnbind(intent):该方法是Service都必须实现的方法,该方法会返回一个IBinder对象,app通过该对象与Service组件进行通信!onUnbind(intent):当该Service上绑定的所有客户端都断开时会回调该方法!2)StartService启动Service①首次启动会创建一个Service实例,依次调用onCreate()和onStartCommand()方法,此时Service进入运行状态,如果再次调用StartService启动Service,将不会再创建新的Service对象,系统会直接复用前面创建的Service对象,调用它的onStartCommand()方法!②但这样的Service与它的调用者无必然的联系,就是说当调用者结束了自己的生命周期,但是只要不调用stopService,那么Service还是会继续运行的!③无论启动了多少次Service,只需调用一次StopService即可停掉Service3)BindService启动Service①当首次使用bindService绑定一个Service时,系统会实例化一个Service实例,并调用其onCreate()和onBind()方法,然后调用者就可以通过IBinder和Service进行交互了,此后如果再次使用bindService绑定Service,系统不会创建新的Sevice实例,也不会再调用onBind()方法,只会直接把IBinder对象传递给其他后来增加的客户端!②如果我们解除与服务的绑定,只需调用unbindService(),此时onUnbind和onDestory方法将会被调用!这是一个客户端的情况,假如是多个客户端绑定同一个Service的话,情况如下当一个客户完成和service之间的互动后,它调用unbindService()方法来解除绑定。当所有的客户端都和service解除绑定后,系统会销毁service。(除非service也被startService()方法开启)③另外,和上面那张情况不同,bindService模式下的Service是与调用者相互关联的,可以理解为"一条绳子上的蚂蚱",要死一起死,在bindService后,一旦调用者销毁,那么Service也立即终止!通过BindService调用Service时调用的Context的bindService的解析bindService(IntentService,ServiceConnectionconn,intflags)service:通过该intent指定要启动的Serviceconn:ServiceConnection对象,用户监听访问者与Service间的连接情况,连接成功回调该对象中的onServiceConnected(ComponentName,IBinder)方法;如果Service所在的宿主由于异常终止或者其他原因终止,导致Service与访问者间断开连接时调用onServiceDisconnected(CompanentName)方法,主动通过unBindService()方法断开并不会调用上述方法!flags:指定绑定时是否自动创建Service(如果Service还未创建),参数可以是0(不自动创建),BIND_AUTO_CREATE(自动创建)4)StartService启动Service后bindService绑定如果Service已经由某个客户端通过StartService()启动,接下来由其他客户端再调用bindService()绑定到该Service后调用unbindService()解除绑定最后在调用bindService()绑定到Service的话,此时所触发的生命周期方法如下:onCreate()->onStartCommand()->onBind()->onUnbind()->onRebind()PS:前提是:onUnbind()方法返回true!!!这里或许部分读者有疑惑了,调用了unbindService后Service不是应该调用onDistory()方法么!其实这是因为这个Service是由我们的StartService来启动的,所以你调用onUnbind()方法取消绑定,Service也是不会终止的!得出的结论:假如我们使用bindService来绑定一个启动的Service,注意是已经启动的Service!!!系统只是将Service的内部IBinder对象传递给Activity,并不会将Service的生命周期与Activity绑定,因此调用unBindService()方法取消绑定时,Service也不会被销毁!onCreate():当Service第一次被创建后立即回调该方法,该方法在整个生命周期中只会调用一次!onDestory():当Service被关闭时会回调该方法,该方法只会回调一次!onStartCommand(intent,flag,startId):早期版本是onStart(intent,startId),当客户端调用startService(Intent)方法时会回调,可多次调用StartService方法,但不会再创建新的Service对象,而是继续复用前面产生的Service对象,但会继续回调onStartCommand()方法!IBinderonOnbind(intent):该方法是Service都必须实现的方法,该方法会返回一个IBinder对象,app通过该对象与Service组件进行通信!onUnbind(intent):当该Service上绑定的所有客户端都断开时会回调该方法!5)IntentService如果是大量耗时的service则使用intentService()4、IBundlerIBinder是Android给我们提供的一个进程间通信的一个接口,而我们一般是不直接实现这个接口的,而是通过继承Binder类来实现进程间通信!是Android中实现IPC(进程间通信)的一种方式!1)Binder机制浅析Android中的Binder机制由一系列系统组件构成:Client、Server、ServiceManager和Binder驱动程序大概调用流程如下,另外ServiceManager比较复杂,这里并不详细研究!流程解析:->Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的参数打包成Parcel对象;->然后代理接口把该Parcel对象发送给内核中的Binderdriver;;->然后Server会读取BinderDriver中的请求数据,假如是发送给自己的,解包Parcel对象,处理并将结果返回;PS:代理接口中的定义的方法和Server中定义的方法是一一对应的,另外,整个调用过程是一个同步的,即Server在处理时,Client会被Block(锁)住!而这里说的代理接口的定义就是等下要说的AIDL(Android接口描述语言)!3)为何Android使用Binder机制来实现进程间的通信?可靠性:在移动设备上,通常采用基于Client-Server的通信方式来实现互联网与设备间的内部通信。目前linux支持IPC包括传统的管道,SystemVIPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。Android系统为开发者提供了丰富进程间通信的功能接口,媒体播放,传感器,无线传输。这些功能都由不同的server来管理。开发都只关心将自己应用程序的client与server的通信建立起来便可以使用这个服务。毫无疑问,如若在底层架设一套协议来实现Client-Server通信,增加了系统的复杂性。在资源有限的手机上来实现这种复杂的环境,可靠性难以保证。传输性能:socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中,然后从内核缓存区拷贝到接收方缓存区,其过程至少有两次拷贝。虽然共享内存无需拷贝,但控制复杂。比较各种IPC方式的数据拷贝次数。共享内存:0次。Binder:1次。Socket/管道/消息队列:2次。安全性:Android是一个开放式的平台,所以确保应用程序安全是很重要的。Android对每一个安装应用都分配了UID/PID,其中进程的UID是可用来鉴别进程身份。传统的只能由用户在数据包里填写UID/PID,这样不可靠,容易被恶意程序利用。而我们要求由内核来添加可靠的UID。所以,出于可靠性、传输性、安全性。android建立了一套新的进程间通信方式。——摘自:Android中的Binder机制的简要理解当然,作为一个初级的开发者我们并不关心上述这些,Binder机制给我们带来的最直接的好处就是:我们无需关心底层如何实现,只需按照AIDL的规则,自定义一个接口文件,然后调用调用接口中的方法,就可以完成两个进程间的通信了!3).AIDL使用详解AIDL是什么?嘿嘿,前面我们讲到IPC这个名词,他的全名叫做:跨进程通信(interprocesscommunication),因为在Android系统中,个个应用程序都运行在自己的进程中,进程之间一般是无法直接进行数据交换的,而为了实现跨进程,Android给我们提供了上面说的Binder机制,而这个机制使用的接口语言就是:AIDL(AndroidInterfaceDefinitionLanguage),他的语法很简单,而这种接口语言并非真正的编程语言,只是定义两个进程间的通信接口而已!而生成符合通信协议的Java代码则是由AndroidSDK的platform-tools目录下的aidl.exe工具生成,生成对应的接口文件在:gen目录下,一般是:Xxx.java的接口!而在该接口中包含一个Stub的内部类,该类中实现了在该类中实现了IBinder接口与自定义的通信接口,这个类将会作为远程Service的回调类——实现了IBinder接口,所以可作为Service的onBind()方法的返回值!AIDL实现两个进程间的简单通信(没写)4)BroadcastReceiver两种广播类型接收系统广播1)两种注册广播的方式前面也讲了,系统在某些时候会发送相应的系统广播,下面我们就来让我们的APP接收系统广播,接收之前,还需要为我们的APP注册广播接收器哦!而注册的方法又分为以下两种:动态与静态!5)ContentProvider6)Intent1.显式Intent与隐式Intent的区别显式Intent:通过组件名指定启动的目标组件,比如startActivity(newIntent(A.this,B.class));每次启动的组件只有一个~隐式Intent:不指定组件名,而指定Intent的Action,Data,或Category,当我们启动组件时,会去匹配AndroidManifest.xml相关组件的Intent-filter,逐一匹配出满足属性的组件,当不止一个满足时,会弹出一个让我们选择启动哪个的对话框~2.Intent的七个属性:1)ComponentName(组件名称)--------------一个全限定类2)Action(动作)--------------------------别如get一个权限3)Category(类别)-----------------与action结合使用4)Data(数据),Type(MIME类型)-----------URI等5)Extras(额外)----------------------------例如传递字符6)Flags(标记)--------------------------大多用于android如何启动3、intent复杂数据传递https://www.runoob.com/w3cnote/android-tutorial-intent-pass-data.html

2022-9-20 771 0
java

借图流程图解析:相关名词UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue;Handler:作用就是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象Message:Handler接收与处理的消息对象MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理!简单点说:当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而我们发送的信息会先到主线程的MessageQueue进行等待,由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理!4.Handler的相关方法:voidhandleMessage(Messagemsg):处理消息的方法,通常是用于被重写!sendEmptyMessage(intwhat):发送空消息sendEmptyMessageDelayed(intwhat,longdelayMillis):指定延时多少毫秒后发送空信息sendMessage(Messagemsg):立即发送信息sendMessageDelayed(Messagemsg):指定延时多少毫秒后发送信息finalbooleanhasMessage(intwhat):检查消息队列中是否包含what属性为指定值的消息如果是参数为(intwhat,Objectobject):除了判断what属性,还需要判断Object属性是否为指定对象的消息5、Handler写在子线程中在主线程中,因为系统已经初始化了一个Looper对象,所以我们直接创建Handler对象,就可以进行信息的发送与处理了!在子线程中则需要自己创建一个Looper对象了!创建的流程如下:1)直接调用Looper.prepare()方法即可为当前线程创建Looper对象,而它的构造器会创建配套的MessageQueue;2)创建Handler对象,重写handleMessage()方法就可以处理来自于其他线程的信息了!3)调用Looper.loop()方法启动Looper使用示例:输入一个数,计算后通过Toast输出在这个范围内的所有质数实现代码:activity_main.xml:<?xmlversion="1.0"encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditTextandroid:id="@+id/etNum"android:inputType="number"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入上限"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="cal"android:text="计算"/></LinearLayout></androidx.constraintlayout.widget.ConstraintLayout>实现代码:MainActivity.java:packagecom.example.tets4;importandroidx.appcompat.app.AppCompatActivity;importandroid.app.Activity;importandroid.os.Bundle;importandroid.os.Handler;importandroid.os.Looper;importandroid.os.Message;importandroid.view.View;importandroid.widget.EditText;importandroid.widget.Toast;importjava.util.ArrayList;importjava.util.List;publicclassMainActivityextendsActivity{staticfinalStringUPPER_NUM="upper";EditTextetNum;CalThreadcalThread;//定义一个线程类classCalThreadextendsThread{publicHandlermHandler;publicvoidrun(){Looper.prepare();mHandler=newHandler(){//定义处理消息的方法@OverridepublicvoidhandleMessage(Messagemsg){if(msg.what==0x123){intupper=msg.getData().getInt(UPPER_NUM);List<Integer>nums=newArrayList<Integer>();//计算从2开始、到upper的所有质数outer:for(inti=2;i<=upper;i++){//用i处于从2开始、到i的平方根的所有数for(intj=2;j<=Math.sqrt(i);j++){//如果可以整除,表明这个数不是质数if(i!=2&&i%j==0){continueouter;}}nums.add(i);}//使用Toast显示统计出来的所有质数Toast.makeText(MainActivity.this,nums.toString(),Toast.LENGTH_LONG).show();}}};Looper.loop();}}@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);etNum=(EditText)findViewById(R.id.etNum);calThread=newCalThread();//启动新线程calThread.start();}//为按钮的点击事件提供事件处理函数publicvoidcal(Viewsource){//创建消息Messagemsg=newMessage();msg.what=0x123;Bundlebundle=newBundle();bundle.putInt(UPPER_NUM,Integer.parseInt(etNum.getText().toString()));msg.setData(bundle);//向新线程中的Handler发送消息calThread.mHandler.sendMessage(msg);}}

java

1.什么是方法回调?文字表述:答:是将功能定义与功能分开的一种手段,一种解耦合的设计思想;在Java中回调是通过接口来实现的,作为一种系统架构,必须要有自己的运行环境,且需要为用户提供实现接口;实现依赖于客户,这样就可以达到接口统一,实现不同,系统通过在不同的状态下"回调"我们的实现类,从而达到接口和实现的分离!找了一篇文章,挺不错https://blog.csdn.net/aigestudio/article/details/408698932.Android回调的事件处理机制详解:在Android中基于回调的事件处理机制使用场景有两个:1)自定义view当用户在GUI组件上激发某个事件时,组件有自己特定的方法会负责处理该事件通常用法:继承基本的GUI组件,重写该组件的事件处理方法,即自定义view注意:在xml布局中使用自定义的view时,需要使用"全限定类名"常见View组件的回调方法:android为GUI组件提供了一些事件处理的回调方法,以View为例,有以下几个方法①在该组件上触发屏幕事件:booleanonTouchEvent(MotionEventevent);②在该组件上按下某个按钮时:booleanonKeyDown(intkeyCode,KeyEventevent);③松开组件上的某个按钮时:booleanonKeyUp(intkeyCode,KeyEventevent);④长按组件某个按钮时:booleanonKeyLongPress(intkeyCode,KeyEventevent);⑤键盘快捷键事件发生:booleanonKeyShortcut(intkeyCode,KeyEventevent);⑥在组件上触发轨迹球屏事件:booleanonTrackballEvent(MotionEventevent);*⑦当组件的焦点发生改变,和前面的6个不同,这个方法只能够在View中重写哦!protectedvoidonFocusChanged(booleangainFocus,intdirection,RectpreviouslyFocusedRect)MyButton.javapackagecom.example.test3;importandroid.content.Context;importandroid.util.AttributeSet;importandroid.util.Log;importandroid.view.KeyEvent;importandroid.view.MotionEvent;importandroid.widget.Button;publicclassMyButtonextendsButton{privatestaticStringTAG="呵呵";publicMyButton(Contextcontext,AttributeSetattrs){super(context,attrs);}//重写键盘按下触发的事件@OverridepublicbooleanonKeyDown(intkeyCode,KeyEventevent){super.onKeyDown(keyCode,event);Log.i(TAG,"onKeyDown方法被调用");returntrue;}//重写弹起键盘触发的事件@OverridepublicbooleanonKeyUp(intkeyCode,KeyEventevent){super.onKeyUp(keyCode,event);Log.i(TAG,"onKeyUp方法被调用");returntrue;}//组件被触摸了@OverridepublicbooleanonTouchEvent(MotionEventevent){super.onTouchEvent(event);Log.i(TAG,"onTouchEvent方法被调用");returntrue;}}布局文件<com.example.test3.MyButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/btnshow"android:text="按钮"/>通过重写Button的三个回调方法,当发生点击事件后就不需要我们在Java文件中进行事件监听器的绑定就可以完成回调,即组件会处理对应的事件,即事件由事件源(组件)自身处理!(按键自己搞自己,监控是后台搞)(2)回调事件传播代码不贴了,只记录一下传播顺序:传播的顺序是:监听器--->view组件的回调方法--->Activity的回调方法;