首页 文章 精选 留言 我的

精选列表

搜索[官方],共10000篇文章
优秀的个人博客,低调大师

中小企业如何建官方网站 | 推荐阿里云自营建站服务

近期准备使用ThinkCMF管理架构搭建一个新的资讯网站,已经拥有的两个域名全是在阿里购买以及备案的,现在需要选择一个适合的云服务器,所以就逛了逛阿里云,看看对于建站方面有没有什么推荐,然后无意中发现阿里云推出的云建站服务,这对于中小企业来说简直是福利了,基本上是个标准的公司都开始有了自己的官网,笑话,没有官网客户怎么了解你的产品,有可能就是因为你的官网设计的标准,大气,客户就会对你的信任度增加,从而促进一笔不小的订单,这都是有可能的啊。 然鹅,现实的问题是,不是所有的公司都是互联网公司,不是每个公司都有软件开发人员的,所以建站难免成了一个障碍,很多公司可能会选择花钱找私人承包,这样的选择弊端太多了,① 价格不透明 ② 后期维护不方便 ③ 容易被攻击 等等问题你还放心的下吗? 阿里云企业建站服务介绍 云速成美站,标配阿里云空间,PC站+手机站+微信公众号+小程序一应俱全,只要你会打字就可以完成建站操作,难易程度大概和制作一个PPT差不多,根本没有什么门槛。阿里云还提供千套企业模板,风格颜色随心选择,40余种功能控件,按需搭配使用,建站就是这么简单! 云企业官网,阿里云会为客户量身打造,一站式标准化建站服务,满足企业建站所需。从咨询到购买,标准价格,标准功能配置,足不出户,轻松搞定,购买成功后,应用标准化服务流程,客户可享受从网站策划到上线的全程定值服务。更有项目经理为客户整理需求,搭建网站架构,预置网站内容;设计师一对一进行视觉美化,风格由客户选择: 高大上?满足建筑美?满足极简风?满足全部建站过程,无需客户亲自操作,简单高效,品质有保障,毕竟大公司,值得信赖,哈哈哈。 为什么选择阿里云自营建站服务? 选择小公司,后期维护难,安全系数低,轻易被攻击、挂马,而且网站的后续升级改版同样比较麻烦,运营人员需要不断的联系程序人员,费钱还不省心。我们选择阿里云建站服务,安全省心,花钱不再担心打水漂! 阿里云建站全面应用云计算,标配HTTPS,网站安全稳定访问快。网站后台可视化操作,每季度版本更新,免费享用,轻松简单易维护。阿里云的承诺是只有你想不到,没有他们办不到,建站就找阿里云,和传统建站说拜拜。 想要了解更多的可以自己到官网去看看详细内容,选择一个大公司毕竟还是比较靠谱的事情:https://www.aliyun.com

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:Activity测试之UI组件测试

原文地址:http://android.xsoftlab.net/training/activity-testing/activity-ui-testing.html 一般来说,正因为Activity含有UI组件,所以使得用户可以与程序交互。这节课将会介绍如何测试Activity中的Button组件。在课程学习之后便可以使用相同的方法对其它UI组件进行测试。 Note:这节课中所涉及的UI测试被称为白盒测试,因为开发者本身持有被测试的源代码。Android设备框架适用于UI组件的白盒测试。另一种测试类型被称为黑盒测试,因为不能够得到程序的源代码,故此得名。这种测试适用于与其它APP或系统交互的测试情况。黑盒测试在这里并不会涵盖。有关更多如何执行黑盒测试的相关内容,请参见UI测试指南。 创建UI测试用例 虽然Activity运行于UI线程,但是测试程序本身是运行在子线程中的。这意味着,虽然TestAPP可以引用UI线程的对象,但是如果要更改这些对象的属性或者发送事件给UI线程,那么将会得到一个WrongThreadException错误。 为了可以安全的发送Intent到Activity或者在UI线程中运行测试方法,开发者可以使测试类继承于ActivityInstrumentationTestCase2类。 设置测试先决条件 当为UI测试设置先决条件时,则需要在setUp()方法中指定TouchMode。设置TouchMode为true可以使后面的测试方法在自动化点击UI组件时防止真正点击(例如,测试Button只是调用了它的onclick方法)。另外要确保在调用getActivity()方法之前调用了setActivityInitialTouchMode()方法。 例如: public class ClickFunActivityTest extends ActivityInstrumentationTestCase2 { ... @Override protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); mClickFunActivity = getActivity(); mClickMeButton = (Button) mClickFunActivity .findViewById(R.id.launch_next_activity_button); mInfoTextView = (TextView) mClickFunActivity.findViewById(R.id.info_text_view); } } 添加测试方法 一般需要测试的点会包含以下部分: 当Activity启动时,验证Button的布局是否显示正确。 验证TextView在初始化时是否是隐藏的。 验证Button按下后,TextView上的文本是否变为了期望的值。 下面将会演示如何测试以上部分: 验证Button的布局参数 开发者可能需要以下代码来验证Button的布局是否正确: @MediumTest public void testClickMeButton_layout() { final View decorView = mClickFunActivity.getWindow().getDecorView(); ViewAsserts.assertOnScreen(decorView, mClickMeButton); final ViewGroup.LayoutParams layoutParams = mClickMeButton.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } 在调用assertOnScreen()方法时,应当将rootView以及需要验证的View传递进去。如果需要验证的View没有在rootView中出现,那么判断方法会抛出一个AssertionFailedError异常。 开发者还可以通过Button的布局参数来验证Button的布局是否正确,然后通过判断方法来验证Button的高宽是否是期望中的值。 @MediumTest注解说明了这个测试方法应当如何分类。如何分类取决于测试方法的执行时间。 验证TextView的布局参数 开发者也可能需要通过以下代码来验证TextView在初始化时是否是隐藏的: @MediumTest public void testInfoTextView_layout() { final View decorView = mClickFunActivity.getWindow().getDecorView(); ViewAsserts.assertOnScreen(decorView, mInfoTextView); assertTrue(View.GONE == mInfoTextView.getVisibility()); } 开发者可以通过getDecorView()方法获得Activity的DecorView引用。DecorView在布局层级中属于最高等级的ViewGroup. 验证Button的行为 开发者可以根据以下测试方法来验证在Button按下后TextView是否变为可见状态。 @MediumTest public void testClickMeButton_clickButtonAndExpectInfoText() { String expectedInfoText = mClickFunActivity.getString(R.string.info_text); TouchUtils.clickView(this, mClickMeButton); assertTrue(View.VISIBLE == mInfoTextView.getVisibility()); assertEquals(expectedInfoText, mInfoTextView.getText()); } 为了可以自动点击Button,需要调用clickView()方法。该方法需要传入测试用例的引用以及对应Button的引用。 Note: 辅助类TouchUtils提供了一些用于模拟交互的简单方法,开发者可以使用这些方法来模拟点击,拖拽等事件。 Note: TouchUtils中的方法用于从测试线程向UI线程中发送事件。开发者最好不要在UI线程中直接调用TouchUtils的相关方法,否则会引起WrongThreadException异常. 测试注解 以下注解可以用来标明测试方法的大小: @SmallTest @MediumTest @LargeTest 一般来说,一个只有几毫秒的剪短测试一般应该标为@SmallTest。稍长一点的,大概100毫秒左右的,通常应该标为@MediumTest或@LargeTest,测试的执行时间通常取决于是否需要访问本地资源或者网络资源。 开发者应当通过注解来标记测试方法,以便更好的组织、运行测试。

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:Activity测试之创建运行测试

