Android invalidate 与postInvalidate用法

合集下载

android postdelay用法

android postdelay用法

android postdelay用法
`postDelayed()` 方法是 `Handler` 类中的一个方法,它允许您延迟一段时间后执行一个操作。

`postDelayed()` 方法有两个参数:
1. `Runnable` 对象,用于定义要执行的操作。

2. 要延迟的时间,以毫秒为单位。

以下是 `postDelayed()` 方法的用法示例:
```java
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
// 在延迟后要执行的操作
}
};
// 延迟 1000 毫秒(1 秒)后执行操作
handler.postDelayed(runnable, 1000);
```
在上面的示例中,`runnable` 对象中的 `run()` 方法定义了要在延迟后执行的操作。

然后,通过 `handler.postDelayed()` 方法将该 `runnable` 对象传递给 `Handler`,并指定延迟的时间(1000 毫秒)。

请注意,延迟时间不一定非得是固定的,您可以根据需要自行指定。

livedata用法

livedata用法

livedata用法
LiveData是一个用于观察数据变化的Android组件,主要用于数据的观察,进行UI更新或者业务处理等操作。

以下是一些常见的使用场景:
1. 当数据发生改变时,通过调用`setValue()`方法来更新LiveData的值,这会触发观察者的回调函数,进行UI更新。

2. 如果在子线程中更新了数据,需要调用`postValue()`方法将更新操作派发到主线程中,以避免在子线程中进行UI操作导致的异常。

3. 在观察LiveData时,可以使用`observe()`方法来注册观察者,并指定一个回调函数来处理数据变化。

通常在Activity或Fragment的`onCreate()`
方法中调用该方法。

4. LiveData可以和生命周期绑定,例如与Activity或Fragment的活跃状
态绑定。

这样当Lifecycle处于活跃状态时才进行数据回调,避免不必要的
操作。

以上是LiveData的基本用法,具体使用方式可能会根据不同的应用场景和
需求有所不同。

invalidate原理

invalidate原理

invalidate(失效)是指将缓存中的数据标记为无效或过期状态,以便在下次访问时重
新获取最新的数据。

在计算机系统中,缓存是用于暂时存储数据的高速存储区域,旨在提高数据访问的速
度和效率。

然而,缓存中的数据可能会因为外部变化(比如数据更新、删除、修改等)而变得不再有效。

当数据发生变化时,为了保持数据的一致性,需要将缓存中的对应数据标记为无效。

这就是invalidate的原理。

invalidate的过程通常包括以下几个步骤:
1. 监听数据变化:系统会监测数据源(比如数据库、文件系统、网络接口等)的变化。

这可以通过轮询、回调、触发器等方式实现。

2. 数据变化检测:当系统检测到数据源发生变化时,会根据预先设定的规则判断哪些
缓存数据需要失效。

3. 标记数据失效:对于需要失效的缓存数据,系统会将其标记为无效状态。

这可以通
过设置一个标记位、修改缓存中的状态字段等方式实现。

4. 下次访问重新获取数据:当应用程序下次访问缓存数据时,系统会检查数据的有效性。

如果数据被标记为无效,系统会重新从数据源中获取最新的数据,并更新缓存。

通过invalidate机制,可以保证缓存中的数据与数据源的一致性。

这样可以提高系统性
能和响应速度,并减轻对数据源的访问压力。

android中intent的用法

android中intent的用法

android中intent的用法Intent是Android开发中一个非常重要的概念,它是一种对象,用于在应用程序组件(如Activity、Service、BroadcastReceiver 等)之间传递信息。

通过使用Intent,我们可以启动Activity、传递数据给Service、接收广播等。

下面将详细介绍Intent在Android开发中的用法。

一、Intent的基本用法Intent可以通过AndroidSDK中的Context类的getIntent()方法创建,它可以包含多个Action和数据,用于启动不同的组件。

以下是Intent的基本用法示例:1.启动Activity:```javaIntentintent=newIntent(context,ActivityClass.class);startActivity(intent);```上述代码创建了一个Intent对象,指定了要启动的Activity的类名,并通过startActivity()方法启动该Activity。

2.启动Service:```javaIntentintent=newIntent(context,ServiceClass.class);intent.putExtra("key",value);//传递数据给Servicecontext.startService(intent);```上述代码创建了一个Intent对象,指定了要启动的Service的类名,并通过startService()方法启动该Service,并传递了一些数据给Service。

二、使用Intent传递数据除了启动组件之外,Intent还可以用于在组件之间传递数据。

可以使用putExtra()方法向Intent中添加数据,这些数据可以在另一个组件中使用getIntent()方法获取。

以下是一些传递数据的示例:1.启动Activity并传递数据:```javaIntentintent=newIntent(context,ActivityClass.class);intent.putExtra("key",value);//添加数据到Intent中startActivity(intent);```在另一个Activity中,可以使用getIntent()方法获取Intent,并使用getExtra()方法获取之前添加的数据。

Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等

Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等

方法一:(java习惯,在android不推荐使用)刚刚开始接触android线程编程的时候,习惯好像java一样,试图用下面的代码解决问题new Thread( new Runnable() {public void run() {myView.invalidate();}}).start();可以实现功能,刷新UI界面。

但是这样是不行的,因为它违背了单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

方法二:(Thread+Handler)查阅了文档和apidemo后,发觉常用的方法是利用Handler来实现UI线程的更新的。

Handler来根据接收的消息,处理UI更新。

Thread线程发出Handler消息,通知更新UI。