原文地址:http://android.xsoftlab.net/training/activity-testing/activity-basic-testing.html 为了验证在布局与功能上没有差池,很重要的一点就是需要为每个Activity创建对应的测试类。对每个测试类还需要创建单独的测试用例,这其中包含测试环境,测试条件以及对Activity测试的测试方法。实现这些便可以进行测试并获知结果。如果其中一项测试失败了,这便意味着在代码中可能存在潜在的问题。 创建测试用例 Activity测试都以结构化的方式实现。要确保将所有的测试放在一个单独的包中,与被测试代码区别开来。 依照惯例,测试包的包名应当由应用的包名+后缀”.tests”组成。在完成测试包的创建之后,添加一个Java类以用于测试。依照惯例,该类的名称应当由要测试的类的类名+后缀”Test”组成。 在Eclipse中创建测试用例的步骤如下: a.在工程中新建一个包。 b.设置包名为< your_app_package_name>.tests(例如,com.example.android.testingfun.tests),并点击Finish。 c.在该包名下创建一个类。 d.设置类名为< your_app_activity_name>Test(例如,MyFirstTestActivityTest),并点击Finish。 设置测试先决条件 测试先决条件由一系列用于测试的对象组成。设置这些先决条件需要重写setUp()方法以及tearDown()方法。TestRunner会在测试之前调用setUp()方法,在测试结束之后调用tearDown()方法。开发者可以通过这两个方法来确保测试被正常初始化以及可以确保在测试结束之后可以及时清理。 在Eclipse中设置测试先决条件: 1.将上面创建好的测试类继承于ActivityTestCase的任一子类。例如: public class MyFirstTestActivityTest extends ActivityInstrumentationTestCase2<MyFirstTestActivity> { 2.接下来,在这个类的内部添加构造方法以及setUp()方法,并添加要测试的Activity的变量声明。例如: public class MyFirstTestActivityTest extends ActivityInstrumentationTestCase2<MyFirstTestActivity> { private MyFirstTestActivity mFirstTestActivity; private TextView mFirstTestText; public MyFirstTestActivityTest() { super(MyFirstTestActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); mFirstTestActivity = getActivity(); mFirstTestText = (TextView) mFirstTestActivity .findViewById(R.id.my_first_test_text_view); } } 构造方法会在类初始化时由TestRunner调用,而setUp()方法则会在开始测试之前调用。 通常情况下,在setUp()方法内,应当实现以下内容: - 调用父类的setUp()方法。 - 通过以下步骤初始化先决条件: - 定义实例变量用于存储先决条件的状态。 - 创建并存储接下来要测试的Activity的引用。 - 持有Activity中需要进行测试的UI组件的引用。 可以通过getActivity()获取要测试的Activity的引用。 添加测试条件 在进行测试之前,还有一个步骤就是需要验证前一步是否设置正确,以及需要测试的对象是否已被正确的实例化、初始化。这样的话,便不需要确认测试本身是否有误,因为测试的先决条件已经发生了错误。依照惯例,用于验证先决条件的方法被称为testPreconditions(). 例如: public void testPreconditions() { assertNotNull(“mFirstTestActivity is null”, mFirstTestActivity); assertNotNull(“mFirstTestText is null”, mFirstTestText); } 其中的判断方法来自于JUnit的Assert类。通常情况下可以使用这些判断方法来验证需要测试的指定条件是否为true。 - 如果条件为false,那么判断方法会抛出一个AssertionFailedError异常。该异常由TestRunner抛出。如果判断失败,那么可以通过判断方法的第一个参数得知是哪个条件失败。 - 如果条件为true,那么测试会顺利执行。 在这两种情况中,TestRunner会继续执行其它的判断方法。 添加测试方法 接下来,添加测试方法来验证Activity的布局与功能。 例如,如果Activity包含了一个TextView,你可以像下面这样添加一个测试方法来验证该TextView的文本是否正确: public void testMyFirstTestTextView_labelText() { final String expected = mFirstTestActivity.getString(R.string.my_first_test); final String actual = mFirstTestText.getText().toString(); assertEquals(expected, actual); } testMyFirstTestTextView_labelText()方法用于检测TextView的默认文本与定义在string.xml中的文本是否一致。 Note: 当命名测试方法时,可以使用下划线来分开要测试的内容,这种编写风格可以更容易明确测试的内容。 要执行比较,将期望的值与实际的值传给assertEquals()方法。如果两个值不相等,那么将会抛出一个AssertionFailedError异常。 如果添加testPreconditions()方法,那么请将测试代码放在testPreconditions()之后。 构建运行测试 在Eclipse中进行代码测试非常容易。 请执行以下步骤: 1.将Android设备连接到计算机。打开Setting菜单,选择Developer选项,并确保USB调试模式已开启。 2.在测试的类中选择RunAs > Android Junit Test. 3.在Android设备选择对话框中,选择刚刚连接好的设备,点击OK。 4.在JUnit界面中,验证测试是否通过。

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:Activity测试之测试环境配置

原文地址:http://android.xsoftlab.net/training/activity-testing/index.html 引言 开发者应当将测试作为应用开发周期的一部分。良好的测试用例可以帮助开发者及早的发现Bug,同时也可以增强开发者对代码的信心。 测试用例定义了一系列对象与方法,使各个测试可以独自进行。测试用例既可以组合运行,也可以重复进行。 这节课的课程将会介绍如何使用Android的自定义测试框架进行测试,该框架基于很受欢迎的JUnit框架。开发者可以通过编写测试用例来验证程序的某些功能,也可以检查不同设备的兼容性。 配置测试环境 在开始进行测试之前,开发者首先应当配置测试环境。这节课将会学习如何通过命令行来配置基于Gradle的测试用例。 配置Eclipse Note: 因为目前使用Eclipse开发所占的比例已经很少了,所以接下来的翻译凡涉及到Eclipse的,文字描述都极为精简。需要了解的请查看原文。 配置命令行 如果开发者使用的是Gradle 1.6或以上的版本,那么可以使用Gradle Wrapper来构建运行测试用例。要确保在gradle.build文件中,defaultConfig下的minSdkVersion属性设置的是8以上的值(含)。 要运行基于Gradle Wrapper的测试,需要执行以下步骤: 1.将物理设备连接到计算机上。 2.在工程目录下运行以下命令: ./gradlew build connectedCheck 学习更多关于使用Gradle进行Android测试的相关内容,请参见Gradle Plugin User Guide.

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:布局性能优化之布局复用

原文地址:http://android.xsoftlab.net/training/improving-layouts/reusing-layouts.html 尽管Android提供了种类繁多的常用控件,但是有时你可能希望重用一些比较复杂的布局。如果要重用这些布局,可以使用< include/>标签与< merge/>标签,它们可将一个布局嵌入进另一个布局中。 可重用布局这项功能特别强大,它可以使你创建那些复杂的可重用布局。比方说,可以用来创建一个含有yes和no按钮的容器或者一个含有progressBar及一个文本框的容器。它还意味着程序可以对这些布局进行单独控制。所以,虽然说你可以通过自定义View的方式来实现更为复杂的UI组件,但是重用布局的方法更简便一些。 创建一个可重用的布局 如果你已经知道哪一个布局需要重用,那么就创建一个新的xml文件用来定义这个布局。下面就定义了一个ActionBar的布局文件,众所周知,ActionBar是会在每个Activity中统一出现的: <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width=”match_parent” android:layout_height="wrap_content" android:background="@color/titlebar_bg"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/gafricalogo" /> </FrameLayout> 使用< include/>标签 在希望添加重用布局的布局内,添加< include/>标签。下面的例子就是将上面的布局加入到了当前的布局中: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=”match_parent” android:layout_height=”match_parent” android:background="@color/app_bg" android:gravity="center_horizontal"> <include layout="@layout/titlebar"/> <TextView android:layout_width=”match_parent” android:layout_height="wrap_content" android:text="@string/hello" android:padding="10dp" /> ... </LinearLayout> 你也可以重写布局的参数,但只仅限于以android:layout_*开头的布局参数。就像下面这样: <include android:id=”@+id/news_title” android:layout_width=”match_parent” android:layout_height=”match_parent” layout=”@layout/title”/> 如果你要重写< include>标签指定布局的布局属性,那么必须重写android:layout_height及android:layout_width这两个属性,以便使其它属性的作用生效。 使用< merge>标签 在将一个布局内嵌进另一个布局时,< merge>标签可以帮助消除冗余的View容器。举个例子,如果你的主布局是一个垂直的LinearLayout,在它的内部含有两个View,并且这两个View需要在多个布局中重用,那么重用这两个View的布局需要有一个root View。然而,使用单独的LinearLayout作为这个root View会导致在一个垂直的LinearLayout中又嵌了一个垂直的LinearLayout。其实这个内嵌的LinearLayout并不是我们真正想要的,此外它还会降低UI性能。 为了避免出现这种冗杂的View容器,你可以使用< merge>标签作为这两个View的root View: <merge xmlns:android="http://schemas.android.com/apk/res/android"> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/add"/> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/delete"/> </merge> 那么现在再使用这个布局的时候,系统会自动忽略< merge>标签,并会将两个Button View直接加入到布局< include/>标签所指定的位置。

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:手势处理之拖拽或缩放