Handler myHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case TestHandler.GUIUPDATEIDENTIFIER:myBounceView.invalidate();break;}super.handleMessage(msg);}};class myThread implements Runnable {public void run() {while (!Thread.currentThread().isInterrupted()) {Message message = new Message();message.what = TestHandler.GUIUPDATEIDENTIFIER;TestHandler.this.myHandler.sendMessage(message);try {Thread.sleep(100);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}}以上方法demo看:/blog/411860方法三:(java习惯,不推荐)在Android平台中需要反复按周期执行方法可以使用Java上自带的TimerTask类,Tim erTask相对于Thread来说对于资源消耗的更低,除了使用Android自带的AlarmManager使用Timer定时器是一种更好的解决方法。

Android判断当前是否在主线程

Android判断当前是否在主线程

Android判断当前是否在主线程
Android开发中, 有时需要判断当前线程到底是主线程, 还是⼦线程, 例如: 我们在⾃定义View时, 想要让View重绘, 需要先判断当前线程到底是不是主线程, 然后根据判断结果来决定到底是调⽤invalidate()还是postInvalidate()⽅法. 如果当前是主线程, 就调⽤invalidate()⽅法; ⽽如果当前是⼦线程, 就调⽤postInvalidate()⽅法, 注意: ⼦线程中不能调⽤invalidate()⽅法, 否则就会报异常, 提⽰我们不能在⼦线程中更新UI。

那么, 我们如何判断当前线程到底是主线程, 还是⼦线程呢? 答案是: 可以借助于Looper. 代码如下:
public boolean isMainThread() {
return Looper.getMainLooper() == Looper.myLooper();
}
或者
public boolean isMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
或者
public boolean isMainThread() {
return Looper.getMainLooper().getThread().getId() == Thread.currentThread().getId();
}。

Invalidate与UpdateWindow的区别

Invalidate与UpdateWindow的区别

Invalidate()与UpdateWindow()的区别:void Invalidate( BOOL bErase = TRUE );该函数的作用是使整个窗口客户区无效。

窗口的客户区无效意味着需要重绘,例如,如果一个被其它窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。

这时Windows会在应用程序的消息队列中放置WM_PAINT消息。

MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。

视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。

参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。

BOOL UpdateWindow(HWND hWnd);这个UpdateWindow 函数通过发送重绘消息WM_PAINT 给目标窗体来更新目标窗体客户区的无效区域。

如果那个窗体的无效区域没有,就不发送重绘消息WM_PAINT 了。

注意了,这个API 函数是直接发送消息WM_PAINT 给目标窗体的,没有进入过消息队列。

函数参数:hWnd 一个要更新的窗体的句柄函数返回值:如果函数调用成功,返回值为非零值。

如果函数调用不成功,返回值为零。

在Windows NT/2000/XP中,我们可以使用API 函数GetLastError 来得到扩展的错误信息。

使用要求:Windows NT/2000/XP:包括Windows NT 3.1 及以后版本。

Windows 95/98/Me:包括Windows 95 及以后版本。

需要的头文件:需要包含Windows.h,在Winuser.h 中声明了此函数。

需要的库文件: Use User32.lib。

Invalidate()与UpdateAllViews()的分别:Invalidate()和UpdateWindow()区别在于:UpdateWindow()的作用是使窗口立即重绘。

invalidate postInvalidate 区别

invalidate postInvalidate 区别

由于最近需要用到很多的UI处理、动画处理、图片处理等问题,牵涉到的动态刷新和局部刷新的问题挺多的,现将在网上的一篇博文加上自己的理解在这里做个记号,以便方便的时候查询使用。

原文地址:/blog/168358根据Android SDK api文档说明invalidate 方法是用来更新视图(View)的方法,不过这东西的用法比较古怪invalidate 方法如果你直接在主线程中调用,是看不到任何更新的。

如果跟线程结合使用的话比如在下面的代码中就会抛出异常UIThread implements Runnable{public void run(){invalidate();}}上面的代码会抛出Only the original thread that created a view hierarchy can touch its views。

怎么样解决上面的问题呢,如果你有两个View,你需要一个View用来显示当前的状态,一个Thread去下载网络数据或者是读取文件等,这些数据读取完毕后你要更新View到当前屏幕上怎么办呢。

看看下面的代码,也许可以帮助你第一种解决方案是:class UIUpdateThread implements Runnable{public void run() {try {Thread.sleep(1000*5);mHandler.post(mUpdateResults);} catch (InterruptedException e) {e.printStackT race();}}final Handler mHandler = new Handler();final Runnable mUpdateResults = new Runnable() {public void run() {invalidate(); //更新视图}};}你必须实现一个Handler.然后再你下载数据的线程中放上一个mHandler.post(mUpdateResults);这样就可以了。

java&android的面试题(都是亲身经历的)

java&android的面试题(都是亲身经历的)

Http协议HTTP 是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。

它于1990 年提出,经过几年的使用与发展,得到不断地完善和扩展。

HTTP 协议的主要特点可概括如下:1.支持客户/服务器模式。

2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。

请求方法常用的有GET、HEAD、POST。

每种方法规定了客户与服务器联系的类型不同。

由于HTTP 协议简单,使得HTTP 服务器的程序规模小,因而通信速度很快。

3.灵活:HTTP 允许传输任意类型的数据对象。

正在传输的类型Content-Type 加以标记。

4.无连接:无连接的含义是限制每次连接只处理一个请求。

服务器处理完客户的请求,并收到客户的应答后,即断开连接。

采用这种方式可以节省传输时间。

5.无状态:HTTP 协议是无状态协议。

无状态是指协议对于事务处理没有记忆能力。

缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。

另一方面,在服务器不需要先前信息时它的应答就较快。

Http 请求由三部分组成,分别是:请求行、消息报头、请求正文请求行以一个方法符号开头,以空格分开,后面跟着请求的URI 和协议的版本,格式如下:Method Request-URI HTTP-Version CRLF其中Method 表示请求方法;Request-URI 是一个统一资源标识符;HTTP-Version 表示请求的HTTP 协议版本;CRLF 表示回车和换行(除了作为结尾的CRLF 外,不允许出现单独的CR 或LF 字符)。

请求方法(所有方法全为大写)有多种,各个方法的解释如下:GET 请求获取Request-URI 所标识的资源POST 在Request-URI 所标识的资源后附加新的数据HEAD 请求获取由Request-URI 所标识的资源的响应消息报头状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:1xx:指示信息--表示请求已接收,继续处理2xx:成功--表示请求已被成功接收、理解、接受3xx:重定向--要完成请求必须进行更进一步的操作4xx:客户端错误--请求有语法错误或请求无法实现5xx:服务器端错误--服务器未能实现合法的请求Intent的基本用法Intent主要用来在各个界面之间实现跳转及数据传递等意图,也可用来访问android系统提供一些功能。

Android中invalidate()和postInvalidate()的区别及使用方法

Android中invalidate()和postInvalidate()的区别及使用方法

Android中invalidate()和postInvalidate()的区别及使⽤⽅法Android中实现view的更新有两组⽅法,⼀组是invalidate,另⼀组是postInvalidate,其中前者是在UI线程⾃⾝中使⽤,⽽后者在⾮UI线程中使⽤。

Android提供了Invalidate⽅法实现界⾯刷新,但是Invalidate不能直接在线程中调⽤,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调⽤。

invalidate()是⽤来刷新View的,必须是在UI线程中进⾏⼯作。

⽐如在修改某个view的显⽰时,调⽤invalidate()才能看到重新绘制的界⾯。

invalidate()的调⽤是把之前的旧的view从主UI线程队列中pop掉。

⼀个Android 程序默认情况下也只有⼀个进程,但⼀个进程下却可以有许多个线程。

在这么多线程当中,把主要是负责控制UI界⾯的显⽰、更新和控件交互的线程称为UI线程,由于onCreate()⽅法是由UI线程执⾏的,所以也可以把UI线程理解为主线程。

其余的线程可以理解为⼯作者线程。

invalidate()得在UI线程中被调动,在⼯作者线程中可以通过Handler来通知UI线程进⾏界⾯更新;⽽postInvalidate()在⼯作者线程中被调⽤。

利⽤invalidate()刷新界⾯ 实例化⼀个Handler对象,并重写handleMessage⽅法调⽤invalidate()实现界⾯刷新;⽽在线程中通过sendMessage发送界⾯更新消息。

// 在onCreate()中开启线程new Thread( new GameThread()).start();、// 实例化⼀个handlerHandler myHandler = new Handler() {// 接收到消息后处理public void handleMessage(Message msg) {switch (msg.what) {case Activity01.REFRESH:mGameView.invalidate(); // 刷新界⾯break ;}super .handleMessage(msg);}};class GameThread implements Runnable {public void run() {while (!Thread.currentThread().isInterrupted()) {Message message = new Message();message.what = Activity01.REFRESH;// 发送消息Activity01.this .myHandler.sendMessage(message);try {Thread.sleep(100 );} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}}// 在onCreate()中开启线程new Thread(new GameThread()).start();、// 实例化⼀个handlerHandler myHandler = new Handler() {// 接收到消息后处理public void handleMessage(Message msg) {switch (msg.what) {case Activity01.REFRESH:mGameView.invalidate(); // 刷新界⾯break;}super.handleMessage(msg);}};class GameThread implements Runnable {public void run() {while (!Thread.currentThread().isInterrupted()) {Message message = new Message();message.what = Activity01.REFRESH;// 发送消息Activity01.this.myHandler.sendMessage(message);try {Thread.sleep(100);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}}使⽤postInvalidate()刷新界⾯ 使⽤postInvalidate则⽐较简单,不需要handler,直接在线程中调⽤postInvalidate即可。

android date 用法

android date 用法

android date 用法在Android开发中,处理日期和时间通常涉及到几个关键类,如 `Date`、`Calendar` 和 `` 包中的新日期时间API。

以下是这些类的一些基本用法:1. 使用 `Date` 类`Date` 类用于表示特定的瞬间,精确到毫秒。

```javaimport ;public class DateExample {public static void main(String[] args) {// 创建一个Date对象,代表当前时间Date currentDate = new Date();("当前时间: " + currentDate);// 获取年、月、日、时、分、秒int year = ();int month = () + 1; // getMonth() 是从0开始的,所以要+1 int day = ();int hour = ();int minute = ();int second = ();("年: " + year);("月: " + month);("日: " + day);("时: " + hour);("分: " + minute);("秒: " + second);}}```2. 使用 `Calendar` 类`Calendar` 类是一个抽象类,用于处理日期和时间。

```javaimport ;public class CalendarExample {public static void main(String[] args) {// 创建一个Calendar对象,并设置时区为GMTCalendar calendar = (("GMT"));("当前时间(GMT): " + ());// 设置特定的年、月、日、时、分、秒(2023, , 23, 10, 30, 0); // 年月日时分秒 (注意:月份是从0开始的,所以要-1)("特定时间(GMT): " + ());}}```3. 使用 `` 包中的新日期时间API(推荐)从Java 8开始,引入了一个新的日期和时间API,位于 `` 包中。

浅谈Androidinvalidate分析

浅谈Androidinvalidate分析

浅谈Androidinvalidate分析1. invalidate 和 postInvalidate 的关系postInvalidate 是通过 Handler 切换回到主线程,然后在调⽤ invalidate 的,源码:public void postInvalidate() {postInvalidateDelayed(0);}public void postInvalidateDelayed(long delayMilliseconds) {// We try only with the AttachInfo because there's no point in invalidating// if we are not attached to our windowfinal AttachInfo attachInfo = mAttachInfo;if (attachInfo != null) {attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);}}// ViewRootImpl 中public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);mHandler.sendMessageDelayed(msg, delayMilliseconds);}final class ViewRootHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_INVALIDATE:((View) msg.obj).invalidate();break;...}2. ⼦线程是否可以更新 UI ?可以的,在 Activity 的 onCreate 中直接开启⼦线程并在⼦线程中更新 UI 是没问题的:public class MainActivity extends Activity {private TextView tvText;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);tvText = (TextView) findViewById(R.id.main_tv);new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}tvText.setText("OtherThread");}}).start();}}原因:校验线程是 ViewRootImpl 来做的,但是它的创建流程是在 Activity 的 onResume 的时候:// ActivityThread 中final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {ActivityClientRecord r = mActivities.get(token);...if (r != null) {final Activity a = r.activity;if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE);ViewManager wm = a.getWindowManager();youtParams l = r.window.getAttributes();...if (a.mVisibleFromClient) {if (!a.mWindowAdded) {a.mWindowAdded = true;// 关键代码wm.addView(decor, l);} else {a.onWindowAttributesChanged(l);}}...}// WindowManagerGlobal 中public void addView(View view, youtParams params,Display display, Window parentWindow) {...ViewRootImpl root;View panelParentView = null;synchronized (mLock) {...// 在这⾥创建 ViewRootImplroot = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);...}}// 在 ViewRootImpl 中有这么段代码,所有更新 UI 都会⾛到这⾥void checkThread() {if (mThread != Thread.currentThread()) { // mThread 就是主线程throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");}}所以⼦线程只要在 ViewRootImpl 创建之前更新 UI 就没问题!3. invalidate 的源码分析先看⼀张图:invalidate 的流程于是⾃⼰尝试⾛⾛源码:// view 中public void invalidate() {invalidate(true);}public void invalidate(boolean invalidateCache) {invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);}void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,boolean fullInvalidate) {if (mGhostView != null) {mGhostView.invalidate(true);return;}if (skipInvalidate()) {return;}if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {if (fullInvalidate) {mLastIsOpaque = isOpaque();mPrivateFlags &= ~PFLAG_DRAWN;}mPrivateFlags |= PFLAG_DIRTY;if (invalidateCache) {mPrivateFlags |= PFLAG_INVALIDATED;mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;}// Propagate the damage rectangle to the parent view.final AttachInfo ai = mAttachInfo;final ViewParent p = mParent;if (p != null && ai != null && l < r && t < b) {final Rect damage = ai.mTmpInvalRect;damage.set(l, t, r, b);// 调⽤⽗类的 invalidateChild ⽅法p.invalidateChild(this, damage);}// Damage the entire projection receiver, if necessary.if (mBackground != null && mBackground.isProjected()) {final View receiver = getProjectionReceiver();if (receiver != null) {receiver.damageInParent();}}}}看到 View 的 invalidate 最后是调⽤了 p.invalidateChild(this, damage); p 是 ViewParent 的对象,具体实现是 ViewGroup // ViewGroup 中@Overridepublic final void invalidateChild(View child, final Rect dirty) {final AttachInfo attachInfo = mAttachInfo;...ViewParent parent = this;do {View view = null;...// 关键代码parent = parent.invalidateChildInParent(location, dirty);...} while (parent != null);}@Overridepublic ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {// either DRAWN, or DRAWING_CACHE_VALIDif ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))!= FLAG_OPTIMIZE_INVALIDATE) {dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,location[CHILD_TOP_INDEX] - mScrollY);if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {dirty.union(0, 0, mRight - mLeft, mBottom - mTop);}final int left = mLeft;final int top = mTop;if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {dirty.setEmpty();}}location[CHILD_LEFT_INDEX] = left;location[CHILD_TOP_INDEX] = top;} else {if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {dirty.set(0, 0, mRight - mLeft, mBottom - mTop);} else {// in case the dirty rect extends outside the bounds of this containerdirty.union(0, 0, mRight - mLeft, mBottom - mTop);}location[CHILD_LEFT_INDEX] = mLeft;location[CHILD_TOP_INDEX] = mTop;mPrivateFlags &= ~PFLAG_DRAWN;}mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;if (mLayerType != LAYER_TYPE_NONE) {mPrivateFlags |= PFLAG_INVALIDATED;}return mParent;}return null;}上⾯ invalidateChildInParent 开始时会调⽤ ViewGroup ⾃⼰的 invalidateChildInParent ⽅法,但到最后还是会调⽤到ViewRootImpl 中的 invalidateChildInParent,看下 ViewRootImpl 中的具体实现// ViewRootImpl 中@Overridepublic ViewParent invalidateChildInParent(int[] location, Rect dirty) {checkThread();if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);if (dirty == null) {invalidate();return null;} else if (dirty.isEmpty() && !mIsAnimating) {return null;}if (mCurScrollY != 0 || mTranslator != null) {mTempRect.set(dirty);dirty = mTempRect;if (mCurScrollY != 0) {dirty.offset(0, -mCurScrollY);}if (mTranslator != null) {mTranslator.translateRectInAppWindowToScreen(dirty);}if (mAttachInfo.mScalingRequired) {dirty.inset(-1, -1);}}// ⼜调⽤了这个⽅法invalidateRectOnScreen(dirty);return null;}private void invalidateRectOnScreen(Rect dirty) {final Rect localDirty = mDirty;if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {mAttachInfo.mSetIgnoreDirtyState = true;mAttachInfo.mIgnoreDirtyState = true;}// Add the new dirty rect to the current onelocalDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);// Intersect with the bounds of the window to skip// updates that lie outside of the visible regionfinal float appScale = mAttachInfo.mApplicationScale;final boolean intersected = localDirty.intersect(0, 0,(int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));if (!intersected) {localDirty.setEmpty();}if (!mWillDrawSoon && (intersected || mIsAnimating)) {// 关键⼜调⽤了这个⽅法scheduleTraversals();}}void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();// 会调⽤ mTraversalRunnable 中的 run ⽅法mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) {scheduleConsumeBatchedInput();}notifyRendererOfFramePending();pokeDrawLockIfNeeded();}}final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}}void doTraversal() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);if (mProfile) {Debug.startMethodTracing("ViewAncestor");}// 终于到了关键⽅法了:performTraversals();if (mProfile) {Debug.stopMethodTracing();mProfile = false;}}}ViewRootImpl 最终调⽤到了performTraversals 中,这个⽅法巨长,涉及到了 onMeasure/onLayout/onDraw 等重要⽅法的起源:private void performTraversals() {...// 这⾥最终会触发 view 的 onMeasureperformMeasure(childWidthMeasureSpec, childHeightMeasureSpec);performLayout(lp, mWidth, mHeight);performDraw();...mIsInTraversal = false;}看到上⾯就是对应着 View 的绘制流程了,继续看 performDraw 的实现:private void performDraw() {...try {draw(fullRedrawNeeded);} finally {mIsDrawing = false;Trace.traceEnd(Trace.TRACE_TAG_VIEW);}...}private void draw(boolean fullRedrawNeeded) {if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {return;}}private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty) {...try {canvas.translate(-xoff, -yoff);if (mTranslator != null) {mTranslator.translateCanvas(canvas);}canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);attachInfo.mSetIgnoreDirtyState = false;// 最终调⽤了 View 的 draw ⽅法了mView.draw(canvas);} finally {...}return true;}看到终于调⽤到 View 的 draw ⽅法来了,继续看下 ViewGroup 和 View 在这⽅法中的处理⽅式:// View 中的 draw ⽅法:public void draw(Canvas canvas) {final int privateFlags = mPrivateFlags;final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;/** Draw traversal performs several drawing steps which must be executed* in the appropriate order:** 1. Draw the background* 2. If necessary, save the canvas' layers to prepare for fading* 3. Draw view's content* 4. Draw children* 5. If necessary, draw the fading edges and restore layers* 6. Draw decorations (scrollbars for instance)*/// Step 1, draw the background, if neededint saveCount;if (!dirtyOpaque) {drawBackground(canvas);}// skip step 2 & 5 if possible (common case)final int viewFlags = mViewFlags;boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;if (!verticalEdges && !horizontalEdges) {// Step 3, draw the contentif (!dirtyOpaque) onDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);drawAutofilledHighlight(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);// Step 7, draw the default focus highlightdrawDefaultFocusHighlight(canvas);if (debugDraw()) {debugDrawFocus(canvas);}// we're done...return;}/** Here we do the full fledged routine...* (this is an uncommon case where speed matters less,* this is why we repeat some of the tests that have been* done above)*/boolean drawTop = false;boolean drawBottom = false;boolean drawLeft = false;boolean drawRight = false;float topFadeStrength = 0.0f;float bottomFadeStrength = 0.0f;float leftFadeStrength = 0.0f;float rightFadeStrength = 0.0f;// Step 2, save the canvas' layersint paddingLeft = mPaddingLeft;final boolean offsetRequired = isPaddingOffsetRequired();if (offsetRequired) {paddingLeft += getLeftPaddingOffset();}int left = mScrollX + paddingLeft;int right = left + mRight - mLeft - mPaddingRight - paddingLeft;int top = mScrollY + getFadeTop(offsetRequired);int bottom = top + getFadeHeight(offsetRequired);if (offsetRequired) {right += getRightPaddingOffset();bottom += getBottomPaddingOffset();}final ScrollabilityCache scrollabilityCache = mScrollCache;final float fadeHeight = scrollabilityCache.fadingEdgeLength;int length = (int) fadeHeight;// clip the fade length if top and bottom fades overlap// overlapping fades produce odd-looking artifactsif (verticalEdges && (top + length > bottom - length)) {length = (bottom - top) / 2;}// also clip horizontal fades if necessaryif (horizontalEdges && (left + length > right - length)) {length = (right - left) / 2;}if (verticalEdges) {topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));drawTop = topFadeStrength * fadeHeight > 1.0f;bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength())); drawBottom = bottomFadeStrength * fadeHeight > 1.0f;}if (horizontalEdges) {leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));drawLeft = leftFadeStrength * fadeHeight > 1.0f;rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));drawRight = rightFadeStrength * fadeHeight > 1.0f;}saveCount = canvas.getSaveCount();int solidColor = getSolidColor();if (solidColor == 0) {final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;if (drawTop) {canvas.saveLayer(left, top, right, top + length, null, flags);}if (drawBottom) {canvas.saveLayer(left, bottom - length, right, bottom, null, flags);}if (drawLeft) {canvas.saveLayer(left, top, left + length, bottom, null, flags);}if (drawRight) {canvas.saveLayer(right - length, top, right, bottom, null, flags);}} else {scrollabilityCache.setFadeColor(solidColor);}// Step 3, draw the contentif (!dirtyOpaque) onDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);// Step 5, draw the fade effect and restore layersfinal Paint p = scrollabilityCache.paint;final Matrix matrix = scrollabilityCache.matrix;final Shader fade = scrollabilityCache.shader;if (drawTop) {matrix.setScale(1, fadeHeight * topFadeStrength);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);canvas.drawRect(left, top, right, top + length, p);}if (drawBottom) {matrix.setScale(1, fadeHeight * bottomFadeStrength);matrix.postRotate(180);matrix.postTranslate(left, bottom);fade.setLocalMatrix(matrix);p.setShader(fade);canvas.drawRect(left, bottom - length, right, bottom, p);}if (drawLeft) {matrix.setScale(1, fadeHeight * leftFadeStrength);matrix.postRotate(-90);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);canvas.drawRect(left, top, left + length, bottom, p);}if (drawRight) {matrix.setScale(1, fadeHeight * rightFadeStrength);matrix.postRotate(90);matrix.postTranslate(right, top);fade.setLocalMatrix(matrix);p.setShader(fade);canvas.drawRect(right - length, top, right, bottom, p);}canvas.restoreToCount(saveCount);drawAutofilledHighlight(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);}这个⽅法的⼤体意思是这样的:@Overridepublic void draw(Canvas canvas) {...drawBackground(canvas); // 绘制背景onDraw(canvas); // 调⽤⾃⼰的 onDraw ⽅法来绘制内容dispatchDraw(canvas); // 分发绘制onDrawForeground(canvas); // 绘制前景...}上⾯⼏个⽅法中,只有 dispatchDraw 涉及到分发绘制,其他的都是对⾃⾝的绘制,所以继续看 dispatchDraw// View 中的实现,是个空⽅法,也就是 View 没有孩⼦,不需要什么分发protected void dispatchDraw(Canvas canvas) {}View 中的实现,是个空⽅法,也就是 View 没有孩⼦,不需要什么分发。

android常用控件属性方法事件说明

android常用控件属性方法事件说明

1,TextView用于在界面上显示文字,内容不可编辑,继承View2,EditText是可供用户编辑内容的文本框。

继承TextView,可重用TextView定义的属性3,按钮控件Button,继承TextView,ImageButton继承ImageViewButton可根据用户的操作动态切换图片,需定义drawable资源<selector><?xml version="1.0" encoding="utf-8"?><selector xmlns:android="/apk/res/android"><item android:drawable="@drawable/app_icon" android:state_pressed="true"/> <item android:drawable="@drawable/app_notes"android:state_pressed="false"/> </selector>4,单选按钮RadioButton继承至Button按钮。

可通过android:checked属性指定是否选中。

一组RadioButton只能选择其一,因此它需要与RadioGroup一起使用。

5,复选框按钮CheckBox。

继承至Button按钮。

可通过android:checked属性指定是否选中7,AnalogClock继承View,重写了onDraw()方法。

显示模拟时钟,不会显示当前的秒数。

DigitalClock继承TextView.显示数字时钟,会显示当前秒数。

8,图片显示控件ImageView,继承View,任何Drawable对象都可以用它来显示,注意src 属性和background属性的区别9,自动完成AutoCompleteTextView继承至EditText,当用户输入信息后,会显示下拉菜单10,MultiAutoCompleteTextView可支持选择多个值,分别用分隔符分开,并且在每个值选中的时候再次输入值时会自动去匹配,它与AutoCompleteTextView的区别是AutoCompleteTextView一个文本框只能输入一个值,而MultiAutoCompleteTextView可以输入多个值,中间的分隔符在代码中设置11,日期时间控件DatePicker/TimePicker,继承至FrameLayout,DatePicker用于选择日期,TimePicker用于选择时间12,进度条ProgressBar用于向用户显示某个耗时操作完成的百分比,可通过style属性为其指定风格,setProgress(int) 设置进度完成的百分比,incrementProgressBy(int) 设置增量/减量13,拖动条seekBar,继承AbsSeekBar,通常用于对系统的某些参数进行调节,允许改变滑块的外观,Android:thumb 用来指定一个Drawable对象,OnSeekBarChangeListener可处理滑动事件14,星级评分条RatingBar,继承AbsSeekBar,OnRatingBarChangeListener可处理评分事件15,代表应用程序的菜单Menu,Android中的菜单需要用户单击手机上的“MENU”按键时才会显示。

android lambda 表达式使用方法(一)

android lambda 表达式使用方法(一)

android lambda 表达式使用方法(一)Android Lambda 表达式使用介绍Lambda 表达式是 Java 8 引入的一个重要特性,它能够简化代码编写和提升代码可读性。

在 Android 开发中,使用 Lambda 表达式可以更便捷地处理各种事件和回调。

Lambda 表达式的基本语法Lambda 表达式的基本语法如下:(parameter) -> { code }其中,参数列表可以为空,也可以包含一个或多个参数;代码块可以包含一条或多条语句。

使用 Lambda 表达式的步骤使用 Lambda 表达式的步骤如下:1.引入 Java 8 支持。

2.根据需要定义 Lambda 表达式的输入参数和输出类型。

3.通过使用合适的函数式接口,将 Lambda 表达式作为方法的参数进行传递。

使用 Lambda 表达式处理点击事件下面是一个处理点击事件的例子:button.setOnClickListener((view) -> {// 处理点击事件的代码});在这个例子中,我们使用 Lambda 表达式替代了传统的OnClickListener 匿名类。

使用 Lambda 表达式处理后台线程任务使用 Lambda 表达式可以更便捷地执行后台线程任务,例如使用线程池执行一个异步任务:executor.submit(() -> {// 后台线程任务的代码});在这个例子中,我们使用 Lambda 表达式将后台线程任务传递给线程池的 submit 方法。

使用 Lambda 表达式处理集合元素在处理集合元素时,Lambda 表达式也能够派上用场。

下面是一个对列表元素进行遍历的例子:list.forEach((item) -> {// 处理集合元素的代码});在这个例子中,我们使用 Lambda 表达式替代了传统的迭代器方式。

使用 Lambda 表达式创建新线程Lambda 表达式也可用于创建新线程并执行任务。

postinvalidate的调用流程

postinvalidate的调用流程

postinvalidate的调用流程
1. 找到对应的View对象:在调用postinvalidate方法之前,首先需要找到需要刷新的View对象。

可以通过findViewById方法、或者监听事件的回调函数中获得View对象的引用。

2. 创建一个Runnable对象:postinvalidate方法的参数是一个Runnable对象,用于在UI线程中执行刷新操作。

这里我们可以自定义一个Runnable对象或者使用匿名内部类的方式创建一个Runnable对象。

3. 将Runnable对象添加到消息队列中:通过调用View对象的post 方法,将刚刚创建的Runnable对象添加到主线程的消息队列中。

post方法内部会调用Handler对象的post方法,将Runnable对象封装成一个Message对象,并通过Handler对象将Message对象发送到主线程的消息队列中。

4. 主线程处理消息队列中的消息:在主线程空闲时,会从消息队列中取出一个Message对象,并调用Message对象中封装的Runnable对象的run方法,在UI线程中执行刷新操作。

总结起来,postinvalidate的调用流程主要分为两个部分:在子线程中创建Runnable对象并添加到消息队列中,以及在主线程中处理消息队列中的Runnable对象,从而实现View的刷新。

这个流程保证了刷新操作在主线程进行,避免了在子线程中直接操作UI导致的线程安全问题。

androidSerialiazble和Parcelable的用法

androidSerialiazble和Parcelable的用法

androidSerialiazble和Parcelable的⽤法⼀.通过dumpsys meminfo命令查看⼀个进程的内存情况1.通过dos 链接⼿机命令如下:(前提⼿机需要root)2.输⼊命令:adb shell "dumpsys meminfo com.winorout.travelclient.activity"3.运⾏结果如下:⼆.Serialiazble 和 Parcelable 的⽤法1.总体描述:在Android开发过程中,经常要在Activity之间传递参数,使⽤Android系统提供的⽅法可以传递基本数据类型的变量,但有时候我们经常要传递⼀些复杂的数据类型或⾃定义的类,这种情况的参数⽆法直接传递,我们可以通过序列化实现。

为什么要将对象序列化?1、永久性保存对象,保存对象的字节序列到本地⽂件中;2、⽤过序列化对象在⽹络中传递对象;3、通过序列化对象在进程间传递对象。

常⽤到serialiable和parcelable接⼝区别:Serializable的作⽤是为了保存对象的属性到本地⽂件、数据库、⽹络流、rmi以⽅便数据传输,当然这种传输可以是程序内的也可以是两个程序间的Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)⾼效的传输数据⽽设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。

Parcelable的性能⽐Serializable好,在内存开销⽅⾯较⼩,所以在内存间数据传输时推荐使⽤Parcelable,如activity 间传输数据,⽽Serializable可将数据持久化⽅便保存,所以在需要保存或⽹络传输数据时选择Serializable,因为android 不同版本Parcelable可能不同,所以不推荐使⽤Parcelable进⾏数据持久化实现:对于Serializable,类只需要实现Serializable接⼝,并提供⼀个序列化版本id(serialVersionUID)即可。

Android开发要求规范

Android开发要求规范

安卓应用开发规1. 开发工具设置1.1 编码方式统一用UTF-8. Android Studio默认已是UTF-8,只要不去改动它就可以了。

1.2 缩进统一为4个空格,将Tab size设置为4则可以保证tab键按4个空格缩进。

另外,不要勾选上Use tab character,可以保证切换到不同tab长度的环境时还能继续保持统一的4个空格的缩进样式。

1.3 一行声明一个变量,不要一行声明多个变量,这样有利于写注释。

private String param1; // 参数1private String param2; // 参数21.4. 行宽设置为100,设置格式化时自动断行到行宽位置。

1.5. 使用快捷键进行代码自动格式化。

1.6. 一个方法最多不要超过40行代码。

1.7 围型的常量用枚举类定义,而不要直接用整型或字符,这样可以减少围值的有效性检查。

// 用枚举类定义,Goodpublic enum CouponType {// 现金券SerializedName("1")CASH,// 抵用券SerializedName("2")DEBIT,// 折扣券SerializedName("3")DISCOUNT}// 用整型定义,Badpublic static final int TYPE_CASH = 1; // 现金券public static final int TYPE_DEBIT = 2; // 抵扣券public static final int TYPE_DISCOUNT = 3; // 折扣券1.8 文字大小的单位统一用sp,元素大小的单位统一用dp。

1.9 应用中的字符串统一在strings.xml中定义,然后在代码和布局文件中引用。

1.10 颜色值统一在colors.xml中定义,然后在代码和布局文件中引用。

另外,不要在代码和布局文件中引用系统的颜色,除了透明。

Android高级面试题

Android高级面试题

Android高级面试题(⭐⭐⭐)一、性能优化1、App稳定性优化1、你们做了哪些稳定性方面的优化?随着项目的逐渐成熟,用户基数逐渐增多,DAU持续升高,我们遇到了很多稳定性方面的问题,对于我们技术同学遇到了很多的挑战,用户经常使用我们的App 卡顿或者是功能不可用,因此我们就针对稳定性开启了专项的优化,我们主要优化了三项:•Crash专项优化(=>2)•性能稳定性优化(=>2)•业务稳定性优化(=>3)通过这三方面的优化我们搭建了移动端的高可用平台。

同时,也做了很多的措施来让App真正地实现了高可用。

2、性能稳定性是怎么做的?•全面的性能优化:启动速度、内存优化、绘制优化•线下发现问题、优化为主•线上监控为主•Crash专项优化我们针对启动速度,内存、布局加载、卡顿、瘦身、流量、电量等多个方面做了多维的优化。

我们的优化主要分为了两个层次,即线上和线下,针对于线下呢,我们侧重于发现问题,直接解决,将问题尽可能在上线之前解决为目的。

而真正到了线上呢,我们最主要的目的就是为了监控,对于各个性能纬度的监控呢,可以让我们尽可能早地获取到异常情况的报警。

同时呢,对于线上最严重的性能问题性问题:Crash,我们做了专项的优化,不仅优化了Crash的具体指标,而且也尽可能地获取了Crash发生时的详细信息,结合后端的聚合、报警等功能,便于我们快速地定位问题。

3、业务稳定性如何保障?•数据采集 + 报警•需要对项目的主流程与核心路径进行埋点监控,•同时还需知道每一步发生了多少异常,这样,我们就知道了所有业务流程的转换率以及相应界面的转换率•结合大盘,如果转换率低于某个值,进行报警•异常监控 + 单点追查•兜底策略移动端业务高可用它侧重于用户功能完整可用,主要是为了解决一些线上一些异常情况导致用户他虽然没有崩溃,也没有性能问题,但是呢,只是单纯的功能不可用的情况,我们需要对项目的主流程、核心路径进行埋点监控,来计算每一步它真实的转换率是多少,同时呢,还需要知道在每一步到底发生了多少异常。

android lambda 表达式使用方法

android lambda 表达式使用方法

android lambda 表达式使用方法在Android开发中,Lambda表达式是一种简洁的语法形式,可以用于简化代码和增强可读性。

Lambda表达式实际上是一个函数的匿名实现,它可以替代传统的匿名内部类。

Lambda表达式的基本语法形式如下:(parameters) -> expression或者(parameters) -> { statements; }其中,parameters是方法的参数列表,expression是一个表达式,statements 是一组语句。

下面是一个使用Lambda表达式的例子:```javaButton button = findViewById(R.id.button);button.setOnClickListener(view -> {Toast.makeText(this, 'Button clicked!',Toast.LENGTH_SHORT).show();});```在上面的例子中,我们使用Lambda表达式来实现了按钮的点击事件。

Lambda表达式替代了传统的匿名内部类,使代码更加简洁。

Lambda表达式可以用于各种场景,例如列表的排序和过滤,线程的处理等。

下面是一个使用Lambda表达式对一个字符串列表进行排序的例子:```javaList<String> names = Arrays.asList('John', 'Alice', 'Bob'); Collections.sort(names, (a, b) -> pareTo(b));```在上面的例子中,我们使用Lambda表达式作为参数传递给`Collections.sort`方法,实现了对字符串列表的排序。

除了简化代码,Lambda表达式还可以增强可读性。

通过使用Lambda表达式,我们可以更清晰地表达代码的意图,使代码更易于理解和维护。

android xml meta-data value语法

android xml meta-data value语法

在Android开发中,XML Meta-Data值通常用于在AndroidManifest.xml文件中定义应用的特性(features)、权限(permissions)、活动(activities)、服务(services)、广播接收器(receivers)和内容提供器(providers)。

Meta-Data元素用于为这些组件提供额外的配置信息。

下面是一个示例,展示了如何在AndroidManifest.xml文件中使用Meta-Data元素:xml<activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="UNCHER" /></intent-filter><meta-dataandroid:name="com.example.app_name"android:value="meta_data_value" /></activity>在这个例子中,我们定义了一个活动(Activity),并为其添加了一个名为com.example.app_name 的Meta-Data元素,其值为meta_data_value。

请注意以下几点:1. android:name属性是必须的,它定义了Meta-Data的键。

这个键是唯一的,在整个应用中不能重复。

2. android:value属性是可选的,它定义了Meta-Data的值。

这个值可以是字符串、整数、布尔值等。

3. Meta-Data元素可以添加到任何组件定义中,如活动、服务、广播接收器、内容提供器和权限。

相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

根据Android SDK api文档说明 invalidate 方法是用来更新视图(View)的方法,不过这东西的用法比较古怪 invalidate 方法如果你直接在主线程中调用,是看不到任何更新的。

如果跟线程结合使用的话比如在下面的代码中就会抛出异常 UIThread implements Runnable{ public voi
根据Android SDK api文档说明
invalidate 方法是用来更新视图(View)的方法,不过这东西的用法比较古怪
invalidate 方法如果你直接在主线程中调用,是看不到任何更新的。

如果跟线程结合使用的话
比如在下面的代码中就会抛出异常
UIThread implements Runnable{
public void run(){
invalidate();
}
}
上面的代码会抛出Only the original thread that created a view hierarchy can touch its views。

怎么样解决上面的问题呢,如果你有两个View,你需要一个View用来显示当前的状态,一个Thread去下载网络数据
或者是读取文件等,这些数据读取完毕后你要更新View到当前屏幕上怎么办呢。

看看下面的代码,也许可以帮助你
第一种解决方案是:
class UIUpdateThread implements Runnable{
public void run() {
try {
Thread.sleep(1000*5);
mHandler.post(mUpdateResults);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
final Handler mHandler = new Handler();
final Runnable mUpdateResults = new Runnable() {
public void run() {
invalidate(); //更新视图
}
};
}
你必须实现一个Handler.然后再你下载数据的线程中放上一个
mHandler.post(mUpdateResults);这样就可以了。

第2中方案比较简单
LoadDataThread implements Runnable{
public void run(){
doLoadData();
mHandler.sendMessage(mHandler.obtainMessage()); //这里系统会自动调用handleMessage;这样就可以更新视图了
}
}
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 这里处理视图需要更新的代码。

}
};
总结,发现Google Android 好像有点弱智了。

Api这么难用,不如J2ME简单啊。

相关文档
最新文档