原文地址:https://developer.android.com/training/gestures/scale.html 这节课主要学习如何使用触摸手势来拖动、放大屏幕上的对象。 拖动对象 如果你的重点在Android 3.0以上的版本,那么你可以使用内置的拖拽事件监听器View.OnDragListener。 触摸手势最常见的操作就是使用它来拖动屏幕上的对象。下面的代码允许用户拖动屏幕上的图像。要注意以下几点: 在拖动操作中,APP会一直保持手指拖动的轨迹,就算是另一只手指触到屏幕也是。举个例子,想象一根手指在拖动着一张图像,这时用户将第二根手指放置到屏幕上,如果APP只是追踪单根手指的轨迹,那么它会将第二根手指作为默认位置,并会将图像移动到这个位置。 为了防止这样的事件发生,APP需要区分第一根手指与其它手指。为此,需要追踪 ACTION_POINTER_DOWN 及 ACTION_POINTER_UP 。ACTION_POINTER_DOWN 及 ACTION_POINTER_UP在第二根手指落下或抬起的时候由onTouchEvent()方法传回。 在ACTION_POINTER_UP的情况下,示例提取了这个事件的索引,并确保当前活动的指针不是那个已经不在屏幕上的指针。如果是那个指针的话,那么APP会选择一个不同的指针使其活动并保存它的X及Y的位置。一旦这个值被保存下来,那么APP将会使用正确指针的数据一直计算剩余移动的距离。 下面的代码允许用户在屏幕上拖动对象。它记录了当前活动指针的初始位置,计算了它所位移的距离,并将对象移动到新的位置上。 这里要注意,代码段使用了getActionMasked()方法。你应该一直使用这个方法来接收MotionEvent对象的活动。与getAction()方法不同,getActionMasked()工作于多点触控模式下。它会返回被执行的掩饰活动,不包括指针的索引比特。 // The ‘active pointer’ is the one currently moving our object. private int mActivePointerId = INVALID_POINTER_ID; @Override public boolean onTouchEvent(MotionEvent ev) { // Let the ScaleGestureDetector inspect all events. mScaleDetector.onTouchEvent(ev); final int action = MotionEventCompat.getActionMasked(ev); switch (action) { case MotionEvent.ACTION_DOWN: { final int pointerIndex = MotionEventCompat.getActionIndex(ev); final float x = MotionEventCompat.getX(ev, pointerIndex); final float y = MotionEventCompat.getY(ev, pointerIndex); // Remember where we started (for dragging) mLastTouchX = x; mLastTouchY = y; // Save the ID of this pointer (for dragging) mActivePointerId = MotionEventCompat.getPointerId(ev, 0); break; } case MotionEvent.ACTION_MOVE: { // Find the index of the active pointer and fetch its position final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, pointerIndex); final float y = MotionEventCompat.getY(ev, pointerIndex); // Calculate the distance moved final float dx = x - mLastTouchX; final float dy = y - mLastTouchY; mPosX += dx; mPosY += dy; invalidate(); // Remember this touch position for the next move event mLastTouchX = x; mLastTouchY = y; break; } case MotionEvent.ACTION_UP: { mActivePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_CANCEL: { mActivePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_POINTER_UP: { final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex); mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex); mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } break; } } return true; } 平移 上面的部分展示了如何在屏幕上拖动对象。另一个通用的场景就是平移了,平移的意思是:用户的拖动动作引起的x及y轴方向上的滚动。上面的代码直接将MotionEvent拦截实现拖动。这部分的代码将会采用另一种更具有优势的方法,以便支持通用手势。它重写了GestureDetector.SimpleOnGestureListener的onScroll()方法。 只有用户在使用手指移动内容时,onScroll()才会被调用。onScroll()只有在手指按下的时候才会调用,一旦手指离开屏幕,那么平移手势也随之终止。 下面是onScroll()的使用摘要: // The current viewport. This rectangle represents the currently visible // chart domain and range. private RectF mCurrentViewport = new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX); // The current destination rectangle (in pixel coordinates) into which the // chart data should be drawn. private Rect mContentRect; private final GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { ... @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // Scrolling uses math based on the viewport (as opposed to math using pixels). // Pixel offset is the offset in screen pixels, while viewport offset is the // offset within the current viewport. float viewportOffsetX = distanceX * mCurrentViewport.width() / mContentRect.width(); float viewportOffsetY = -distanceY * mCurrentViewport.height() / mContentRect.height(); ... // Updates the viewport, refreshes the display. setViewportBottomLeft( mCurrentViewport.left + viewportOffsetX, mCurrentViewport.bottom + viewportOffsetY); ... return true; } 下面是setViewportBottomLeft()方法的实现,它主要实现了移动内容的逻辑: /** * Sets the current viewport (defined by mCurrentViewport) to the given * X and Y positions. Note that the Y value represents the topmost pixel position, * and thus the bottom of the mCurrentViewport rectangle. */ private void setViewportBottomLeft(float x, float y) { /* * Constrains within the scroll range. The scroll range is simply the viewport * extremes (AXIS_X_MAX, etc.) minus the viewport size. For example, if the * extremes were 0 and 10, and the viewport size was 2, the scroll range would * be 0 to 8. */ float curWidth = mCurrentViewport.width(); float curHeight = mCurrentViewport.height(); x = Math.max(AXIS_X_MIN, Math.min(x, AXIS_X_MAX - curWidth)); y = Math.max(AXIS_Y_MIN + curHeight, Math.min(y, AXIS_Y_MAX)); mCurrentViewport.set(x, y - curHeight, x + curWidth, y); // Invalidates the View to update the display. ViewCompat.postInvalidateOnAnimation(this); } 缩放 在Detecting Common Gestures中,我们讨论到GestureDetector可以帮助我们来检测比如滑动、滚动、长按等手势。而对于缩放,Android提供了ScaleGestureDetector. GestureDetector 以及 ScaleGestureDetector。 为了可以反馈检测到的手势事件,手势探测器使用了监听器对象ScaleGestureDetector.OnScaleGestureListener。如果你只关心部分手势的话,Android还提供了ScaleGestureDetector.SimpleOnScaleGestureListener,你可以通过重写它的方法来使用。 缩放基础示例 下面的代码是缩放所需要的基础: private ScaleGestureDetector mScaleDetector; private float mScaleFactor = 1.f; public MyCustomView(Context mContext){ ... // View code goes here ... mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); } @Override public boolean onTouchEvent(MotionEvent ev) { // Let the ScaleGestureDetector inspect all events. mScaleDetector.onTouchEvent(ev); return true; } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); canvas.scale(mScaleFactor, mScaleFactor); ... // onDraw() code goes here ... canvas.restore(); } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { mScaleFactor *= detector.getScaleFactor(); // Don't let the object get too small or too large. mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); invalidate(); return true; } } 稍微复杂点的示例 下面是一个稍微复杂一点的示例,它摘自与这节课所提供的示例InteractiveChart(PS:示例工程请参见原网页)。InteractiveChart同时支持平移、缩放,它使用了ScaleGestureDetector的“平移”(getCurrentSpanX/Y)及“焦点” (getFocusX/Y)特性: @Override private RectF mCurrentViewport = new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX); private Rect mContentRect; private ScaleGestureDetector mScaleGestureDetector; ... public boolean onTouchEvent(MotionEvent event) { boolean retVal = mScaleGestureDetector.onTouchEvent(event); retVal = mGestureDetector.onTouchEvent(event) || retVal; return retVal || super.onTouchEvent(event); } /** * The scale listener, used for handling multi-finger scale gestures. */ private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() { /** * This is the active focal point in terms of the viewport. Could be a local * variable but kept here to minimize per-frame allocations. */ private PointF viewportFocus = new PointF(); private float lastSpanX; private float lastSpanY; // Detects that new pointers are going down. @Override public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { lastSpanX = ScaleGestureDetectorCompat. getCurrentSpanX(scaleGestureDetector); lastSpanY = ScaleGestureDetectorCompat. getCurrentSpanY(scaleGestureDetector); return true; } @Override public boolean onScale(ScaleGestureDetector scaleGestureDetector) { float spanX = ScaleGestureDetectorCompat. getCurrentSpanX(scaleGestureDetector); float spanY = ScaleGestureDetectorCompat. getCurrentSpanY(scaleGestureDetector); float newWidth = lastSpanX / spanX * mCurrentViewport.width(); float newHeight = lastSpanY / spanY * mCurrentViewport.height(); float focusX = scaleGestureDetector.getFocusX(); float focusY = scaleGestureDetector.getFocusY(); // Makes sure that the chart point is within the chart region. // See the sample for the implementation of hitTest(). hitTest(scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), viewportFocus); mCurrentViewport.set( viewportFocus.x - newWidth * (focusX - mContentRect.left) / mContentRect.width(), viewportFocus.y - newHeight * (mContentRect.bottom - focusY) / mContentRect.height(), 0, 0); mCurrentViewport.right = mCurrentViewport.left + newWidth; mCurrentViewport.bottom = mCurrentViewport.top + newHeight; ... // Invalidates the View to update the display. ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this); lastSpanX = spanX; lastSpanY = spanY; return true; } };

优秀的个人博客,低调大师

提前曝光:【阿里Java技术进阶】官方钉群2月至3月直播排期预告!

作为【阿里Java技术进阶】钉钉群的管理员,小编我拿到了一手2月至3月份的直播排期,看到本篇文章的你有福了~如果你在本群的话我敢肯定你会准时来看直播!如果你不在本群的话,我敢打赌你读完本篇文章会立即扫码入群,快看看本次我们都邀请谁来直播了!!! 讲师:徐雷(阿里特邀专家)社区昵称:徐雷frank(点击昵称可关注讲师) 讲师介绍: 资深架构师,MongoDB中文社区联席主席,吉林大学计算机学士,上海交通大学硕士。从事了 10年+开发工作,专注于分布式架构,Java Spring Boot、Spring Cloud、MongoDB、Redis。 喜欢专研技术问题,擅长讲课,《MongoDB实战》第2版、《24种云计算架构设计模式》译者,2018年5月受邀成为《阿里巴巴Java和MongoDB技术认证》讲师,2018年8月与阿里巴巴资深专家P9

优秀的个人博客,低调大师

【JAVA集合框架一 】java集合框架官方介绍 Collections Framework Overview 集合框架总览 翻译 jav...

原文链接: https://docs.oracle.com/javase/8/docs/technotes/guides/collections/overview.html 原文内容也一并附加在本文最后. 简介: Java平台包含一个集合框架。 集合是表示一组对象的对象(如经典的Vector类)。 集合框架是用于表示和操作集合的统一体系结构,使集合可以独立于实现细节而被操纵。 集合框架的主要有点在于: 通过提供数据结构和算法减少编程工作量,因此您不必亲自编写它们。 通过提供数据结构和算法的高性能实现来提高性能。由于每个接口的各种实现都是可以互换的,因此可以通过切换实现来调整程序。 通过建立通用语言来回传递集合,提供不相关的API之间的互操作性。--可以部分理解为向上转型 多态 通过学习多个特定的集合API来减少学习API所需的工作量。 --通过自顶而下的API设计风格 不要求您实现特定的集合API来减少设计和实现API所需的工作量。--通过提供抽象类提供了很多默认实现 通过为集合和算法提供标准接口来操纵它们,从而促进软件重用。 集合框架包括: 集合接口。表示不同类型的集合,例如Set,List和Map。这些接口构成了框架的基础。 通用实现。集合接口的主要实现。 遗留实现。早期版本Vector和Hashtable中的集合类进行了改进,以实现集合接口。 特殊用途的实现。设计用于特殊情况的实现。这些实现展示非标准的性能特征,使用限制或行为。 并发实现。为高并发使用而设计的实现。 包装实现。将功能(如同步)添加到其他实现。 更简化便利的实现. 集合接口的高性能低复杂实现. 抽象实现。集合接口的部分实现,以促进自定义实现。 算法。collections中非常有用的静态方法,比如例如排序列表。 基础架构。为集合即可欧提供重要支持的接口。--就是一些其他辅助的,比如Iterable 数组工具类。基本类型和引用对象数组的实用函数。严格来说,并不是集合框架的一部分,这个特性与集合框架同时添加到Java平台,并依赖于相同的基础结构。 集合接口: 集合接口分为两大阵营,最基础的接口java.util.Collection,有下面这些后代: java.util.Setjava.util.SortedSetjava.util.NavigableSetjava.util.Queuejava.util.concurrent.BlockingQueuejava.util.concurrent.TransferQueuejava.util.Dequejava.util.concurrent.BlockingDeque 另外的一些集合接口派生于 java.util.Map 不过他们并是真正的集合。 但是,这些接口包含集合视角的操作,这些操作可以将它们作为集合进行操作。Map有以下后代: java.util.SortedMapjava.util.NavigableMapjava.util.concurrent.ConcurrentMapjava.util.concurrent.ConcurrentNavigableMap 集合中的许多修改方法是可选的. 也就是允许实现类并不必须要实现一个或者多个这种操作. 如果你尝试使用,将会抛出运行时异常UnsupportedOperationException 每个实现的文档必须指定支持哪些可选操作。 引入了几个术语来帮助本规范: 不支持修改操作的集合(如添加,删除和清除)称为不可修改,非不可修改的集合是可修改的。; 如果集合额外保证集合对象中没有任何可见的修改那么称之为为不可变,非不可变的就是可变的; 保证集合大小不变的的集合,即使元素可以变化更改,称之为固定大小,非固定大小的列表称之为可变大小; 支持快速(通常是固定时间)索引元素访问的List是随机访问(RandomAccess) List,比如arrayList,不支持快速索引访问的称之为顺序访问 (sequential access)List ,RandomAccess标记接口指明一个list具有支持随机访问的特性,这一点保证了你可以编写通用的算法代码,通过选择不同的随机访问还是顺序访问List,以达到更好的性能. 一些实现可以做到限制哪些元素(或者在Map场景下的键和值)可以被存储。可能的限制包括要求元素: 特定类型 不能为null 遵从一些自定的断言判断式(Obey some arbitrary predicate) 如果尝试往有限制的接口实现中添加不符合的元素,会出现运行时异常,比如 ClassCastException, IllegalArgumentException, or a NullPointerException. 如果尝试在有限制的实现中移除一个不符合条件的元素或者测试是否存在,会导致异常 不过一些受限制的集合支持这种用法. 集合实现: 集合的实现类一般遵从这样子的形式 < 实现特性>加上<接口名称>的形式 下表中列出了通用实现 通用实现类支持集合框架中所有的可选操作,并且对于元素没有任何的限制 他们都是非同步的 不过Collections类中包含了很多静态的工厂方法-synchronization wrappers同步包装器 可以在很多非同的集合中提供同步的功能. 所有新的实现都具有快速失败迭代器,它可以检测到非法的并发修改,并且快速而干净地(而不是报错异常)失败。 ps: fail-fast java集合框架中的一种机制,检测并发修改 AbstractCollection, AbstractSet, AbstractList, AbstractSequentialList 和 AbstractMap 为了减少实现接口所需代价,提供了核心集合框架的基本实现。 这些类的API文档准确描述了每种方法的实现方式, 因此实现者知道哪些方法必须被重写,考虑到具体实现类的基本操作的性能。 并发集合: 应用程序中使用多个线程的集合的必须仔细编程,这个一般叫做并发编程 Java平台包含对并发编程的广泛支持。有关详细信息,请参阅Java Concurrency Utilities。 由于集合经常被使用,各种支持并发编程的接口和集合的实现都被包含在这些API中 这些类型,超出了前面提到过的同步包装,提供了并发编程中经常需要用到的特性 并发接口 BlockingQueueTransferQueueBlockingDequeConcurrentMapConcurrentNavigableMap 并发实现类 LinkedBlockingQueueArrayBlockingQueuePriorityBlockingQueueDelayQueueSynchronousQueueLinkedBlockingDequeLinkedTransferQueueCopyOnWriteArrayListCopyOnWriteArraySetConcurrentSkipListSetConcurrentHashMapConcurrentSkipListMap 设计目标: 主要的设计目标是编写一个体积小概念强大的API 新功能与当前程序差别不大,这一点非常重要.而且,需要在现有的基础上增加 而不是替代他们。 而且,新的API必须足够强大,才能够提供前面所述的所有优势。 为了使核心接口的数量保持较小, 接口不会尝试捕捉诸如易变性,可修改性和可调节性之类的细微区别 相反,在核心接口中一些调用是可选的 使得实现类可以抛出一个UnsupportedOperationException异常指示出他们不支持指定的可选操作 集合实现者必须清楚地在文档中表明实现支持哪些可选操作。 为了使每个核心接口中的方法数量保持较小, 只有在以下情况下,接口才包含方法: 这是一项真正的基本操作:就其他方面可以被合理定义而言, 它是一个基本的操作, 有一个不可抗拒的性能因素导致重要的实现类将会想要去重写它 所有关于集合的表现形式的互操作性至关重要。 这包括数组,在不改变语言的情况下,不能直接实现Collection接口。 因此, 该框架包含的方法可以将集合移入数组,将数组视为集合,将Map视为集合。 原文内容: Skip to Content Collections Framework Overview Introduction The Java platform includes acollections framework. Acollectionis an object that represents a group of objects (such as the classicVectorclass). A collections framework is a unified architecture for representing and manipulating collections, enabling collections to be manipulated independently of implementation details. The primary advantages of a collections framework are that it: Reduces programming effortby providing data structures and algorithms so you don't have to write them yourself. Increases performanceby providing high-performance implementations of data structures and algorithms. Because the various implementations of each interface are interchangeable, programs can be tuned by switching implementations. Provides interoperability between unrelated APIsby establishing a common language to pass collections back and forth. Reduces the effort required to learn APIsby requiring you to learn multiple ad hoc collection APIs. Reduces the effort required to design and implement APIsby not requiring you to produce ad hoc collections APIs. Fosters software reuseby providing a standard interface for collections and algorithms with which to manipulate them. The collections framework consists of: Collection interfaces. Represent different types of collections, such as sets, lists, and maps. These interfaces form the basis of the framework. General-purpose implementations. Primary implementations of the collection interfaces. Legacy implementations. The collection classes from earlier releases,VectorandHashtable, were retrofitted to implement the collection interfaces. Special-purpose implementations. Implementations designed for use in special situations. These implementations display nonstandard performance characteristics, usage restrictions, or behavior. Concurrent implementations. Implementations designed for highly concurrent use. Wrapper implementations. Add functionality, such as synchronization, to other implementations. Convenience implementations. High-performance "mini-implementations" of the collection interfaces. Abstract implementations. Partial implementations of the collection interfaces to facilitate custom implementations. Algorithms. Static methods that perform useful functions on collections, such as sorting a list. Infrastructure. Interfaces that provide essential support for the collection interfaces. Array Utilities. Utility functions for arrays of primitive types and reference objects. Not, strictly speaking, a part of the collections framework, this feature was added to the Java platform at the same time as the collections framework and relies on some of the same infrastructure. Collection Interfaces Thecollection interfacesare divided into two groups. The most basic interface,java.util.Collection, has the following descendants: java.util.Set java.util.SortedSet java.util.NavigableSet java.util.Queue java.util.concurrent.BlockingQueue java.util.concurrent.TransferQueue java.util.Deque java.util.concurrent.BlockingDeque The other collection interfaces are based onjava.util.Mapand are not true collections. However, these interfaces containcollection-viewoperations, which enable them to be manipulated as collections.Maphas the following offspring: java.util.SortedMap java.util.NavigableMap java.util.concurrent.ConcurrentMap java.util.concurrent.ConcurrentNavigableMap Many of the modification methods in the collection interfaces are labeledoptional. Implementations are permitted to not perform one or more of these operations, throwing a runtime exception (UnsupportedOperationException) if they are attempted. The documentation for each implementation must specify which optional operations are supported. Several terms are introduced to aid in this specification: Collections that do not support modification operations (such asadd,removeandclear) are referred to asunmodifiable. Collections that are not unmodifiable aremodifiable. Collections that additionally guarantee that no change in theCollectionobject will be visible are referred to asimmutable. Collections that are not immutable aremutable. Lists that guarantee that their size remains constant even though the elements can change are referred to asfixed-size. Lists that are not fixed-size are referred to asvariable-size. Lists that support fast (generally constant time) indexed element access are known asrandom accesslists. Lists that do not support fast indexed element access are known assequential accesslists. TheRandomAccessmarker interface enables lists to advertise the fact that they support random access. This enables generic algorithms to change their behavior to provide good performance when applied to either random or sequential access lists. Some implementations restrict what elements (or in the case ofMaps, keys and values) can be stored. Possible restrictions include requiring elements to: Be of a particular type. Be not null. Obey some arbitrary predicate. Attempting to add an element that violates an implementation's restrictions results in a runtime exception, typically aClassCastException, anIllegalArgumentException, or aNullPointerException. Attempting to remove or test for the presence of an element that violates an implementation's restrictions can result in an exception. Some restricted collections permit this usage. Collection Implementations Classes that implement the collection interfaces typically have names in the form of <Implementation-style><Interface>. The general purpose implementations are summarized in the following table: The general-purpose implementations support all of theoptional operationsin the collection interfaces and have no restrictions on the elements they may contain. They are unsynchronized, but theCollectionsclass contains static factories calledsynchronization wrappersthat can be used to add synchronization to many unsynchronized collections. All of the new implementations havefail-fast iterators, which detect invalid concurrent modification, and fail quickly and cleanly (rather than behaving erratically). TheAbstractCollection,AbstractSet,AbstractList,AbstractSequentialListandAbstractMapclasses provide basic implementations of the core collection interfaces, to minimize the effort required to implement them. The API documentation for these classes describes precisely how each method is implemented so the implementer knows which methods must be overridden, given the performance of the basic operations of a specific implementation. Concurrent Collections Applications that use collections from more than one thread must be carefully programmed. In general, this is known asconcurrent programming. The Java platform includes extensive support for concurrent programming. SeeJava Concurrency Utilitiesfor details. Collections are so frequently used that various concurrent friendly interfaces and implementations of collections are included in the APIs. These types go beyond the synchronization wrappers discussed previously to provide features that are frequently needed in concurrent programming. These concurrent-aware interfaces are available: BlockingQueue TransferQueue BlockingDeque ConcurrentMap ConcurrentNavigableMap The following concurrent-aware implementation classes are available. See the API documentation for the correct usage of these implementations. LinkedBlockingQueue ArrayBlockingQueue PriorityBlockingQueue DelayQueue SynchronousQueue LinkedBlockingDeque LinkedTransferQueue CopyOnWriteArrayList CopyOnWriteArraySet ConcurrentSkipListSet ConcurrentHashMap ConcurrentSkipListMap Design Goals The main design goal was to produce an API that was small in size and, more importantly, in "conceptual weight." It was critical that the new functionality not seem too different to current Java programmers; it had to augment current facilities, rather than replace them. At the same time, the new API had to be powerful enough to provide all the advantages described previously. To keep the number of core interfaces small, the interfaces do not attempt to capture such subtle distinctions as mutability, modifiability, and resizability. Instead, certain calls in the core interfaces areoptional, enabling implementations to throw anUnsupportedOperationExceptionto indicate that they do not support a specified optional operation. Collection implementers must clearly document which optional operations are supported by an implementation. To keep the number of methods in each core interface small, an interface contains a method only if either: It is a trulyfundamental operation: a basic operations in terms of which others could be reasonably defined, There is a compelling performance reason why an important implementation would want to override it. It was critical that all reasonable representations of collections interoperate well. This included arrays, which cannot be made to implement theCollectioninterface directly without changing the language. Thus, the framework includes methods to enable collections to be moved into arrays, arrays to be viewed as collections, and maps to be viewed as collections. Copyright ©1993, 2018, Oracle and/or its affiliates. All rights reserved. Contact Us

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:Activity测试之创建单元测试

原文地址:http://android.xsoftlab.net/training/activity-testing/activity-unit-testing.html Activity单元测试除了可以快速的验证Activity的状态之外,还可以验证Activity与底层组件之间的交互。单元测试通常用于测试较小的代码单元(它们通常不依赖系统或者网络资源),它们可能是一个方法,一个类或者其它组件。例如,开发者可以通过单元测试来检查Activity是否含有正确的布局,或者是否触发了正确的Intent。 不过单元测试通常不适用于测试与系统有交互的UI组件,该类测试情况应当使用ActivityInstrumentationTestCase2。 这节课将会学习如何使用单元测试来验证用于启动Activity的Intent。因为测试运行于独立的环境之中,所以Intent并不会实际发送到Android系统,但是你可以检测该Intent所携带的数据是否正确。 创建用于Activity单元测试的测试用例 类ActivityUnitTestCase对单个的Activity测试提供了支持。要进行Activity的单元测试,需继承ActivityUnitTestCase。 在ActivityUnitTestCase中的Activity并不会由Android系统自动启动。如果要在这里启动Activity,必须在这里显式的调用startActivity()方法,并传入要执行的Intent。 例如: public class LaunchActivityTest extends ActivityUnitTestCase<LaunchActivity> { ... @Override protected void setUp() throws Exception { super.setUp(); mLaunchIntent = new Intent(getInstrumentation() .getTargetContext(), LaunchActivity.class); startActivity(mLaunchIntent, null, null); final Button launchNextButton = (Button) getActivity() .findViewById(R.id.launch_next_activity_button); } } 验证另一个Activity的启动 单元测试可能含有以下目的: 验证在Button按下后LaunchActivity是否启动了Intent. 验证被启动的Intent所包含的数据是否正确. 为了验证在Button按下后是否有Intent被触发,开发者可以使用getStartedActivityIntent()方法获得被触发的Intent。然后通过断言方法来验证该方法返回的Intent是否为null,以及该Intent所含的数据是否正确。如果两个断言方法都正确,那么可以断定成功了触发了该Intent。 开发者所实现的代码可能如下: @MediumTest public void testNextActivityWasLaunchedWithIntent() { startActivity(mLaunchIntent, null, null); final Button launchNextButton = (Button) getActivity() .findViewById(R.id.launch_next_activity_button); launchNextButton.performClick(); final Intent launchIntent = getStartedActivityIntent(); assertNotNull("Intent was null", launchIntent); assertTrue(isFinishCalled()); final String payload = launchIntent.getStringExtra(NextActivity.EXTRAS_PAYLOAD_KEY); assertEquals("Payload is empty", LaunchActivity.STRING_PAYLOAD, payload); } 因为LaunchActivity是独立运行的,所以不能够使用库TouchUtils来直接控制UI。为了可以模拟Button的点击时间,可以直接调用performClick()方法。

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:线程执行操作之线程间通讯

原文地址:http://android.xsoftlab.net/training/multiple-threads/communicate-ui.html 上节课我们学习了如何启动一项由ThreadPoolExecutor所管理的线程任务。最后这节课我们将学习如何从任务中发送结果数据给UI线程。这项手段可以使任务在执行完毕后将结果显示到UI中去。 每个APP拥有独立的UI线程。只有在UI线程中创建的对象才可以访问该线程中的其它对象。正因为运行任务的线程不是UI线程,所以它们不可以直接访问UI对象。为了将数据从后台线程转移到UI线程,需要使用运行在UI线程中的Handler对象。 在UI线程中定义Handler Handler是Android系统框架管理线程的一部分。Handler对象专门用于接收消息处理消息。 一般来说,可以为新线程创建一个Handler,也可以为一个已经连接好的线程创建Handler。当你将Handler连接到UI线程时,处理消息的代码都会运行在UI线程中。 在构建线程池的类的构造方法中实例化一个Handler对象,并将该对象的引用存储于全局变量中。通过Handler(Looper)重载构造方法所实例化Handler可以与UI线程产生关联。这个构造方法所使用的Looper参数是Android系统的线程管理框架的另一部分。当以指定的Looper实例初始化一个Handler对象时,Handler对象会运行在Looper对象所在的线程中。 private PhotoManager() { ... // Defines a Handler object that's attached to the UI thread mHandler = new Handler(Looper.getMainLooper()) { ... 在Handler内,重写handleMessage()方法。Android系统会在Handler所管理的线程中接收到一条新的消息时回调该方法: /* * handleMessage() defines the operations to perform when * the Handler receives a new Message to process. */ @Override public void handleMessage(Message inputMessage) { // Gets the image task from the incoming Message object. PhotoTask photoTask = (PhotoTask) inputMessage.obj; ... } ... } } 将数据从非UI线程转移到UI线程 为了将数据从后台进程转移到UI进程,首先将数据的引用以及UI对象存储于任务对象中。接下来,将该任务对象及状态码传给由Handler对象所初始化的对象。在这个对象内,发送一条包含状态码的任务对象给Handler。因为Handler是运行在UI线程的,所以它可以将数据交给UI对象。 存储数据于任务对象中 举个例子,这里有一个Runnable对象,运行于后台线程,它用于解码一个Bitmap对象,并将其存储于它所属的对象PhotoTask中。该Runnable还会存储一个状态码:DECODE_STATE_COMPLETED。 // A class that decodes photo files into Bitmaps class PhotoDecodeRunnable implements Runnable { ... PhotoDecodeRunnable(PhotoTask downloadTask) { mPhotoTask = downloadTask; } ... // Gets the downloaded byte array byte[] imageBuffer = mPhotoTask.getByteBuffer(); ... // Runs the code for this task public void run() { ... // Tries to decode the image buffer returnBitmap = BitmapFactory.decodeByteArray( imageBuffer, 0, imageBuffer.length, bitmapOptions ); ... // Sets the ImageView Bitmap mPhotoTask.setImage(returnBitmap); // Reports a status of "completed" mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED); ... } ... } ... PhotoTask还包含了一个用于展示Bitmap的ImageView的句柄。尽管引用Bitmap以及ImageView的是同一个对象,但是还是不能将Bitmap赋值给ImageView,因为当前并没有处在UI线程中。 发送状态到对象层级 PhotoTask在层级内处于第二高度。它维护了图像的解码数据以及一个View对象。它会接收PhotoDecodeRunnable中的状态码,并将其传给维护线程池以及Handler的那个对象。 public class PhotoTask { ... // Gets a handle to the object that creates the thread pools sPhotoManager = PhotoManager.getInstance(); ... public void handleDecodeState(int state) { int outState; // Converts the decode state to the overall state. switch(state) { case PhotoDecodeRunnable.DECODE_STATE_COMPLETED: outState = PhotoManager.TASK_COMPLETE; break; ... } ... // Calls the generalized state method handleState(outState); } ... // Passes the state to PhotoManager void handleState(int state) { /* * Passes a handle to this task and the * current state to the class that created * the thread pools */ sPhotoManager.handleState(this, state); } ... } 将数据展示到UI PhotoManager收到从PhotoTask中发来的状态码,然后处理这个PhotoTask对象。因为状态是TASK_COMPLETE,所以先创建一个Message,然后通过这个Message对象将状态以及人物对象发送给Handler: public class PhotoManager { ... // Handle status messages from tasks public void handleState(PhotoTask photoTask, int state) { switch (state) { ... // The task finished downloading and decoding the image case TASK_COMPLETE: /* * Creates a message for the Handler * with the state and the task object */ Message completeMessage = mHandler.obtainMessage(state, photoTask); completeMessage.sendToTarget(); break; ... } ... } 最后,在Handler.handleMessage()中检查每条消息的状态。如果消息的状态为TASK_COMPLETE,那么表明该任务已经终结,那么Message中所包含的PhotoTask对象包含了一个Bitmap以及一个ImageView。因为Handler.handleMessage()是运行在UI线程的,所以它可以安全的将Bitmap赋给ImageView: private PhotoManager() { ... mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message inputMessage) { // Gets the task from the incoming Message object. PhotoTask photoTask = (PhotoTask) inputMessage.obj; // Gets the ImageView for this task PhotoView localView = photoTask.getPhotoView(); ... switch (inputMessage.what) { ... // The decoding is done case TASK_COMPLETE: /* * Moves the Bitmap from the task * to the View */ localView.setImageBitmap(photoTask.getImage()); break; ... default: /* * Pass along other messages from the UI */ super.handleMessage(inputMessage); } ... } ... } ... } ... }

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:线程执行操作之线程池操作

原文地址:http://android.xsoftlab.net/training/multiple-threads/run-code.html#StopThread 上节课我们学习了如何定义一个类用于管理线程以及任务。这节课将会学习如何在线程池中运行任务。要做到这一点,只需要往线程池的工作队列中添加任务即可。当一条线程处于闲置状态时,那么ThreadPoolExecutor会从任务队列中取出一条任务并放入该线程中运行。 这节课还介绍了如何停止一个正在运行中的任务。如果在任务开始后,可能发现这项任务并不是必须的,那么就需要用到任务取消的功能了。这样可以避免浪费处理器的时间。举个例子,如果你正从网络上下载一张图像,如果侦测到这张图像已经在缓存中了,那么这时就需要停止这项网络任务了。 在线程池中的线程内运行任务 为了在指定的线程池中启动一项线程任务,需要将Runnable对象传给ThreadPoolExecutor的execute()方法。这个方法会将任务添加到线程池的工作队列中去。当其中一个线程变为闲置状态时,那么线程池管理器会从队列中取出一个已经等待了很久的任务,然后放到这个线程中运行: public class PhotoManager { public void handleState(PhotoTask photoTask, int state) { switch (state) { // The task finished downloading the image case DOWNLOAD_COMPLETE: // Decodes the image mDecodeThreadPool.execute( photoTask.getPhotoDecodeRunnable()); ... } ... } ... } 当ThreadPoolExecutor启动一个Runnable时,它会自动调用Runnable的run()方法。 中断执行中的代码 如果要停止一项任务,那么需要中断该任务所在的线程。为了可以预先做到这一点,那么需要在任务创建时存储该任务所在线程的句柄: class PhotoDecodeRunnable implements Runnable { // Defines the code to run for this task public void run() { /* * Stores the current Thread in the * object that contains PhotoDecodeRunnable */ mPhotoTask.setImageDecodeThread(Thread.currentThread()); ... } ... } 我们可以调用Thread.interrupt()方法来中断一个线程。这里要注意Thread对象是由系统控制的,系统会在应用进程的范围之外修改它们。正因为这个原因,在中断线程之前,需要对线程的访问加锁。通常需要将这部分代码放入同步代码块中: public class PhotoManager { public static void cancelAll() { /* * Creates an array of Runnables that's the same size as the * thread pool work queue */ Runnable[] runnableArray = new Runnable[mDecodeWorkQueue.size()]; // Populates the array with the Runnables in the queue mDecodeWorkQueue.toArray(runnableArray); // Stores the array length in order to iterate over the array int len = runnableArray.length; /* * Iterates over the array of Runnables and interrupts each one's Thread. */ synchronized (sInstance) { // Iterates over the array of tasks for (int runnableIndex = 0; runnableIndex < len; runnableIndex++) { // Gets the current thread Thread thread = runnableArray[taskArrayIndex].mThread; // if the Thread exists, post an interrupt to it if (null != thread) { thread.interrupt(); } } } } ... } 在多数情况下,Thread.interrupt()会使线程立刻停止。然而,它只会将那些正在等待的线程停下来,它并不会中止CPU或网络任务。为了避免使系统变慢或卡顿,你应当在开始任意一项操作之前测试是否有中断请求: /* * Before continuing, checks to see that the Thread hasn't * been interrupted */ if (Thread.interrupted()) { return; } ... // Decodes a byte array into a Bitmap (CPU-intensive) BitmapFactory.decodeByteArray( imageBuffer, 0, imageBuffer.length, bitmapOptions); ...

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:布局性能优化之ListView的优化

原文地址:http://android.xsoftlab.net/training/improving-layouts/smooth-scrolling.html 想要让ListView滑动流畅的关键所在是减轻主线程的负担。要确保任何的磁盘访问、网络访问、或者SQL访问都是在单独的线程中执行的。如果要测试APP的状态,可以开启StrictMode。 使用后台线程 使用工作线程可以使UI线程将所有的注意力都集中在UI的绘制上。在很多情况下,使用AsyncTask所提供的功能就可以在工作线程中处理耗时任务。AsyncTask会自动的将execute()发起的请求排队,并依次执行。这意味着你不要自己创建线程池。 在下面的示例代码中,AsyncTask被用来加载一张图像,并在加载结束后自动的将其渲染到UI上。它还在图像加载时展示了一个旋转的进度条。 // Using an AsyncTask to load the slow images in a background thread new AsyncTask<ViewHolder, Void, Bitmap>() { private ViewHolder v; @Override protected Bitmap doInBackground(ViewHolder... params) { v = params[0]; return mFakeImageLoader.getImage(); } @Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); if (v.position == position) { // If this item hasn't been recycled already, hide the // progress and set and show the image v.progress.setVisibility(View.GONE); v.icon.setVisibility(View.VISIBLE); v.icon.setImageBitmap(result); } } }.execute(holder); 从Android 3.0开始,AsyncTask提供了一项新特性:可以将任务运行在多核处理器上。你可以使用executeOnExecutor()方法发起执行请求,这样多个请求就可以同时进行,同时进行的任务数量取决于CPU的核心数量。 使用View Holder持有View对象 在滑动ListView时,代码可能会频繁的调用findViewById(),这会降低性能。就算是Adapter将已经加载过的View返回,但是在复用时还是需要去查询这些View来更新它们。杜绝重复使用findViewById()的方法就是使用”View Holder”设计模式。 ViewHolder对象将每个View组件存储于布局容器的tag属性内,所以你可以快速访问它们而不需要每次都去查询。首先,你需要创建一个类来持有已加载的View: static class ViewHolder { TextView text; TextView timestamp; ImageView icon; ProgressBar progress; int position; } 然后对ViewHolder的成员属性赋值,然后将其存放在布局容器内: ViewHolder holder = new ViewHolder(); holder.icon = (ImageView) convertView.findViewById(R.id.listitem_image); holder.text = (TextView) convertView.findViewById(R.id.listitem_text); holder.timestamp = (TextView) convertView.findViewById(R.id.listitem_timestamp); holder.progress = (ProgressBar) convertView.findViewById(R.id.progress_spinner); convertView.setTag(holder); 那么现在就可以很方便的对这些View组件进行访问,而不再需要对它们单独进行查询,如此便可以节省出宝贵的CPU资源。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册