关于android:Android-优雅处理重复点击建议收藏

个别手机上的 Android App,次要的交互方式是点击。用户在点击后,App 可能做出在页面内更新 UI、新开一个页面或者发动网络申请等操作。Android 零碎自身没有对反复点击做解决,如果用户在短时间内屡次点击,则可能呈现新开多个页面或者反复发动网络申请等问题。因而,须要对反复点击有影响的中央,减少解决反复点击的代码。 之前的解决形式之前在我的项目中应用的是 RxJava 的计划,利用第三方库 RxBinding 实现了避免反复点击: fun View.onSingleClick(interval: Long = 1000L, listener: (View) -> Unit) {    RxView.clicks(this)        .throttleFirst(interval, TimeUnit.MILLISECONDS)        .subscribe({            listener.invoke(this)        }, {            LogUtil.printStackTrace(it)        })}然而这样有一个问题,比方应用两个手指同时点击两个不同的按钮,按钮的性能都是新开页面,那么有可能会新开两个页面。因为 Rxjava 这种形式是针对单个控件实现避免反复点击,不是多个控件。 当初的解决形式当初应用的是工夫判断,在工夫范畴内只响应一次点击,通过将上次单击工夫保留到 Activity Window 中的 decorView 里,实现一个 Activity 中所有的 View 共用一个上次单击工夫。 fun View.onSingleClick(    interval: Int = SingleClickUtil.singleClickInterval,    isShareSingleClick: Boolean = true,    listener: (View) -> Unit) {    setOnClickListener {        val target = if (isShareSingleClick) getActivity(this)?.window?.decorView ?: this else this        val millis = target.getTag(R.id.single_click_tag_last_single_click_millis) as? Long ?: 0        if (SystemClock.uptimeMillis() - millis >= interval) {            target.setTag(                R.id.single_click_tag_last_single_click_millis, SystemClock.uptimeMillis()            )            listener.invoke(this)        }    }}private fun getActivity(view: View): Activity? {    var context = view.context    while (context is ContextWrapper) {        if (context is Activity) {            return context        }        context = context.baseContext    }    return null}参数 isShareSingleClick 的默认值为 true,示意该控件和同一个 Activity 中其余控件共用一个上次单击工夫,也能够手动改成 false,示意该控件本人独享一个上次单击工夫。 mBinding.btn1.onSingleClick {    // 解决单次点击}mBinding.btn2.onSingleClick(interval = 2000, isShareSingleClick = false) {    // 解决单次点击}其余场景解决反复点击间接设置点击除了间接在 View 上设置的点击监听外,其余间接设置点击的中央也存在须要解决反复点击的场景,比如说富文本和列表。 为此将判断是否触发单次点击的代码抽离进去,独自作为一个办法: fun View.onSingleClick(    interval: Int = SingleClickUtil.singleClickInterval,    isShareSingleClick: Boolean = true,    listener: (View) -> Unit) {    setOnClickListener { determineTriggerSingleClick(interval, isShareSingleClick, listener) }}fun View.determineTriggerSingleClick(    interval: Int = SingleClickUtil.singleClickInterval,    isShareSingleClick: Boolean = true,    listener: (View) -> Unit) {    ...}间接在点击监听回调中调用 determineTriggerSingleClick 判断是否触发单次点击。上面拿富文本和列表举例。 富文本继承 ClickableSpan,在 onClick 回调中判断是否触发单次点击: inline fun SpannableStringBuilder.onSingleClick(    listener: (View) -> Unit,    isShareSingleClick: Boolean = true,    ...): SpannableStringBuilder = inSpans(    object : ClickableSpan() {        override fun onClick(widget: View) {            widget.determineTriggerSingleClick(interval, isShareSingleClick, listener)        }        ...    },    builderAction = builderAction)这样会有一个问题, onClick 回调中的 widget,就是设置富文本的控件,也就是说如果富文本存在多个单次点击的中央, 就算 isShareSingleClick 值为 false,这些单次点击还是会共用设置富文本控件的上次单击工夫。 因而,这里须要非凡解决,在 isShareSingleClick 为 false 的时候,创立一个假的 View 来触发单击事件,这样富文本中多个单次点击 isShareSingleClick 为 false 的中央都有一个本人的假的 View 来独享上次单击工夫。 class SingleClickableSpan(    ...) : ClickableSpan() {    private var mFakeView: View? = null    override fun onClick(widget: View) {        if (isShareSingleClick) {            widget        } else {            if (mFakeView == null) {                mFakeView = View(widget.context)            }            mFakeView!!        }.determineTriggerSingleClick(interval, isShareSingleClick, listener)    }    ...}在设置富文本的中央,应用设置 onSingleClick 实现单次点击: mBinding.tvText.movementMethod = LinkMovementMethod.getInstance()mBinding.tvText.highlightColor = Color.TRANSPARENTmBinding.tvText.text = buildSpannedString {    append("normalText")    onSingleClick({        // 解决单次点击    }) {        color(Color.GREEN) { append("clickText") }    }}列表列表应用 RecyclerView 控件,适配器应用第三方库 BaseRecyclerViewAdapterHelper。 ...

December 14, 2021 · 1 min · jiezi

关于android:效果炸了Android自定义View实现一个炫酷的时钟

一、背景1.1、控件成果要实现的自定义控件成果大抵如下,实现过程中用到了比拟多的自定义View的API,感觉比拟有代表性,就分享进去也当做学习总结 我的项目代码已上传github :https://github.com/DaLeiGe/An... 1.2、从性能上剖析一下这个控件,大抵有以下特点随机静止粒子从圆周向圆心静止,并与切线方向有正负30°的角度差,粒子透明度、半径、静止速度随机,静止超过肯定间隔或者工夫隐没背景圆有一个从内到外的渐变色计时模式下圆环有一个色彩突变的顺时针rotate动画整个背景圆色彩随着扇形角度变动而变动指针色彩变动数字变动是高低切换动画1.3、从构造上剖析这个控件能够拆分为两个局部,由背景圆+数字控件两个局部形成的组合控件,之所以把数字控件独自拆分进去,也是为了不便做数字上下跳动的动画,毕竟通过管制drawText的地位实现动画感觉不不便,间接通过View的属性动画更好实现 二、 背景圆实现2.1、实现粒子静止应用AnimPoint.java示意静止粒子,它具备x,y坐标,半径,角度,静止速度,透明度等属性,通过这些属性就能够画出一个动态的粒子 public class AnimPoint implements Cloneable {    /**     * 粒子原点x坐标     */    private float mX;    /**     * 粒子原点y坐标     */    private float mY;    /**     * 粒子半径     */    private float radius;    /**     * 粒子初始地位的角度     */    private double anger;    /**     * 一帧挪动的速度     */    private float velocity;    /**     * 总共挪动的帧数     */    private int num = 0;    /**     * 透明度 0~255     */    private int alpha = 153;    /**     * 随机偏移角度     */    private double randomAnger = 0;}粒子的初始地位位于随机角度的圆周,且一个粒子具备随机的半径,透明度,速度等,通过init()办法,实现初始化粒子如下 public void init(Random random, float viewRadius) {        anger = Math.toRadians(random.nextInt(360));        velocity = random.nextFloat() * 2F;        radius = random.nextInt(6) + 5;        mX = (float) (viewRadius * Math.cos(anger));        mY = (float) (viewRadius * Math.sin(anger));        //随机偏移角度-30°~30°        randomAnger = Math.toRadians(30 - random.nextInt(60));        alpha = 153 + random.nextInt(102);    }想让粒子静止起来,应用update更新粒子的这些坐标属性就能实现,比方粒子当初坐标在(5,5)`,通过update()扭转粒子的坐标到(6,6),联合属性动画不停地调用update()则就能不停的扭转x,y的坐标,实现粒子静止,而后当粒子挪动超过肯定间隔,或者调用update超过肯定次数,再从新调用init()让粒子从新从圆周上开始下一个生命周期静止 public void updatePoint(Random random, float viewRadius) {        //每一帧偏移的像素大小        float distance = 1F;        double moveAnger = anger + randomAnger;        mX = (float) (mX - distance * Math.cos(moveAnger) * velocity);        mY = (float) (mY - distance * Math.sin(moveAnger) * velocity);        //模仿半径逐步变小        radius = radius - 0.02F * velocity;        num++;        //如果到了最大值 则从新给静止粒子一个轨迹属性        int maxDistance = 180;        int maxNum = 400;        if (velocity * num > maxDistance || num > maxNum) {            num = 0;            init(random, viewRadius);        }    }在View中大抵实现如下 /**     * 初始化动画     */    private void initAnim() {        //绘制静止的粒子        AnimPoint mAnimPoint = new AnimPoint();        for (int i = 0; i < pointCount; i++) {            //通过clone创建对象,防止反复创立            AnimPoint cloneAnimPoint = mAnimPoint.clone();            //先给每个粒子初始化各类属性            cloneAnimPoint.init(mRandom, mRadius - mOutCircleStrokeWidth / 2F);            mPointList.add(cloneAnimPoint);        }        //画静止粒子        mPointsAnimator = ValueAnimator.ofFloat(0F, 1F);        mPointsAnimator.setDuration(Integer.MAX_VALUE);        mPointsAnimator.setRepeatMode(ValueAnimator.RESTART);        mPointsAnimator.setRepeatCount(ValueAnimator.INFINITE);        mPointsAnimator.addUpdateListener(animation -> {            for (AnimPoint point : mPointList) {                //通过属性动画不停的计算下粒子的下一个坐标                point.updatePoint(mRandom, mRadius);            }            invalidate();        });        mPointsAnimator.start();    }    @Override    protected void onDraw(final Canvas canvas) {        super.onDraw(canvas);        canvas.save();        canvas.translate(mCenterX, mCenterY);        //画静止粒子        for (AnimPoint animPoint : mPointList) {            mPointPaint.setAlpha(animPoint.getAlpha());            canvas.drawCircle(animPoint.getmX(), animPoint.getmY(),                    animPoint.getRadius(), mPointPaint);        }     }2.2、实现渐变色圆实现圆从内到外突变应用RadialGradient 大抵实现形式如下 float[] mRadialGradientStops = {0F, 0.69F, 0.86F, 0.94F, 0.98F, 1F};mRadialGradientColors[0] = transparentColor;mRadialGradientColors[1] = transparentColor;mRadialGradientColors[2] = parameter.getInsideColor();mRadialGradientColors[3] = parameter.getOutsizeColor();mRadialGradientColors[4] = transparentColor;mRadialGradientColors[5] = transparentColor;mRadialGradient = new RadialGradient(                    0,                    0,                    mCenterX,                    mRadialGradientColors,                    mRadialGradientStops,                    Shader.TileMode.CLAMP);mSweptPaint.setShader(mRadialGradient);...//onDraw()绘制canvas.drawCircle(0, 0, mCenterX, mSweptPaint);2.3、展现背景圆的扇形区域本来想通过DrawArc实现这个成果,然而DrawArc无奈实现到圆心的区域 那么如何实现这么一个不规则的形态呢,能够应用canvas.clipPath()实现裁剪不规则的形态,所以只有失去扇形的Path就能实现,通过圆点+弧形再闭合path就能实现 /**     * 绘制扇形path     *     * @param r 半径     * @param startAngle 开始角度     * @param sweepAngle 扫过的角度     */private void getSectorClip(float r, float startAngle, float sweepAngle) {        mArcPath.reset();        mArcPath.addArc(-r, -r, r, r, startAngle, sweepAngle);        mArcPath.lineTo(0, 0);        mArcPath.close();    }//而后再onDraw()中,裁剪画布 canvas.clipPath(mArcPath);2.4、实现指针变色指针是不规则形态,无奈通过绘制几何图形实现,所以选用drawBitmap实现 至于如何实现bitmap指针图片的色彩变动呢,本来的计划是应用AvoidXfermode扭转指定像素通道范畴内的色彩,然而AvoidXfermode在API 24曾经被移除,所以这计划有效 最终采纳图层混合模式实现指针图片变色 通过PorterDuff.Mode.MULTIPLY模式能够实现bitmap色彩,源图像为要批改的指针色彩,指标图像为红色指针,通过获取两个图像的重叠局部实现变色 大抵实现如下 /**     * 初始化指针图片的Bitmap     */    private void initBitmap() {        float f = 130F / 656F;        mBitmapDST = BitmapFactory.decodeResource(getResources(), R.drawable.indicator);        float mBitmapDstHeight = width * f;        float mBitmapDstWidth = mBitmapDstHeight * mBitmapDST.getWidth() / mBitmapDST.getHeight();        //初始化指针的图层混合模式        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);        mPointerRectF = new RectF(0, 0, mBitmapDstWidth, mBitmapDstHeight);        mBitmapSRT = Bitmap.createBitmap((int) mBitmapDstWidth, (int) mBitmapDstHeight, Bitmap.Config.ARGB_8888);        mBitmapSRT.eraseColor(mIndicatorColor);    }    @Override    protected void onDraw(final Canvas canvas) {        super.onDraw(canvas);        //画指针       canvas.translate(mCenterX, mCenterY);       canvas.rotate(mCurrentAngle / 10F);       canvas.translate(-mPointerRectF.width() / 2, -mCenterY);       mPointerLayoutId = canvas.saveLayer(mPointerRectF, mBmpPaint);       mBitmapSRT.eraseColor(mIndicatorColor);       canvas.drawBitmap(mBitmapDST, null, mPointerRectF, mBmpPaint);       mBmpPaint.setXfermode(mXfermode);       canvas.drawBitmap(mBitmapSRT, null, mPointerRectF, mBmpPaint);       mBmpPaint.setXfermode(null);       canvas.restoreToCount(mPointerLayoutId);    }2.5、实现背景圆色彩随扇形角度变动把圆形控件拆成3600°,每一个角度对应控件一种具体色彩值,那么如何计算特定角度他具体的色彩值呢? 参考属性动画中的变色动画android.animation.ArgbEvaluator实现形式,计算两个色彩中具体某一个点的色彩值形式如下 public Object evaluate(float fraction, Object startValue, Object endValue) {     int startInt = (Integer) startValue;     float startA = ((startInt >> 24) & 0xff) / 255.0f;     float startR = ((startInt >> 16) & 0xff) / 255.0f;     float startG = ((startInt >>  8) & 0xff) / 255.0f;     float startB = ( startInt        & 0xff) / 255.0f;     int endInt = (Integer) endValue;     float endA = ((endInt >> 24) & 0xff) / 255.0f;     float endR = ((endInt >> 16) & 0xff) / 255.0f;     float endG = ((endInt >>  8) & 0xff) / 255.0f;     float endB = ( endInt        & 0xff) / 255.0f;     // convert from sRGB to linear     startR = (float) Math.pow(startR, 2.2);     startG = (float) Math.pow(startG, 2.2);     startB = (float) Math.pow(startB, 2.2);     endR = (float) Math.pow(endR, 2.2);     endG = (float) Math.pow(endG, 2.2);     endB = (float) Math.pow(endB, 2.2);     // compute the interpolated color in linear space     float a = startA + fraction * (endA - startA);     float r = startR + fraction * (endR - startR);     float g = startG + fraction * (endG - startG);     float b = startB + fraction * (endB - startB);     // convert back to sRGB in the [0..255] range     a = a * 255.0f;     r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;     g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;     b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;     return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b); }控件中总共有四个色彩段,3600/4=900,所以 fraction = progressValue % 900 / 900;而后判断以后的角度位于第几段色彩值中,通过android.animation.ArgbEvaluator.evaluate(float fraction, Object startValue, Object endValue)就能回去具体的色彩值 大抵实现过程如下 private ProgressParameter getProgressParameter(float progressValue) {        float fraction = progressValue % 900 / 900;        if (progressValue < 900) {            //第一个色彩段            mParameter.setInsideColor(evaluate(fraction, insideColor1, insideColor2));            mParameter.setOutsizeColor(evaluate(fraction, outsizeColor1, outsizeColor2));            mParameter.setProgressColor(evaluate(fraction, progressColor1, progressColor2));            mParameter.setPointColor(evaluate(fraction, pointColor1, pointColor2));            mParameter.setBgCircleColor(evaluate(fraction, bgCircleColor1, bgCircleColor2));            mParameter.setIndicatorColor(evaluate(fraction, indicatorColor1, indicatorColor2));        } else if (progressValue < 1800) {            //第二个色彩段            mParameter.setInsideColor(evaluate(fraction, insideColor2, insideColor3));            mParameter.setOutsizeColor(evaluate(fraction, outsizeColor2, outsizeColor3));            mParameter.setProgressColor(evaluate(fraction, progressColor2, progressColor3));            mParameter.setPointColor(evaluate(fraction, pointColor2, pointColor3));            mParameter.setBgCircleColor(evaluate(fraction, bgCircleColor2, bgCircleColor3));            mParameter.setIndicatorColor(evaluate(fraction, indicatorColor2, indicatorColor3));        } else if (progressValue < 2700) {            //第三个色彩段            mParameter.setInsideColor(evaluate(fraction, insideColor3, insideColor4));            mParameter.setOutsizeColor(evaluate(fraction, outsizeColor3, outsizeColor4));            mParameter.setProgressColor(evaluate(fraction, progressColor3, progressColor4));            mParameter.setPointColor(evaluate(fraction, pointColor3, pointColor4));            mParameter.setBgCircleColor(evaluate(fraction, bgCircleColor3, bgCircleColor4));            mParameter.setIndicatorColor(evaluate(fraction, indicatorColor3, indicatorColor4));        } else {            //第四个色彩段            mParameter.setInsideColor(evaluate(fraction, insideColor4, insideColor5));            mParameter.setOutsizeColor(evaluate(fraction, outsizeColor4, outsizeColor5));            mParameter.setProgressColor(evaluate(fraction, progressColor4, progressColor5));            mParameter.setPointColor(evaluate(fraction, pointColor4, pointColor5));            mParameter.setBgCircleColor(evaluate(fraction, bgCircleColor4, bgCircleColor5));            mParameter.setIndicatorColor(evaluate(fraction, indicatorColor4, indicatorColor5));        }        return mParameter;    }三、跳动数字动画实现3.1、属性动画+2个TextView实现数字高低切换动画实现数字切换动画,本来打算用RecycleView实现,然而思考到动效上未来可能面临UI小姐姐各种骚操作,所以最终决定就用两个TextView做高低translation动画,这样可控性高,对View执行属性动画也简略 NumberView应用FrameLayout包裹两个TextView,widget_progress_number_item_layout.xml <?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content">    <TextView        android:id="@+id/tv_number_one"        style="@style/progress_text_font"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:gravity="center"        android:padding="0dp"        android:text="0"        android:textColor="@android:color/white" />    <TextView        style="@style/progress_text_font"        android:id="@+id/tv_number_tow"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:gravity="center"        android:text="1"        android:textColor="@android:color/white" /></FrameLayout>而后通过属性动画管制两个TextView高低切换 mNumberAnim = ValueAnimator.ofFloat(0F, 1F);        mNumberAnim.setDuration(400);        mNumberAnim.setInterpolator(new OvershootInterpolator());        mNumberAnim.setRepeatCount(0);        mNumberAnim.setRepeatMode(ValueAnimator.RESTART);        mNumberAnim.addUpdateListener(animation -> {            float value = (float) animation.getAnimatedValue();            if (UP_OR_DOWN_MODE == UP_ANIMATOR_MODE) {                //数字变大,向下挪动                mTvFirst.setTranslationY(-mHeight * value);                mTvSecond.setTranslationY(-mHeight * value);            } else {                //数字变小,向上挪动                mTvFirst.setTranslationY(mHeight * value);                mTvSecond.setTranslationY(-2 * mHeight + mHeight * value);            }        });这样NumberView就能实现一位数字的变动是高低切换动画,具备个十百位还有时钟冒号的通过容器布局AnimNumberView组合布局的形式实现示意工夫和个十百位数 四、我的项目源码博客只是大抵讲了实现思路,具体实现请浏览源码 https://github.com/DaLeiGe/An...原文链接:https://juejin.cn/post/698407... 文末您的点赞珍藏就是对我最大的激励!欢送关注我,分享Android干货,交换Android技术。对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

December 14, 2021 · 1 min · jiezi

关于android:Android自定义view流程

Android自定义view流程,次要目标是总结实现过程中的思路以及一些须要留神的中央。首先,咱们先来看一张效果图: 实现逻辑从新指定View宽高绘制外圆圆弧背景及进度绘制中圆圆弧背景及进度绘制内圆圆弧背景及进度知识点onMeasure用于测量View的大小。创立时View无需测量,当将这个View放入一个容器(父控件)时候才须要测量,而测量方法由父控件调用。当控件的父控件要搁置该控件的时候,父控件会调用子控件的onMeasure办法确定子控件须要的空间大小,而后传入widthMeasureSpec和heightMeasureSpec来通知子控件可取得的空间大小,子控件通过这两个参数就能够测量本身的宽高了。setMeasuredDimension用于从新设置View宽高Canvas#drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)绘制以oval为边界的圆弧onDraw用来确定View长什么样。onDraw绘制过程如下: Draw the background(绘制背景)If necessary, save the canvas’ layers to prepare for fading(如果须要,为保留这层为边缘的滑动成果作筹备)Draw view’s content(绘制内容)Draw children(绘制子View)If necessary, draw the fading edges and restore layers(如果须要,绘制边缘成果并且保留图层)Draw decorations (scrollbars for instance)(绘制边框,比方scrollbars,TextView)次要代码@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 依据父控件传递的widthMeasureSpec和heightMeasureSpec调用MeasureSpec.getSize测量本身宽高 int measureWidth = MeasureSpec.getSize(widthMeasureSpec); int measureHeight = MeasureSpec.getSize(heightMeasureSpec); int finalWidth = measureWidth; int finalHeight = measureHeight; // 依据本身宽高从新计算新的宽高,使新的宽高比为2:1 if (measureWidth >= measureHeight * 2) { finalWidth = measureHeight * 2; } else { finalHeight = measureWidth / 2; } // 设置View新的宽高 setMeasuredDimension(finalWidth, finalHeight);}/** * 绘制圆弧 * @param canvas * @param progress 进度 * @param color 进度色彩 * @param radius 圆弧半径 */private void drawArc(Canvas canvas, float progress, int color, float radius){ // 圆心 mXCenter = getWidth() / 2; mYCenter = getHeight() ; mPaint.setColor(mBackgroundArcColor); // 结构边界矩形 RectF oval = new RectF(); oval.left = (mXCenter - radius); oval.top = (mYCenter - radius); oval.right = mXCenter + radius; oval.bottom = radius * 2 + (mYCenter - radius); //绘制圆弧背景 canvas.drawArc(oval, -180, 180, false, mPaint); //绘制圆弧进度 float showDegree = progress / 100 * 180; mPaint.setColor(color); canvas.drawArc(oval, -180, showDegree, false, mPaint);}@Overrideprotected void onDraw(Canvas canvas) { super.onDraw(canvas); // 初始半径 float originalRadius = getWidth() * .5f; // 画笔半宽 float halfArcStokeWidth = mArcStrokeWidth * .5f; // 外圆环半径=初始半径-画笔半宽 float outSideArcRadius = originalRadius - halfArcStokeWidth; drawArc(canvas, mOutsideProgress, mOutsideArcColor, outSideArcRadius); // 中圆环半径=外圆的半径-圆环偏移值-画笔半宽 float middleArcRadius = outSideArcRadius - mArcOffset - halfArcStokeWidth; drawArc(canvas, mMiddleProgress, mMiddleArcColor, middleArcRadius); // 内圆环半径=中圆的半径-圆环偏移值-画笔半宽 float insideArcRadius = middleArcRadius - mArcOffset - halfArcStokeWidth; drawArc(canvas, mInsideProgress, mInsideArcColor, insideArcRadius);}全副代码ThreeArcView.javapublic class ThreeArcView extends View { //圆弧画笔 private Paint mPaint; //背景圆环色彩 private int mBackgroundArcColor; //外圆环色彩 private int mOutsideArcColor; //中圆环色彩 private int mMiddleArcColor; //内圆环色彩 private int mInsideArcColor; //外圆展现弧度 private float mOutsideProgress; //中圆展现弧度 private float mMiddleProgress; //内圆展现弧度 private float mInsideProgress; //圆弧宽度 private float mArcStrokeWidth; //圆偏移值 private float mArcOffset; // 圆心x坐标 private int mXCenter; // 圆心y坐标 private int mYCenter; public ThreeArcView(Context context) { this(context, null); } public ThreeArcView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public ThreeArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttrs(context, attrs); initVariable(); } private void initAttrs(Context context, AttributeSet attrs) { TypedArray typeArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThreeArcView, 0, 0); mArcStrokeWidth = typeArray.getDimension(R.styleable.ThreeArcView_ts_strokeWidth, dp2px(context, 20)); // 圆环背景色彩 mBackgroundArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_bgArcColor, 0xFFFFFFFF); // 圆环色彩 mOutsideArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_outsideBgColor, 0xFFFFFFFF); mMiddleArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_middleBgColor, 0xFFFFFFFF); mInsideArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_insideBgColor, 0xFFFFFFFF); // 圆进度 mOutsideProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_outsideProgress, 0f); mMiddleProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_middleProgress, 0f); mInsideProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_insideProgress, 0f); // 圆环偏移值 mArcOffset = typeArray.getDimension(R.styleable.ThreeArcView_ts_radiusOffset, dp2px(context, 20)); typeArray.recycle(); // 偏移值不能小于画笔宽度的一半,否则会产生笼罩 if (mArcOffset < mArcStrokeWidth / 2){ mArcOffset = mArcStrokeWidth / 2; } } private void initVariable() { //背景圆弧画笔设置 mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(mArcStrokeWidth); mPaint.setStrokeCap(Paint.Cap.ROUND);//开启显示边缘为圆形 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 别离获取冀望的宽度和高度,并取其中较小的尺寸作为该控件的宽和高 int measureWidth = MeasureSpec.getSize(widthMeasureSpec); int measureHeight = MeasureSpec.getSize(heightMeasureSpec); //裁剪出一个 (宽:高) = (2:1) 的矩形 int finalWidth = measureWidth; int finalHeight = measureHeight; if (measureWidth >= measureHeight * 2) { finalWidth = measureHeight * 2; } else { finalHeight = measureWidth / 2; } setMeasuredDimension(finalWidth, finalHeight); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 初始半径 float originalRadius = getWidth() * .5f; // 画笔半宽 float halfArcStokeWidth = mArcStrokeWidth * .5f; // 外圆环半径=初始半径-画笔半宽 float outSideArcRadius = originalRadius - halfArcStokeWidth; drawArc(canvas, mOutsideProgress, mOutsideArcColor, outSideArcRadius); // 中圆环半径=外圆的半径-圆环偏移值-画笔半宽 float middleArcRadius = outSideArcRadius - mArcOffset - halfArcStokeWidth; drawArc(canvas, mMiddleProgress, mMiddleArcColor, middleArcRadius); // 内圆环半径=中圆的半径-圆环偏移值-画笔半宽 float insideArcRadius = middleArcRadius - mArcOffset - halfArcStokeWidth; drawArc(canvas, mInsideProgress, mInsideArcColor, insideArcRadius); } /** * 绘制圆弧 * @param canvas * @param progress 进度 * @param color 进度色彩 * @param radius 圆弧半径 */ private void drawArc(Canvas canvas, float progress, int color, float radius){ // 圆心 mXCenter = getWidth() / 2; mYCenter = getHeight() ; mPaint.setColor(mBackgroundArcColor); // 结构边界矩形 RectF oval = new RectF(); oval.left = (mXCenter - radius); oval.top = (mYCenter - radius); oval.right = mXCenter + radius; oval.bottom = radius * 2 + (mYCenter - radius); //绘制圆弧背景 canvas.drawArc(oval, -180, 180, false, mPaint); //绘制圆弧进度 float showDegree = progress / 100 * 180; mPaint.setColor(color); canvas.drawArc(oval, -180, showDegree, false, mPaint); } private void setOutSideProgress(float progress){ this.mOutsideProgress = progress; postInvalidate(); } private void setMiddleProgress(float progress){ this.mMiddleProgress = progress; postInvalidate(); } private void setInsideProgress(float progress){ this.mInsideProgress = progress; postInvalidate(); } public void setProgress(float outSideProgress, float middleProgress, float insideProgress) { mOutsideProgress = outSideProgress; mMiddleProgress = middleProgress; mInsideProgress = insideProgress; postInvalidate(); } public int dp2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } public int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } public int px2sp(Context context, float pxValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (pxValue / fontScale + 0.5f); }}styes.xml<declare-styleable name="ThreeArcView"> <!-- 画笔宽度 --> <attr name="ts_strokeWidth" format="dimension" /> <!-- 圆弧背景色 --> <attr name="ts_bgArcColor" format="color" /> <!-- 外圆进度色彩 --> <attr name="ts_outsideBgColor" format="color" /> <!-- 中圆进度色彩 --> <attr name="ts_middleBgColor" format="color" /> <!-- 内圆进度色彩 --> <attr name="ts_insideBgColor" format="color" /> <!-- 外圆进度 --> <attr name="ts_outsideProgress" format="float" /> <!-- 中圆进度 --> <attr name="ts_middleProgress" format="float" /> <!-- 内圆进度 --> <attr name="ts_insideProgress" format="float" /> <!-- 圆偏移值 --> <attr name="ts_radiusOffset" format="dimension" /></declare-styleable>OK,本文到此结束,若发现问题,欢送一起留言一起探讨,感激~ ...

December 14, 2021 · 4 min · jiezi

关于android:Android就业市场究竟怎么样还能不能坚持下去

我的状况自己一般本科毕业,计算机专业,从事Android开发也有3年了。2020年因为疫情的起因,整个互联网行业都进入了低谷期,许多公司都进行了裁员,而我所在的公司也不例外。就这样,我来到了工作了3年多的公司。开启了2020年的面试旅程。 当初抉择从事Android开发,前景怎么样呢?当初想想,挪动互联网的倒退人不知;鬼不觉曾经十多年了,Mobile First 也曾经变成了 AI First。换句话说,咱们曾经不再是“风口上的猪”。挪动开发的光环和溢价开始缓缓隐没,并且正在向 AI、区块链等新的畛域转移。挪动开发的新鲜血液也曾经变少,最显著的是国内应届生都纷纷涌向了 AI 方向。 能够说,国内挪动互联网的红利期曾经过来了,当初是增量降落、存量厮杀,从抢夺用户到抢夺时长。比拟显著的是手机厂商纷纷互联网化,与传统互联网企业间接竞争。另外一方面,过来渠道的打法失灵,小程序、快利用等新兴渠道崛起,无论是手机厂商,还是各大 App 都把出海摆到了策略的地位。 这一年里我也遇到很多艰难,也踩了很多坑,同时我本人也摸索了很多的学习办法,总结了很多心得体会,并且,我对招聘也做了一些钻研和相应的筹备。在往年的秋季招聘完结当前,参考了阿里P7,腾讯T12等大佬的面试总结经验做成专题,以便分享给更多将来将要加入招聘的敌人。 面试专题合集目录1.如何对 Android 利用进行性能剖析 2.什么状况下会导致内存泄露 3.如何防止 OOM 异样 4.Android 中如何捕捉未捕捉的异样 5.ANR 是什么?怎么防止和解决 ANR(重要) 6.Android 线程间通信有哪几种形式 7.Devik 过程,linux 过程,线程的区别 8.形容一下 android 的零碎架构 9.android 利用对内存是如何限度的?咱们应该如何正当应用内存? 简述 android 应用程序构造是哪些11.请解释下 Android 程序运行时权限与文件系统权限的区别 12.Framework 工作形式及原理,Activity 是如何生成一个 view 的,机制是什么 13.多线程间通信和多过程之间通信有什么不同,别离怎么实现 14.Android 屏幕适配 15.什么是 AIDL 以及如何应用 16.Handler 机制 17.事件散发机制 18.子线程发消息到主线程进行更新 UI,除了 handler 和 AsyncTask,还有什么 19.子线程中能不能 new handler?为什么 20.Android 中的动画有哪几类,它们的特点和区别是什么 21.如何批改 Activity 进入和退出动画 22.SurfaceView & View 的区别 ...

December 14, 2021 · 3 min · jiezi

关于android:2022-Android面试题含答案

此面试题合集分为9个局部:Java根底、Android根底、UI控件篇、网络通信篇、架构设计篇、性能优化篇、源码流程篇、新技术篇、面试篇,共1932页,从知识点到面试题到新技术都进行了全面以及具体的解析! 残缺目录如下: 如果感觉此合集对你有用的话,能够间接群文件支付! 局部内容截图第一章 Java根底动态外部类和非动态外部类的比拟多态的了解与利用java办法的多态性了解java中接口和继承的区别线程池的益处,详解,单例(相对好记)线程池的长处及其原理线程池的长处(重点)为什么不举荐通过Executors间接创立线程池不怕难之BlockingQueue及其实现深刻了解ReentrantLock与ConditionJava多线程:线程间通信之LockSynchronized 关键字原理ReentrantLock原理HashMap中的Hash抵触解决和扩容机制JVM常见面试题JVM内存构造类加载机制/双亲委托 第二章 Android根底Activity知识点(必问)Fragment知识点Service知识点Intent知识点 第三章 UI控件篇屏幕适配次要控件优化事件散发与嵌套滚动动态化页面构建计划第四章 网络通信篇网络协议第五章 架构设计篇MVP架构设计组件化架构 第六章 性能优化篇启动优化内存优化绘制优化安装包优化第七章 源码流程篇开源库源码剖析Glide源码剖析Android面试题:Glideday 20 面试题:Glide面试题聊一聊对于Glide在面试中的那些事面试官:简历上如果写Glide,请留神以下几点...OkHttp源码剖析okhttp连接池复用机制okhttp 流程和优化的实现一篇让你受用的okhttp剖析OkHttp面试之--OkHttp的整个异步申请流程OkHttp面试之--HttpEngine中的sendRequest办法详解OkHttp解析大总结Okhttp工作队列工作原理Android高频面试专题 - 架构篇(二)okhttp面试必知必会Android 网络优化,应用 HTTPDNS 优化 DNS,从原理到 OkHttp 集成Retrofit源码剖析RxJava源码剖析 第八章 新技术篇实战问题篇第九章 面试篇开源文档面试文献 篇幅无限,具体内容就不作齐全展现了,须要此面试题合集的群文件支付~ 最初祝大家都能把握住每一次的晋升机会,成为更高级的开发人员。

December 13, 2021 · 1 min · jiezi

关于android:零代码快速集成AGC崩溃服务xamarin框架iOS

华为AGC的解体服务反对跨平台,依照文档整顿了个Xamarin插件集成的文档,有须要的开发者能够参考。 环境配置和我的项目设置1.装置Xamarin环境 次要是先装置visual studio for MAC,而后装置Mobile development with .NET,具体能够参考Xamarin环境搭建。 2.AGC创立我的项目工程,并且开明华为剖析服务。 这部分是基本操作,能够参见创立我的项目和开明华为剖析 3.集成AGC Xamarin NuGet包 点击创立的我的项目工程,右键抉择”Manage NuGet Packages” 抉择对应的包后装置: 持续增加HA包,留神须要抉择1.2.0.300版本: 4.增加Json文件到我的项目目录下 5.将“Build Action”设置为“BundleResource”。 6.设置利用包名。 7.配置收费预配证书 如果没有申请付费证书,能够应用收费证书,具体参见: https://developer.huawei.com/... 集成实现1.布局界面设计 双击main.storyboard拉起Xcode创立3个按键“MakeCrash”,” CatchException”,” CustomReport”。 2.代码调用 编辑 ViewController.cs 文件, 调用 AGCCrash.GetSharedInstance.TestIt 制作一次解体事件,调用 AGCCrash.GetSharedInstance.SetUserId 自定义用户标识,调用 AGCCrash.GetSharedInstance.SetCustomKey 自定义键值对,调用 AGCCrash.GetSharedInstance.Log 自定义日志级别,调用 AGCCrash.GetSharedInstance. RecordException 产生并记录一次非重大异样。 using System;using UIKit;using Huawei.Agconnect.Crash;using Foundation;  namespace crashios0512{    public partial class ViewController : UIViewController    {        public ViewController(IntPtr handle) : base(handle)        {        }         public override void ViewDidLoad()        {            base.ViewDidLoad();            // Perform any additional setup after loading the view, typically from a nib.        }         public override void DidReceiveMemoryWarning()        {            base.DidReceiveMemoryWarning();            // Release any cached data, images, etc that aren't in use.        }          partial void MakeCrash(UIKit.UIButton sender)        {            AGCCrash.GetSharedInstance().TestIt();         }         partial void CatchException(UIKit.UIButton sender)        {            AGCCrash.GetSharedInstance().RecordError(new Foundation.NSError());        }         partial void CustomReport(UIKit.UIButton sender)        {            AGCCrash.GetSharedInstance().SetUserId("testuser");            AGCCrash.GetSharedInstance().Log("default info level");            AGCCrash.GetSharedInstance().SetCustomValue(new NSString("test"), "this is string value");            AGCCrash.GetSharedInstance().LogWithLevel(AGCCrashLogLevel.Warning, "this is warning log level");            AGCCrash.GetSharedInstance().SetCustomValue(new NSNumber(123), "this is number");                 }    }}解体报告查看集成完后点击按键制作解体和非重大异样,并产生自定义报告,能够在AGC页面查看 1.解体概览 2.问题概览 3.查看解体详情堆栈 4.查看自定义键值对 5.查看自定义日志级别 6.查看自定义用户标识 欲了解更多详情,请参见: 1、华为AGC 解体服务文档:https://developer.huawei.com/... ...

December 13, 2021 · 1 min · jiezi

关于android:Android-App秒开的奥秘

什么是秒开Android App秒开,广义的讲是指你的App的Activity从启动到显示所破费的工夫在1秒以内,狭义的讲是指这个过程所破费的工夫越少越好。这个工夫越短,你的App给用户的感觉就是响应越快,应用越晦涩,用户体验更好。秒开是Android App的一个很重要的性能指标。须要咱们继续的给予关注和优化。 如何优化秒开Google提供了很多性能优化的倡议和官网的工具,网上也有十分多的对于Android App性能优化的文章和工具,能够帮忙你解决大部分卡顿的问题。然而事实却可能是即便你付出了很多精力去做优化,你的App还是在启动新Activity的时候破费过多的工夫。特地是随着需要的一直增长,你的App会变得复杂而宏大,要做优化首先要定位须要优化的点,而这会变得愈发艰难。同时大型App在启动新Activity的工夫破费过多状况呈现的可能性反而会越来越大。 在泛滥的优化倡议中,有一条比拟根本的准则是尽量避免在主线程(或者说UI线程)中进行耗时操作。例如文件读写操作、网络申请、大量计算、循环等等。直观的了解是因为启动新Activity须要在主线程执行很多代码,例如onCreate()等生命周期的回调。如果此时有耗时操作的代码在主线程被执行,到新Activity展现进去所须要的工夫就会缩短。要优化秒开,首先要能监测主线程的运行状态,那么问题来了,主线程到底是怎么在运行呢?你的代码又是什么时候,如何在主线程被执行的呢? 深刻主线程要理解主线程的工作过程,首先要理解Android的音讯机制。 音讯机制先看一下现实生活中的一个例子,尽管当初都是挪动领取了,但置信大家都去银行取过钱。当你达到银行的时候,如果你是第一个,那祝贺你,你能够马上到柜员那里办理你的业务;如果你后面还有人,那就比拟惨了,你须要排队,得等到你后面的人都办完业务才会轮到你;更可怕的是如果你后面有几位须要办理的业务破费的工夫比拟长,那你须要等更长的工夫;前面来的人则会按程序排在你身后,和你一样不耐烦的推敲什么时候能力轮到本人。 形象一下,音讯机制其实和这个例子非常相似。每个人都看做是个音讯,什么时候到的银行是不确定的。柜员能够看做一个音讯处理器,他帮你办业务就相当于在解决你的音讯;而人们依照先后顺序排起来的队伍能够看做是个音讯队列。所以这个过程能够形象为有个音讯处理器,他有个音讯队列,随机来到的音讯依照肯定顺序排列在这个队列里,音讯处理器不停的从队列头部获取音讯而后解决之,周而复始的循环反复这个过程。如下图所示: 那么Android是怎么实现这个音讯机制的呢? Android的音讯机制音讯机制首先得有音讯,在Android中就是Message。怎么能确定一个音讯呢?音讯要有起源或者指标,也就是target;音讯要表明本人要做什么,也就是what或者callback;音讯要表明本人心愿在什么时候执行,也就是when。有了这几个因素,基本上这个音讯就是个齐备的音讯了,能够被退出到音讯队列中了。Android中的音讯队列是MessageQueue。音讯解决循环是Looper。Looper是个死循环,不停的从MessageQueue中获取音讯而后解决之,具体的执行是在Handler外面进行的。另外音讯退出音讯队列也须要Handler来操作。Message,MessageQueue,Looper,Handler组合在一起,就形成了整个Android的音讯机制。 Android的主线程就运行着这样一个音讯机制。 Android的主线程主线程是在ActivityThread中创立的,能够看到在main函数中 public static void main(String[] args) { ... Looper.prepareMainLooper(); ... Looper.loop(); }主线程实现了一个音讯机制。所以Android的主线程就是个音讯解决的循环。它所做的工作就是在不停的从音讯队列获取音讯,解决音讯,周而复始。你的App所有的在UI上的操作,例如点击事件的解决、页面动画、显示更新页面、View绘制、启动新Activity等操作都是在给主线程发消息,主线程而后挨个解决这些音讯。 主线程如何影响秒开咱们理解了主线程的工作机制后,就要看看主线程中的音讯解决是如何影响Activity秒开的。当咱们要启动一个新的Activity的时候,从调用startActivity开始到新Activity显示进去,Android零碎会发送一系列的音讯给主线程。这一系列的音讯解决所破费的总工夫会影响页面的秒开,如果执行工夫过长,用户就会有响应十分慢的感觉。此外,除了Android零碎会给主线程发消息,App本身也会给主线程发消息,如果在启动新Activity的过程中,这些App本人的音讯正好插入这一系列的Android零碎音讯中,那也会导致总的解决工夫缩短,造成不能秒开。 上图代表了启动新Activity的主线程的三种状况,每个矩形代表主线程解决一个音讯所花的工夫,越宽代表解决的工夫越长。绿色填充的代表这是一个Android零碎发过来的音讯;蓝色填充的代表这是一个App本人发过来的音讯。最下方的向右箭头代表工夫,终点是startActivity被调用的时刻。 第一种情况代表失常的情景,主线程中只有和startActivity相干的零碎音讯被解决,而且解决每个音讯所破费的工夫都在正当范畴内。所以这个页面能够满足秒开。第二种状况代表一个异样的情景,尽管主线程解决的音讯都是零碎音讯,然而某一个或某几个音讯的解决工夫超出了正当值,导致页面不能秒开。第三种状况代表另一种异样的情景,在零碎音讯中混入了App本人的音讯,主线程不仅要解决零碎音讯,还要解决App本人的音讯,后果就是总的启动工夫要额定加上App音讯的解决工夫,导致页面不能秒开。理论状况中还有可能会呈现既有零碎音讯解决工夫过长同时也混有App本人的音讯的情景。秒开优化理解了影响秒开的因素之后,咱们只有有方法能监测主线程中每个音讯解决工夫,咱们就能定位到造成页面卡慢的起因,而后再做优化。幸好Android工程师为咱们在Looper中预留了打log的地位。 public static void loop() { final Looper me = myLooper(); ... final MessageQueue queue = me.mQueue; ... for (;;) { Message msg = queue.next(); // might block ... Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } ... msg.recycleUnchecked(); } }public void setMessageLogging(@Nullable Printer printer) { mLogging = printer; }可见在音讯被解决的开始和解决完结之后都会打印log。你只须要在代码中调用Looper.setMessageLogging()设置一下就好。 ...

December 13, 2021 · 1 min · jiezi

关于android:Android性能优化笔记一启动优化

利用启动类型冷启动场景:开机后第一次启动利用 或者 利用被杀死后再次启动生命周期:Process.start->Application创立->attachBaseContext->onCreate->onStart->onResume->Activity生命周期启动速度:在几种启动类型中最慢,也是咱们优化启动速度最大的拦路虎温启动场景:利用曾经启动,返回键退出生命周期:onCreate->onStart->onResume->Activity生命周期启动速度:较快热启动场景:Home键最小化利用生命周期:onResume->Activity生命周期启动速度:快从下面的总结能够看出,在利用的启动过程中,冷启动是最慢最耗时的,零碎以及利用自身都有大量的工作须要解决,所以,冷启动对于利用的启动速度是最具挑战以及最有必要进行优化的。 冷启动流程冷启动指的是应用程序从过程在零碎不存在,到零碎创立利用运行过程空间的过程。冷启动通常会产生在一下两种状况: 设施启动以来首次启动应用程序零碎杀死应用程序之后再次启动应用程序在冷启动的最开始,零碎须要负责做三件事: 加载以及启动appapp启动之后立即显示一个空白的预览窗口创立app过程一旦零碎实现创立app过程后,app过程将要接着负责实现上面的工作: 创立Application对象创立并且启动主线程ActivityThread创立启动第一个ActivityInflating views布局屏幕执行第一次绘制一旦app过程完实现了第一次绘制工作,零碎过程就会用main activity替换后面显示的预览窗口,这个时候,用户就能够正式开始与app进行交互了。 ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/12/26/167e9f92508dd4ec~tplv-t2oaga2asx-watermark.image) 从冷启动的流程看,咱们无奈干涉app过程创立等零碎操作,咱们可能干涉的有: 预览窗口Application生命周期回调Activity生命周期回调优化剖析测量工具对研发人员来说,启动速度是咱们的“门面”,它清清楚楚能够被所有人看到,咱们都心愿本人利用的启动速度能够秒杀所有竞争对手。 “工欲善其事必先利其器”,咱们须要先找到一款适宜做启动优化剖析的工具或者形式。 adb shell am start -W [packageName]/[ packageName. AppstartActivity]在统计 app 启动工夫时,零碎为咱们提供了 adb 命令,能够输入启动工夫。零碎在绘制实现后,ActivityManagerService 会回调该办法,然而可能不便咱们通过脚本屡次启动测量 TotalTime,比照版本间启动工夫差别。然而统计工夫不如 Systrace 精确。 代码埋点通过代码埋点来精确获取记录每个办法的执行工夫,晓得哪些地方耗时,而后再有针对性地优化。例如通过在 app 启动生命周期中,要害地位退出工夫点记录,达到测量目标;又例如能够在 Application 的 attachBaseContext办法中记录开始工夫,而后在启动的第一个 Activity 的 onWindowFocusChanged办法记录完结工夫。 然而从用户点击 app Icon 到 Application 被创立,再到 Activity 的渲染,两头还是有很多步骤的,比方冷启动的过程创立过程,而这个工夫用此版本是没方法统计了,必须得接受这点数据的不准确性。 NanoscopeNanoscope 十分实在,不过临时只反对 Nexus 6 和 x86 模拟器。 SimpleperfSimpleperf 的火焰图并不适宜做启动流程剖析。 TraceView通过 TraceView 次要能够失去两种数据:单次执行耗时的办法 以及 执行次数多的办法。然而TraceView 性能耗损太大,不能比拟正确反映真实情况。 SystraceSystrace 可能追踪要害零碎调用的耗时状况,如零碎的 IO 操作、内核工作队列、CPU 负载、Surface 渲染、GC 事件以及 Android 各个子系统的运行状况等。然而不反对利用程序代码的耗时剖析。 ...

December 13, 2021 · 3 min · jiezi

关于android:快速入门Python基础知识

读完本篇文章后,可对 Python 语言个性、编码格调有肯定理解,并可写出简略的 Python 程序。 一、装置与运行各个系统的 Python 装置教程请自行查阅材料,这里不再赘述。 查看 Python 版本,在命令行输出 python 即可,同时会进入命令行交互模式,能够在这里执行 python 命令。 如果电脑中装置了 python2.x 和 python3.x 两个版本,输出 python 运行的是 2.x 版本。想运行 3.x,则需输出 python3。 在命令行输出 python : Solo-mac:~ solo$ pythonPython 2.7.10 (default, Aug 17 2018, 19:45:58)[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.0.42)] on darwinType "help", "copyright", "credits" or "license" for more information.>>>在命令行输出 python3 : Solo-mac:~ solo$ python3Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24)[Clang 6.0 (clang-600.0.57)] on darwinType "help", "copyright", "credits" or "license" for more information.>>>输出 exit() 即可退出命令行模式。 ...

December 13, 2021 · 10 min · jiezi

关于android:记一个-Compose-版华容道你值得拥有

基本思路游戏逻辑比较简单,所以没有应用 MVI 之类的框架,然而整体依然听从数据驱动UI的设计思维: 定义游戏的状态基于状态的UI绘制用户输出触发状态变动 1. 定义游戏状态游戏的状态很简略,即以后各棋子(Chees)的摆放地位,所以能够将一个棋子的 List 作为承载 State 的数据结构 1.1 棋子定义先来看一下单个棋子的定义 data class Chess(    val name: String, //角色名称    val drawable: Int //角色图片    val w: Int, //棋子宽度    val h: Int, //棋子长度    val offset: IntOffset = IntOffset(0, 0) //偏移量)通过 w,h 能够确定棋子的形态,offset 确定在棋牌中的以后地位 1.2 开局棋子摆放接下来咱们定义各个角色的棋子,并依照开局的状态摆放这些棋子 val zhang = Chess("张飞", R.drawable.zhangfei, 1, 2)val cao = Chess("曹操", R.drawable.caocao, 2, 2)val huang = Chess("黄忠", R.drawable.huangzhong, 1, 2)val zhao = Chess("赵云", R.drawable.zhaoyun, 1, 2)val ma = Chess("马超", R.drawable.machao, 1, 2)val guan = Chess("关羽", R.drawable.guanyu, 2, 1)val zu = buildList {  repeat(4) { add(Chess("卒$it", R.drawable.zu, 1, 1)) } }各角色的定义中明确棋子形态,比方“张飞”的长宽比是 2:1,“曹操” 的长宽比是2:2。 接下来定义一个游戏开局: val gameOpening: List<Triple<Chess, Int, Int>> = buildList {    add(Triple(zhang, 0, 0)); add(Triple(cao,   1, 0))    add(Triple(zhao,  3, 0)); add(Triple(huang, 0, 2))    add(Triple(ma,    3, 2)); add(Triple(guan,  1, 2))    add(Triple(zu[0], 0, 4)); add(Triple(zu[1], 1, 3))    add(Triple(zu[2], 2, 3)); add(Triple(zu[3], 3, 4))}Triple 的三个成员别离示意棋子以及其在棋盘中的偏移,例如  Triple(cao, 1, 0) 示意曹操开局处于(1,0)坐标。 最初通过上面代码,将 gameOpening 转化为咱们所需的 State, 即一个 List<Chess>: const val boardGridPx = 200 //棋子单位尺寸fun ChessOpening.toList() =    map { (chess, x, y) ->        chess.moveBy(IntOffset(x * boardGridPx, y * boardGridPx))    }2. UI渲染,绘制棋局有了 List<Chess> 之后,顺次绘制棋子,从而实现整个棋局的绘制。 @Composablefun ChessBoard (chessList: List<Chess>) {    Box(        Modifier            .width(boardWidth.toDp())            .height(boardHeight.toDp())    ) {        chessList.forEach { chess ->             Image( //棋子图片                    Modifier                        .offset { chess.offset } //偏移地位                        .width(chess.width.toDp()) //棋子宽度                        .height(chess.height.toDp())) //棋子高度                    painter = painterResource(id = chess.drawable),                    contentDescription = chess.name             )        }    }}Box 确定棋盘的范畴,Image 绘制棋子,并通过 Modifier.offset{ } 将其摆放到正确的地位。 到此为止,咱们应用 Compose 绘制了一个动态的开局,接下来就是让棋子追随手指动起来,这就波及到 Compose Gesture 的应用了 3. 拖拽棋子,触发状态变动Compose 的事件处理也是通过 Modifier 设置的, 例如 Modifier.draggable(), Modifier.swipeable() 等能够做到开箱即用。华容道的游戏场景中,能够应用 draggable 监听拖拽 3.1 监听手势1) 应用 draggable 监听手势棋子能够x轴、y轴两个方向进行拖拽,所以咱们别离设置两个 draggable : @Composablefun ChessBoard (    chessList: List<Chess>,    onMove: (chess: String, x: Int, y: Int) -> Unit) {    Image(        modifier = Modifier           ...           .draggable(//监听程度拖拽                 orientation = Orientation.Horizontal,                 state = rememberDraggableState(onDelta = {                     onMove(chess.name, it.roundToInt(), 0)                 })            )            .draggable(//监听垂直拖拽                 orientation = Orientation.Vertical,                 state = rememberDraggableState(onDelta = {                     onMove(chess.name, 0, it.roundToInt())                 })            ),            ...    )}orientation 用来指定监听什么方向的手势:程度或垂直。rememberDraggableState保留拖动状态,onDelta 指定手势的回调。咱们通过自定义的 onMove 将拖拽手势的位移信息抛出。 此时有人会问了,draggable 只能监听或者程度或者垂直的拖拽,那如果想监听任意方向的拖拽呢,此时能够应用 detectDragGestures 2) 应用 pointerInput 监听手势draggable , swipeable 等,其外部都是通过调用 Modifier.pointerInput() 实现的,基于 pointerInput 能够实现更简单的自定义手势: fun Modifier.pointerInput(    key1: Any?,    block: suspend PointerInputScope.() -> Unit) : Modifier = composed (...) {    ...}pointerInput 提供了 PointerInputScope,在其中能够应用suspend函数对各种手势进行监听。例如,能够应用 detectDragGestures 监听任意方向的拖拽: suspend fun PointerInputScope.detectDragGestures(    onDragStart: (Offset) -> Unit = { },    onDragEnd: () -> Unit = { },    onDragCancel: () -> Unit = { },    onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit)detectDragGestures 也提供了程度、垂直版本供选择,所以在华容道的场景中,也能够应用以下形式进行程度和垂直方向的监听: @Composablefun ChessBoard (    chessList: List<Chess>,    onMove: (chess: String, x: Int, y: Int) -> Unit) {    Image(        modifier = Modifier            ...            .pointerInput(Unit) {                scope.launch {//监听程度拖拽                    detectHorizontalDragGestures { change, dragAmount ->                        change.consumeAllChanges()                        onMove(chess.name, 0, dragAmount.roundToInt())                    }                }                scope.launch {//监听垂直拖拽                    detectVerticalDragGestures { change, dragAmount ->                        change.consumeAllChanges()                        onMove(chess.name, 0, dragAmount.roundToInt())                    }                }            },            ...    )}须要留神 detectHorizontalDragGestures 和  detectVerticalDragGestures 是挂起函数,所以须要别离启动协程进行监听,能够类比成多个 flow 的 collect。 3.2 棋子的碰撞检测获取了棋子拖拽的位移信息后,能够更新棋局状态并最终刷新UI。然而在更新状态之前须要对棋子的碰撞进行检测,棋子的拖拽是有边界的。 碰撞检测的准则很简略:棋子不能越过以后挪动方向上的其余棋子。 1) 绝对地位断定首先,须要确定棋子之间的绝对地位。能够应用上面办法,断定棋子A在棋子B的上方: val Chess.left get() = offset.xval Chess.right get() = left + widthinfix fun Chess.isAboveOf(other: Chess) =    (bottom <= other.top) && ((left until right) intersect (other.left until other.right)).isNotEmpty()拆解上述条件表达式,即 棋子A的下边界位于棋子B上边界之上 且 在程度方向上棋子A与棋子B的区域有交加: 比方下面的棋局中,能够失去如下断定后果: 曹操 位于 关羽 之上关羽 位于 卒1 黄忠 之上卒1 位于 卒2 卒3 之上尽管地位上 关羽位于卒2的上方,然而从碰撞检测的角度看,关羽 和 卒2 在x轴方向没有交加,因而 关羽 在y轴方向上的挪动不会碰撞到 卒2, guan.isAboveOf(zu1) == false同理,其余几种地位关系如下: infix fun Chess.isToRightOf(other: Chess) =    (left >= other.right) && ((top until bottom) intersect (other.top until other.bottom)).isNotEmpty()infix fun Chess.isToLeftOf(other: Chess) =    (right <= other.left) && ((top until bottom) intersect (other.top until other.bottom)).isNotEmpty()infix fun Chess.isBelowOf(other: Chess) =    (top >= other.bottom) && ((left until right) intersect (other.left until other.right)).isNotEmpty()2) 越界检测接下来,判断棋子挪动时是否越界,即是否越过了其挪动方向上的其余棋子或者出界 ...

December 13, 2021 · 1 min · jiezi

关于android:Android笔记apk嵌套

a.apk-主利用 b.apk-被启动利用次要思维:把b.apk放到assets目录下,因为有大小限度(1M),所以改名成b.mp3(因为mp3,jpg,png,mp4等不会查看,不会限度大小),而后在用的时候再改回来 1.具体实现:public void intallApp(Context context) {try {String path = context.getFilesDir().getAbsolutePath()+ "/b.apk"; //从assets中解压到这个目录File f = new File(path);if (!f.exists()) {f.createNewFile();}InputStream is = context.getAssets().open("b.mp3");//assets里的文件在利用装置后依然存在于apk文件中inputStreamToFile(is, f);String cmd = "chmod 777 " + f.getAbsolutePath();Runtime.getRuntime().exec(cmd);cmd = "chmod 777 " + f.getParent();Runtime.getRuntime().exec(cmd);// 尝试晋升上2级的父文件夹权限,在浏览插件下载到手机存储时,刚好用到了2级目录// /data/data/packagename/files/这个目录上面所有的层级目录都须要晋升权限,才可装置apk,弹出装置界面cmd = "chmod 777 " + new File(f.getParent()).getParent();Runtime.getRuntime().exec(cmd);Intent intent = new Intent();intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setAction(android.content.Intent.ACTION_VIEW);String type = "application/vnd.android.package-archive";/* 设置intent的file与MimeType */intent.setDataAndType(Uri.fromFile(f), type);context.startActivity(intent);} catch (ActivityNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}public void inputStreamToFile(InputStream inputStream,File file){///InputStream inputStream = null;OutputStream outputStream = null;try {// read this file into InputStream//inputStream = new FileInputStream("test.txt"); // write the inputStream to a FileOutputStreamoutputStream = new FileOutputStream(file); int read = 0;byte[] bytes = new byte[1024]; while ((read = inputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, read);} System.out.println("Done!"); } catch (IOException e) {e.printStackTrace();} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if (outputStream != null) {try {// outputStream.flush();outputStream.close();} catch (IOException e) {e.printStackTrace();} }}}2.如果是启动已装置的利用,实现如下:public boolean startApp(Context context, String packageName) {//String packageName = "XXX";Intent intent = new Intent(Intent.ACTION_MAIN, null);intent.addCategory(Intent.CATEGORY_LAUNCHER);PackageManager pm = context.getPackageManager();List<ResolveInfo> listInfos = pm.queryIntentActivities(intent, 0);String className = null;for (ResolveInfo info : listInfos) {if (packageName.equals(info.activityInfo.packageName)) {className = info.activityInfo.name;break;}}if (className != null && className.length() > 0) {intent.setComponent(new ComponentName(packageName, className));intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);context.startActivity(intent);return true;}return false;}如果你晓得包名,还晓得作为启动的那activity的类名,就更简略了,就能够省掉下面查找的过程,间接启动。 ...

December 13, 2021 · 1 min · jiezi

关于android:华为云函数中使用云数据库的JavaScript-SDK基础入门

背景介绍应用云数据库Server端的SDK,此处我以华为提供的官网Demo为例,他们的Demo也曾经开源放在了GitHub上,大家须要的能够自行下载。 https://github.com/AppGallery... 下载完Demo的我的项目代码后,还须要依照以下步骤操作: 后期在调研华为云函数服务的时候,发现能够在云函数中配置云数据库的触发器,能够通过云数据库的插入删除批改等事件,用来触发云函数中代码的执行。当初华为AGC的云数据库服务全网公布了,并且还提供了Server端的JavaScript SDK,刚好能够运行在云函数反对的Node.js环境中。话不多说,先来尝试一下如何在云函数中应用云数据库提供的JavaScript SDK,进行最简略的数据的插入和查问吧。 开明服务这里波及开明两个服务: 首先须要应用华为账号登录AGC网站 ,依照以下步骤进行操作: 1.开明云函数服务在AGC内抉择 "我的我的项目"–>抉择对应的我的项目->"构建"->"云函数",界面上点击 "立刻开明" , 开明当前点击右侧的创立函数即可。 2.开明云数据库服务在AGC内抉择 "我的我的项目"–>抉择对应的我的项目->"构建"->"云数据库",界面上点击“立刻开明”,开明当前,还须要创立对应的对象类型和存储区。 此处对应类型和存储区的创立,我都是依照数据库文档中的应用入门来创立的。 包含创立一个 BookInfo 的对象类型, 还有一个QuickStartDemo 的存储区。 云数据库JavaScript代码开发应用云数据库Server端的SDK,此处我以华为提供的官网Demo为例,他们的Demo也曾经开源放在了GitHub上,大家须要的能够自行下载。 https://github.com/AppGallery... 下载完Demo的我的项目代码后,还须要依照以下步骤操作: 1.导出对象类型文件 在云数据库的界面中,对曾经创立的对象类型BookInfo,导出成js的ServerSDK的格局,将其放到demo的model目录下。 2.下载认证凭据。 AGC的界面中,抉择我的项目设置,在ServerSDK页签下,点击 “下载认证凭据” 进行下载,并且同样放到demo的model目录下。 3、批改credentialPath门路。 因为我的认证凭据下载当前放在model目录下,因而还须要同步批改代码中credentialPath门路,在CloudDBZoneWrapper.js文件中,批改初始化的代码,具体如下: (留神对于云函数环境,获取门路下的文件,要应用_dirname办法) let api_client_name = "agc-apiclient-testDB.json";let path = require('path');let api_client_path = path.join(__dirname,api_client_name);agconnect.AGCClient.initialize(agconnect.CredentialParser.toCredential(api_client_path));4.配置函数入口。 我这没有应用示例代码中默认的Start.js的接口,而是本人创立了一个inde.js的文件作为函数的入口,对应的代码如下 const CloudDBZoneWrapper = require("./model/CloudDBZoneWrapper.js");let myHandler = async function(event, context, callback, logger) {logger.info(JSON.stringify(event));logger.info("event start");const cloudDBZoneWrapper = new CloudDBZoneWrapper();const bookInfo = cloudDBZoneWrapper.getSingleBook();// upsert a list of booksawait cloudDBZoneWrapper.upsertBookInfos(bookInfo);let result = {"message":"Run Success"}callback(result);}module.exports.myHandler = myHandler;打包上传与配置触发器1.如需将本地我的项目运行到云函数中,须要将我的项目整体打包上传,而后应用事件进行测试。留神函数入口须要在根目录下 ...

December 13, 2021 · 1 min · jiezi

关于android:Android入门教程-Android-MediaPlayer-播放音频

MediaPlayer的应用形式创立 MediaPlayer 能够间接 new MediaPlayer,也能够用 MediaPlayer 提供的 create 办法创立。 mediaPlayer = new MediaPlayer();应用 create 办法创立胜利后,mediaPlayer 处于 Prepared 状态。能够间接start播放。 mediaPlayer = MediaPlayer.create(getApplicationContext(), Uri.fromFile(file)); mediaPlayer.start();设置音源 - setDataSource 通过调用 setDataSource 来设置音源。setDataSource 有多个重载办法,咱们来看罕用的几种。 例如设置应用 assets 里的资源。理论状况可能须要 try catch。 AssetFileDescriptor fd = null; MediaPlayer mediaPlayer = new MediaPlayer(); fd = context.getApplicationContext().getAssets().openFd(name); mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());本地文件,须要文件的绝对路径。 mediaPlayer.setDataSource(file.getAbsolutePath());或者获取文件的 Uri 来创立 mediaPlayer。 mediaPlayer = MediaPlayer.create(getApplicationContext(), Uri.fromFile(file));设置网络音频,也是用 setDataSource 办法,设置url。 mediaPlayer.setDataSource("https://demo.com/sample.mp3"));播放网络音频时,如果应用的是http,有可能会报错 java.io.IOException: Cleartext HTTP traffic to demo.com not permitted能够简略地设置一下manifest,设置usesCleartextTraffic="true" ...

December 13, 2021 · 3 min · jiezi

关于android:Android开发不会ANR这里有ANR全解析和各种案例包教包会

前言相比于产生应用程序解体,产生ANR更加让人头大,次要起因是解体产生的时候会在Logcat中打印出产生异样的地位,开发人员很容易就能定位到解体并解决,显然ANR没那么轻松;然而咱们大可不必这么难过,因为有问题就会有解决办法,解决不了,只是因为没有用对办法 总体思路导出ANR日志信息,依据日志信息,判断确认产生ANR的包名类名,过程号,产生工夫,导致ANR起因类型等。关注系统资源信息,包含ANR产生前后的CPU,内存,IO等系统资源的应用状况。查看主线程状态,关注主线程是否存在耗时、死锁、等锁等问题,判断该ANR是App导致还是零碎导致的。联合利用日志,代码或源码等,剖析ANR问题产生前,利用是否有异样,其中具体问题具体分析。导出ANR日志ANR问题产生时,零碎会收集ANR相干的日志信息,CPU应用状况,trace日志也就是各线程执行状况等信息,生成一个traces.txt的文件并且放在/data/anr/门路下。 留神:每一次新的ANR问题的产生,会把之前的ANR信息笼罩掉。 咱们能够通过adb命令将traces文件导出到本地。 adb rootadb shell ls /data/anradb pull /data/anr/<filename>读取要害日志信息1)在log中找到ANR产生信息: Traces文件中的关键字,例如: 09-24 15:20:20.211 1001 1543 1570 XXXXXXX: ANR in xxxxxx09-24 15:20:20.211 1001 1543 1570 XXXXXXX: PID: xxxxx09-24 15:20:20.211 1001 1543 1570 XXXXXXX: Reason: xxxxxx其中: ANR in中,包含导致ANR的包名,类名PID 中,为产生ANR的过程PIDReason 中,为导致ANR的起因,例如keyDispatchingTimedOut2)找到CPU Usage信息 09-24 15:20:20.211 1001 1543 1570 XXXXXX: CPUusage from xxx to xxx ago xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx09-24 15:20:20.211 1001 1543 1570 XXXXXX: CPUusage from xxx to xxx later xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx其中 ago 示意ANR产生前的CPU的应用状况later示意ANR产生后的CPU的应用状况重点关注xxx%TOTAL: xxx% user + xxx% kernel + xxx% iowait,可通过这几项理解到CPU的占用状况。具体分析剖析CPU usage当前,如若还是无奈找出问题起因,则须要进一步剖析trace文件。traces文件中具体记录了产生ANR前后该过程的各个线程的Stack,个别从主线程的stack动手剖析,查看剖析ANR问题产生前,利用是否有异样。 ...

December 13, 2021 · 1 min · jiezi

关于android:Android-实现抖音传送带特效

一、实现成果1.1 首先来看抖音的传送带特效 从上图能够看到,抖音的传送带特效有如下特点 屏幕左半边局部是失常预览视频屏幕右半边局部像传送带个别,将画面一直地像左边运送依据此特效的特点,咱们能够制作出各种乏味的视频 1.2 笔者实现传送带特效 从上图来看,笔者实现的成果基本上和抖音实现的统一 那么,对于该特效,咱们应该如何去实现呢? 其实在介绍抖音蓝线挑战特效那一章曾经将到一个外围知识点Fbo,对,没错,过后做蓝线挑战特效用到的就是Fbo,接下来传送带特效也须要应用Fbo的保留上一帧性能 接下来,咱们就来进行特效剖析和具体实现 二、特效剖析首先,依据下面的效果图,咱们能够简略画出示意图,如下图所示(小格子的数量越多,画面越精密) 咱们以横向进行剖析 在OpenGLES中,纹理坐标程度方向的起始地位在左方(精确的说是在左上角,这里只是剖析横向的成果,故图上标点0.0随便标在左方,便于剖析) 依据下面的效果图,理解到,该特效有两个特点 屏幕左半边局部是失常预览视频屏幕右半边局部像传送带个别,将画面一直地像左边运送这里,我用了运送一词,那么,咱们得首先晓得,它运送的是什么 2.1 运送什么?通过剖析特效图,咱们晓得,图像右半局部是一直地向左边挪动,而左半局部是失常预览的,看起来就如同是从左半局部的边缘处一直挪动到左边,那么从这里能够得出一个小论断 它运送的是左半局部的边缘区域,依据上图,精确的说是中线右边0区域的画面那么,晓得了这点,咱们就高深莫测了 2.2 它是如何运送的?后面,咱们晓得了它运送的是0区域的画面,那么接下来就来剖析下,它是如何运送的 在预览时,相机画面个别都是失常显示,0区域的画面当然也是失常一帧帧刷新当0区域显示第一帧(简称f1,前面以f开后,数字为帧序)时,将其挪动到1区域当0区域显示f2时,将1区域的f1挪动到2区域,将0区域的f2挪动到1区域顺次类推,就能够将0区域的画面源源不断地运送到左边2.3 Fbo其实,在晓得了它是运送什么,且如何运送后,咱们还是无奈得悉如何实现这一特效 此刻,就该Fbo退场了,后面蓝线挑战特效的篇章曾经对其做了详细描述,当初简略介绍下 能够将Oes纹理转换成2D纹理能够将纹理数据不显示在屏幕上,并保留下来这里,咱们要实现该特效,就要应用它的保留帧数据的性能 2.4 特效实现在下面,咱们曾经晓得了该特效是如何运送数据,那么通过下图,咱们来理解如何应用Fbo实现 从下面的剖析可知,该特效运送的是左半局部的边缘区域,所有有如何下实现步骤: 首先假如每个小格的步长为0.1,那么左半局部的边缘区域就是0.4 ~ 0.5这个区域Fbo能够保留上一帧,那么在渲染时,咱们将上一帧的数据保留下来在渲染的时候,会有两个纹理,一个是相机的失常预览纹理,另一个是保留的上一帧,此时,咱们在着色器里就要进行判断当纹理坐标x小于0.5时,显示相机的失常预览画面当纹理坐标x大于0.5时,显示保留的上一帧画面,不过这里要留神,并不是对应坐标的上一帧数据,即,不是0.5 ~ 1.0区域的数据,而是0.4 ~ 0.9区域的数据,大家能够思考下这是为什么,前面具体实现的时候会有解答这样,当相机一直产生预览数据时,右半局部将一直地将左半局部的边缘区域向左边运送 三、具体实现后面咱们剖析了该特效的整个实现流程,接下来就是具体的实现 首先,先上大家最关怀的着色器代码 3.1 着色器顶点着色器 attribute vec4 aPos;attribute vec2 aCoordinate;varying vec2 vCoordinate;void main(){    vCoordinate = aCoordinate;    gl_Position = aPos;}对于顶点着色器,并没有做任何非凡解决 片元着色器 precision mediump float;uniform sampler2D uSampler;uniform sampler2D uSampler2;varying vec2 vCoordinate;uniform float uOffset;void main(){    if (vCoordinate.x < 0.5) {        gl_FragColor = texture2D(uSampler, vCoordinate);    } else {        gl_FragColor = texture2D(uSampler2, vCoordinate - vec2(uOffset, 0.0));    }}对于片元着色器,要害就在于main()函数外面的if判断,后面也有提到,会对纹理坐标进行一个判断 当x小于0.5时,显示相机预览画面当x大于0.5时,显示上一帧的数据,且取的是对应坐标往左偏移的数据(uOffset是偏移量,能够了解成小格子的宽度)那么对于为什么要偏移呢? 这是因为通过下面,咱们能够晓得,该特效是从左半局部的边缘区域开始运送的,那么如果咱们从对应坐标取,那么不就得不到左半局部区域的坐标了吗,所有得偏移一个小格子的宽度,从而失去对应的数据 这样,每帧渲染时,都取0.4 ~ 0.9区域数据显示到0.5 ~ 1.0区域,从而就实现了该传送带特效 在晓得了如何实现该特效后,咱们还能够实现纵向的传送带特效,只须要将片元着色器里的x改为y即可 precision mediump float;uniform sampler2D uSampler;uniform sampler2D uSampler2;varying vec2 vCoordinate;uniform float uOffset;void main(){    if (vCoordinate.y < 0.5) {        gl_FragColor = texture2D(uSampler, vCoordinate);    } else {        gl_FragColor = texture2D(uSampler2, vCoordinate - vec2(0.0, uOffset));    }}3.2 Java代码实现局部上面是Java代码实现局部 这外面应用了一个lastRender保留上一帧数据,从而在下一次渲染时可能应用 public class ConveyorBeltHFilter extends BaseFilter {    private final BaseRender lastRender;    private int uSampler2Location;    private int uOffsetLocation;    private int lastTextureId = -1;    private float offset = 0.01f;    public ConveyorBeltHFilter(Context context) {        super(                context,                "render/filter/conveyor_belt_h/vertex.frag",                "render/filter/conveyor_belt_h/frag.frag"        );        lastRender = new BaseRender(context);        lastRender.setBindFbo(true);    }    @Override    public void onCreate() {        super.onCreate();        lastRender.onCreate();    }    @Override    public void onChange(int width, int height) {        super.onChange(width, height);        lastRender.onChange(width, height);    }    @Override    public void onDraw(int textureId) {        super.onDraw(textureId);        lastRender.onDraw(getFboTextureId());        lastTextureId = lastRender.getFboTextureId();    }    @Override    public void onInitLocation() {        super.onInitLocation();        uSampler2Location = GLES20.glGetUniformLocation(getProgram(), "uSampler2");        uOffsetLocation = GLES20.glGetUniformLocation(getProgram(), "uOffset");    }    @Override    public void onActiveTexture(int textureId) {        super.onActiveTexture(textureId);        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, lastTextureId);        GLES20.glUniform1i(uSampler2Location, 1);    }    @Override    public void onSetOtherData() {        super.onSetOtherData();        GLES20.glUniform1f(uOffsetLocation, offset);    }}以上就是抖音传送带特效的实现全过程,心愿大家喜爱!!! 四、GitHubgithub地址:https://github.com/JYangkai/M... ConveyorBeltHFilter.javaConveyorBeltVFilter.java原文链接:https://juejin.cn/post/699849... 文末您的点赞珍藏就是对我最大的激励!欢送关注我,分享Android干货,交换Android技术。对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

December 13, 2021 · 1 min · jiezi

关于android:APK的更新安装隐藏解除隐藏

一、用户装置的apk产生更新 public void registerReceiver(Context context) { Slog.d("PMSdddd", "systemReady1"); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REPLACED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); BroadcastReceiver packgeMonitorReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); final String packageName =intent.getData().getSchemeSpecificPart(); final boolean replacing =intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); final boolean dataRemoved =intent.getBooleanExtra(Intent.EXTRA_DATA_REMOVED, false); Slog.d("PMSdddd", "action: " + action + " packageName:"+ packageName + " replacing:" + replacing + " dataRemoved:" + dataRemoved); } }; context.registerReceiver(packgeMonitorReceiver, filter); }第1步,新装置apk ...

December 13, 2021 · 5 min · jiezi

关于android:华为云函数中使用云数据库的JavaScript-SDK基础入门

背景介绍=========应用云数据库Server端的SDK,此处我以华为提供的官网Demo为例,他们的Demo也曾经开源放在了GitHub上,大家须要的能够自行下载。 https://github.com/AppGallery... 下载完Demo的我的项目代码后,还须要依照以下步骤操作: 后期在调研华为云函数服务的时候,发现能够在云函数中配置云数据库的触发器,能够通过云数据库的插入删除批改等事件,用来触发云函数中代码的执行。当初华为AGC的云数据库服务全网公布了,并且还提供了Server端的JavaScript SDK,刚好能够运行在云函数反对的Node.js环境中。话不多说,先来尝试一下如何在云函数中应用云数据库提供的JavaScript SDK,进行最简略的数据的插入和查问吧。 开明服务这里波及开明两个服务: 首先须要应用华为账号登录AGC网站 ,依照以下步骤进行操作: 1.开明云函数服务在AGC内抉择 "我的我的项目"–>抉择对应的我的项目->"构建"->"云函数",界面上点击 "立刻开明" , 开明当前点击右侧的创立函数即可。 2.开明云数据库服务在AGC内抉择 "我的我的项目"–>抉择对应的我的项目->"构建"->"云数据库",界面上点击“立刻开明”,开明当前,还须要创立对应的对象类型和存储区。 此处对应类型和存储区的创立,我都是依照数据库文档中的应用入门来创立的。 包含创立一个 BookInfo 的对象类型, 还有一个QuickStartDemo 的存储区。 云数据库JavaScript代码开发应用云数据库Server端的SDK,此处我以华为提供的官网Demo为例,他们的Demo也曾经开源放在了GitHub上,大家须要的能够自行下载。 https://github.com/AppGallery... 下载完Demo的我的项目代码后,还须要依照以下步骤操作: 1.导出对象类型文件在云数据库的界面中,对曾经创立的对象类型BookInfo,导出成js的ServerSDK的格局,将其放到demo的model目录下。 2.下载认证凭据。AGC的界面中,抉择我的项目设置,在ServerSDK页签下,点击 “下载认证凭据” 进行下载,并且同样放到demo的model目录下。 3、批改credentialPath门路。因为我的认证凭据下载当前放在model目录下,因而还须要同步批改代码中credentialPath门路,在CloudDBZoneWrapper.js文件中,批改初始化的代码,具体如下: (留神对于云函数环境,获取门路下的文件,要应用_dirname办法) let api_client_name = "agc-apiclient-testDB.json" ; let path = require('path'); let api_client_path = path.join(__dirname,api_client_name); agconnect.AGCClient.initialize(agconnect.CredentialParser.toCredential(api_client_path)); 4.配置函数入口。我这没有应用示例代码中默认的Start.js的接口,而是本人创立了一个inde.js的文件作为函数的入口,对应的代码如下 const CloudDBZoneWrapper = require("./model/CloudDBZoneWrapper.js"); logger.info(JSON.stringify(event));``````logger.info("event start");``````const cloudDBZoneWrapper = new CloudDBZoneWrapper();``````const bookInfo = cloudDBZoneWrapper.getSingleBook();``````// upsert a list of books``````await cloudDBZoneWrapper.upsertBookInfos(bookInfo);``````let result = {"message":"Run Success"}```}` ...

December 13, 2021 · 1 min · jiezi

关于android:Android-Studio查看第三方库依赖树

我的项目的开发过程中,咱们或多或少都会引入第三方库,引入的库越多,越容易产生库之间的依赖抵触。 上面就拿我遇到的问题还原一下: 之前接人容联客服零碎的时候,集成实现后进入客服页面产生闪退,咱们回顾一下错误信息: 咱们要害看一下报错代码: java.lang.NoSuchMethodError: No virtual method into (Landroid/widget/ImageView;)Lcom/bumptech/glide/request/target/Target; in class Lcom/a/a/i; or its super classes (declaration of 'com.a.a.i' appears in/data/app/com.sami91sami.h5-1/base.apk)咱们能够依据报错,跳到报错的中央: 该报错的意思就是:没有 into(Landroid/widget/ImageView)的办法,代码能编译通过,阐明我的项目中必定是增加依赖了,那怎么还会报这个谬误呢?还没增加依赖之前,我的项目中也是应用的Glide进行图片的加载,会不会是我的项目中的Glide与容联Demo中的Glide有抵触呢。 咱们能够依据报错的中央into办法,点进入看源码: 能够看到容联Demo应用的Glide版本是3.7.0。 再来看看我的项目中Glide应用的版本: 能够看到我的项目中应用的Glide版本是4.5.0。 这时就想到真的很大概率是两者的Glide版本有抵触了。 果然将容联Demo中的Glide版本改成4.5.0之后,编译运行进入客服界面后,没有报错了,完满解决。 这就是我之前遇到的库抵触的问题,这个问题有错误信息能够定位到是Glide库依赖的问题,要是遇到其它错误信息没那么显著的,那是不是就头疼了呢。 过后遇到这个问题,我并没有应用查看依赖树的形式,而是间接查看了源码,因为过后我并不知道还能这么干,侥幸的是很快就定位到了问题所在,所以当咱们降级第三方库或者引入新的第三方库时,库与库之间依赖抵触,咱们须要晓得每个第三方依赖库的依赖树,晓得依赖树就分明哪里抵触啦。 上面就记录下几种查看依赖树的形式: 计划一: Gradle task工具查看 1、点击Android studio面板右上角“Gradle”,如图所示: 2、依照如图目录找到dependencise双击,会在Run控制台输入打印,如图所示: 3、打印如图所示: 计划二:应用Gradle View插件 1、快捷键Ctrl+Alt+s,关上settings,而后点击按钮Plugins 2、搜寻 Gradle View,而后装置,并重启Android Studio,我这是曾经装置胜利后的截图 3、点击菜单栏上View -> Tool Windows -> Gradle View,而后期待一会,就能够查看了。 如图所示: 计划三:Terminal控制台查看 在windows上Android studio Terminal中应用这个命令: ...

December 13, 2021 · 1 min · jiezi

关于android:Android-SplashPage实现应用秒开3步

Android 利用冷启动时,须要从Application开始启动,加载工夫就会比拟长,这段时间里,用户所能看到的就是”白屏“(这是因为默认的AppTheme的 android:windowBackground 默认是设置成红色的),因而我认为真正的启动页就应该是让用户点开利用时看到的不是”白屏“,而是咱们创立的一个页面,能够是一张图片、一段文字。 就会让人感觉到,这个利用能够秒开。 1.首先在 drawable 目录下新建一个 splash\_screen.xml 文件<?xml version="1.0" encoding="utf-8"?><layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque"> <item android:drawable="@color/colorPrimary"/> <item> <bitmap android:src="@drawable/ic_logo" android:gravity="center"/> </item></layer-list>咱们应用 layer-list 标签创立一个图层列表,理论就是一个 LayerDrawable ,设置一个背景,而后放上利用图标,这是我想展现的启动页,能够依据本人的须要自行定义。 2.而后在 style.xml 文件中定义一个 SplashTheme<resources> ... <style name="SplashTheme" parent="AppTheme"> <item name="android:windowBackground">@drawable/splash_screen</item> </style></resources>这里只须要将窗口背景设置为咱们方才定义的 LayerDrawable。 3.而后须要在 AndroidMenifest.xml 文件中将咱们的主页面,我这里是 MainActivity 的 android:theme 设置成咱们定义的SplashTheme<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" ... > ... <application ... > <activity android:name=".activity.MainActivity" android:launchMode="singleTask" android:theme="@style/SplashTheme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> ... </application></manifest>是不是很简略这样就能够了 Android零根底系列教程:【Android根底课程】 本文转自 https://juejin.cn/post/6963874611960217631,如有侵权,请分割删除。

December 13, 2021 · 1 min · jiezi

关于android:强烈推荐移动端音视频从零到上手

概述随着整个互联网的崛起,数据传递的模式也在一直降级变动,总的风行趋势如下: 纯文本的短信,QQ -> 空间,微博,朋友圈的图片文字联合 -> 微信语音 -> 各大直播软件 -> 抖音快手短视频音视频的倒退正在向各个行业一直扩大,从教育的近程授课,交通的人脸识别,医疗的近程就医等等,音视频方向曾经占据一个相当重要的地位,而音视频真正入门的文章又少之甚少,一个刚毕业小白可能很难切入了解,因为音视频中波及大量理论知识,而代码的书写须要联合这些实践,所以搞懂音视频,编解码等理论知识至关重要.自己也是从实习开始接触音视频我的项目,看过很多人的文章,在这里总结一个通俗易懂的文章,让更多筹备学习音视频的同学更快入门。 划重点本文中理论知识来自于各种音视频文章的演绎音视频编码基本原理汇总,其中也会有一些我本人总结减少的局部.若有谬误可评论,查看后会更正. 为了避免大家了解过于空洞,作者花了三个月工夫将最罕用,最重要的一些性能的理论知识及实战Demo亲自写进去,配合文章浏览成果更佳.每一部分的文章能够在上面每章章节开始的深刻学习中点击链接查看, 链接中文章均有Github地址,每个Demo都亲测能够通过,能够下载Demo运行. 如果喜爱,请帮忙点赞并反对转载,转载请附原文链接. 原理采集 无论是iOS平台,还是安卓平台,咱们都是须要借助官网的API实现一系列相干性能.首先咱们要明确咱们想要什么,最开始咱们须要一部手机,智能手机中摄像头是不可短少的一部分,所以咱们通过一些零碎API获取就要能够获取物理摄像头将采集到的视频数据与麦克风采集到的音频数据. 解决 音频和视频原始数据实质都是一大段数据,零碎将其包装进自定义的构造体中,通常都以回调函数模式提供给咱们,拿到音视频数据后,能够依据各自我的项目需要做一系列非凡解决,如: 视频的旋转,缩放,滤镜,美颜,裁剪等等性能, 音频的单声道降噪,打消回声,静音等等性能. 编码 原始数据做完自定义解决后就能够进行传输,像直播这样的性能就是把采集好的视频数据发送给服务器,以在网页端供所有粉丝观看,而传输因为自身就是基于网络环境,宏大的原始数据就必须压缩后能力带走,能够了解为咱们搬家要将物品都打包到行李箱这样了解. 传输 编码后的音视频数据通常以RTMP协定进行传输,这是一种专门用于传输音视频的协定,因为各种各样的视频数据格式无奈对立,所以须要有一个规范作为传输的规定.协定就起到这样的作用. 解码 服务端接管到咱们送过来的编码数据后,须要对其解码成原始数据,因为编码的数据间接送给物理硬件的设施是不能间接播放的,只有解码为原始数据能力应用. 音视频同步 解码后的每帧音视频中都含有最开始录制时候设置的工夫戳,咱们须要依据工夫戳将它们正确的播放进去,然而在网络传输中可能会失落一些数据,或者是延时获取,这时咱们就须要肯定的策略去实现音视频的同步,大体分为几种策略:缓存肯定视频数据,视频追音频等等. 推流,拉流流程推流: 将手机采集到的视频数据传给后盾播放端进行展现,播放端能够是windows, linux, web端,即手机充当采集的性能,将手机摄像头采集到视频和麦克风采集到的音频合成编码后传给对应平台的播放端。拉流: 将播放端传来的视频数据在手机上播放,推流的逆过程,行将windows, linux, web端传来的视频数据进行解码后传给对应音视频硬件,最终将视频渲染在手机界面上播放.推流如下: 拉流如下: 具体分析推流,拉流理论为互逆过程,这里依照从采集开始介绍. 1. 采集采集是推流的第一个环节,是原始的音视频数据的起源.采集的原始数据类型为音频数据PCM,视频数据YUV,RGB...。 1.1. 音频采集深入研究 iOS Core Audio简介iOS Audio Session治理音频上下文iOS Audio Queue采集播放音频数据iOS Audio Queue采集音频数据实战iOS Audio Unit采集音频数据iOS Audio Unit采集音频数据实战采集起源 内置麦克风外置具备麦克风性能的设施(相机,话筒...)零碎自带相册音频主要参数 采样率(samplerate): 模拟信号数字化的过程,每秒钟采集的数据量,采样频率越高,数据量越大,音质越好。声道数(channels): 即单声道或双声道 (iPhone无奈间接采集双声道,但能够模仿,即复制一份采集到的单声道数据.安卓局部机型能够)位宽: 每个采样点的大小,位数越多,示意越精密,音质越好,个别是8bit或16bit.数据格式: iOS端设施采集的原始数据为线性PCM类型音频数据其余: 还能够设置采样值的精度,每个数据包有几帧数据,每帧数据占多少字节等等.音频帧 音频与视频不同,视频每一帧就是一张图片,音频是流式,自身没有明确的帧的概念,理论中为了不便,取2.5ms~60ms为单位的数据为一帧音频. 计算 数据量(字节 / 秒)=(采样频率(Hz)* 采样位数(bit)* 声道数)/ 8 ...

December 12, 2021 · 2 min · jiezi

关于android:手写自定义View流式布局

视频:价值100w+Android我的项目实战大全:手把手实战,自定义View原文: https://juejin.cn/post/6969132819855441934View的生命周期先onMeasure()测量 、 再onLayout()布局 、最初onDraw()绘制。 在onMeasure()度量时,会先去度量儿子(先走儿子的生命周期),想确认儿子的大小能力确认本人的大小。 自定义View分成两类: 自定义View个别继承自View,SurfaceView或其余的View。 onMeasure()--> onDraw()都会执行,onLayout()看需要 自定义ViewGroup继承自ViewGroup或各种Layout onMeasure()--> onLayout()都会执行, onDraw()看需要 自定义View蕴含什么布局: onlayout onmeausre/ Layout:viewGroup 显示: onDraw :view: canvas paint matrix clip rect animation path(贝塞尔) line text绘制 frameWork: 交互: onTouchEvent :组合的viewGroup LayoutParams与MeasureSpecLayoutParamsLayoutParams(布局参数),也就是xml里定义的 获取 MeasureSpecMeasureSpec是一个(32位)的int值,高两位示意父容器对 view 的布局上的限度(Mode),低30位示意view的Size 1.unspecified--无限度 2.exactly -- 确定的大小 3.at\_most -- 最大不超过 实现流式布局1.继承ViewGroup 2.自定义ViewGroup须要实现onMeasure()度量和onLayout() 3.规范开局,要度量本人的大小得先度量儿子的大小,所以遍历儿子View,去view.measure() 4.儿子的宽高是依据父亲给他的MeasureSpec的Mode + 上本人的LayoutParams算进去的,也就是下面的那个表格。如父亲给的是exactly(确定的大小),儿子又是android:layout\_width="10dp",两个都是确定的,那就是10dp 5.取到儿子的宽高 6.儿子的宽高拿到了,那就是计算每行的宽度(并且取最大行宽度)。计算总的高度 7.for循环跑完了,这样每个孩子都度量完了,也晓得了最大的宽高,就能够度量本人的宽高了 8.然而本人的宽高并不只受儿子的影响,还跟父亲给的Mode相干 到此onMeasure()度量完了,接下来开始布局onLayout() 9.因为度量的时候,曾经确定了每一行存哪几个View,把他存到数组里,这样布局的时候简略很多 ...

December 12, 2021 · 1 min · jiezi

关于android:Android性能优化这些绘制优化你一定不能忽略

前言本文次要解说Android性能优化中的绘制优化 适度绘制的优化准则尽可能地管制 适度绘制的次数 = 2 次(绿色)以下,蓝色最现实尽可能防止 适度绘制的粉色 & 红色状况不容许 3 次以上的优化计划移除默认的 Window 背景移除 控件中不必要的背景缩小布局文件的层级(嵌套)自定义控件View优化:应用 clipRect() 、 quickReject()优化计划1: 移除默认的 Window 背景背景 个别应用程序 默认 继承的主题 = windowBackground ,如默认的 Light 主题: <style name="Theme.Light"> <item name="isLightTheme">true</item> <item name="windowBackground">@drawable/screen\_background\_selector\_light</item> ... </style>问题 个别状况下,该默认的 Window 背景根本用不上:因背景都自定义设置 若不移除,则导致所有界面都多 1 次绘制解决方案 移除默认的 Window 背景 形式1:在利用的主题中增加如下的一行属性<item name="android:windowBackground">@android:color/transparent</item> <!-- 或者 --> <item name="android:windowBackground">@null</item>形式2:在 BaseActivity 的 onCreate() 办法中应用上面的代码移除 getWindow().setBackgroundDrawable(null); <!-- 或者 --> getWindow().setBackgroundDrawableResource(android.R.color.transparent);优化计划2:移除 控件中不必要的背景如2个常见场景: 场景1:ListView 与 Item 列表页(ListView) 与 其内子控件(Item)的背景雷同 = 红色,故可移除子控件(Item)布局中的背景 场景2:ViewPager 与 Fragment 对于1个ViewPager + 多个 Fragment 组成的首页界面,若每个 Fragment 都设有背景色,即 ViewPager 则无必要设置,可移除 ...

December 12, 2021 · 2 min · jiezi

关于android:Android笔记Android之ContentProvider总结

1.实用场景 ContentProvider为存储和读取数据提供了对立的接口 应用ContentProvider,应用程序能够实现数据共享 android内置的许多数据都是应用ContentProvider模式,供开发者调用的(如视频,音频,图片,通讯录等) 2.相干概念介绍 1)ContentProvider简介当利用继承ContentProvider类,并重写该类用于提供数据和存储数据的办法,就能够向其余利用共享其数据。尽管应用其余办法也能够对外共享数据,但数据拜访形式会因数据存储的形式而不同,如:采纳文件形式对外共享数据,须要进行文件操作读写数据;采纳sharedpreferences共享数据,须要应用sharedpreferences API读写数据。而应用ContentProvider共享数据的益处是对立了数据拜访形式。 2)Uri类简介Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")在Content Provider中应用的查问字符串有别于规范的SQL查问。很多诸如select, add, delete, modify等操作咱们都应用一种非凡的URI来进行,这种URI由3个局部组成, “content://”, 代表数据的门路,和一个可选的标识数据的ID。以下是一些示例URI: content://media/internal/images 这个URI将返回设施上存储的所有图片content://contacts/people/ 这个URI将返回设施上的所有联系人信息content://contacts/people/45 这个URI返回单个后果(联系人信息中ID为45的联系人记录) 只管这种查问字符串格局很常见,然而它看起来还是有点令人蛊惑。为此,Android提供一系列的帮忙类(在android.provider包下),外面蕴含了很多以类变量模式给出的查问字符串,这种形式更容易让咱们了解一点,因而,如下面content://contacts/people/45这个URI就能够写成如下模式: Uri person = ContentUris.withAppendedId(People.CONTENT_URI, 45); 而后执行数据查问: Cursor cur = managedQuery(person, null, null, null); 这个查问返回一个蕴含所有数据字段的游标,咱们能够通过迭代这个游标来获取所有的数据: package com.wissen.testApp;public class ContentProviderDemo extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); displayRecords(); } private void displayRecords() { //该数组中蕴含了所有要返回的字段 String columns[] = new String[] { People.NAME, People.NUMBER }; Uri mContacts = People.CONTENT_URI; Cursor cur = managedQuery( mContacts, columns, // 要返回的数据字段 null, // WHERE子句 null, // WHERE 子句的参数 null // Order-by子句 ); if (cur.moveToFirst()) { String name = null; String phoneNo = null; do { // 获取字段的值 name = cur.getString(cur.getColumnIndex(People.NAME)); phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER)); Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show(); } while (cur.moveToNext()); } }}上例示范了一个如何顺次读取联系人信息表中的指定数据列name和number。 ...

December 12, 2021 · 3 min · jiezi

关于android:Android-多线程IntentService详解

IntentService一、IntentService概述上一篇咱们聊到了HandlerThread,本篇咱们就来看看HandlerThread在IntentService中的利用,看本篇前倡议先看看上篇的HandlerThread,有助于咱们更好把握IntentService。同样地,咱们先来看看IntentService的特点: 它实质是一种非凡的Service,继承自Service并且自身就是一个抽象类它能够用于在后盾执行耗时的异步工作,当工作实现后会主动进行它领有较高的优先级,不易被零碎杀死(继承自Service的缘故),因而比拟适宜执行一些高优先级的异步工作它外部通过HandlerThread和Handler实现异步操作创立IntentService时,只需实现onHandleIntent和构造方法,onHandleIntent为异步办法,能够执行耗时操作二、IntentService的惯例应用套路大略理解了IntentService的特点后,咱们就来理解一下它的应用形式,先看个案例:IntentService实现类如下: package com.zejian.handlerlooper; import android.app.IntentService;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.IBinder;import android.os.Message; import com.zejian.handlerlooper.util.LogUtils; import java.io.BufferedInputStream;import java.io.IOException;import java.net.HttpURLConnection;import java.net.URL; /** Created by zejianTime 16/9/3.Description: */public class MyIntentService extends IntentService { public static final String DOWNLOAD_URL="download_url";public static final String INDEX_FLAG="index_flag";public static UpdateUI updateUI;public static void setUpdateUI(UpdateUI updateUIInterface){ updateUI=updateUIInterface;}public MyIntentService(){ super("MyIntentService");}/** * 实现异步工作的办法 * @param intent Activity传递过去的Intent,数据封装在intent中 */@Overrideprotected void onHandleIntent(Intent intent) { //在子线程中进行网络申请 Bitmap bitmap=downloadUrlBitmap(intent.getStringExtra(DOWNLOAD_URL)); Message msg1 = new Message(); msg1.what = intent.getIntExtra(INDEX_FLAG,0); msg1.obj =bitmap; //告诉主线程去更新UI if(updateUI!=null){ updateUI.updateUI(msg1); } //mUIHandler.sendMessageDelayed(msg1,1000); LogUtils.e("onHandleIntent");}//----------------------重写一下办法仅为测试------------------------------------------@Overridepublic void onCreate() { LogUtils.e("onCreate"); super.onCreate();}@Overridepublic void onStart(Intent intent, int startId) { super.onStart(intent, startId); LogUtils.e("onStart");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { LogUtils.e("onStartCommand"); return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() { LogUtils.e("onDestroy"); super.onDestroy();}@Overridepublic IBinder onBind(Intent intent) { LogUtils.e("onBind"); return super.onBind(intent);}public interface UpdateUI{ void updateUI(Message message);}private Bitmap downloadUrlBitmap(String urlString) { HttpURLConnection urlConnection = null; BufferedInputStream in = null; Bitmap bitmap=null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024); bitmap= BitmapFactory.decodeStream(in); } catch (final IOException e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } try { if (in != null) { in.close(); } } catch (final IOException e) { e.printStackTrace(); } } return bitmap;}}通过代码能够看出,咱们继承了IntentService,这里有两个办法是必须实现的,一个是构造方法,必须传递一个线程名称的字符串,另外一个就是进行异步解决的办法onHandleIntent(Intent intent) 办法,其参数intent能够附带从activity传递过去的数据。这里咱们的案例次要利用onHandleIntent实现异步下载图片,而后通过回调监听的办法把下载完的bitmap放在message中回调给Activity(当然也能够应用播送实现),最初通过Handler去更新UI。上面再来看看Acitvity的代码: ...

December 12, 2021 · 6 min · jiezi

关于android:Android笔记Jetpack-Compose

留神,Jetpack Compose中的控件被定义成一个一个的可组合函数,官网称这些控件为Composable,翻译成中文是“可组合项”,当强调它作为一个界面的一部分呈现时,我会应用“控件”或“元素”之类的术语,要留神这三者之间的差异,我不晓得有没有更好的词,所以我只能用这两个。当仅仅强调它是一个可组合项时,我会失常应用“可组合项”这个术语。Layout零碎 根本准则元素须要通过一些束缚来测量本人,这限度了一个元素的最大和最小的宽高。如果一个元素有子元素,那么它会测量每一个子元素来帮忙决定本人的大小,每当一个元素向父元素报告了它本人的大小时,那么它就失去了绝对于本身来搁置本人的子元素的机会。 compose不容许屡次测量,和Flutter一样,起因就是反复测量作用于UI这种树形构造的是时候会带来指数级的性能降落。当然有很多时候你须要反复获取子元素的一些信息,这会有其它的方法。 自定义 layout modifier在compose中,Modifier提供了一系列函数,应用它们能够提供很多布局上的参数,比方padding等信息,通过自定义modifier来看下它是怎么工作的。 通过扩大函数来扩大Modifier中的办法,因为modifier是链式调用的,咱们扩大的办法也应该合乎链式调用规定,Modifier.then办法用来辅助实现链式调用,它承受一个Modifier,返回一个与这个Modifier联合后的Modifier。 fun Modifier.firstBaselineToTop( firstBaseLineToTop: Dp) = this.then( layout { measurable, constraints -> // do something... })外面的这个layout也是一个Modifier中的办法,它承受一个参数,这个参数是一个lambda,一会再说,这个lambda外面就是咱们进行测量和摆放子控件的中央。 measurable:被摆放的子控件constraints:子控件的最大和最小宽高限度上面实现一个这个成果,能够通过咱们扩大的firstBaselineToTop办法,设置文字的FristBaseline与顶部的间距。 第一步,咱们须要测量这个子控件,取得一个Placeable对象,咱们能够通过这个Placeable对象,绝对于父控件的地位来摆放这个子控件。 layout { measurable, constraints -> val placeable = measurable.measure(constraints = constraints)}这里能够将给定的constraints限度间接传入,也能够本人结构。 当初这个子控件曾经依据给定的限度被测量好,下一步,咱们就须要计算它离顶部的高度,这里应该应用用户传入的高度减去FirstBaseline的地位,失去的就是这个控件应该离顶部的高度。 // 检测子元素是否有FirstBaseLinecheck(placeable[FirstBaseline] != AlignmentLine.Unspecified)val firstBaseLine = placeable[FirstBaseline]// 计算元素该被搁置到的Y坐标,并减少元素的高度val placeableY = firstBaseLineToTop.roundToPx() - firstBaseLineval height = placeable.height + placeableY万事俱备,该摆放这个控件了。 应用MeasureScope.layout办法向内部报告大小,并摆放本人,这个办法会返回一个MeasureResult,正好是内部整个lambda表达式所要求的返回值。 layout(placeable.width,height) { placeable.placeRelative(0,placeableY)}残缺代码: fun Modifier.firstBaselineToTop( firstBaseLineToTop: Dp) = this.then( layout { measurable, constraints -> val placeable = measurable.measure(constraints = constraints) check(placeable[FirstBaseline] != AlignmentLine.Unspecified) val firstBaseLine = placeable[FirstBaseline] val placeableY = firstBaseLineToTop.roundToPx() - firstBaseLine val height = placeable.height + placeableY layout(placeable.width,height) { placeable.placeRelative(0,placeableY) } })@Preview@Composablefun useFirstBaselineToTop() { Column { Text("Hi,there", modifier = Modifier.firstBaselineToTop(24.dp)) }}@Preview@Composablefun usePadding() { Column { Text("Hi,there",modifier = Modifier.padding(top = 24.dp)) }}自定义Layout上面是自定义的一个简略的Column布局。因为和自定义Modifier差不多,不多说了。 ...

December 12, 2021 · 1 min · jiezi

关于android:如何做好性能优化字节大佬历时3个月为你整理出这份Android性能优化实战全解析

前言面试造火箭,工作拧螺丝,近些年有数开发者都对面试官疾恶如仇。尤其是在性能优化方面,各大厂的面试官根本都会发动夺命连环炮: 面试官: 性能优化你理解么? 我: 有。 面试官: 你都做过哪方面的性能优化呢?  我: 启动速度、电量、页面、内存… 面试官: 看来你教训还是比拟丰盛的,我想问一下,你个别如何缩小APP启动工夫? 我: …  面试官: ok,方才你提到了内存优化,说一下你对内存泄露的了解。  我: …  面试官: … 这个局面预计让很多开发者都痛不欲生,在各大厂的面试中,性能优化的问题或者会早退,但必定不会缺席。这也能够看出,目前各大厂都尤为关注开发者在性能优化局部的能力边界。一款产品的从开发到面世,凝聚了有数的心血,但如果最初因为卡顿、闪退等问题影响用户体验,导致用户散失,那么所有的致力都将付诸东流。 而且随着Android开发越来越趋于欠缺,工程师的开发程度以及用户对产品的要求也日益增长,所以对于开发品质的要求,甚至有点不近人情的刻薄。内存优化、UI卡顿优化、App监控解体等性能调优,也逐步成为了中高级开发者的必备技能。所以大厂面试官,会抽丝剥茧到极致,直到探到你的能力边界为止。 很多五年教训左右的Android工程师,对于性能优化相干内容都还不够相熟,很多人都只是偶然应用过,甚至是据说过。为了帮忙大家更好地把握性能优化技能,早日胜利拥抱高薪,在这里给大家分享一份字节大佬历时三个月整理出来的《Android性能优化-实战全解析》,从ANR,内存优化,耗电优化,网络优化等板块,给大家带来全方位源码实操解说! 第一章.ANR问题解析1.Android ANR:原理剖析及解决办法 ANR阐明和起因ANR剖析方法造成ANR的起因以及解决办法ANR源码剖析Android ANR的信息采集2.卡顿监控-ANR底层机制源码剖析前言四大组件启动超时ANRInput响应超时ANR... 第二章.crash监控计划1.线程监控-死锁。存活周期与CPU占用率 前言监控死锁监控存活周期监控CPU占用率总结... 第三章.启动速度与执法效率优化我的项目实站1.Android卡顿检测及优化 卡顿帧率卡顿起因卡顿检测卡顿优化2.微信越滑越卡背景卡顿的起因剖析FlingRunnable沉积的起因代码剖析ontouchdownmflingRunnable.flywheeltouch... 第四章.内存优化1.Android内存优化工具 topdumpaya meminfomemory profilerLeak canaryMAT内存问题高效分析方法参考资料2.Android内存透露剖析及检测工具LeakCanary简介背景什么是内存透露如何检测内存透露profilerLeakcanary... 纸上得来终觉浅,绝知此事要躬行,心愿大家都能早日增强性能优化技能,这份《Android性能优化-实战全解析》肯定能够给到大家帮忙,让大家早日成为真正的高级Android开发者,材料内容细节比拟多因为文章篇幅无限,须要完整版的敌人能够点击这里收费支付! 最初明天的文章就到这里,感谢您的浏览,有问题能够在评论区留言探讨,期待与大家共同进步。喜爱的话不要忘了三连。大家的反对和认可,是我分享的最大能源。

December 12, 2021 · 1 min · jiezi

关于android:感受一波Android自定义view实现超萌动感小炸弹

老规矩,咱们先来看看效果图! 再来看android的实现成果。 上面咱们和自定义view实现超萌动感天气小太阳一样,开始解析动画!(没看过天气小太阳的敌人能够先去看天气小太阳,有些天气小太阳讲过的套路将不再讲,同时须要把握path、camera、贝塞尔曲线等,不然局部代码可能会引起不适)。 咱们先把动态view绘制进去,而后再实现动画,Let’s go。 1.地板 能够看到地板其实就是一条直线。而后两头两个缺口。这要个么实现呢?看到小太阳的小伙伴可能都会说,这很简略。只有画一线直线而后笼罩两个白的区间就能够了。确实这能够实现,然而仔细观察能够发现下方的缺口是两个半圆加矩形实现的,这样的话就有点麻烦,而且不不便缺口地位的挪动。那有什么简略的办法呢?有,那就是应用Path进行绘画一条直线,而后通过设置圆笔头,再设置DashPathEffect(实现虚线,一段画,一段不画的成果,能够自在管制各段长度)来实现距离(本view的缺口都是应用此个性实现的,不相熟的小伙伴能够去看一下),代码如下: float[] groundEffectFloat=new float[]  {        bombLineWidth/4,bombLineWidth/2+bombLineWidth,bombLineWidth*2,bombLineWidth/3*2+bombLineWidth,getMeasuredWidth(),0};//设置画与不画所占长度        groundDashPathEffect=new DashPathEffect(groundEffectFloat,0);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(bombLineColor);        mPaint.setPathEffect(groundDashPathEffect);//设置虚线成果        mPath.reset();        mPath.moveTo(bombLineWidth/2,getMeasuredHeight()-bombLineWidth/2);        mPath.lineTo(getMeasuredWidth()-bombLineWidth/2,getMeasuredHeight()-          bombLineWidth/2);        canvas.drawPath(mPath,mPaint);2.身材的边框 认真一看!聪慧的你肯定会说太简略了,这不就是一个圆而后再用DashPathEffect实现缺口不就能够了!!嗯,对,就是这样的。间接放代码: mPaint.setPathEffect(bodyDashPathEffect);        mPaint.setColor(bombLineColor);        mPaint.setStyle(Paint.Style.STROKE);        mPath.reset();        mPath.addCircle(bombCenterX,bombCenterY,bodyRadius,          Path.Direction.CW);        canvas.drawPath(mPath,mPaint);        canvas.restore();简略!简略的不能再简略了,上面看身材 ...

December 12, 2021 · 2 min · jiezi

关于android:Android入门教程-MediaPlayer-多媒体播放器

MediaPlayer 根底简介简略介绍 MediaPlayer 的基本概念,状态,罕用的办法与监听器。 什么是 MediaPlayerMediaPlayer 类能够用来播放音视频文件,或者是音频流。开发者能够用它来播放本地音频,或者是网络在线音频。 MediaPlayer 属于 android.media 包。 MediaPlayer 的状态播放管制由状态机管制。在日常生活中,咱们常见的音频状态有播放中,暂停,进行,缓冲等等。 MediaPlayer 的状态有如下几种: - Idle - End - Error - Initialized - Preparing - Prepared - Started - Stopped - Paused - PlaybackCompleted 状态的切换参考官网图例。 这里略微解释一下状态转换图片。椭圆代表 MediaPlayer 可能停留的状态。椭圆之间的箭头示意办法调用,状态切换的方向。单箭头示意办法同步调用,双箭头示意异步调用。 从图中咱们能够看出状态切换的门路和波及到的办法。 Idle 与 End 状态 当 new 一个 MediaPlayer 或者调用了reset 办法,以后 MediaPlayer 会处于 Idle 状态。调用release 后,会处于 End 状态。在这 2 个状态之间的状态能够看做是 MediaPlayer 对象的生命周期。 在新创建 MediaPlayer 和调用 reset 的 MediaPlayer 之间有一些轻微的差异。 这两种状况都处于 Idle 状态,调用 getCurrentPosition(), getDuration(), getVideoHeight(), getVideoWidth(), setAudioAttributes(android.media.AudioAttributes), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(long, int), prepare() 或 prepareAsync() 办法都会抛出谬误,如果是新实例化的 MediaPlayer,不会回调 OnErrorListener.onError();但如果是 reset 后的 MediaPlayer,会回调 OnErrorListener.onError() 并且转换到 Error 状态。 ...

December 12, 2021 · 3 min · jiezi

关于android:Android-类加载器

类的生命周期 加载阶段加载阶段能够细分如下 加载类的二进制流数据结构转换,将二进制流所代表的动态存储构造转化成办法区的运行时的数据结构生成java.lang.Class对象,作为办法区这个类的各种数据的拜访入口加载类的二进制流的办法 从zip包中读取。咱们常见的JAR、AAR依赖运行时动静生成。咱们常见的动静代理技术,在java.reflect.Proxy中就是用ProxyGenerateProxyClass来为特定的接口生成代理的二进制流验证验证是连贯阶段的第一步,这一阶段的目标是为了确保 Class 文件的字节流中蕴含的信息合乎以后虚拟机的要求,并且不会危害虚拟机本身的平安。 文件格式验证:如是否以魔数 0xCAFEBABE 结尾、主、次版本号是否在以后虚拟机解决范畴之内、常量合理性验证等。 此阶段保障输出的字节流能正确地解析并存储于办法区之内,格局上合乎形容一个 Java类型信息的要求。元数据验证:是否存在父类,父类的继承链是否正确,抽象类是否实现了其父类或接口之中要求实现的所有办法,字段、办法是否与父类产生矛盾等。 第二阶段,保障不存在不合乎 Java 语言标准的元数据信息。字节码验证:通过数据流和控制流剖析,确定程序语义是非法的、合乎逻辑的。例如保障跳转指令不会跳转到办法体以外的字节码指令上。符号援用验证:在解析阶段中产生,保障能够将符号援用转化为间接援用。能够思考应用 -Xverify:none 参数来敞开大部分的类验证措施,以缩短虚拟机类加载的工夫。 筹备为类变量分配内存并设置类变量初始值,这些变量所应用的内存都将在办法区中进行调配。 解析虚拟机将常量池内的符号援用替换为间接援用的过程。 解析动作次要针对类或接口、字段、类办法、接口办法、办法类型、办法句柄和调用点限定符 7 类符号援用进行 初始化到初始化阶段,才真正开始执行类中定义的 Java 程序代码,此阶段是执行 <clinit>() 办法的过程。 类加载的机会虚拟机标准规定了有且只有 5 种状况必须立刻对类进行“初始化”(而加载、验证、筹备天然须要在此之前开始) 遇到new、getstatic 和 putstatic 或 invokestatic 这4条字节码指令时,如果类没有进行过初始化,则须要先触发其初始化。对应场景是:应用 new 实例化对象、读取或设置一个类的动态字段(被 final 润饰、已在编译期把后果放入常量池的动态字段除外)、以及调用一个类的静态方法。对类进行反射调用的时候,如果类没有进行过初始化,则须要先触发其初始化。当初始化类的父类还没有进行过初始化,则须要先触发其父类的初始化。(而一个接口在初始化时,并不要求其父接口全副都实现了初始化)虚拟机启动时,用户须要指定一个要执行的主类(蕴含 main() 办法的那个类), 虚构机会先初始化这个主类。当应用 JDK 1.7 的动静语言反对时,如果一个 java.lang.invoke.MethodHandle 实例最初的解析后果 REF\_getStatic、REF\_putStatic、REF\_invokeStatic 的办法句柄,并且这个办法句柄所对应的类没有进行过初始化,则须要先触发其初始化。留神: 通过子类援用父类的动态字段,不会导致子类初始化。通过数组定义来援用类,不会触发此类的初始化。MyClass[] cs = new MyClass[10];常量在编译阶段会存入调用类的常量池中,实质上并没有间接援用到定义常量的类,因而不会触发定义常量的类的初始化。类加载器把实现类加载阶段中的“通过一个类的全限定名来获取形容此类的二进制字节流”这个动作的代码模块称为“类加载器”。 将 class 文件二进制数据放入办法区内,而后在堆内(heap)创立一个 java.lang.Class 对象,Class 对象封装了类在办法区内的数据结构,并且向开发者提供了拜访办法区内的数据结构的接口。 类的唯一性对于任意一个类,都须要由加载它的类加载器和这个类自身一起确立其在Java虚拟机中的唯一性。 即便两个类来源于同一个 Class 文件,被同一个虚拟机加载,只有加载它们的类加载器不同,那这两个类也不相等。 这里所指的“相等”,包含代表类的 Class 对象的 equals() 办法、 isAssignableFrom() 办法、isInstance() 办法的返回后果,也包含应用 instanceof 关键字做对象所属关系断定等状况 ...

December 12, 2021 · 3 min · jiezi

关于android:Android实现优雅快速的网络请求

指标简略调用、少写反复代码不依赖第三方库(只含Retrofit+Okhttp+协程)齐全不懂协程也能立马上手(模板代码)用Kotlin的形式写Kotlin代码,什么意思呢?比照一下上面2个代码就晓得了: mViewModel.wxArticleLiveData.observe(this, object : IStateObserver<List<WxArticleBean>>() {    override fun onSuccess(data: List<WxArticleBean>?) {    }    override fun onError() {    }})mViewModel.wxArticleLiveData.observeState(this) {    onSuccess { data: List<WxArticleBean>? ->    }    onError {    }}既然是用Kotlin了,就不要用Java的形式写接口回掉了,DSL表达式不香么? 提供两种形式实现: 形式一代码量更少,网络申请自带Loading,不须要手动调用Loading形式二解耦更彻底两种形式设计思路在解耦这一块存在差别,看具体需要,没有谁好谁差,按照本人的我的项目,哪个更不便用哪个。 基于官网架构的封装: 一、封装一核心思想是:通过一个LiveData 贯通整个流程,借用网上一张图: Activity中的代码示例 点击申请网络mViewModel.getArticleData()设置监听,只监听胜利的后果,应用默认异样解决 mViewModel.wxArticleLiveData.observeState(this) {    onSuccess { data ->        Log.i("wutao","网络申请的后果是:$data")    }}如果须要独自解决每一个回调 这些回调都是可选的,不须要可不实现 mViewModel.wxArticleLiveData.observeState(this) {    onSuccess { data ->        Log.i("wutao","网络申请的后果是:$data")    }    onEmpty{        Log.i("wutao", "返回的数据是空,展现空布局")    }    onFailed {        Log.i("wutao", "后盾返回的errorCode: $it")    }    onException { e ->        Log.i("wutao","这是非后盾返回的异样回调")    }    onShowLoading {         Log.i("wutao","自定义单个申请的Loading")    }    onComplete {        Log.i("wutao","网络申请完结")    }}申请自带Loading很多网络申请都须要Loading,不想每次都写onShowLoading{}办法,也so easy。 mViewModel.wxArticleLoadingLiveData.observeState(this, this) {    onSuccess { data ->  Log.i("wutao","网络申请的后果是:$data")    }}observeState()第二个办法传入ui的援用就可,这样单个网络申请之前会主动加载Loading,胜利或者失败主动勾销Loading。 下面代码都是Activity中,咱们来看下ViewModel中。 ViewModel中代码示例 class MainViewModel{    private val repository by lazy { WxArticleRepository() }    val wxArticleLiveData = StateLiveData<List<WxArticleBean>>()    fun requestNet() {        viewModelScope.launch {            repository.fetchWxArticle(wxArticleLiveData)        }    }}很简略,引入对应的数据仓库Repo,而后应用协程执行网络申请办法。来看下Repo中的代码。 Repository中代码示例 class WxArticleRepository : BaseRepository() {    private val mService by lazy { RetrofitClient.service }    suspend fun fetchWxArticle(stateLiveData: StateLiveData<List<WxArticleBean>>) {        executeResp(stateLiveData, mService::getWxArticle)    }  }interface ApiService {    @GET("wxarticle/chapters/json")    suspend fun getWxArticle(): BaseResponse<List<WxArticleBean>>}获取一个Retrofit实例,而后调用ApiService接口办法。 封装一的劣势代码很简洁,不须要手写线程切换代码,没有很多的接口回调。自带Loading状态,不须要手动启用Loading和敞开Loading。数据驱动ui,以LiveData为载体,将页面状态和网络后果通过在LiveData返回给ui。我的项目地址见: https://github.com/ldlywt/Fas...  (分支名字是:withLoading) 封装一的有余 *封装一的核心思想是:一个LiveData贯通整个网络申请链。这是它的劣势,也是它的劣势。 解耦不彻底,违反了"在利用的各个模块之间设定明确定义的职责界线"的思维LiveData监听时,如果须要Loading,BaseActivity都须要实现带有Loading办法接口。obserState()办法第二个参数中传入了UI援用。不能达到"看办法如其意",如果是刚接触,会有很多疑难:为什么须要一个livedata作为办法的参数。网络申请的返回值去哪了?封装一还有一个最大的缺点:对于是多数据源,封装一就展现了很不敌对的一面。Repository是做一个数据仓库,我的项目中获取数据的形式都在这里批准治理,网络获取数据只是其中一个形式而已。 如果想加一个从数据库或者缓存中获取数据,封装一想改都不好改,如果强制改就毁坏了封装,侵入性很大。 针对封装一的有余,优化出了封装二。 二、封装二思路想要解决下面的有余,不能以LiveData为载体贯通整个网络申请。Observe()办法中去掉ui援用,不要小看一个ui援用,这个援用代表着具体的Activity跟Observe耦合起来了,并且Activity还要实现IUiView接口。网络申请跟Loading状态离开了,须要手动管制Loading。Repository中的办法都有返回值,会返回后果,也不须要用livedata作为办法参数。LiveData只存在于ViewModel中,LiveData不会贯通整个申请链。Repository中也不须要LiveData的援用,Repository的代码就是单纯的获取数据。针对多数据源,也十分好解决。跟ui没任何关系,能够齐全作为一个独立的Lib应用。Activity中代码 // 申请网络mViewModel.login("username", "password")// 注册监听mViewModel.userLiveData.observeState(this) {    onSuccess {data ->        mBinding.tvContent.text = data.toString()    }    onComplete {        dismissLoading()    }}observeState()中不再须要一个ui援用了。 ViewModel中 class MainViewModel {    val userLiveData = StateLiveData<User?>()    fun login(username: String, password: String) {        viewModelScope.launch {            userLiveData.value = repository.login(username, password)        }    }}通过livedata的setValue或者postValue办法将数据发送进来。 Repository中 suspend fun login(username: String, password: String): ApiResponse<User?> {    return executeHttp {        mService.login(username, password)    }}Repository中的办法都返回申请后果,并且办法参数不须要livedata。Repository齐全能够独立进去了。 针对多数据源// WxArticleRepositoryclass WxArticleRepository : BaseRepository() {    private val mService by lazy {        RetrofitClient.service    }    suspend fun fetchWxArticleFromNet(): ApiResponse<List<WxArticleBean>> {        return executeHttp {            mService.getWxArticle()        }    }    suspend fun fetchWxArticleFromDb(): ApiResponse<List<WxArticleBean>> {        return getWxArticleFromDatabase()    }}// MainViewModel.kt  private val dbLiveData = StateLiveData<List<WxArticleBean>>()private val apiLiveData = StateLiveData<List<WxArticleBean>>()val mediatorLiveDataLiveData = MediatorLiveData<ApiResponse<List<WxArticleBean>>>().apply {    this.addSource(apiLiveData) {        this.value = it    }    this.addSource(dbLiveData) {        this.value = it    }}能够看到,封装二更合乎职责繁多准则,Repository单纯的获取数据,ViewModel对数据进行解决和发送。 三、实现原理数据来源于鸿洋大神的玩Android 凋谢API 回数据结构定义: {    "data": ...,    "errorCode": 0,    "errorMsg": ""}封装一和封装二的代码差距很小,次要看封装二。 定义数据返回类 open class ApiResponse<T>(        open val data: T? = null,        open val errorCode: Int? = null,        open val errorMsg: String? = null,        open val error: Throwable? = null,) : Serializable {    val isSuccess: Boolean        get() = errorCode == 0}data class ApiSuccessResponse<T>(val response: T) : ApiResponse<T>(data = response)class ApiEmptyResponse<T> : ApiResponse<T>()data class ApiFailedResponse<T>(override val errorCode: Int?, override val errorMsg: String?) : ApiResponse<T>(errorCode = errorCode, errorMsg = errorMsg)data class ApiErrorResponse<T>(val throwable: Throwable) : ApiResponse<T>(error = throwable)基于后盾返回的基类,依据不同的后果,定义不同的状态数据类。 网络申请对立解决:BaseRepository open class BaseRepository {    suspend fun <T> executeHttp(block: suspend () -> ApiResponse<T>): ApiResponse<T> {        runCatching {            block.invoke()        }.onSuccess { data: ApiResponse<T> ->            return handleHttpOk(data)        }.onFailure { e ->            return handleHttpError(e)        }        return ApiEmptyResponse()    }    /**     * 非后盾返回谬误,捕捉到的异样     */    private fun <T> handleHttpError(e: Throwable): ApiErrorResponse<T> {        if (BuildConfig.DEBUG) e.printStackTrace()        handlingExceptions(e)        return ApiErrorResponse(e)    }    /**     * 返回200,然而还要判断isSuccess     */    private fun <T> handleHttpOk(data: ApiResponse<T>): ApiResponse<T> {        return if (data.isSuccess) {            getHttpSuccessResponse(data)        } else {            handlingApiExceptions(data.errorCode, data.errorMsg)            ApiFailedResponse(data.errorCode, data.errorMsg)        }    }    /**     * 胜利和数据为空的解决     */    private fun <T> getHttpSuccessResponse(response: ApiResponse<T>): ApiResponse<T> {        return if (response.data == null || response.data is List<*> && (response.data as List<*>).isEmpty()) {            ApiEmptyResponse()        } else {            ApiSuccessResponse(response.data!!)        }    }}Retrofit协程的错误码解决是通过异样抛出来的,所以通过try...catch来捕获非200的错误码。包装成不同的数据类对象返回。 扩大LiveData和Observer在LiveData的Observer()来判断是哪种数据类,进行相应的回调解决: abstract class IStateObserver<T> : Observer<ApiResponse<T>> {    override fun onChanged(apiResponse: ApiResponse<T>) {        when (apiResponse) {            is ApiSuccessResponse -> onSuccess(apiResponse.response)            is ApiEmptyResponse -> onDataEmpty()            is ApiFailedResponse -> onFailed(apiResponse.errorCode, apiResponse.errorMsg)            is ApiErrorResponse -> onError(apiResponse.throwable)        }        onComplete()    }再扩大LiveData,通过kotlin的DSL表达式替换java的callback回调,简写代码。 class StateLiveData<T> : MutableLiveData<ApiResponse<T>>() {    fun observeState(owner: LifecycleOwner, listenerBuilder: ListenerBuilder.() -> Unit) {        val listener = ListenerBuilder().also(listenerBuilder)        val value = object : IStateObserver<T>() {            override fun onSuccess(data: T) {                listener.mSuccessListenerAction?.invoke(data)            }            override fun onError(e: Throwable) {                listener.mErrorListenerAction?.invoke(e) ?: toast("Http Error")            }            override fun onDataEmpty() {                listener.mEmptyListenerAction?.invoke()            }            override fun onComplete() {                listener.mCompleteListenerAction?.invoke()            }            override fun onFailed(errorCode: Int?, errorMsg: String?) {                listener.mFailedListenerAction?.invoke(errorCode, errorMsg)            }        }        super.observe(owner, value)    }}四、总结封装一:代码量更少,能够依据我的项目须要封装一些具体的ui相干,开发起来更疾速,用起来更爽。 封装二:解耦更彻底,能够独立于ui模块运行。 集体认为,框架设计次要还是服务于本人的我的项目需要(开源我的项目除外),合乎设计模式和设计准则更好,然而不满足也没关系,适宜本人我的项目需要,能节俭本人的工夫,就是好的。 咱们本人我的项目中应用,怎么轻便,怎么疾速,怎么写的爽就怎么来。 原文链接:https://juejin.cn/post/699329... 文末您的点赞珍藏就是对我最大的激励!欢送关注我,分享Android干货,交换Android技术。对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

December 12, 2021 · 1 min · jiezi

关于android:密码学-02-Base64

1. 简述:Base64是一种用64个字符示意任意二进制数据的办法。它是一种编码,而非加密A-Z a-z 0-9 + / = 2. 改编在url 传输应用的码表中, + / 被 - _ 代替。 因为后端接管到 + ,会成为 空字符串。相似Hexbin编码,通过批改码表,能够生成变种base64 3. Base64的利用RSA密钥、加密后的密文、图片等数据中,会有一些不可见字符。间接转成文本传输的话,会有乱码、数据谬误、数据失落等状况呈现,就能够应用Base64编码 4. Base64的代码实现和码表java public static void main(String[] args) { String name = "横笛"; byte[] bytes = name.getBytes(StandardCharsets.UTF_8); String encode = Base64.getEncoder().encodeToString(bytes); byte[] encode1 = Base64.getEncoder().encode(bytes); System.out.println(encode); System.out.println(new String(encode1));}Android String name1 = "横笛";// okio.Base64 encodeByteString byteString1 = ByteString.of(name1.getBytes(StandardCharsets.UTF_8));String encode1 = byteString1.base64();System.out.println("okhttp3:" + encode1);// java.util.Base64// 这里是因为对Android版本有要求 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { String string2 = Base64.getEncoder().encodeToString(name1.getBytes(StandardCharsets.UTF_8)); byte[] string3 = Base64.getEncoder().encode(name1.getBytes(StandardCharsets.UTF_8)); System.out.println("java- utils:"+string2); System.out.println("java- utils:"+ Arrays.toString(string3)); }// android.util.Base64String string4 = android.util.Base64.encodeToString(name1.getBytes(StandardCharsets.UTF_8),0);byte[] string5 = android.util.Base64.encode(name1.getBytes(StandardCharsets.UTF_8),0);System.out.println("android.util.Base64:"+string4);System.out.println("android.util.Base64:"+new String(string5));5. Base64编码细节每个Base64字符代表原数据中的6bitBase64编码后的字符数,是4的倍数编码的字节数是3的倍数时,不须要填充 ...

December 11, 2021 · 1 min · jiezi

关于android:密码学-01-HEX

1. 简介:Hex编码是一种用16个字符示意任意二进制数据的办法。它是一种编码,而非加密 码表为 "0123456789ABCDEF" 这16个字符。能够自行批改为改编版本。 字符编码 URL编码 2. Hex编码的代码实现和码表java 中: public static void main(String[] args) { String name = "小肩膀"; byte[] bytes = name.getBytes(StandardCharsets.UTF_8); System.out.println(bytes.length); System.out.println(Arrays.toString(bytes)); String encode = HexBin.encode(bytes); String encode1= encode(bytes); System.out.println(encode); System.out.println(encode1); }Android 中: 配置: app-> build.gradle--> dependencies 中减少如下 api "com.squareup.okhttp3:okhttp:3.10.0"代码 String name = "小肩膀";byte[] bytes = name.getBytes(StandardCharsets.UTF_8);ByteString byteString = ByteString.of(bytes);System.out.println(byteString.hex());Hex编码特点a) 用0-9 a-f 16个字符示意。b) 每个十六进制字符代表4bit, 也就是2个十六进制字符代表一个字节。c) 在理论利用中,比方密钥初始化,肯定要分分明传进去的密钥是哪种编码的,采纳对应 形式解析,能力失去正确的后果d) 编程中很多问题,须要从字节甚至二进制位的角度去思考,能力明确

December 11, 2021 · 1 min · jiezi

关于android:码上来战探索智感生活HMS-Core线上Codelabs挑战赛第4期开始

HMS Core线上Codelabs挑战赛第4期正式开始!咱们向所有实际力超强、创新力满满的开发者收回邀请用你的超级“码”力,解锁更多利用价值!生存里,咱们被手机“秒懂”的时刻越来越多: 出行游览,目的地天气提前预报,玩法攻略一秒到手; 商场逛吃,优惠券码点亮手机,超值买买买不在话下; 晨练健身,插入耳机,适宜静止的音乐歌单就位响应…… 手机里的利用成了一个个术业有专攻的贴身管家,总能第一工夫洞悉需要,预判动机,再将适配的服务适时推出。这些细致入微的服务都得益于挪动利用开发中一系列感知能力的使用,以动静的形式、精细化治理用户生存,驱动生存更智能化和品质化。 HMS Core凋谢的情景感知服务提供了让利用“更懂用户”的一系列感知能力,助力打造诸多便捷优质体验。 例如:①在游览出行APP中,调用工夫、地位和天气感知能力;②在静止衰弱APP中,获取用户耳机状态及静止状态…… 本期挑战赛围绕HMS Core 情景感知服务开展,通过对情景感知服务提供工夫、地位、流动、信标、音频设备、环境光和天气感知等能力的组合使用,解锁更多的体验场景,构建具备实用价值和创新力的性能利用,让生存“智慧”起来。【示例参考】 示例场景阐明:事实领取场景中,领取时关上领取码往往操作繁琐,借助情景感知服务的环境光感知能力,联合手机的重力传感器。当手机屏幕向下且环境光强度大于肯定阈值时,手机主动调取付款二维码,实现智感领取。→参考示例demo源码。 “智”感生存:应用情景感知服务的系列感知能力,构建场景化利用demo。 #### 第1步:应用HMS Core情景感知服务中的各个能力及其示例代码,构建你的智慧利用性能。实现构建,进行场景化利用性能演示。 √ 挑战者能够在本人开发的利用中,基于理论用户应用场景,联合情景感知服务的能力,实现"智"感生存的场景化性能演示。 √ 同时,HMS Core的开源社区也提供了一些App Demo,开发者也可基于这些利用构建本人的创意场景。 (1)新闻垂域demo (2)电商垂域demo (3)音频播放demo (4)静止衰弱demo 学习参考:情景感知服务精品实战课第2步:提交作品 √ 将demo源码上传至指定gitee仓:https://gitee.com/hms-core/co... √ 论坛发帖展现: ① 在【论坛-HMS Core板块】以图文+gif演示模式投稿,全面展现作品性能。 ② 发帖题目:前带【HMS Core挑战赛第4期】 ③ 发帖要求:内容原创,语句通顺,排版整洁。 ④ HMS Core论坛首发,著作权归发帖人所有,华为有收费使用权。 ⑤ 发帖内容合乎法律法规要求,不得进犯第三方合法权益,否则责任自负。 挑战赛专家评委将基于以下几个方面对作品进行打分: 实用性:性能正当,操作稳固,满足实在场景下较广泛的应用需要;创新性:性能独特新鲜,构思奇妙,具备较强商业转化价值更佳;完成度:作品无效调用了情景感知能力的1至多个能力,实现价值性能;代码好看度:代码整洁利落,清晰明了。 即日起至12月27日 扫描下方二维码,退出HMS Core 技术交换群,获取挑战赛最新信息,探讨技术,专家答疑。 (已退出挑战赛群的小伙伴无需反复加群哦~) 帮忙改良 >> 你的体验反馈,能帮忙咱们一直打磨、以更好的产品发明更大价值。你可在华为开发者联盟官方论坛-HMS Core板块,提出在挑战赛过程中遇到的问题及改良倡议。 更多精彩内容,请见华为开发者官方论坛→https://developer.huawei.com/...

December 10, 2021 · 1 min · jiezi

关于android:Android入门教程-Camera-相机二

应用 Camera API 进行视频的采集,别离应用 SurfaceView、TextureView 来预览 Camera 数据,取到NV21 的数据回调 筹备应用相机权限 <uses-permission android:name="android.permission.CAMERA" />camera 预览回调中默认应用 NV21格局。查看手机是否反对摄像头。 UI筹备 <!-- 全屏显示 --><style name="FullScreenTheme" parent="AppTheme"> <item name="windowNoTitle">true</item> <item name="android:windowFullscreen">true</item></style>承载预览图像 <FrameLayout android:id="@+id/camera_preview" android:layout_width="match_parent" android:layout_height="match_parent" />应用 SurfaceView 预览 Camera,取到 NV21 数据自定义 CameraPreview 继承 SurfaceView,实现 SurfaceHolder.Callback 接口 获取 NV21 数据,Camera.setPreviewCallback() 要放在 Camera.startPreview() 之前。 应用Camera.PreviewCallback 获取预览数据回调。默认是NV21格局。 surfaceChanged 中,camera 启动预览前能够进行设置,例如设置尺寸,调整方向 /** * camera预览视图 * Created by Rust on 2018/2/26. */public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private static final String TAG = "rustApp"; private SurfaceHolder mHolder; private Camera mCamera; private int mFrameCount = 0; public CameraPreview(Context context) { super(context); } public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; mHolder = getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void setCamera(Camera c) { this.mCamera = c; } @Override public void surfaceCreated(SurfaceHolder holder) { // 开启预览 try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 可在此开释camera } @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // 若须要旋转、更改大小或从新设置,请确保障已进行预览 if (mHolder.getSurface() == null) { return; } try { mCamera.stopPreview(); } catch (Exception e) { // ignore: tried to stop a non-existent preview } Camera.Parameters parameters = mCamera.getParameters(); // ImageFormat.NV21 == 17 Log.d(TAG, "parameters.getPreviewFormat(): " + parameters.getPreviewFormat()); if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) { mCamera.setDisplayOrientation(90); } else { mCamera.setDisplayOrientation(0); } try { mCamera.setPreviewDisplay(mHolder); mCamera.setPreviewCallback(mCameraPreviewCallback); // 回调要放在 startPreview() 之前 mCamera.startPreview(); } catch (Exception e) { Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } private Camera.PreviewCallback mCameraPreviewCallback = new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { mFrameCount++; Log.d(TAG, "onPreviewFrame: data.length=" + data.length + ", frameCount=" + mFrameCount); } };}为了避免阻塞 UI 线程,在子线程中关上 camera。camera 常放在 try catch 中应用。 ...

December 10, 2021 · 4 min · jiezi

关于android:Android性能全面分析与优化方案研究教科书级总结

前言对于现在的Android开发者来说,性能优化是开发者们肯定要把握的开发技能。晦涩度和应用体验很大水平上影响着产品在市场体现中的命根子。而且当初招聘要求上,各大厂也是对精通性能优化的开发者求贤若渴。 一、为什么要学性能优化? 咱们身为Android开发者,而这是一篇对于Android高级架构师的招聘,从上图咱们能够看进去性能优化不论是对咱们本身也好还是对Android待业还是十分重要的。我发现很多人对于 Android性能优化 常识的把握大多浮于外表,对一些技术点只停留在“应用据说过”甚至是“不理解”的阶段,这其中甚至不乏一些工作 5 年以上的 Android 工程师。 随着 Android 开发越来越标准,国内工程师的素质,以及用户对产品的要求也越来越高。这也间接导致咱们对研发我的项目的品质要求到了近乎刻薄的境地,内存优化、UI 卡顿优化、App 解体监控等性能调优也逐步成了人手必备的技能。然而,还是有很多小伙伴在入门性能优化或者说学习性能优化上总是不足系统地、办法级别的指引,导致本人不足思路! 二、怎么去学Android性能优化?在这整顿收集的对于Android性能优化的常识脑图总结和学习手册文档!既可能夯实底层原理、性能调优等核心技术点,又可能把握一般开发者,难以涉及的架构设计那你在工作中、团队里、面试时,也就领有了同行难以复制的外围竞争力。 深刻摸索Android稳定性优化深刻摸索Android启动速度优化深刻摸索Android内存优化Android性能优化—实战解析 思维导图纲要正确认识Crash优化ANR优化挪动端业务高可用计划建设...... —、启动优化的意义二、利用启动流程三、启动耗时检测四、启动优化惯例计划启动过程中的常见问题...... 重识内存优化中常见工具抉择Android内存管理机制回顾内存抖动内存优化体系化搭建......对字符串匹配算法的一点了解安卓APP解体捕捉计划——xCrash深刻了解Gradle框架之一: Plugin, Extension, buildSrcAndroid H5首屏优化实际任意URL跳转破绽修复与JDK中getHost()办法之间的坑......因为文章篇幅无限,文档资料内容较多,本能够提供链接下载,但无奈容易被谐和,所以全副存档,须要这些文档这里的敌人,能够点击我的【Gitee】,同时也给大家提供一个技术交换探讨平台,心愿可能共同进步,共勉! 总结性能优化不是更新一两个版本就能够解决的,是持续性的需要,继续集成迭代反馈。在理论的我的项目中,在我的项目刚开始的时候,因为人力和我的项目实现工夫限度,性能优化的优先级比拟低,等进入我的项目投入使用阶段,就须要把优先级进步,但在我的项目初期,在设计架构计划时,性能优化的点也须要提前思考进去,这就体现出一个程序员的技术功底了。什么时候开始有性能优化的需要,往往都是从发现问题开始,而后剖析问题起因及背景,进而寻找最优解决方案,最终解决问题,这也是日常工作中常会用到的解决形式。

December 10, 2021 · 1 min · jiezi

关于android:Android开发Jetpack-Compose-ButtonIconButton等各种Button的讲解

前言本文会解说Button,IconButton, ExtendedFloatingActionButton, FloatingActionButton,IconToggleButton,OutlinedButton,RadioButton,TextButton这几个Button的用法详解,感兴趣的请往下看 一:Button的用法先来看看Button的源码(OutlinedButton跟Button的属性一样只是两个按钮的形态不太一样) @Composablefun Button( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, elevation: ButtonElevation? = ButtonDefaults.elevation(), shape: Shape = MaterialTheme.shapes.small, border: BorderStroke? = null, colors: ButtonColors = ButtonDefaults.buttonColors(), contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit) { ...}content 是示意Button的内容。比方外面能够是一个TextonClick点击的回调 @Preview()@Composablefun buttonTest(){ val context = LocalContext.current Column(modifier = Modifier.padding(10.dp,10.dp)) { Button( onClick = { Toast.makeText(context,"点击了登录",Toast.LENGTH_SHORT).show() } ){ Text(text = stringResource(id = R.string.login)) } }}modifier 修饰符enabled 是否能够 (不可用默认是灰色,可用默认是蓝色) ...

December 10, 2021 · 6 min · jiezi

关于android:Android-PDF开发androidpdfview

Android PDF开发:android-pdfview android-pdfview应用比较简单,要害的中央是PDFView,将PDFView作为像Android的ImageView或者TextView一样写进xml布局文件: <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.joanzapata.pdfview.PDFView android:id="@+id/pdfView" android:layout_width="match_parent" android:layout_height="match_parent" /></FrameLayout>而后在Java下层代码间接加载pdf文件资源装载进去即可: package zhangphil.pdfview;import com.joanzapata.pdfview.PDFView;import com.joanzapata.pdfview.listener.OnPageChangeListener;import android.app.Activity;import android.os.Bundle;import android.widget.Toast;public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PDFView pdfView = (PDFView) findViewById(R.id.pdfView); // 在我这个测试例子中,当时筹备一个叫做sample.pdf的pdf大文件放到assets目录下。 // 从assets文件目录下读取名为 sample.pdf的文件,缺省把该pdf定位到第一页。 pdfView.fromAsset("sample.pdf").defaultPage(1).onPageChange(new OnPageChangeListener() { @Override public void onPageChanged(int page, int pageCount) { // 当用户在翻页时候将回调。 Toast.makeText(getApplicationContext(), page + " / " + pageCount, Toast.LENGTH_SHORT).show(); } }).load(); }}

December 10, 2021 · 1 min · jiezi

关于android:领导谁再用定时任务实现关闭订单立马滚蛋

在电商、领取等畛域,往往会有这样的场景,用户下单后放弃领取了,那这笔订单会在指定的时间段后进行敞开操作,仔细的你肯定发现了像某宝、某东都有这样的逻辑,而且工夫很精确,误差在1s内;那他们是怎么实现的呢? 个别的做法有如下几种 定时工作敞开订单rocketmq提早队列rabbitmq死信队列工夫轮算法redis过期监听一、定时工作敞开订单(最low)个别状况下,最不举荐的形式就是关单形式就是定时工作形式,起因咱们能够看上面的图来阐明 咱们假如,关单工夫为下单后10分钟,定时工作距离也是10分钟;通过上图咱们看出,如果在第1分钟下单,在第20分钟的时候能力被扫描到执行关单操作,这样误差达到10分钟,这在很多场景下是不可承受的,另外须要频繁扫描主订单号造成网络IO和磁盘IO的耗费,对实时交易造成肯定的冲击,所以PASS 二、rocketmq提早队列形式提早音讯 生产者把音讯发送到音讯服务器后,并不心愿被立刻生产,而是期待指定工夫后才能够被消费者生产,这类音讯通常被称为提早音讯。 在RocketMQ开源版本中,反对提早音讯,然而不反对任意工夫精度的提早音讯,只反对特定级别的提早音讯。 音讯提早级别别离为1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h,共18个级别。 发送提早音讯(生产者) /** * 推送提早音讯 * @param topic * @param body * @param producerGroup * @return boolean */ public boolean sendMessage(String topic, String body, String producerGroup) { try { Message recordMsg = new Message(topic, body.getBytes()); producer.setProducerGroup(producerGroup); //设置音讯提早级别,我这里设置14,对应就是延时10分钟 // "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h" recordMsg.setDelayTimeLevel(14); // 发送音讯到一个Broker SendResult sendResult = producer.send(recordMsg); // 通过sendResult返回音讯是否胜利送达 log.info("发送提早音讯后果:======sendResult:{}", sendResult); DateFormat format =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); log.info("发送工夫:{}", format.format(new Date())); return true; } catch (Exception e) { e.printStackTrace(); log.error("提早音讯队列推送音讯异样:{},推送内容:{}", e.getMessage(), body); } return false; }生产提早音讯(消费者) ...

December 10, 2021 · 4 min · jiezi

关于android:大厂迎来了寒冬

前言12月1日,爱奇艺员工张章透漏出大规模裁员的音讯,裁员比例20%~40%,多位爱奇艺员工也证实了裁员的真实情况。 优胜劣汰本是常事,但裁员的不只爱奇艺一家,字节跳动的教育、游戏、本地生存业务,腾讯CSIG事业群下的儿童启蒙教育业务,快手、金山云、58近期也开始裁员,并且裁员规模都不小。 互联网人开始人人自危,兴许是早就人人自危了。 ”往年是10年以来最差的一年,也将是将来10年最好的一年““老话”常谈,王兴在2019年说了一句话:2019年是互联网10年以来最差的一年,也将是将来10年最好的一年。当初看来,这句话每年都实用。 已经进入大厂是一个人实力的体现、是有高薪资的保障、是集体职业路线的富丽终点,但互联网曾经过了高速发展期,时常曝出一些大规模的裁员,无论是应届生、基层员工还是高薪员工,无一例外可能呈现在裁员名单下面。 当初进大厂仍旧是实力、高薪、富丽终点的体现,但也同样意味着能够在任何时候被优化、被取代。 如何度过寒冬?被裁与否,生存和工作都要持续,在各行各业要平安度过寒冬,唯有晋升集体实力,对于Android开发来说,也是如此。 在这里给大家分享一份Android架构开发手册,年后跳槽、想进大厂或者想晋升本人的都能够看一看。 手册手册次要介绍:Android框架的初始化过程、次要组件的工作原理。 间接剖析和整顿了:Android框架的次要源代码。 并具体解说了:了解框架工作原理所需的各种基础知识和形成理论Android平台骨干的服务框架。 次要内容包含: Android Jetpack实战教程Android框架的原理和源码解析(MVC/MVP/MVVM)大厂的架构演进,包含抖音、美团、安居客、携程、微信、淘宝等(互联网寒冬之下,大厂更值得冲一把了,把握大厂架构很有必要)内容概览以及局部截图: 第一章 Android Jetpack实战和教程 1.Android Jetpack - Navigation 2.Android Jetpack - Data Binding 3.Android Jetpack - ViewModel & LiveData 4.Android Jetpack - Room 5.Android Jetpack - Paging 6.Android Jetpack - WorkManger 7.Android Jetpack - Paging 3 第二章 MVC/MVP/MVVM 1.MVC框架-导言 2.MVC框架-ASP.NET窗体 3.MVC框架-第一应用程序 4.MVC框架-文件夹 5.MVC框架-模型 6.MVC框架-控制器 7.MVC框架-视图 8.MVC框架-布局 9.MVC框架-路由引擎 10.MVC框架-动作过滤器 11.MVC框架-高级示例 12.MVC框架-Ajax反对 13.MVC框架-捆绑 14.MVC框架-异样解决 15.MVP架构设计:Google官网MVP思维解读 16.开源MVP框架 ...

December 10, 2021 · 1 min · jiezi

关于android:全面解析-钥匙环服务的应用场景商业价值

在互联互通的场景驱动下,同一开发者旗下经常领有多款利用或者多个利用状态,用户在同一设施的不同利用或端口登录时,即使应用同一帐号,仍须要反复输出明码进行验证,操作简单,间接影响到用户的应用体验,而华为钥匙环服务的呈现则无效地解决了这一问题。 钥匙环服务性能盘点 华为钥匙环服务(Keyring)为开发者提供全生命周期的凭据治理能力,包含对凭据进行获取、加密存储、受权共享、查问读取、删除等等,保障开发者的业务流畅性。 1.本地加密存储应用钥匙环服务所获取的凭据在可信执行环境(TEE)中能够随机生成密钥,而后再进行加密,每个设施的密钥均不雷同,而且密钥只能在TEE内应用,无奈来到设施,华为也不把握密钥的内容,反对凭据在本地平安存储。保留凭据时,开发者还能够设置读取此凭据的内容时是否通过锁屏明码或生物特色认证用户的身份。 1.凭据共享受权钥匙环服务可能提供同一团队开发的App之间受权共享凭据的能力,被受权应用凭据的利用能够是Android利用、快利用或者Web利用。 用户在下次进行登录操作时,利用在钥匙环服务中查找可用的凭据。查找到的凭据可能是本利用存储的凭据,也可能是其它利用受权给本利用应用的凭据。 1.保障数据安全除此之外,应用钥匙环服务,共享凭据过程中的安全性也可能失去保障。钥匙环服务通过验证安卓利用的APK包名、快利用的包名和证书哈希,或者获取以后网页的实在URL作为身份信息等形式,认证读写凭据的APK或网站的实在身份,避免凭据被仿冒的程序或网站盗用。 1.凭据删除或更新钥匙环服务向开发者提供删除和更新凭据的API。如果用户须要退出利用帐户,相应的认证凭据也将从钥匙环服务中删除。 利用场景 跨状态登录同一利用App在挪动端的入口出现多样化的散布趋势,用户在不同的利用状态之间来回切换,重复登录,导致体验割裂。而钥匙环服务对于不同的利用状态提供了不同的接口,包含钥匙环服务SDK、快利用API和Web API,反对跨利用状态共享用户认证凭据。 以电商购物类利用为例,节假日促销短信推送是咱们最罕用的营销形式之一,也广泛被用户所承受。用户在安卓利用登录之后,点开促销短信中的链接,在华为浏览器中关上Web利用,间接处于登录状态,无需反复登录即可实现下单。 跨利用登录在业务倒退过程中,同一个团队往往会开发多个App,新利用上架之后,取得用户并非易事。针对全新业务场景的利用,钥匙环服务会为其提供一条畅通无阻、高效便捷的桥梁,将流量从原有的利用当中引进来,用户无需反复输出帐号密码,只有一键受权,就能够实现无缝登录。 也就是说,用户在利用A已登录,装置同一开发者旗下的新利用B,无需输出帐号密码,能应用登录利用A的帐号,实现间接登录利用B。 帐号易切换钥匙环服务还能够存储多个凭据,如果用户在某一利用登录过多个帐号,利用能够提供确认页面,让用户自主抉择某个帐号登录,操作便捷。当然了,作为开发者,您能够抉择在用户登录之前,验证用户的生物特色或者锁屏明码,无效躲避帐号被冒用等安全隐患。 商业价值 钥匙环服务的跨利用登录场景将帮忙同一开发者旗下的关联利用之间共享流量,助力孵化新产品。同时实现流量在安卓利用、快利用、Web利用之间的双向流转。通过发明一处登录,处处登录的无缝登录体验,钥匙环服务帮忙开发者缩短用户的交互转化门路,升高登录操作的复杂度,进一步晋升导流转化率。总之,钥匙环服务是助力开发者取得商业胜利的利器。 总体来看,华为钥匙环服务具备便捷登录、平安无忧、流量共享等外围劣势,能够帮忙开发者疾速满足用户在购物、出行、社交、浏览等多个应用场景下的平安需要。 之后,华为钥匙环服务还会推出其余个性,HMS Core也将在平安畛域凋谢新的能力,为开发者们带来更优质的服务和体验。

December 10, 2021 · 1 min · jiezi

关于android:做Android开发怎么才能不被淘汰看完这篇你就知道

前言真正淘汰你的不是因为技术提高太快,是你之于企业集体价值感的丢失。或者说,你没有致力跟上当初倒退的潮流。在中国岗位性质个别分为两类:业余技术深刻型的专家与综合倒退管理层人员。这就是深度和宽度的比拼。 大环境都这样,那咱们该朝哪方面倒退呢? 对于程序员,就是要么始终学习常识跟上时代倒退,成为能超过他人疾速实现需求的人。要么就是走向负责整个我的项目、或者转行为产品经理等职位的管理层。但专家数才占总比例的5%,同时,工资也是因物以稀为贵而水涨船高。如果只会写代码,那就不是不可代替的。 因而,大多数人都会往治理岗位倒退,也就是复合技能型人才倒退,所以什么是复合型人才呢?来源于技术、跨界、认知格局。但往这方面倒退须要学什么呢?其实多看看招聘的岗位要求就能明确。 以京东招聘Android高级工程师为例,相比以前须要理解的常识更多,并且还要分明和工程师相干岗位的工作内容,并且有相应的理解。 咱们能够通过这则招聘理解当初企业、大环境须要什么样的人才。 多学一项技能,可能就会成为你升职加薪的利器。常常混迹于各简单业务线的人,能力跳出反复工作、一直踩坑的怪圈。而一个成熟的码农在于技术过关后,更突出其余技能对业余技术的附加值。 毋须讳言的是,35岁当前你的一线coding能力肯定是降落的。到时候敲代码能力就显得没那么重要了,因为编程只是你整个武器库当中的一种,你的教训,你的视线,你的架构能力,你的治理能力,你剖析和解决问题的能力曾经远远不局限于技术这个畛域。 不可替代性是决定咱们价值的惟一起因 不可替代性也是程序员不被淘汰,并且能瀑布逆行的基本。机会经常昙花一现,一不小心就错过一个时代,比方苹果安卓时代、java时代、微信时代、抖音时代。 当初Android技术更新的太快了,每年甚至每个月都有新货色。作为程序猿的咱们,肯定要花费肯定的精力和工夫去学习。如果在你最迷茫,而又不晓得怎么做的时候,最好的形式,就是进阶本人。加油吧,小伙伴们,没有谁是天生都会的,只有本人真正的口头。 Android学习之路任重而道远,咱们也都在奋斗的路上。上面是我整顿的最新的学习材料,心愿能帮到想在Android这条路上一路走到黑的敌人。 1.Jetpack架构组件从入门到精通 Android Jetpack - NavigationAndroid Jetpack - Data BindingAndroid Jetpack - ViewModel & LiveDataAndroid Jetpack - RoomAndroid Jetpack - PagingAndroid Jetpack - WorkMangerAndroid Jetpack架构组件之LifecycleAndroid Jetpack Compose 最全上手指南2.Framework精编内核解析 次要内容蕴含: 深刻解析Binder深刻解析HandlerDalvik VM 过程零碎深刻解析 WMSPackagerManagerService 3.Kotlin强化实战(附Demo) 第一章 Kotlin入门教程第二章 Kotlin 实战避坑指南第三章 我的项目实战《Kotlin Jetpack 实战》从一个膜拜大神的 Demo 开始Kotlin 写 Gradle 脚本是一种什么体验?Kotlin 编程的三重境界Kotlin 高阶函数Kotlin 泛型Kotlin 扩大Kotlin 委托协程“鲜为人知”的调试技巧图解协程:suspend 4.Android设计思维解读开源框架 热修复插件化组件化框架设计图片加载框架网络申请框架RXJava 响应式编程框架设计IOC 架构设计Android架构组件Jetpack 5.NDK模块开发 NDK 模块开发JNI 模块Native 开发工具Linux 编程最初一线互联网Android面试题含详解(高级到高级专题)这些题目是往年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整顿了答案,相熟这些知识点会大大增加通过前两轮技术面试的几率 ...

December 10, 2021 · 1 min · jiezi

关于android:Android笔记android-Toast

1.默认成果: Toast.makeText(getApplicationContext(), "默认Toast款式", Toast.LENGTH_SHORT).show(); 2.自定义显示地位成果 Toast toast = new Toast(Class.this); toast = Toast.makeText(getApplicationContext(), "自定义地位Toast", Toast.LENGTH_LONG); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); 3.带图片成果 Toast toast = new Toast(Class.this); toast = Toast.makeText(getApplicationContext(), "带图片的Toast", Toast.LENGTH_LONG); toast.setGravity(Gravity.CENTER, 0, 0); LinearLayout toastView = (LinearLayout) toast.getView(); ImageView p_w_picpathCodeProject = new ImageView(getApplicationContext()); p_w_picpathCodeProject.setImageResource(R.drawable.icon); toastView.addView(p_w_picpathCodeProject, 0); toast.show(); 4.齐全自定义成果 LayoutInflater inflater = getLayoutInflater(); View layout = inflater.inflate(R.layout.custom, (ViewGroup) findViewById(R.id.llToast)); ImageView p_w_picpath = (ImageView) layout .findViewById(R.id.tvImageToast); p_w_picpath.setImageResource(R.drawable.icon); TextView title = (TextView) layout.findViewById(R.id.tvTitleToast); title.setText("Attention"); TextView text = (TextView) layout.findViewById(R.id.tvTextToast); text.setText("齐全自定义Toast"); toast = new Toast(getApplicationContext()); toast.setGravity(Gravity.RIGHT | Gravity.TOP, 12, 40); toast.setDuration(Toast.LENGTH_LONG); toast.setView(layout); toast.show(); 5.其余线程 ...

December 10, 2021 · 1 min · jiezi

关于android:Android高仿京东2020版首页联动效果

本篇效果图: 新增成果(不同于本篇成果的另一种成果,蕴含在本我的项目中): 第一张图 通过RecyclerView+Vlayout多布局实现;第二张具备实战性质的效果图 通过CoordinatorLayout+RecyclerView实现; 第一版得布局结构图: 起初思考到TabLayout和RecyclerView(ViewPager中)能够一起滑动,所以很容易想到的方法就是用Scrollview将两者嵌套进去,成果是实现了,然而Scrollview嵌套Viewpager的弊病不言而喻! 而第二版即本篇博客并不是为了解决Scrollview嵌套Viewpager的问题,而是换一种思路去实现! 布局结构图,很简略,就两层: <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="#f7f7f7"    android:focusable="true"    android:focusableInTouchMode="true">    <LinearLayout        android:id="@+id/ll_content"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical">        <net.lucode.hackware.magicindicator.MagicIndicator            android:id="@+id/magicIndicator"            android:layout_width="match_parent"            android:layout_height="35dp"            android:background="#acddee"            android:visibility="gone" />        <com.byl.jdrefresh.v1.CustomViewPager            android:id="@+id/customViewPager"            android:layout_width="match_parent"            android:layout_height="match_parent" />    </LinearLayout>    <RelativeLayout 搜寻栏.../></RelativeLayout>就是将第一版中的第一层和第二层(自定义JdScrollVIew)放在了Tab1的fragment中: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <com.byl.jdrefresh.v2.JdScrollView2        android:id="@+id/jdScrollView"        android:layout_width="match_parent"        android:layout_height="match_parent" /></LinearLayout>JdScrollView布局 仅须要将原来布局中的ViewPager换成RecyclerView即可,具体可参考源码! 但这样做如同并没有解决TabLayout和列表一起滑动的成果啊?! 其实,这里取了一个巧,MainActivity中的有一个TabLayout,而tab1也就是首页中的Fragment也蕴含了一个一摸一样的TabLayout(NestedScrollview嵌套TabLayout+RecyclerView),当viewpager的position==0时,MainActivity中的TabLayout暗藏,其它页面时显示,所有的成果操作由MainActivity转移到了Tab1Fragment中,这样也就防止了应用ScrollView嵌套Viewpager这种模式! <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/rl_content"    android:layout_width="match_parent"    android:layout_height="match_parent">    <View        android:id="@+id/view"        android:layout_width="match_parent"        android:layout_height="180dp"        android:background="#acddee" />    <ImageView        android:id="@+id/iv_ad"        android:layout_width="match_parent"        android:layout_height="1000dp"        android:layout_marginTop="-820dp"        android:scaleType="centerCrop"        android:src="@mipmap/bg_ad" />    <LinearLayout        android:id="@+id/ll_content"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical">        <TextView            android:id="@+id/tv_refresh_state"            android:layout_width="match_parent"            android:layout_height="40dp"            android:gravity="center"            android:text="下拉刷新"            android:textColor="#dddddd" />        <net.lucode.hackware.magicindicator.MagicIndicator            android:id="@+id/magicIndicator"            android:layout_width="match_parent"            android:layout_height="35dp" />        <com.byl.jdrefresh.v2.CustomRecyclerView            android:id="@+id/recyclerView"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:layout_marginHorizontal="15dp"            android:nestedScrollingEnabled="false" />    </LinearLayout></RelativeLayout>另外,本篇在原来的根底上多加了一个性能,能够参考京东app,即下拉超过肯定间隔后,背景会主动向下全屏开展,而后主动进入到广告页面: 实现计划,就是在手势抬起(ACTION_UP)时,判断以后下拉的间隔,超过某一设定值时,则主动在肯定工夫内让图片及整体布局处于全屏状态,其实就是依附ValueAnimator,一直的设置背景图的marginTop以及内容的paddingTop: case MotionEvent.ACTION_UP:                if (adScrollDistance > 500) {                    isInterceptTouch = true;                    AnimUtils.start(-(int) (marginTop + adScrollDistance), 500, new AnimUtils.OnAnimListener() {                        @Override                        public void onUpdate(int value) {                            layoutAd(marginTop + adScrollDistance + value);                            ll_content.setPadding(0, (int) (paddingTop + adScrollDistance + AD_START_SCROLL_DISTANCE) + value, 0, 0);                        }                        @Override                        public void onEnd() {                            context.startActivity(new Intent(context, AdActivity.class));                            new Handler().postDelayed(() -> {                                tv_refresh_state.setText("下拉刷新");                                isInterceptTouch = false;                                recyclerView.setRefreshing(false);                                isInterceptScroll = false;                                REFRESH_STATUS = REFRESH_DONE;                                layoutAd(marginTop);                                iv_ad.setImageAlpha(0);                                if (onPullListener != null) onPullListener.onPull(255);                                ll_content.setPadding(0, paddingTop, 0, 0);                                reset();                            }, 300);                        }                    });                    return true;                }                ......有一点须要留神的是,背景图片的高度,并不是屏幕高度,而是屏幕的高度加上 这一部分的高度: screenHeight = SysUtils.getScreenHeight(context);topRemainHeight = SysUtils.Dp2Px(context, imageShowHeight) - StatusBarUtil.getStatusBarHeight(context) - SysUtils.Dp2Px(context, 40);//40是搜寻栏高度,是多少就写多少RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, screenHeight + topRemainHeight);marginTop = -(screenHeight + topRemainHeight - SysUtils.Dp2Px(context, imageShowHeight));layoutParams.topMargin = marginTop;iv_ad.setLayoutParams(layoutParams);这样做的起因是,如果只把背景图设为屏幕高度,则背景图通过一直设置marginTop直至为0齐全开展时,红框局部会正好卡在底部,并不会齐全暗藏掉,起因其实很简略,如图: 图片达到底部时,因为红框与图片底部是持平的,所以正好漏在了里面,因而,这就须要下面所说的办法,将图片高度在屏幕高度根底上再+红框局部高度,这样在背景图片全屏时,可见内容区就移至了屏幕外,整个屏幕就只有背景图片可见了! Github地址:https://github.com/baiyuliang... 原文地址:https://juejin.cn/post/702168... 文末您的点赞珍藏就是对我最大的激励! 欢送关注我,分享Android干货,交换Android技术。 对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

December 10, 2021 · 1 min · jiezi

关于android:Android-studio-点击按钮-跳转界面

问题形容首先,咱们有两个Java文件和与之绑定的xml文件。此处以HistoryActivity.java,activity\_history.xml 和 EventDetail.java,activity\_event\_detail.xml为例子。咱们要实现在HistoryActivity界面中增加一个按钮,并且点击跳转到EventDetail界面。 为HistoryActivity界面增加按钮在其对应的activity\_history.xml 中: <?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns: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=".HistoryActivity"> <Button android:id="@+id/History" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Historical Event" android:layout_alignParentLeft="true" android:layout_alignParentStart="true"/></android.support.constraint.ConstraintLayout>咱们通过android:id="@+id/History"语句讲button的id设置为History,在之后设置点击事件时应用。 为History按钮增加点击事件 在HistoryActivity.java中: package com.example.xff.tm;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.content.Intent;import android.widget.Button;import android.widget.*;public class HistoryActivity extends AppCompatActivity { Button button = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_history); button = (Button)findViewById(R.id.History); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass(HistoryActivity.this,EventDetail.class); startActivity(intent); } }); }}通过之前定义的button的id来找到对应button,为之设置点击监听。当产生点击事件时,通过Intent进行跳转。 在manifests->AndroidManifest.xml中增加activity(这个步骤通常是增加点击事件之后零碎主动生成,能够进行查看) ...

December 10, 2021 · 1 min · jiezi

关于android:在fragment中使用viewpager嵌套fragment

步骤:1、在Activity布局文件中定义framelayout用于增加Fragment2、创立两个Fragment用于切换3、获取Fragment管理器,并开启事物FragmentTransaction4、通FragmentTransaction.add(resource id, fragment)将fragment增加到布局上,提交事物commit5、通FragmentTransaction. replace(resource id, fragment)切换显示的fragment,提交事物commit acvitity_dynamic.xml <Button android:id="@+id/btn_change" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="切换" /> <FrameLayout android:id="@+id/fl_fragment" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_green_light"></FrameLayout>DynamicAcvitity.javapublic class DynamicActivity extends AppCompatActivity { private Button btn_change; private BlankFragment blank; private SecFragment sec; private Fragment fragment;//用该变量示意当初展现的是哪一个fragment private FragmentManager manager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dynamic); blank = new BlankFragment(); sec = new SecFragment(); fragment=blank; manager=getSupportFragmentManager(); //开始一个事物 FragmentTransaction transaction=manager.beginTransaction(); transaction.add(R.id.fl_fragment,blank); transaction.commit();//事物要提交过后才会无效 //实例化按钮 btn_change= (Button) findViewById(R.id.btn_change); btn_change.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //一个事物提交后完结不能再用,要新建一个事物 FragmentTransaction transaction1=manager.beginTransaction(); if(fragment instanceof BlankFragment){ fragment=sec; }else { fragment=blank; } transaction1.replace(R.id.fl_fragment,fragment); transaction1.commit(); } });Fragment中嵌套fragment问题在fragment中应用viewpager嵌套fragment,获取fragmentManager的时候应用getChildFragmentManager替换getFragmentManager. ...

December 10, 2021 · 1 min · jiezi

关于android:今日份分享Flutter自定义之旋转木马

先上图,带你回到童年时光: 成果剖析子布局依照圆形程序搁置且平分角度子布局旋转、反对手势滑动旋转、疾速滑动抬手持续旋转、主动旋转反对X轴旋转反对前后缩放子布局(起始角度为前,绝对地位为后,最后面最大,反而越小)多个布局叠加时后面遮挡前面成果难点问题Flutter如何实现控件布局达到3D成果?Flutter如何实现子控件旋转、主动旋转、手势滑动时关联子控件旋转滚动?疾速滑动抬手持续旋转滚动?Flutter如何实现多个布局叠加时后面遮挡前面?1.子布局依照圆形程序搁置且平分角度如上图所示: 如上图所示(参考系:最下方为0度,逆时针旋转角度减少) 第一个点解:依据已知条件列方程式x2=width/2+sin(a)*Ry2=height/2+cos(a)*R    第二个点解:依据已知条件列方程式①① x=width/2-sin(b)*R    y=height/2-cos(b)*R因为b=a-180,所以带入①方程得:② x=width/2-sin(a-180)*R   y=height/2-cos(a-180)*R 又因为sin(k*360+a)=sin(a),所以②形式能够批改为:③ x=width/2-sin(180+a)*R   y=height/2-cos(180+a)*R又又因为 sin(180+a)=-sin(a),cos(180+a)=-cosa 带入③方程式得:④ x=width/2+sin(a)*R   y=height/2+cos(a)*R 由下面2点计算得,每个子布局的中心点坐标公式对立为:x=width/2+sin(a)*R y=height/2+cos(a)*R以上所用三角函数公式表: 通过下面计算得出子控件的地位公式后,开始咱们的代码。 实现子控件依照圆形布局及平分角度代码如下: //所有子控件的地位数据//count:子控件数量;  //startAngle:开始角度默认为0;  //rotateAngle:偏转角度默认为0;List<Point> _childPointList({Size size = Size.zero}) {    List<Point> childPointList = [];    double averageAngle = 360 / count;    double radius = size.width / 2 - childWidth / 2;       for (int i = 0; i < count; i++) {       /********************子布局角度*****************/      double angle = startAngle + averageAngle * i + rotateAngle;      //子布局中心点坐标      var centerX = size.width / 2 + sin(radian(angle)) * radius;      var centerY = size.height / 2 + cos(radian(angle)) * radius;      childPointList.add(Point(        centerX,        centerY,        childWidth,        childHeight,        centerX - childWidth / 2,        centerY - childHeight / 2,        centerX + childWidth / 2,        centerY + childHeight / 2,        1,        angle,        i,      ));    }    return childPointList;  }///角度转弧度///弧度 =度数 * ( / 180)///度数 =弧度 * (180 / )double radian(double angle) {    return angle * pi / 180;}2.子布局如何旋转?主动旋转?反对手势滑动旋转?疾速滑动抬手持续旋转?子布局如何旋转 所谓的旋转就是所有的子布局绕着圆形挪动,布局一旦挪动就代表两头地位扭转,依据下面咱们计算的子布局地位的公式来看: 中心点坐标x=width/2+sin(a)*R y=height/2+cos(a)*R因为width和R都是已知并且定下来的尺寸,所以说,想要扭转中心点坐标,只需批改 角度a就能够了。要想达到旋转成果的话就是让所有的子布局都同时挪动雷同的角度即可。 子布局原始角度值:double angle = startAngle + averageAngle * i; 咱们能够在此基础上加上一个可变的角度值,通过扭转这个值,所有的子布局都会同时加上此值同时挪动了地位。如下:double angle = startAngle + averageAngle * i + rotateAngle; 其中 rotateAngle 就是可变的值。扭转这个值就能让布局动起来主动旋转同理,咱们只有搞个定时器,周期性批改这个rotateAngle值,并setState(() {})刷新下,看起来就主动旋转了。 反对手势滑动旋转大家曾经晓得通过批改rotateAngle值去实现旋转,那么反对手势滑动旋转顾名思义就是通过手势批改这个rotateAngle值就OK,那么手势解决Flutter提供了GestureDetector组件,这个组件性能很弱小,这外面咱们应用了他的几个回调办法。 本次实现间接应用程度滑动监听,大家如果想兼容竖直滑动能够本人尝试批改就能够。 GestureDetector(        ///程度滑动按下        onHorizontalDragDown: (DragDownDetails details) {...},        ///程度滑动开始        onHorizontalDragStart: (DragStartDetails details) {          //记录拖动开始时以后的抉择角度值          downAngle = rotateAngle;          //记录拖动开始时的x坐标          downX = details.globalPosition.dx;        },        ///程度滑动中        onHorizontalDragUpdate: (DragUpdateDetails details) {           //滑动中X坐标值          var updateX = details.globalPosition.dx;          //计算以后旋转角度值并刷新          rotateAngle = (downX - updateX) * slipRatio + downAngle;          if (mounted) setState(() {});        },        ///程度滑动完结        onHorizontalDragEnd: (DragEndDetails details) {...},        ///滑动勾销        onHorizontalDragCancel: () {...},        behavior: HitTestBehavior.opaque,//deferToChild   translucent        child: xxx,);疾速滑动抬手持续旋转抬手还能持续旋转,也就是当咱们疾速滑动抬手的时候只有持续批改旋转角度值rotateAngle就能够达到持续旋转的成果。当咱们抬手的时候咱们能够拿到什么呢? 例如:当咱们骑着小黄单车在大路上疾速的蹬着脚蹬子而后进行蹬,你的小黄已过后的速度飞驰在这个大路上,因为高空的摩擦力的影响,速度会越来越小,最初进行。 ///程度滑动完结onHorizontalDragEnd: (DragEndDetails details) {          //x方向上每秒速度的像素数          velocityX = details.velocity.pixelsPerSecond.dx;           _controller.reset();          _controller.forward(); },  //动画设置rotateAngle   _controller = AnimationController(      vsync: this,      duration: Duration(milliseconds: 1000),    );    animation = CurvedAnimation(      parent: _controller,      curve: Curves.linearToEaseOut,    );    animation = new Tween<double>(begin: 1, end: 0).animate(animation)      ..addListener(() {        //以后速度        var velocity = animation.value * -velocityX;        var offsetX = radius != 0 ? velocity * 5 / (2 * pi * radius) : velocity;        rotateAngle += offsetX;        setState(() => {});      })      ..addStatusListener((status) {        if (status == AnimationStatus.completed) {          rotateAngle = rotateAngle % 360;          _startRotateTimer();        }      });3.反对X轴旋转 上图是X轴方向查看旋转切面图,依照x轴旋转所有的x坐标都是雷同的,y值从上往下一直减少。因为绕着X轴旋转时,X坐标是不变的,Y坐标值扭转,当旋转了a角度时,当初的Y坐标如图所示为 Y坐标旋转后=height/2+y*cos(a)     y值咱们曾经在下面计算过了,y=cos(a)*R 所以最终的计算公式是:Y坐标值=height/2+cos(a)*R*cos(a)cos(a)在a=[0,90]区间时对应的值是1-0   即是 a=0度时cos(a)=1,就是原始状态(与Y轴平行),a=90度时cos(a)=0,就是与Y轴垂直准状态。所以咱们能够设置a在0-90之间即可。4.反对前后缩放子布局(起始角度为前,绝对地位为后,最后面最大,反而越小) 上图为cos余弦曲线图。0度和360度最大 ,180度最小,刚好与咱们设计的初始值从0开始,而后逆时针绕一圈角度从0-360度。 所以缩放比scale计算公式能够写为: var scale = (1 - minScale) / 2 * (1 + cos(radian(angle - startAngle))) + minScale;minScale为最小缩放比,为了让缩放有个极限值,即 scale范畴为:(minScale,1) 5.多个布局叠加时后面遮挡前面从视觉感触,凑近后面的布局应该遮挡前面的布局,在Android当中bringToFront()办法能够让布局置于后面,Flutter没有提供此办法,咱们该如何解决这种状况呢? Flutter提供一个Stack布局,也叫层叠式布局,当咱们增加子布局到Stack布局中时,前面增加的会遮住后面增加的,所以只有咱们在增加子布局的时候依照由后到前来增加即可。话说怎么晓得是前是后呢? 晓得实现思路当初要解决的问题是: 如何辨别前与后?有什么条件能够辨别? 思考中...1、依据坐标值?Y坐标小就是前面,Y坐标大就是后面?答案是不肯定;因为当我启动角度不是0的时候,比方是90度,那么最右面是后面,最右边是前面,这个时候是X坐标的大小辨别前后关系,所以说独自应用坐标值的大小来决定前后关系是不对的。 2、依据前大后小准则?依据缩放值排序来增加子布局?答案是可行;因为咱们曾经实现了后面的布局缩放值是1,前面的缩放值越来越小,而且咱们曾经解决了启动角度问题,所以依据缩放值来实现是可行的。 ///通过缩放值进行排序,从小到大childPointList.sort((a, b) {  return a.scale.compareTo(b.scale);});///遍历增加子布局Stack(  children: childPointList.map(              (Point point) {                return Positioned(                    width: point.width,                    left: point.left,                    top: point.top,                    child: this.widget.children[point.index]);              },            ).toList(),   ),通过下面形式即可实现前后遮挡成果了。 小知识点Flutter 之Stack 组件Stack一个能够叠加子控件的布局,这里次要讲一下 Positioned,其余应用形式能够看下官网阐明。 Positioned({  Key key,  this.left,  this.top,  this.right,  this.bottom,  this.width,  this.height,  @required Widget child,})应用Positioned管制Widget的地位,通过Positioned能够随便摆放一个组件,有点像相对布局。其中left、top 、right、 bottom别离代表离Stack左、上、右、底四边的间隔。 Flutter之LayoutBuilder 组件有时咱们心愿依据组件的大小确认组件的外观,比方竖屏的时候高低展现,横屏的时候左右展现,通过LayoutBuilder组件能够获取父组件的束缚尺寸。 附:github链接:https://github.com/yixiaolunh... 原文链接:https://www.jianshu.com/p/451... 文末您的点赞珍藏就是对我最大的激励!欢送关注我,分享Android干货,交换Android技术。对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

December 10, 2021 · 1 min · jiezi

关于android:全面解析-钥匙环服务的应用场景商业价值

在互联互通的场景驱动下,同一开发者旗下经常领有多款利用或者多个利用状态,用户在同一设施的不同利用或端口登录时,即使应用同一帐号,仍须要反复输出明码进行验证,操作简单,间接影响到用户的应用体验,而华为钥匙环服务的呈现则无效地解决了这一问题。 钥匙环服务性能盘点 华为钥匙环服务(Keyring)为开发者提供全生命周期的凭据治理能力,包含对凭据进行获取、加密存储、受权共享、查问读取、删除等等,保障开发者的业务流畅性。 1.本地加密存储应用钥匙环服务所获取的凭据在可信执行环境(TEE)中能够随机生成密钥,而后再进行加密,每个设施的密钥均不雷同,而且密钥只能在TEE内应用,无奈来到设施,华为也不把握密钥的内容,反对凭据在本地平安存储。保留凭据时,开发者还能够设置读取此凭据的内容时是否通过锁屏明码或生物特色认证用户的身份。 1.凭据共享受权钥匙环服务可能提供同一团队开发的App之间受权共享凭据的能力,被受权应用凭据的利用能够是Android利用、快利用或者Web利用。 用户在下次进行登录操作时,利用在钥匙环服务中查找可用的凭据。查找到的凭据可能是本利用存储的凭据,也可能是其它利用受权给本利用应用的凭据。 1.保障数据安全除此之外,应用钥匙环服务,共享凭据过程中的安全性也可能失去保障。钥匙环服务通过验证安卓利用的APK包名、快利用的包名和证书哈希,或者获取以后网页的实在URL作为身份信息等形式,认证读写凭据的APK或网站的实在身份,避免凭据被仿冒的程序或网站盗用。 1.凭据删除或更新钥匙环服务向开发者提供删除和更新凭据的API。如果用户须要退出利用帐户,相应的认证凭据也将从钥匙环服务中删除。 利用场景 跨状态登录同一利用App在挪动端的入口出现多样化的散布趋势,用户在不同的利用状态之间来回切换,重复登录,导致体验割裂。而钥匙环服务对于不同的利用状态提供了不同的接口,包含钥匙环服务SDK、快利用API和Web API,反对跨利用状态共享用户认证凭据。 以电商购物类利用为例,节假日促销短信推送是咱们最罕用的营销形式之一,也广泛被用户所承受。用户在安卓利用登录之后,点开促销短信中的链接,在华为浏览器中关上Web利用,间接处于登录状态,无需反复登录即可实现下单。 跨利用登录在业务倒退过程中,同一个团队往往会开发多个App,新利用上架之后,取得用户并非易事。针对全新业务场景的利用,钥匙环服务会为其提供一条畅通无阻、高效便捷的桥梁,将流量从原有的利用当中引进来,用户无需反复输出帐号密码,只有一键受权,就能够实现无缝登录。 也就是说,用户在利用A已登录,装置同一开发者旗下的新利用B,无需输出帐号密码,能应用登录利用A的帐号,实现间接登录利用B。 帐号易切换钥匙环服务还能够存储多个凭据,如果用户在某一利用登录过多个帐号,利用能够提供确认页面,让用户自主抉择某个帐号登录,操作便捷。当然了,作为开发者,您能够抉择在用户登录之前,验证用户的生物特色或者锁屏明码,无效躲避帐号被冒用等安全隐患。 商业价值 钥匙环服务的跨利用登录场景将帮忙同一开发者旗下的关联利用之间共享流量,助力孵化新产品。同时实现流量在安卓利用、快利用、Web利用之间的双向流转。通过发明一处登录,处处登录的无缝登录体验,钥匙环服务帮忙开发者缩短用户的交互转化门路,升高登录操作的复杂度,进一步晋升导流转化率。总之,钥匙环服务是助力开发者取得商业胜利的利器。 总体来看,华为钥匙环服务具备便捷登录、平安无忧、流量共享等外围劣势,能够帮忙开发者疾速满足用户在购物、出行、社交、浏览等多个应用场景下的平安需要。 之后,华为钥匙环服务还会推出其余个性,HMS Core也将在平安畛域凋谢新的能力,为开发者们带来更优质的服务和体验。 更多精彩内容,请见华为开发者官方论坛→https://developer.huawei.com/...

December 10, 2021 · 1 min · jiezi

关于android:全面解析-钥匙环服务的应用场景商业价值

在互联互通的场景驱动下,同一开发者旗下经常领有多款利用或者多个利用状态,用户在同一设施的不同利用或端口登录时,即使应用同一帐号,仍须要反复输出明码进行验证,操作简单,间接影响到用户的应用体验,而华为钥匙环服务的呈现则无效地解决了这一问题。 钥匙环服务性能盘点 华为钥匙环服务(Keyring)为开发者提供全生命周期的凭据治理能力,包含对凭据进行获取、加密存储、受权共享、查问读取、删除等等,保障开发者的业务流畅性。 1.本地加密存储应用钥匙环服务所获取的凭据在可信执行环境(TEE)中能够随机生成密钥,而后再进行加密,每个设施的密钥均不雷同,而且密钥只能在TEE内应用,无奈来到设施,华为也不把握密钥的内容,反对凭据在本地平安存储。保留凭据时,开发者还能够设置读取此凭据的内容时是否通过锁屏明码或生物特色认证用户的身份。 1.凭据共享受权钥匙环服务可能提供同一团队开发的App之间受权共享凭据的能力,被受权应用凭据的利用能够是Android利用、快利用或者Web利用。 用户在下次进行登录操作时,利用在钥匙环服务中查找可用的凭据。查找到的凭据可能是本利用存储的凭据,也可能是其它利用受权给本利用应用的凭据。 1.保障数据安全除此之外,应用钥匙环服务,共享凭据过程中的安全性也可能失去保障。钥匙环服务通过验证安卓利用的APK包名、快利用的包名和证书哈希,或者获取以后网页的实在URL作为身份信息等形式,认证读写凭据的APK或网站的实在身份,避免凭据被仿冒的程序或网站盗用。 1.凭据删除或更新钥匙环服务向开发者提供删除和更新凭据的API。如果用户须要退出利用帐户,相应的认证凭据也将从钥匙环服务中删除。 利用场景 跨状态登录同一利用App在挪动端的入口出现多样化的散布趋势,用户在不同的利用状态之间来回切换,重复登录,导致体验割裂。而钥匙环服务对于不同的利用状态提供了不同的接口,包含钥匙环服务SDK、快利用API和Web API,反对跨利用状态共享用户认证凭据。 以电商购物类利用为例,节假日促销短信推送是咱们最罕用的营销形式之一,也广泛被用户所承受。用户在安卓利用登录之后,点开促销短信中的链接,在华为浏览器中关上Web利用,间接处于登录状态,无需反复登录即可实现下单。 跨利用登录在业务倒退过程中,同一个团队往往会开发多个App,新利用上架之后,取得用户并非易事。针对全新业务场景的利用,钥匙环服务会为其提供一条畅通无阻、高效便捷的桥梁,将流量从原有的利用当中引进来,用户无需反复输出帐号密码,只有一键受权,就能够实现无缝登录。 也就是说,用户在利用A已登录,装置同一开发者旗下的新利用B,无需输出帐号密码,能应用登录利用A的帐号,实现间接登录利用B。 帐号易切换钥匙环服务还能够存储多个凭据,如果用户在某一利用登录过多个帐号,利用能够提供确认页面,让用户自主抉择某个帐号登录,操作便捷。当然了,作为开发者,您能够抉择在用户登录之前,验证用户的生物特色或者锁屏明码,无效躲避帐号被冒用等安全隐患。 商业价值 钥匙环服务的跨利用登录场景将帮忙同一开发者旗下的关联利用之间共享流量,助力孵化新产品。同时实现流量在安卓利用、快利用、Web利用之间的双向流转。通过发明一处登录,处处登录的无缝登录体验,钥匙环服务帮忙开发者缩短用户的交互转化门路,升高登录操作的复杂度,进一步晋升导流转化率。总之,钥匙环服务是助力开发者取得商业胜利的利器。 总体来看,华为钥匙环服务具备便捷登录、平安无忧、流量共享等外围劣势,能够帮忙开发者疾速满足用户在购物、出行、社交、浏览等多个应用场景下的平安需要。 之后,华为钥匙环服务还会推出其余个性,HMS Core也将在平安畛域凋谢新的能力,为开发者们带来更优质的服务和体验。 更多精彩内容,请见华为开发者官方论坛→https://developer.huawei.com/...

December 10, 2021 · 1 min · jiezi

关于android:安卓逆向-3840-编译-release版本的app

release版本须要生成签名秘钥 1. 配置生成秘钥证书信息随便写而后勾销 2. 配置配置签名信息 点击ok。 3. 抉择build变量 4. 利用签名配置信息 5. 从新生成apkbuild --> Generate Signed Bundle or APK --> 始终抉择下一步,直到finish.最初release版本的apk会生成app/release/app-release.apk

December 9, 2021 · 1 min · jiezi

关于android:Android入门教程-Camera-相机一

概述Android 应用 Android Camera API 实现音视频的采集、编码、封包成 mp4 输入。 基于android.hardware.Camera,创立一个横屏利用,实时预览摄像头图像,实现录像并输入MP4的性能。 这里不应用Camera2。 申请权限<!-- 须要录制音视频权限和写内部存储权限 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.CAMERA" /><uses-feature android:name="android.hardware.camera" />在 activity 中动静申请权限 private static final String[] VIDEO_PERMISSIONS = { Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE};实现摄像头预览性能应用SurfaceView来预览。新建CameraPreview类继承自SurfaceView并实现SurfaceHolder.Callback; camera相干操作都放在这个View里。 surfaceCreated中获取Camera实例,启动预览;设置预览相干参数surfaceDestroyed开释Camerapublic class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private static final String TAG = "rustAppCameraPreview"; private SurfaceHolder mHolder; private Camera mCamera; public static final int MEDIA_TYPE_IMAGE = 1; public static final int MEDIA_TYPE_VIDEO = 2; private static int mOptVideoWidth = 1920; // 默认视频帧宽度 private static int mOptVideoHeight = 1080; private Uri outputMediaFileUri; private String outputMediaFileType; public CameraPreview(Context context) { super(context); mHolder = getHolder(); mHolder.addCallback(this); } private static Camera getCameraInstance() { Camera c = null; try { c = Camera.open(); } catch (Exception e) { Log.d(TAG, "camera is not available"); } return c; } @Override public void surfaceCreated(SurfaceHolder holder) { mCamera = getCameraInstance(); try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); getCameraOptimalVideoSize(); // 找到最合适的分辨率 } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } private void getCameraOptimalVideoSize() { try { Camera.Parameters parameters = mCamera.getParameters(); List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes(); List<Camera.Size> mSupportedVideoSizes = parameters.getSupportedVideoSizes(); Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes, mSupportedPreviewSizes, getWidth(), getHeight()); mOptVideoWidth = optimalSize.width; mOptVideoHeight = optimalSize.height; Log.d(TAG, "prepareVideoRecorder: optimalSize:" + mOptVideoWidth + ", " + mOptVideoHeight); } catch (Exception e) { Log.e(TAG, "getCameraOptimalVideoSize: ", e); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { mHolder.removeCallback(this); mCamera.setPreviewCallback(null); mCamera.stopPreview(); mCamera.release(); mCamera = null; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { }}在 Fragment 中显示摄像头预览 ...

December 9, 2021 · 3 min · jiezi

关于android:总结UI原理和高级的UI优化方式

不晓得UI原理如何做UI优化? 本文内容分为三个局部,UI原理、LayoutInflater原理、UI优化,篇幅有点长,能够抉择本人喜爱的章节进行浏览,每一个局部最初都有小结。 置信大家多多少少看过一些Activity启动源码剖析的文章,也能大略说出Activity启动流程,例如这种答复: AMS负责管理系统所有Activity,所以利用startActivity 最终会通过Binder调用到AMS的startActivity办法,AMS启动一个Activity之前会做一些查看,例如权限、是否在清单文件注册等,而后就能够启动了,AMS是一个零碎服务,在独自过程,所以要将生命周期通知利用,又波及到跨过程调用,这个跨过程同样采纳Binder,媒介是通过ActivityThread的外部类ApplicationThread,AMS将生命周期跨过程传到ApplicationThread,而后ApplicationThread 再分发给ActivityThread外部的Handler,这时候生命周期曾经回调到利用主线程了,回调Activity的各个生命周期办法。还能够细分,比方Activity、Window、DecorView之间的关系,这个其实也应该难度不大,又忽然想到,setContentView为什么要放在onCreate中?,放在其它办法里行不行,能不能放在onAttachBaseContext办法里?其实,这些问题能够在源码中找到答案。 本文参考Android 9.0源码,API 28。 注释写一个Activity,咱们个别都是通过在onCreate办法中调用setContentView办法设置咱们的布局,有没有想过这个问题,设置完布局,界面就会开始绘制吗? 一、从生命周期源码剖析UI原理Activity启动流程大抵如下: Context -> startActivityAMS -> startActivity过程不存在则告诉Zygote启动过程,启动完过程,执行ActivityThread的main办法,进入loop循环,通过Handler散发音讯。ApplicationThread -> scheduleLaunchActivityActivityThread -> handleLaunchActivity其它生命周期回调从第4点开始剖析 1.1 ActivityThread1.1.1 外部类 ApplicationThreadAMS暂且先不剖析,AMS启动Activity会通过ApplicationThread告诉到ActivityThread,启动Activity从ApplicationThread开始说起,看下 scheduleLaunchActivity 办法 1.1.2 ApplicationThread#scheduleLaunchActivitypublic final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { ActivityClientRecord r = new ActivityClientRecord(); r.token = token; r.ident = ident; r.intent = intent; ... sendMessage(H.LAUNCH_ACTIVITY, r); }sendMessage 最终封装一个ActivityClientRecord对象到msg,调用mH.sendMessage(msg);,mH 是一个Handler,间接看解决局部吧, ...

December 9, 2021 · 21 min · jiezi

关于android:BinderHwBinder-和-VndBinder

碰到钻研过 Android 过程间通信的敌人,我通常喜爱求教 Binder 相干的问题。然而,太细节的问题有点求全责备了,对于实质的了解是我冀望的。题目的问题通常是我最喜爱问的一个。Android 8.0 当前的版本中,Binder有哪几种?它们都是怎么应用的?IPC 域阐明/dev/binder框架/利用过程之间的 IPC,应用 AIDL 接口/dev/hwbinder框架/供应商过程之间的 IPC,应用 HIDL 接口 供应商过程之间的 IPC,应用 HIDL 接口/dev/vndbinder供应商/供应商过程之间的 IPC,应用 AIDL 接口为什么会存在这三种 Binder?Android 8.0 从新设计了 Android 零碎框架,引入 Treble 机制。在新的架构中,引入了 HAL 接口定义语言(HIDL),提供了独立的供应商分区(vendor),以及供应商原生开发套件 (VNDK)。通过这些新技术,能够将零碎框架与供应商实现分隔开来,使得用户能够独立替换分区镜像,以便制造商可能更轻松、更疾速地更新 Android 零碎。 Treble 的引进,使得 system 和 vendor 分区间无奈间接拜访,导致原有的 Binder 机制不能持续应用。因而将 Binder 拆分为 Binder、HwBinder 和 VndBinder,用于在 system/system、system/vendor 和 vendor/vendor 之间进行过程间通信。三种 Binder 的应用如下图所示, ] 三种 Binder 应用的资源有什么不同?这个问题的答案曾经在上图中,能够归结为以下几点: Device nodeBinder LibraryServiceInterface LanguageBinder/dev/binderlibbinderservicemanagerAIDLHwBinder/dev/hwbinderlibhwbinderhwservicemanagerHIDLVndBinder/dev/vndbinderlibbindervndservicemanagerAIDL为什么会引入 HwBinder?HwBinder 引入的实质还是 Treble 机制的应用,这使得 system 和 vendor 分区互相隔离。在 Android 8.0 之前,Android HAL 与零碎框架是紧耦合的,它们打包在一个镜像里。HAL只是一个个的so库,framework 通过关上动静库来调用 HAL。 为了适配 HwBinder,Android 8.0 同时引入了 HIDL,用于建设 framework 和 HAL 间的通信。 ...

December 9, 2021 · 1 min · jiezi

关于android:全面解析-钥匙环服务的应用场景商业价值

在互联互通的场景驱动下,同一开发者旗下经常领有多款利用或者多个利用状态,用户在同一设施的不同利用或端口登录时,即使应用同一帐号,仍须要反复输出明码进行验证,操作简单,间接影响到用户的应用体验,而华为钥匙环服务的呈现则无效地解决了这一问题。 钥匙环服务性能盘点 华为钥匙环服务(Keyring)为开发者提供全生命周期的凭据治理能力,包含对凭据进行获取、加密存储、受权共享、查问读取、删除等等,保障开发者的业务流畅性。 1.本地加密存储应用钥匙环服务所获取的凭据在可信执行环境(TEE)中能够随机生成密钥,而后再进行加密,每个设施的密钥均不雷同,而且密钥只能在TEE内应用,无奈来到设施,华为也不把握密钥的内容,反对凭据在本地平安存储。保留凭据时,开发者还能够设置读取此凭据的内容时是否通过锁屏明码或生物特色认证用户的身份。 1.凭据共享受权钥匙环服务可能提供同一团队开发的App之间受权共享凭据的能力,被受权应用凭据的利用能够是Android利用、快利用或者Web利用。 用户在下次进行登录操作时,利用在钥匙环服务中查找可用的凭据。查找到的凭据可能是本利用存储的凭据,也可能是其它利用受权给本利用应用的凭据。 1.保障数据安全除此之外,应用钥匙环服务,共享凭据过程中的安全性也可能失去保障。钥匙环服务通过验证安卓利用的APK包名、快利用的包名和证书哈希,或者获取以后网页的实在URL作为身份信息等形式,认证读写凭据的APK或网站的实在身份,避免凭据被仿冒的程序或网站盗用。 1.凭据删除或更新钥匙环服务向开发者提供删除和更新凭据的API。如果用户须要退出利用帐户,相应的认证凭据也将从钥匙环服务中删除。 利用场景 跨状态登录同一利用App在挪动端的入口出现多样化的散布趋势,用户在不同的利用状态之间来回切换,重复登录,导致体验割裂。而钥匙环服务对于不同的利用状态提供了不同的接口,包含钥匙环服务SDK、快利用API和Web API,反对跨利用状态共享用户认证凭据。 以电商购物类利用为例,节假日促销短信推送是咱们最罕用的营销形式之一,也广泛被用户所承受。用户在安卓利用登录之后,点开促销短信中的链接,在华为浏览器中关上Web利用,间接处于登录状态,无需反复登录即可实现下单。 跨利用登录在业务倒退过程中,同一个团队往往会开发多个App,新利用上架之后,取得用户并非易事。针对全新业务场景的利用,钥匙环服务会为其提供一条畅通无阻、高效便捷的桥梁,将流量从原有的利用当中引进来,用户无需反复输出帐号密码,只有一键受权,就能够实现无缝登录。 也就是说,用户在利用A已登录,装置同一开发者旗下的新利用B,无需输出帐号密码,能应用登录利用A的帐号,实现间接登录利用B。 帐号易切换钥匙环服务还能够存储多个凭据,如果用户在某一利用登录过多个帐号,利用能够提供确认页面,让用户自主抉择某个帐号登录,操作便捷。当然了,作为开发者,您能够抉择在用户登录之前,验证用户的生物特色或者锁屏明码,无效躲避帐号被冒用等安全隐患。 商业价值 钥匙环服务的跨利用登录场景将帮忙同一开发者旗下的关联利用之间共享流量,助力孵化新产品。同时实现流量在安卓利用、快利用、Web利用之间的双向流转。通过发明一处登录,处处登录的无缝登录体验,钥匙环服务帮忙开发者缩短用户的交互转化门路,升高登录操作的复杂度,进一步晋升导流转化率。总之,钥匙环服务是助力开发者取得商业胜利的利器。 总体来看,华为钥匙环服务具备便捷登录、平安无忧、流量共享等外围劣势,能够帮忙开发者疾速满足用户在购物、出行、社交、浏览等多个应用场景下的平安需要。 之后,华为钥匙环服务还会推出其余个性,HMS Core也将在平安畛域凋谢新的能力,为开发者们带来更优质的服务和体验。 更多精彩内容,请见华为开发者官方论坛→https://developer.huawei.com/...

December 9, 2021 · 1 min · jiezi

关于android:全面解析-钥匙环服务的应用场景商业价值

在互联互通的场景驱动下,同一开发者旗下经常领有多款利用或者多个利用状态,用户在同一设施的不同利用或端口登录时,即使应用同一帐号,仍须要反复输出明码进行验证,操作简单,间接影响到用户的应用体验,而华为钥匙环服务的呈现则无效地解决了这一问题。 钥匙环服务性能盘点 华为钥匙环服务(Keyring)为开发者提供全生命周期的凭据治理能力,包含对凭据进行获取、加密存储、受权共享、查问读取、删除等等,保障开发者的业务流畅性。 1.本地加密存储应用钥匙环服务所获取的凭据在可信执行环境(TEE)中能够随机生成密钥,而后再进行加密,每个设施的密钥均不雷同,而且密钥只能在TEE内应用,无奈来到设施,华为也不把握密钥的内容,反对凭据在本地平安存储。保留凭据时,开发者还能够设置读取此凭据的内容时是否通过锁屏明码或生物特色认证用户的身份。 1.凭据共享受权钥匙环服务可能提供同一团队开发的App之间受权共享凭据的能力,被受权应用凭据的利用能够是Android利用、快利用或者Web利用。 用户在下次进行登录操作时,利用在钥匙环服务中查找可用的凭据。查找到的凭据可能是本利用存储的凭据,也可能是其它利用受权给本利用应用的凭据。 1.保障数据安全除此之外,应用钥匙环服务,共享凭据过程中的安全性也可能失去保障。钥匙环服务通过验证安卓利用的APK包名、快利用的包名和证书哈希,或者获取以后网页的实在URL作为身份信息等形式,认证读写凭据的APK或网站的实在身份,避免凭据被仿冒的程序或网站盗用。 1.凭据删除或更新钥匙环服务向开发者提供删除和更新凭据的API。如果用户须要退出利用帐户,相应的认证凭据也将从钥匙环服务中删除。 利用场景 跨状态登录同一利用App在挪动端的入口出现多样化的散布趋势,用户在不同的利用状态之间来回切换,重复登录,导致体验割裂。而钥匙环服务对于不同的利用状态提供了不同的接口,包含钥匙环服务SDK、快利用API和Web API,反对跨利用状态共享用户认证凭据。 以电商购物类利用为例,节假日促销短信推送是咱们最罕用的营销形式之一,也广泛被用户所承受。用户在安卓利用登录之后,点开促销短信中的链接,在华为浏览器中关上Web利用,间接处于登录状态,无需反复登录即可实现下单。 跨利用登录在业务倒退过程中,同一个团队往往会开发多个App,新利用上架之后,取得用户并非易事。针对全新业务场景的利用,钥匙环服务会为其提供一条畅通无阻、高效便捷的桥梁,将流量从原有的利用当中引进来,用户无需反复输出帐号密码,只有一键受权,就能够实现无缝登录。 也就是说,用户在利用A已登录,装置同一开发者旗下的新利用B,无需输出帐号密码,能应用登录利用A的帐号,实现间接登录利用B。 帐号易切换钥匙环服务还能够存储多个凭据,如果用户在某一利用登录过多个帐号,利用能够提供确认页面,让用户自主抉择某个帐号登录,操作便捷。当然了,作为开发者,您能够抉择在用户登录之前,验证用户的生物特色或者锁屏明码,无效躲避帐号被冒用等安全隐患。 商业价值 钥匙环服务的跨利用登录场景将帮忙同一开发者旗下的关联利用之间共享流量,助力孵化新产品。同时实现流量在安卓利用、快利用、Web利用之间的双向流转。通过发明一处登录,处处登录的无缝登录体验,钥匙环服务帮忙开发者缩短用户的交互转化门路,升高登录操作的复杂度,进一步晋升导流转化率。总之,钥匙环服务是助力开发者取得商业胜利的利器。 总体来看,华为钥匙环服务具备便捷登录、平安无忧、流量共享等外围劣势,能够帮忙开发者疾速满足用户在购物、出行、社交、浏览等多个应用场景下的平安需要。 之后,华为钥匙环服务还会推出其余个性,HMS Core也将在平安畛域凋谢新的能力,为开发者们带来更优质的服务和体验。 更多精彩内容,请见华为开发者官方论坛→https://developer.huawei.com/...

December 9, 2021 · 1 min · jiezi

关于android:性能优化Android-开发进阶必经之路

前言性能优化,是 Android 程序员进阶的必经之路。不论是在我的项目开发过程中,还是在面试的时候,咱们都会遇到对于性能优化的问题。 性能优化是一个重点同时也是一个难点。很多 Android 学习者和开发者都会在性能优化上面临着很多的困扰和解决不了的难题。 上面来看看 Android 性能优化蕴含的知识点有哪些? 360°全方位性能调优 程序性能优化: OOM 问题原理解析、ANR 问题解析、Crash 监控计划、启动速度与执行效率优化我的项目实战、布局检测与优化、内存优化、耗电优化、网络传输与数据存储优化、APK大小优化、屏幕适配 开发效率优化: 分布式版本控制系统Git、自动化构建零碎Gradle 性能优化常见问题内存问题: 耗内存:耗内存、内存泄露会影响整机的性能;OOM问题:OOM会影响产品的稳定性;程序切换到后盾后占用内存无奈开释:占用内存多预示着留给其它利用的残余内存空间小;功耗问题:发烫(耗电); 晦涩度问题:启动慢、页面显示须要长时间转圈加载、页面切换卡顿、黑白屏; 针对下面一系列的性能问题,谷歌官网提供了各种各样的工具来针对性的解决各个方面的问题:内存问题: 提供了 Android Studio 的动态代码检测性能、Android Monitor;第三方内存泄露剖析工具 Leakcanary、MAT; 功耗问题: 提供了 GPU 出现模式、battery-historian、Android Monitor; 晦涩度问题: 提供了 Android Studio 的动态代码检测性能、Android Monitor、HierarchyViewer、StrictMode、过渡绘制检测工具、TraceView 等; 性能优化总结360度全方位性能调优: Android 的性能优化牵扯的知识点很多,除了罕用解决方案,底层原理也值得咱们深入探讨。这里给大家分享一份722页超全的性能优化常识手册:《360度全方位性能调优》,帮忙大家更好地学习 Android 性能优化。 设计思维与代码品质优化程序性能优化开发效率优化APP 性能优化实际 Android性能优化——实战解析: 除了实践方面,更重要的就是我的项目实战。这边同时还给大家分享一份各个大厂(腾讯、爱奇艺、字节跳动、百度、京东、支付宝......)的 Android 性能优化实战案例。 因为篇幅无限,只能以截图的模式展现目录和局部内容,须要 Android 性能优化材料 的敌人能够点击这里收费支付完整版PDF文档。

December 9, 2021 · 1 min · jiezi

关于android:全面解析-钥匙环服务的应用场景商业价值

在互联互通的场景驱动下,同一开发者旗下经常领有多款利用或者多个利用状态,用户在同一设施的不同利用或端口登录时,即使应用同一帐号,仍须要反复输出明码进行验证,操作简单,间接影响到用户的应用体验,而华为钥匙环服务的呈现则无效地解决了这一问题。 钥匙环服务性能盘点 华为钥匙环服务(Keyring)为开发者提供全生命周期的凭据治理能力,包含对凭据进行获取、加密存储、受权共享、查问读取、删除等等,保障开发者的业务流畅性。 1.本地加密存储应用钥匙环服务所获取的凭据在可信执行环境(TEE)中能够随机生成密钥,而后再进行加密,每个设施的密钥均不雷同,而且密钥只能在TEE内应用,无奈来到设施,华为也不把握密钥的内容,反对凭据在本地平安存储。保留凭据时,开发者还能够设置读取此凭据的内容时是否通过锁屏明码或生物特色认证用户的身份。 1.凭据共享受权钥匙环服务可能提供同一团队开发的App之间受权共享凭据的能力,被受权应用凭据的利用能够是Android利用、快利用或者Web利用。 用户在下次进行登录操作时,利用在钥匙环服务中查找可用的凭据。查找到的凭据可能是本利用存储的凭据,也可能是其它利用受权给本利用应用的凭据。 1.保障数据安全除此之外,应用钥匙环服务,共享凭据过程中的安全性也可能失去保障。钥匙环服务通过验证安卓利用的APK包名、快利用的包名和证书哈希,或者获取以后网页的实在URL作为身份信息等形式,认证读写凭据的APK或网站的实在身份,避免凭据被仿冒的程序或网站盗用。 1.凭据删除或更新钥匙环服务向开发者提供删除和更新凭据的API。如果用户须要退出利用帐户,相应的认证凭据也将从钥匙环服务中删除。 利用场景 跨状态登录同一利用App在挪动端的入口出现多样化的散布趋势,用户在不同的利用状态之间来回切换,重复登录,导致体验割裂。而钥匙环服务对于不同的利用状态提供了不同的接口,包含钥匙环服务SDK、快利用API和Web API,反对跨利用状态共享用户认证凭据。 以电商购物类利用为例,节假日促销短信推送是咱们最罕用的营销形式之一,也广泛被用户所承受。用户在安卓利用登录之后,点开促销短信中的链接,在华为浏览器中关上Web利用,间接处于登录状态,无需反复登录即可实现下单。 跨利用登录在业务倒退过程中,同一个团队往往会开发多个App,新利用上架之后,取得用户并非易事。针对全新业务场景的利用,钥匙环服务会为其提供一条畅通无阻、高效便捷的桥梁,将流量从原有的利用当中引进来,用户无需反复输出帐号密码,只有一键受权,就能够实现无缝登录。 也就是说,用户在利用A已登录,装置同一开发者旗下的新利用B,无需输出帐号密码,能应用登录利用A的帐号,实现间接登录利用B。 帐号易切换钥匙环服务还能够存储多个凭据,如果用户在某一利用登录过多个帐号,利用能够提供确认页面,让用户自主抉择某个帐号登录,操作便捷。当然了,作为开发者,您能够抉择在用户登录之前,验证用户的生物特色或者锁屏明码,无效躲避帐号被冒用等安全隐患。 商业价值 钥匙环服务的跨利用登录场景将帮忙同一开发者旗下的关联利用之间共享流量,助力孵化新产品。同时实现流量在安卓利用、快利用、Web利用之间的双向流转。通过发明一处登录,处处登录的无缝登录体验,钥匙环服务帮忙开发者缩短用户的交互转化门路,升高登录操作的复杂度,进一步晋升导流转化率。总之,钥匙环服务是助力开发者取得商业胜利的利器。 总体来看,华为钥匙环服务具备便捷登录、平安无忧、流量共享等外围劣势,能够帮忙开发者疾速满足用户在购物、出行、社交、浏览等多个应用场景下的平安需要。 之后,华为钥匙环服务还会推出其余个性,HMS Core也将在平安畛域凋谢新的能力,为开发者们带来更优质的服务和体验。 更多精彩内容,请见华为开发者官方论坛→https://developer.huawei.com/...

December 9, 2021 · 1 min · jiezi

关于android:全面解析-钥匙环服务的应用场景商业价值

在互联互通的场景驱动下,同一开发者旗下经常领有多款利用或者多个利用状态,用户在同一设施的不同利用或端口登录时,即使应用同一帐号,仍须要反复输出明码进行验证,操作简单,间接影响到用户的应用体验,而华为钥匙环服务的呈现则无效地解决了这一问题。 钥匙环服务性能盘点 华为钥匙环服务(Keyring)为开发者提供全生命周期的凭据治理能力,包含对凭据进行获取、加密存储、受权共享、查问读取、删除等等,保障开发者的业务流畅性。 1.本地加密存储应用钥匙环服务所获取的凭据在可信执行环境(TEE)中能够随机生成密钥,而后再进行加密,每个设施的密钥均不雷同,而且密钥只能在TEE内应用,无奈来到设施,华为也不把握密钥的内容,反对凭据在本地平安存储。保留凭据时,开发者还能够设置读取此凭据的内容时是否通过锁屏明码或生物特色认证用户的身份。 1.凭据共享受权钥匙环服务可能提供同一团队开发的App之间受权共享凭据的能力,被受权应用凭据的利用能够是Android利用、快利用或者Web利用。 用户在下次进行登录操作时,利用在钥匙环服务中查找可用的凭据。查找到的凭据可能是本利用存储的凭据,也可能是其它利用受权给本利用应用的凭据。 1.保障数据安全除此之外,应用钥匙环服务,共享凭据过程中的安全性也可能失去保障。钥匙环服务通过验证安卓利用的APK包名、快利用的包名和证书哈希,或者获取以后网页的实在URL作为身份信息等形式,认证读写凭据的APK或网站的实在身份,避免凭据被仿冒的程序或网站盗用。 1.凭据删除或更新钥匙环服务向开发者提供删除和更新凭据的API。如果用户须要退出利用帐户,相应的认证凭据也将从钥匙环服务中删除。 利用场景 跨状态登录同一利用App在挪动端的入口出现多样化的散布趋势,用户在不同的利用状态之间来回切换,重复登录,导致体验割裂。而钥匙环服务对于不同的利用状态提供了不同的接口,包含钥匙环服务SDK、快利用API和Web API,反对跨利用状态共享用户认证凭据。 以电商购物类利用为例,节假日促销短信推送是咱们最罕用的营销形式之一,也广泛被用户所承受。用户在安卓利用登录之后,点开促销短信中的链接,在华为浏览器中关上Web利用,间接处于登录状态,无需反复登录即可实现下单。 跨利用登录在业务倒退过程中,同一个团队往往会开发多个App,新利用上架之后,取得用户并非易事。针对全新业务场景的利用,钥匙环服务会为其提供一条畅通无阻、高效便捷的桥梁,将流量从原有的利用当中引进来,用户无需反复输出帐号密码,只有一键受权,就能够实现无缝登录。 也就是说,用户在利用A已登录,装置同一开发者旗下的新利用B,无需输出帐号密码,能应用登录利用A的帐号,实现间接登录利用B。 帐号易切换钥匙环服务还能够存储多个凭据,如果用户在某一利用登录过多个帐号,利用能够提供确认页面,让用户自主抉择某个帐号登录,操作便捷。当然了,作为开发者,您能够抉择在用户登录之前,验证用户的生物特色或者锁屏明码,无效躲避帐号被冒用等安全隐患。 商业价值 钥匙环服务的跨利用登录场景将帮忙同一开发者旗下的关联利用之间共享流量,助力孵化新产品。同时实现流量在安卓利用、快利用、Web利用之间的双向流转。通过发明一处登录,处处登录的无缝登录体验,钥匙环服务帮忙开发者缩短用户的交互转化门路,升高登录操作的复杂度,进一步晋升导流转化率。总之,钥匙环服务是助力开发者取得商业胜利的利器。 总体来看,华为钥匙环服务具备便捷登录、平安无忧、流量共享等外围劣势,能够帮忙开发者疾速满足用户在购物、出行、社交、浏览等多个应用场景下的平安需要。 之后,华为钥匙环服务还会推出其余个性,HMS Core也将在平安畛域凋谢新的能力,为开发者们带来更优质的服务和体验。 更多精彩内容,请见华为开发者官方论坛→https://developer.huawei.com/...

December 9, 2021 · 1 min · jiezi

关于android:全面解析-钥匙环服务的应用场景商业价值

在互联互通的场景驱动下,同一开发者旗下经常领有多款利用或者多个利用状态,用户在同一设施的不同利用或端口登录时,即使应用同一帐号,仍须要反复输出明码进行验证,操作简单,间接影响到用户的应用体验,而华为钥匙环服务的呈现则无效地解决了这一问题。 钥匙环服务性能盘点 华为钥匙环服务(Keyring)为开发者提供全生命周期的凭据治理能力,包含对凭据进行获取、加密存储、受权共享、查问读取、删除等等,保障开发者的业务流畅性。 1.本地加密存储应用钥匙环服务所获取的凭据在可信执行环境(TEE)中能够随机生成密钥,而后再进行加密,每个设施的密钥均不雷同,而且密钥只能在TEE内应用,无奈来到设施,华为也不把握密钥的内容,反对凭据在本地平安存储。保留凭据时,开发者还能够设置读取此凭据的内容时是否通过锁屏明码或生物特色认证用户的身份。 1.凭据共享受权钥匙环服务可能提供同一团队开发的App之间受权共享凭据的能力,被受权应用凭据的利用能够是Android利用、快利用或者Web利用。 用户在下次进行登录操作时,利用在钥匙环服务中查找可用的凭据。查找到的凭据可能是本利用存储的凭据,也可能是其它利用受权给本利用应用的凭据。 1.保障数据安全除此之外,应用钥匙环服务,共享凭据过程中的安全性也可能失去保障。钥匙环服务通过验证安卓利用的APK包名、快利用的包名和证书哈希,或者获取以后网页的实在URL作为身份信息等形式,认证读写凭据的APK或网站的实在身份,避免凭据被仿冒的程序或网站盗用。 1.凭据删除或更新钥匙环服务向开发者提供删除和更新凭据的API。如果用户须要退出利用帐户,相应的认证凭据也将从钥匙环服务中删除。 利用场景 跨状态登录同一利用App在挪动端的入口出现多样化的散布趋势,用户在不同的利用状态之间来回切换,重复登录,导致体验割裂。而钥匙环服务对于不同的利用状态提供了不同的接口,包含钥匙环服务SDK、快利用API和Web API,反对跨利用状态共享用户认证凭据。 以电商购物类利用为例,节假日促销短信推送是咱们最罕用的营销形式之一,也广泛被用户所承受。用户在安卓利用登录之后,点开促销短信中的链接,在华为浏览器中关上Web利用,间接处于登录状态,无需反复登录即可实现下单。 跨利用登录在业务倒退过程中,同一个团队往往会开发多个App,新利用上架之后,取得用户并非易事。针对全新业务场景的利用,钥匙环服务会为其提供一条畅通无阻、高效便捷的桥梁,将流量从原有的利用当中引进来,用户无需反复输出帐号密码,只有一键受权,就能够实现无缝登录。 也就是说,用户在利用A已登录,装置同一开发者旗下的新利用B,无需输出帐号密码,能应用登录利用A的帐号,实现间接登录利用B。 帐号易切换钥匙环服务还能够存储多个凭据,如果用户在某一利用登录过多个帐号,利用能够提供确认页面,让用户自主抉择某个帐号登录,操作便捷。当然了,作为开发者,您能够抉择在用户登录之前,验证用户的生物特色或者锁屏明码,无效躲避帐号被冒用等安全隐患。 商业价值 钥匙环服务的跨利用登录场景将帮忙同一开发者旗下的关联利用之间共享流量,助力孵化新产品。同时实现流量在安卓利用、快利用、Web利用之间的双向流转。通过发明一处登录,处处登录的无缝登录体验,钥匙环服务帮忙开发者缩短用户的交互转化门路,升高登录操作的复杂度,进一步晋升导流转化率。总之,钥匙环服务是助力开发者取得商业胜利的利器。 总体来看,华为钥匙环服务具备便捷登录、平安无忧、流量共享等外围劣势,能够帮忙开发者疾速满足用户在购物、出行、社交、浏览等多个应用场景下的平安需要。 之后,华为钥匙环服务还会推出其余个性,HMS Core也将在平安畛域凋谢新的能力,为开发者们带来更优质的服务和体验。 更多精彩内容,请见华为开发者官方论坛→https://developer.huawei.com/...

December 9, 2021 · 1 min · jiezi

关于android:Android笔记Android-Service-服务

一、 Service简介Service是android 零碎中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的级别差不多,但不能自己运行只能后盾运行,并且能够和其余组件进行交互。service能够在很多场合的利用中应用,比方播放多媒体的时候用户启动了其余Activity这个时候程序要在后盾持续播放,比方检测SD卡上文件的变动,再或者在后盾记录你地理信息地位的扭转等等,总之服务总是藏在后盾的。 Service的启动有两种形式:context.startService() 和 context.bindService() 二、 Service启动流程context.startService() 启动流程: context.startService() -> onCreate() -> onStart() -> Service running -> context.stopService() -> onDestroy() -> Service stop 如果Service还没有运行,则android先调用onCreate(),而后调用onStart(); 如果Service曾经运行,则只调用onStart(),所以一个Service的onStart办法可能会反复调用屡次。 如果stopService的时候会间接onDestroy,如果是调用者本人间接退出而没有调用stopService的话,Service会始终在后盾运行,该Service的调用者再启动起来后能够通过stopService敞开Service。 所以调用startService的生命周期为: onCreate --> onStart (可屡次调用) --> onDestroy context.bindService()启动流程: context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop onBind()将返回给客户端一个IBind接口实例,IBind容许客户端回调服务的办法,比方失去Service的实例、运行状态或其余操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。 所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可屡次绑定) --> onUnbind --> onDestory。 在Service每一次的开启敞开过程中,只有onStart可被屡次调用(通过屡次startService调用),其余onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。 三、 Service生命周期Service的生命周期并不像Activity那么简单,它只继承了onCreate()、onStart()、onDestroy()三个办法 当咱们第一次启动Service时,先后调用了onCreate()、onStart()这两个办法;当进行Service时,则执行onDestroy()办法。 这里须要留神的是,如果Service曾经启动了,当咱们再次启动Service时,不会在执行onCreate()办法,而是间接执行onStart()办法。 它能够通过Service.stopSelf()办法或者Service.stopSelfResult()办法来进行本人,只有调用一次stopService()办法便能够进行服务,无论调用了多少次的启动服务办法。 四、 Service示例上面我做了一个简略的音乐播放的利用,别离应用startService和bindService来启动本地的服务。 Activity public class PlayMusicService extends Activity implements OnClickListener { private Button playBtn; private Button stopBtn; private Button pauseBtn; private Button exitBtn; private Button closeBtn; private Intent intent; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.music_service); playBtn = (Button) findViewById(R.id.play); stopBtn = (Button) findViewById(R.id.stop); pauseBtn = (Button) findViewById(R.id.pause); exitBtn = (Button) findViewById(R.id.exit); closeBtn = (Button) findViewById(R.id.close); playBtn.setOnClickListener(this); stopBtn.setOnClickListener(this); pauseBtn.setOnClickListener(this); exitBtn.setOnClickListener(this); closeBtn.setOnClickListener(this); } @Override public void onClick(View v) { int op = -1; intent = new Intent("com.homer.service.musicService"); switch (v.getId()) { case R.id.play: // play music op = 1; break; case R.id.stop: // stop music op = 2; break; case R.id.pause: // pause music op = 3; break; case R.id.close: // close activity this.finish(); break; case R.id.exit: // stopService op = 4; stopService(intent); this.finish(); break; } Bundle bundle = new Bundle(); bundle.putInt("op", op); intent.putExtras(bundle); startService(intent); // startService } @Override public void onDestroy(){ super.onDestroy(); if(intent != null){ stopService(intent); } } } Service ...

December 9, 2021 · 3 min · jiezi

关于android:干货分享研效优化实践AI算法助力深层BUG挖掘

导语随着产品在线上的继续经营,产品在线上的规模越来越大,性能也越来越简单。产品体量的增长对品质要求越来越高。为了达到更高的品质要求,必然须要想方法减少测试的强度,但用传统的手工写用例自动化回归的形式老本过高。近年来,AI技术在越来越多的畛域施展了越来越重要的作用。在腾讯外部,咱们也始终放弃着对新技术的好奇心,踊跃学习并利用于日常工作中。本文作者是腾讯安全部零碎测试高级工程师林军克,他领有16年的软件测试教训,对AI技术在测试畛域的落地颇有钻研。 本文以平安防护产品举例子,但此方法论实用于波及多因素组合导致的BUG的深度开掘。上面所示的图为典型的流量攻打的防护流程:黑客在互联网上对业务服务器发动攻打,咱们有检测流量的设施检测攻打,检测到攻打后主动启动防护,将被攻打IP的流量导流到进攻设施,在进攻设施上对流量进行荡涤后将失常流量从新转发到业务服务器。 01平安产品测试的痛点剖析平安防护产品特点:1,黑产攻打手法多样且疾速翻新,须要产品能疾速响应现网的新攻打手法,但绝不能误杀失常用户,所以产品防护策略十分多。上面的表格显示次要策略文件中配置项的数量,加起来达到两三百个,且数量还在快速增长中。每迭代一个版本又会减少大量新配置项,解决逻辑非常复杂。次要的策略配置文件 策略项置项数量anti_*.conf 50anti_*.conf 147.conf 11.conf 11.conf 10即使开发十分小心,实际上仍无奈确保每个性能都是高内聚低耦合的,有时候仍难免会呈现本来不相干的配置项间相互影响的状况。如果本不相互影响的开关间存在不应有的影响,可能导致切换策略后防护不可控。咱们外部已经呈现过一个非预期的影响导致故障的例子,过后的故障是:一个防护UDP流量的配置项影响了HTTPS流量的防护性能,然而这两个配置本来没有任何关系。因而咱们须要测试在各种组合的策略下,产品性能都可能稳固牢靠。 2,对于特定的流量,最终绝大部份流量会被某个特定的防护模块防爱护。利用这个特点能够简化模型,咱们能够抓住次要特色进行建模,其它的防护细节能够临时不关注。 业界解决这种参数组合导致的问题次要利用全对偶算法来对参数进行两两组合。生成的测试集能够用起码的组合数笼罩任意两个变量的所有取值组合。在实践上,该用例集可能裸露所有由两个变量独特作用而引发的缺点。尽管这种算法生成的组合数起码,但如果新增了新的参数从新生成组合,新的组合跟之前的组合齐全没有关联。所以当参数较少时,咱们常常用它来缩减用例数,同时放弃较好的测试笼罩。然而一旦参数量较多时,每次都生成全新的组合,每次都要依据组合从新计算预期后果,整个过程就将变得十分复杂。难以解决“数百个开关在不同的配置下对于特定流量的防护手法”的问题。 咱们项目组内目前都是用手工减少用例,自动化执行用例。在这种形式下,每次新增配置项都要放弃全对偶其实很难。例如,假如目前己有的用例都是全对偶的,当初新增一个配置项,这个配置项只能取0和1两个值。为了保征所有参数都组合一遍,那么必须在原来所有用例的根底上新增配置项取0时测一遍,取1时再测一遍。每减少一个配置项用例数翻一翻,用例数十分宏大。如果每次都全新生成组合的话,150个配置开关在全新生成全对偶组合的状况下只有约130种组合。而增量形式可达2^150种组合。 02业界是如何自动化生成用例的那业界有没有既可能全新生成组合数少又不须要从新人工计算预期后果的计划呢?答案是有的。UML建模技术就是随被测版本更新保护模型,每次测试均从新整体兼顾生成全新用例进行测试。这项技术最外围价值在于:自动化生成用例,用起码的用例数达到最大化性能笼罩,最终更快更全地测试版本。这项技术的劣势是:模型保护简单,对于设计缺点难以发现(用例只是机械地遍历),没有从用户的角度设计用例。 03AI在前端页面测试畛域的利用近年来,AI技术的倒退十分地快,AI技术也有跟UML同样的特点:喜爱建模型。所以是否通过AI技术绕过简单的建模?整体兼顾用例,用起码的用例数达到最大的笼罩。同时防止人工计算预期后果。 为了摸索新技术利用于测试畛域,我疾速扫了一下AI的盲,再进行更深刻的学习时发现,其实AI利用于测试畛域的将来已至。业界己经有不少工具在利用AI做自动化测试了,连用例都是自动化设计的。对于前端的页面,甚至有工具号称只有给定URL链接,测试人员只需坐等测试后果。相似的软件有:eggplant、appvance IQ、Sauce Labs等等。 通过剖析发现这些技术次要利用AI的计算机视觉技术在页面上辨认所有的按纽,依据每一页上的按纽生成遍历树,再依据遍历树主动遍历可能经验的门路(user journey)。从而达到自动化设计用例,自动化测试的目标。   腾讯的共事之前出版过一本《AI自动化测试》的书,外面具体介绍了AI在图像类游戏和数据类游戏上的测试。业界已有的这些技术都很优良,但次要利用于前端页面的测试,后盾的测试还没有相应的技术。所以咱们开始钻研如何将AI技术利用于后盾测试,通过多种尝试,并联合AI的特点,咱们产生了一个大胆的想法:没有人工的参加,机器不可能了解人工设计的业务逻辑,而像UML那样构建模型又太过于重型,但AI是十分善于解决做数据分类的,既然算不进去预期后果是否不计算?测试套只记录流量如何解决的,记录后由AI依据流量及防护后果分类。分完类后再按各类剖析出此类的典型配置?而后人工审查典型配置下的流量处理形式是否正当。 04摸索AI在后盾测试中的利用依据这些想法,咱们很快就制订了实施方案。咱们的指标:用最小的代价晋升多种因素组合的笼罩,深度开掘深层次的BUG。计划施行胜利的实践根底是:基于测试实践,用起码的用例数来笼罩最多的场景。利用AI对各种场景下的响应进行归类、洞察。这两块串起来是可行的。 打算施行步骤如下:Step1:每次新增了新配置项都从新基于全对偶算法生成配置。Step2:对每种配置用典型的攻打手法并记录被测端的防护形式。Setp3:通过AI剖析各种防护跟配置间的关联。找出各种防护形式最次要的配置项。Step4:查看各种防护形式最相干的N种配置是否合乎预期设计? 第一部份基于测试实践生成全对偶的组合非常简单。我花了半天工夫就施行了。为了对多个配置文件中的配置项做组合,我设计了用配置项名@文件名的形式对配置项命名。应用pairwise工具生成。组合之后再用脚本转成配置文件。 基于全对偶算法一共生成了250种组合。选取27种典型特色的流量别离发动'GET','POST','PUT','DELETE','HEAD','OPTIONS','TRACE','CONNECT'申请。流量种数有278=216种,在250种配置下别离过这216种流量并记录下防护手法,失去250216=54000种场景的防护记录。记录下来的后果是这样的:一共分3部份,第一部份为配置项组合数据,第二部份是所发流量的名字,最初一列是被测端所用的防护手法。 数据有了,能够交给AI了。然而团队只有测试专家,没有AI专家。咱们就在腾讯外部找AI专家求教,AI专家理解咱们的需要后认为可行,但具体的落地依然让咱们非常困扰。因为AI畛域的常识跟测试畛域的常识差别太大了,从头开始学习这些常识好像浏览天书个别。不过只有肯动脑多学习,办法总比艰难多。我找到了一个AI小白也很容易上手的data mining工具,通过重复学习和实际,我认为这几个构件在咱们的计划中能够利用。我建设的模型如下:PCA全称叫主成因剖析构件,能够帮咱们找出对后果影响很大的N个配置项,配置项对后果影响大小排序,输入是一个一维的列表。开发设计的配置开关解决程序必定是个网状的,这个后果参考一下就好。分类树对配置项影响定量分析。集体认为这个构件输入的信息比拟有价值。 PCA的剖析后果是这样的。在咱们的案例中,这条曲线挺平滑的,阐明没有影响特地大的配置项。AI应用RANK构件剖析进去的配置项对后果影响大小,跟开发的设计流程图对了下程序,大抵是能够对得上。初步印证了计划还是有点靠谱的。下图是通过分类树对运行后果分类后的展现:咱们以一个典型的例子阐明一下,如何依据AI的提引找到问题:AI对数据处理后失去了一张很大的分类树图,对数据中每一种后果都会用一种色彩标记,如图中所示黄、紫、白绿别离是4种后果相干的数据展现。其中黄色区域的根节点上示意防护手法为dropos_*的数据共74条。该后果最相干配置项:drop_@anti_.conf。右边叶子节点示意:当drop_@anti_.conf配置为android、ios、linux时。防护手法为:dropos_*。左边叶子节点示意:当drop_@anti_.con+f配置为0时。防护手法为:**_trans。 经合被测系统的防护逻辑,我看到这个中央是的确存在问题。这个性能是一个对特定OS指纹作抛弃的性能,因为我跑用例时只用linux零碎发了流量,性能失常的状况下应只有linux下会抛弃。AI却剖析到当drop_@anti_.conf配置为android、ios、win、linux会抛弃,也就是说在配置为android、ios、win时有OS辨认不精确的问题。咱们先下记下这个点。 方框最下方的配置项是跟后果相干的次相干的配置项,持续察看其叶子结点,咱们特地关注各叶子结点的比例,这个例子中这个配置项配置为不同值时,比例靠近,后果倾向性也很显著,这是耦合性低的信号。 依照分类树展现的信息关上原始表格,暗藏掉不相干的列并把相关联的配置项放在一起,这个时候就能够看出问题所在。按有问题场景对应的行号找出相应配置在环境上重现问题,重现问题如图。重现问题后配置如下:预期:流量在linux下发的,不应匹配上策略,预期应被转发。实测发现流量因为os_**被drop了:这个例子阐明在AI的指引下胜利发现特定场景下OS指纹性能的确存在误辨认的可能,也证实了用AI剖析数据的办法是牢靠的。我认为AI对于测试的外围价值在于把简单的数据以可视化的形式出现,使剖析变得更加容易。 综上所述,本办法能够解决“目前多个参数互相耦合导致的深层次BUG有但不多,但要解决这些问题须要做参数组合测试,解决的代价很大”的痛点。用较小的代价验证多个因素间的耦合性。自动化生成了54000个场景的测试用例,耗时3.5天跑完,AI剖析跑出的后果后,己跟开发确认了其中2个BUG。这54000个场景如果人工写用例,按目前每人天30个用例算,节假日无休也须要4.9年能力实现。应用此办法后,生成组合只需几分钟,3.5天跑完,目前摸索阶段预计10天也能够剖析完,大大提高了测试效率。 对于腾讯WeTest腾讯WeTest是由腾讯官网推出的一站式品质开放平台。十余年品质治理教训,致力于质量标准建设、产品质量晋升。腾讯WeTest为挪动开发者提供兼容性测试、云真机、性能测试、平安防护等优良研发工具,为百余行业提供解决方案,笼罩产品在研发、经营各阶段的测试需要,历经千款产品磨砺。金牌专家团队,通过5大维度,41项指标,360度保障您的产品质量。 关注腾讯WeTest,理解更多测试干货常识WeTest腾讯品质开放平台-专一游戏 晋升品质

December 9, 2021 · 1 min · jiezi

关于android:android多线程AsyncTask二

 上篇剖析AsyncTask的一些根本用法以及不同android版本下的区别,接着本篇咱们就来全面分析一下AsyncTask的工作原理。在开始之前咱们先来理解一个多线程的知识点——Callable<V> 、Future<V>和FutureTask类 一、了解Callable<V> 、Future<V>以及FutureTask类Callable<V>Callable的接口定义如下: public interface Callable<V> { V call() throws Exception; } Callable接口申明了一个名称为call()的办法,该办法能够有返回值V,也能够抛出异样。Callable也是一个线程接口,它与Runnable的次要区别就是Callable在线程执行实现后能够有返回值而Runnable没有返回值,Runnable接口申明如下: public interface Runnable { public abstract void run();} 那么Callable接口如何应用呢,Callable须要和ExcutorService联合应用,其中ExecutorService也是一个线程池对象继承自Executor接口,这里就不深刻了,接着看看ExecutorService提供了那些办法供咱们应用: <T> Future<T> submit(Callable<T> task);<T> Future<T> submit(Runnable task, T result);Future<?> submit(Runnable task);submit(Callable task),传递一个实现Callable接口的工作,并且返回封装了异步计算结果的Future。submit(Runnable task, T result),传递一个实现Runnable接口的工作,并且指定了在调用Future的get办法时返回的result对象。submit(Runnable task),传递一个实现Runnable接口的工作,并且返回封装了异步计算结果的Future。 因而咱们只有创立好咱们的线程对象(实现Callable接口或者Runnable接口),而后通过下面3个办法提交给线程池去执行即可。Callable接口介绍就先到这,再来看看Future时什么鬼。 Future<V> Future接口是用来获取异步计算结果的,说白了就是对具体的Runnable或者Callable对象工作执行的后果进行获取(get()),勾销(cancel()),判断是否实现等操作。其办法如下: public interface Future<V> { //勾销工作 boolean cancel(boolean mayInterruptIfRunning); //如果工作实现前被勾销,则返回true。 boolean isCancelled(); //如果工作执行完结,无论是失常完结或是中途勾销还是产生异样,都返回true。 boolean isDone(); //获取异步执行的后果,如果没有后果可用,此办法会阻塞直到异步计算实现。 V get() throws InterruptedException, ExecutionException; // 获取异步执行后果,如果没有后果可用,此办法会阻塞,然而会有工夫限度, //如果阻塞工夫超过设定的timeout工夫,该办法将返回null。 V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;}总得来说Future有以下3点作用: ...

December 9, 2021 · 4 min · jiezi

关于android:一个Android开发程序员的五年总结

前言5年前的我必定想不到5年后的我是一个如此东倒西歪、酷爱学习、对于本人的工作能够做到稳如老狗、一直输入的水平。 我不是一个聪慧的、怠惰的、从头到尾指标清晰的程序员,但只有步履不停,工夫就会带来答案。 附上我这5年的总结,心愿能够给你带来一些持续成长的能源。 常识体系的建设这5年不夸大地说很大一部分工夫我都在构建本人的常识体系,一开始是Android开发相干的常识、技术,起初缓缓裁减为前后端都有涉猎,前面打算缓缓拓展到本人的兴趣爱好。 当初构建本人的常识体系曾经成为了日常的一个习惯,但过后这个行为还不能称为“构建常识体系”,因为那会本人就是工作闲,跟风写一些技术文啊、创立GitHub什么的,过后那些内容也基本上到处拼凑而成的。 事件呈现转折是我在GitHub上看到一个可能被优化的我的项目,我将它优化好并公布了进来,我当初还记得过后的那个成就感,起初我就常常这样做,长此以往这件闲来无事而做的事件让我变得越来越爱思考和总结,那些被解决过的问题和总结到的办法再加上原有的根底就形成了我常识体系的雏形: 必备根底技能:Java泛型+注解+并发编程+数据传输与序列化+Java虚拟机原理+反射与类加载+动静代理+高效IOAndroid高级UI与FrameWork源码:高级UI+Framework内核解析+Android组件内核+数据长久化性能调优:设计思维与代码品质优化+程序性能优化+开发效率优化开源框架设计思维:热修复设计+插件化框架解读+组件化框架设计+图片加载框架+网络拜访框架设计+RXJava响应式编程框架设计+IOC架构设计+Android架构组件JetpackNDK模块开发:NDK基础知识体系+底层图片解决+音视频开发微信小程序:小程序介绍+UI开发+API操作+微信对接Hybrid 开发与Flutter:Html5我的项目实战+Flutter进阶常识体系的裁减程序员须要一直学习这毋庸置疑,过来这五年我基本上摸遍了Android的整个体系,能够说没有拖程序员学习的后腿吧。 Android体系: 一、Java语言根底 整型与浮点型范畴Java管制可见性的4个拜访修饰符接口与抽象类class面向对象容器多线程IO正则表达式按日历计算年龄注解应用与简略阐明Java应用加密算法二、Kotlin语言根底 Kotlin系列简介新建我的项目概念介绍罕用写法lambda高阶函数扩大函数协程三、Android技术栈 ActivitySeverce服务Broadcast播送机制Fragmentres利用资源权限TectView显示文字EditText用户输出Button按钮ImageView图片显示Layout布局DrawerLayout侧滑栏RecyclerView显示列表多线程IO操作存储NetWork网络ProgressBar进度条View自定义View动画WebViewStatusBarCamera相机MediaBlutooth蓝牙WiFi连贯获取手机以后角度launcherAIDLzipBinderNotfication告诉PackageManagerRTFSC设计模式第三方库Android Studio模块化GradleNDK调试四、Android Jetpack全家桶 在Fragment之间共享数据ViewModelProviders示例datebindingLiveData事件传送LifeCycle生命周期WorkManagerViewModel数据库Paging五、数据结构与算法 1.哈希 Java中的HashMap的工作原理是什么?什么是Hashmap?如何结构一致性哈希算法hashCode() 和equals() 办法的重要性体现在什么中央?Object作为HashMap的key的话,对Object有什么要求吗?hashset 存的数是有序的吗?2.二叉树 求二叉树的最大深度求二叉树的最小深度求二叉树中节点的个数求二叉树中叶子节点的个数求二叉树中第k层节点的个数判断二叉树是否是均衡二叉树判断二叉树是否是齐全二叉树两个二叉树是否完全相同两个二叉树是否互为镜像翻转二叉树or镜像二叉树求两个二叉树的最低公共先人节点二叉树的前序遍历二叉树的中序遍历二叉树的后序遍历前序遍历和后序遍历结构二叉树在二叉树中插入节点输出一个二叉树和一个整数,打印出二叉树中节点值的和等于输出整数所有的门路二叉树的搜寻区间二叉树的档次遍历二叉树内两个节点的最长距离不同的二叉树判断二叉树是否是非法的二叉查找树(BST)3.链表 谈一谈,bucket如果⽤链表存储,它的毛病是什么?有一个链表,奇数位升序偶数位降序,如何将链表变成升序?如何反转单链表当初有一个单向链表,谈一谈,如何判断链表中是否呈现了环随机链表的复制4.数组 写一个算法,能够将一个二维数组顺时针旋转90度一个数组,除一个元素外其它都是两两相等,求那个元素?找出数组中和为S的一对组合,找出一组就行求一个数组中间断子向量的最大和寻找一数组中前K个最大的数5.排序 用Java写一·个冒泡排序?排序都有哪几种办法?请列举进去归并排序的原理是什么?堆排序的原理是什么?如何失去一个数据流中的中位数?你晓得哪些排序算法,这些算法的工夫复杂度别离是多少,解释一下快排?6.堆与栈 内存中的栈(stack)、堆(heap) 和动态区(static area) 的用法heap和stack有什么区别最小的k个数滑动窗口最大值丑数前K个高频元素无效的括号最小栈柱状图中最大的矩形7.高级算法 LRU算法的实现原理为什么要设计后缀表达式,有什么益处?设计一个算法,用来压缩一段URLid全局惟一且自增,如何实现?最初一个单词的长度8.动静布局 斐波那契数列不同门路爬楼梯零钱兑换打家劫舍编辑间隔... Android体系是很大的,虽说我摸了一遍但很多还停留在理解层面,将来还是一直学习的过程,在学习过程中对常识体系进行相应的补充,迟早有一天Android体系就成了本人的常识体系。 这听起来兴许不太事实啊,因为很多货色在工作中临时用不到或者永远用不到,所以裁减常识体系的前提是要把重点内容学精、学深。 在减少本人开发畛域技术的同时,也要开辟本人的眼界。互联网是个倒退极快的行业,咱们独守某种技术的话,随着年龄增长和行业倒退很容易被市场淘汰,所以我有始终关注新行业动态的习惯,理解一些新兴技术,尽管极大可能我不会去深刻,但只有晓得他们的存在我就会心安一些。 下一个5年打算是什么?我已经有看到过一个在职业规划方面很经典的问题:设想一下五年后的你、十年后...的你什么样子? 尽管我也问过我本人,但很难说具体,远期指标有时候会让我产生自我狐疑和懈怠,所以我更偏向于明确短期的、具体的指标,比方保持日更文章,实现难度小但能够给我带来成就感,这个成就感还能够累积。 所以如果你也感觉远大的指标不切实际的话,能够尝试着先定每天的指标、每周的指标,拆解成相应的容易实现的打算。 比方你要学Android,先从Java根底开始,或者你要面试Android开发,能够每天刷几道或者几个小时的题,当你把这一步步走实了,原来很大的指标就会变得越来越小。 我置信只有不进行思考总结、不进行去取得一些提高,下一个5年仍旧是能够拿来“吹牛”的哈哈哈。 文末我是毕业于一所双非一般院校的目前有五年教训的Android开发,目前正在发明本人的下一个不留遗憾的五年,心愿和大家共同进步。 如果以上内容对你有所帮忙的话点个赞吧。 以上提到的常识体系、Android体系有须要的能够评论+私信我【体系】支付。

December 9, 2021 · 1 min · jiezi

关于android:Android笔记Kotlin结合Jetpack构建MVVM

JetpackJetpack 是一个由多个库组成的套件,可帮忙开发者遵循最佳做法,缩小样板代码并编写可在各种 Android 版本和设施中统一运行的代码,让开发者精力集中编写重要的代码。Android Architecture Component (AAC)。 官网举荐架构 请留神,每个组件仅依赖于其下一级的组件。例如,Activity 和 Fragment 仅依赖于视图模型。存储区是惟一依赖于其余多个类的类;在本例中,存储区依赖于持久性数据模型和近程后端数据源。MVVMMVVM即Model - View - ViewModel的缩写,它的呈现是为了将图形界面与业务逻辑,数据模型进行解耦。 MVVM也是Google推崇的一种Android我的项目架构模型。 之前学习的Jetpack组建,大部分都是为了可能更好地架构MVVM应用程序而设计的。 API接口接口:https://api.github.com/users/... 工程构造bean:实体类。api:网络申请接口。repository:仓储层。用于寄存Room数据,网络数据,本地数据等。viewmodel:从仓储层获取数据,不须要关怀数据起源。view:Activity,Fragment和布局文件,用会用到DataBinding组件dao:Room数据库操作application:实例化全局文件和获取全局上下文。bindingAdapter:放一些 增加依赖implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'implementation 'de.hdodenhof:circleimageview:3.0.1'搭建我的项目通过获取GitHub API获取个人信息进行展现。 1. 定义User实体类@Entity(tableName = "user")data class User( @PrimaryKey @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER) var id: Int, @ColumnInfo(name = "login", typeAffinity = ColumnInfo.TEXT) var login: String, @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT) var name: String?, @ColumnInfo(name = "avatar_url", typeAffinity = ColumnInfo.TEXT) @SerializedName("avatar_url")var avatar: String?, @ColumnInfo(name = "blog", typeAffinity = ColumnInfo.TEXT) var blog: String, @ColumnInfo(name = "company", typeAffinity = ColumnInfo.TEXT) var company: String?, @ColumnInfo(name = "bio", typeAffinity = ColumnInfo.TEXT) var bio: String?, @ColumnInfo(name = "location", typeAffinity = ColumnInfo.TEXT) var location: String?, @ColumnInfo(name = "htmlUrl", typeAffinity = ColumnInfo.TEXT) @SerializedName("html_url") var htmlUrl: String?)```####2. 定义Dao类```@Daointerface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertUser(user: User) @Delete fun deleteUser(user: User) @Query("select * from user where login =:name") fun getUserByName(name: String): LiveData<User>}```####3. 定义DataBase类```@Database(entities = [User::class], version =7)abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao companion object { private var instance: AppDatabase? = null @Synchronized fun getDatabase(context: Context): AppDatabase { instance?.let { return it } return Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, "user_db" ).fallbackToDestructiveMigration().build().apply { instance = this } } }}4. 定义API接口interface Api { @GET("users/{userName}") fun getUser(@Path("userName") userName: String): Call<User>}5. 定义Retrofit拜访网络object RetrofitClient { private const val BASE_URL = "https://api.github.com/" var retrofit: Retrofit init { retrofit = Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()) .build() } fun getApi(): Api? { return retrofit.create(Api::class.java) }}6. 定义Application类class MyApplication : Application() { companion object { lateinit var context: Context } override fun onCreate() { super.onCreate() context = applicationContext }}7. 定义Repositoryobject UserRepository { var userDao: UserDao = AppDatabase.getDatabase(MyApplication.context).userDao() fun getUser(name: String): LiveData<User> { refresh(name) return userDao.getUserByName(name) } fun refresh(name: String) { RetrofitClient.getApi()?.getUser(name)?.enqueue(object : Callback<User> { override fun onResponse(call: Call<User>, response: Response<User>) { if (response.body() != null) { insertUser(response.body()!!) } } override fun onFailure(call: Call<User>, t: Throwable) { Log.d("UserRepository", "onFailure$t") } }) } fun insertUser(user: User) { thread { userDao.insertUser(user) } }}8. 定义ViewModelclass MvvmViewModel : ViewModel() { val userName = "yaoxin521123" fun getUser() = UserRepository.getUser(userName) fun refresh() = UserRepository.refresh(userName)}9. 绘制xml<?xml version="1.0" encoding="utf-8"?><layout xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.yx.androidseniorpreparetest.eighth.bean.User" /> </data> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/srl_SwipeRefreshLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".eighth.MvvmActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <de.hdodenhof.circleimageview.CircleImageView android:layout_width="95dp" android:layout_height="95dp" android:layout_gravity="center" android:layout_marginTop="20dp" app:image="@{user.avatar}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" android:text="@{user.name}" android:textColor="#000000" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" android:text="@{user.login}" android:textColor="#000000" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" android:text="@{user.blog}" android:textColor="#000000" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" android:text="@{user.company}" android:textColor="#000000" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" android:text="@{user.bio}" android:textColor="#000000" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" android:text="@{user.location}" android:textColor="#000000" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" android:text="@{user.htmlUrl}" android:textColor="#000000" android:textSize="20sp" /> </LinearLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout</layout>10. 在Activity触发事件class MvvmActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = DataBindingUtil.setContentView<ActivityMvvmBinding>(this, R.layout.activity_mvvm) val viewModel = ViewModelProviders.of(this).get(MvvmViewModel::class.java) viewModel.getUser().observe(this, { if (it != null) { binding.user = it } }) binding.srlSwipeRefreshLayout.setOnRefreshListener { viewModel.refresh() binding.srlSwipeRefreshLayout.isRefreshing = false } }}11. 定义BindingAapterclass BindingAdapter { companion object { @JvmStatic @BindingAdapter(value = ["image", "defaultImageResource"], requireAll = false) fun setImage(imageView: ImageView, imageUrl: String?, imageResource: Int) { if (!TextUtils.isEmpty(imageUrl)) { Picasso.get() .load(imageUrl) .placeholder(R.drawable.ic_launcher_background) .error(R.drawable.ic_launcher_background) .into(imageView) } else { imageView.setImageResource(imageResource) } } }}结语:后续会继续更新哦,喜爱的话点赞关注一下吧。相干视频【Android进阶】jetpack教程 ...

December 9, 2021 · 3 min · jiezi

关于android:Jetpack-Compose架构优秀APP

前言Jetpack Compose是Android推出的新一代申明式UI框架,Compose库是用响应式编程的形式对View进行构建,用更少更直观的代码领有更弱小的性能,同时还能进步开发速度。随着alpha版本的公布,其API也逐步稳固,此时谁先把握这一项新技术,谁就能在这一行业中抢占先机。 但一些应用过Compose的小伙伴反馈说Compose的实现成果不好,其实是他们没有搭配框架应用,任何代码都是须要依靠于框架实现的,应用框架能够升高程序之间的依赖性和耦合性,使重复性达到最高。 那么Compose应该在哪个架构中实现呢? 目前市面上支流的几个架构有MVP、MVC、MVVM,那么在 Compose 我的项目中哪种架构最合适呢? 首先咱们先来理解一下各大架构的特点。 MVPMVP次要特点是presenter与View之间通过接口通信,presenter通过调用View的办法实现UI的更新。Compose无奈取得Presenter 持有一个 View 层对象的援用,因为用来创立 UI 的 Composable 必须要求返回 Unit。所以MVP这种依赖接口通信的解耦形式无奈在 Compose 我的项目中应用。 MVCMVC重要特点就是两种拆散:视图和数据模型的拆散和视图和体现逻辑(Controller)的拆散 长处:耦合性低;重用性高;生命周期成本低;部署快;可维护性高;无利软件工程化治理。 毛病:没有明确的定义;不适宜小型,中等规模的应用程序;减少系统结构和实现的复用性;视图与控制器间的过于严密的连贯;视图对模型数据的低效率拜访;个别高级的界面工具或结构器不反对模式。 MVVMMVVM(Model-View-ViewModel)其实就是MVC(Model-View-Controller)的增强版,实质上和MVC没有什么区别,只是代码的地位变动而已。 MVVM的的长处: 低耦合性重复使用性独立开发性可测试性 MVVM的呈现解决了: 1.开发者大量调用雷同的 DOM API时的繁琐,操作冗余。 2.大量的DOM操作会升高页面的渲染性能,导致加载的速度变慢,影响用户体验。 3.不论是用户的操作导致Model发生变化,还是Model频繁发生变化,开发者都须要被动更新将变动的数据同步更新,这样工作既繁琐又很难保护多变的数据状态。 总的来说与Compose最为符合的架构还是MVVM。MVVM凭借着Controller清晰简洁、不便测试、开发解耦等劣势深得各开发大佬的青眼。 如何疾速入门 Compose ?对于宽广开发者来说,Compose是Android UI的将来,现阶段你能够不会用,然而将来如果你还想留在Android平台的话,Compose就是你必不可少的技能之一。为了帮忙大家零碎的学习,在这里给大家分享一份谷歌大佬整顿的《Jetpack Compose 入门到精通》,心愿能够帮忙大家疾速入门Compose。 第一章 初识 Jetpack Compose1. 为什么咱们须要一个新的UI 工具? 2. Jetpack Compose的着重点 减速开发弱小的UI工具直观的Kotlin API 3. API 设计 4. Compose API 的准则 一切都是函数顶层函数(Top-level function)组合优于继承信赖繁多起源 5. 深刻理解Compose CoreFoundationMaterial 6. 插槽API []第二章 Jetpack Compose构建Android UI1. Android Jetpack Compose 最全上手指南 ...

December 9, 2021 · 1 min · jiezi

关于android:正式版-API-确定-Android-12L-Beta-1-发布

作者 / Maru Ahues Bouza, Director, Android Developer Relations 在 10 月份的 Android 开发者峰会 上,咱们强调了平板电脑、可折叠设施和 Chromebook 等大屏幕设施的增长,以及如何通过新的 Jetpack API、工具和指南,让开发者们 更容易为这些设施构建 出良好的利用体验。咱们还公布了 Android 12L 的开发者预览版,这是一个专为大屏幕设计的 Android 12 性能更新。 通过 12L,咱们为大屏幕优化和打磨了零碎界面,使多任务处理更加弱小和直观,并改善了兼容性反对,让利用在默认状况下也有更好的视觉效果。12L 还为开发者提供了一些新的 API,如空间音频以及改良的拖放操作,以打造更好的大屏幕体验。 明天,咱们正式公布 Android 12L 的第一个 Beta 版,供大家对利用进行测试并提交反馈,从而为明年年初公布的性能更新做好筹备。您能够在 Android Studio 中 设置 Android 模拟器 来尝试新的大屏幕性能。Android 12L 也实用于手机,只需 注册参加测试,即可在受反对的 Pixel 设施上获取 Android 12L Beta 1。如果您曾经参加 Android 12 Beta 测试,则会主动取得 12L 更新。咱们与联想单干,让您也能够在联想 Tab P12 Pro 平板电脑上体验 Android 12L,更多对于可用构建和反对的信息,请返回 联想官方网站 查阅。 Android 12L Beta 1 内容一览明天公布的 Beta 1 版本蕴含对性能和用户体验的改良,最新的 bug 修复和优化,以及 2021 年 12 月安全补丁。对于开发者来说,咱们提前实现了 API,因而 Beta 1 还蕴含 Android 12L 的正式版 API (API 级别 32),更新的构建工具,以及用于测试的零碎映像。您能够用这些来测试利用在 Android 12L 的各种性能中的体现。 ...

December 9, 2021 · 2 min · jiezi

关于android:建议收藏Jetpack-Compose编程知识全汇总-含详细实例讲解

一、简述 Jetpack Compose是Google I/O 2019 公布的Andorid UI框架,它不同于Andorid常见的Xml+命令式Coding的UI开发范式,而是基于Kotlin的DSL实现了一套相似React的申明式UI框架。Jetpack Compose目前依然处于Alpha版本指标是2020年可能公布稳固的Beta版本。随同React Native、Flutter等大前端框架的衰亡以及Jetpack Compose、SwiftUI等native框架的呈现,申明式UI正逐步成为客户端UI开发的新趋势。 特点: 申明式编程模型,界面随利用状态自动更新组合 vs 继承关注点拆散(SOC),缩小耦合,减少内聚更少的代码,Kotlin简洁且易保护疾速的开发,反对实时预览界面,并反对互动式预览向后兼容,与现有视图独特应用,无缝链接,并反对Material Design和动画二、环境配置因为Jetpack Compose还未正式公布,须要下载最新Canary版的Android Studio 预览版 以下三种形式可初步体验: 尝试应用Jetpack Compose 示例利用创立反对Jetpack Compose 的新利用现有我的项目中反对Jetpack Compose基于现状,我次要介绍第三种形式: 配置Kotlin plugins { id 'org.jetbrains.kotlin.android' version '1.4.0' }配置Gradle android { defaultConfig { ... minSdkVersion 21 } buildFeatures { // Enables Jetpack Compose for this module compose true } ... // Set both the Java and Kotlin compilers to target Java 8. compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" useIR = true } composeOptions { kotlinCompilerVersion '1.4.0' kotlinCompilerExtensionVersion '1.0.0-alpha05' } }增加工具包依赖项 ...

December 9, 2021 · 6 min · jiezi

关于android:Android应用架构MVVM模式

前言随着Android利用开发技术的一直倒退和成熟,很开发者越来越关注着Android利用架构的设计。目前,Android的利用架构次要有MVC、MVP和MVVM模式,咱们就来说一下MVVM模式。 MVP模式MVVM模式能够说是MVP模式的进一步倒退,所以先来理解一下MVP模式。 MVP (Model-View-Presenter) 模式的构造如下图所示: MVP模式将利用分为三层:Model层次要负责数据的提供,View层次要负责界面的显示,Presenter层次要负责业务逻辑的解决。 在MVP模式中,Model层和View层不能间接通信,Presenter层负责充当中间人,实现Model层和View层之间的间接通信。View层和Presenter层相互持有对方的援用,实现View层和Presenter层之间的通信。 MVP模式的次要长处是:拆散了Model层和View层,拆散了视图操作和业务逻辑,升高了耦合。 MVVM模式MVVM (Model-View-ViewModel) 模式的构造如下图所示: MVVM模式与MVP模式一样,也将利用分为三层,并且各个对应的层的职责类似: Model层,次要负责数据的提供。Model层提供业务逻辑的数据结构(比方,实体类),提供数据的获取(比方,从本地数据库或者近程网络获取数据),提供数据的存储。View层,次要负责界面的显示。View层不波及任何的业务逻辑解决,它持有ViewModel层的援用,当须要进行业务逻辑解决时告诉ViewModel层。ViewModel层,次要负责业务逻辑的解决。ViewModel层不波及任何的视图操作。通过官网提供的Data Binding库,View层和ViewModel层中的数据能够实现绑定,ViewModel层中数据的变动能够主动告诉View层进行更新,因而ViewModel层不须要持有View层的援用。ViewModel层能够看作是View层的数据模型和Presenter层的联合。MVVM模式与MVP模式最大的区别在于:ViewModel层不持有View层的援用。这样进一步升高了耦合,View层代码的扭转不会影响到ViewModel层。 MVVM模式绝对于MVP模式次要有如下长处: 进一步升高了耦合。ViewModel层不持有View层的援用,当View层产生扭转时,只有View层绑定的数据不变,那么ViewModel层就不须要扭转。而在MVP模式下,当View层产生扭转时,操作视图的接口就要进行相应的扭转,那么Presenter层就须要批改了。不必再编写很多样板代码。通过官网的Data Binding库,UI和数据之间能够实现绑定,不必再编写大量的findViewById()和操作视图的代码了。总之,Activity/Fragment的代码能够做到相当简洁。例子上面举一个简略的例子来实际MVVM模式。残缺的我的项目代码能够去GitHub上查看: https://github.com/chongyucaiyan/MVVMDemo例子实现的次要性能是:点击按钮网络查问天气,查问胜利后在界面上显示天气信息。主界面如下图所示: MVVM模式的代码组织构造倡议依照 业务性能 进行划分,具体操作是:每个业务性能独立一个包寄存,每个业务性能包上面再按Model、View、ViewModel分包寄存。所有的Model寄存在model包上面,所有的Activity和Fragment寄存在activity包上面,所有的ViewModel寄存在viewmodel包上面。该例子比较简单,只有一个weather业务功能模块,最终的代码组织构造如下图所示: 编写Model 查问杭州天气的URL为: http://www.weather.com.cn/data/cityinfo/101210101.html拜访该URL将返回一串JSON字符串,如下所示: {"weatherinfo":{"city":"杭州","cityid":"101210101","temp1":"5℃","temp2":"20℃","weather":"晴转多云","img1":"n0.gif","img2":"d1.gif","ptime":"18:00"}}依照此JSON字符串,能够编写相应的实体类。WeatherData类的代码如下所示: public class WeatherData { private WeatherInfo weatherinfo; public WeatherInfo getWeatherinfo() { return weatherinfo; } public void setWeatherinfo(WeatherInfo weatherinfo) { this.weatherinfo = weatherinfo; }}WeatherInfo类的代码如下所示: public class WeatherInfo { private String city; private String cityid; private String temp1; private String temp2; private String weather; private String img1; private String img2; private String ptime; public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getCityid() { return cityid; } public void setCityid(String cityid) { this.cityid = cityid; } public String getTemp1() { return temp1; } public void setTemp1(String temp1) { this.temp1 = temp1; } public String getTemp2() { return temp2; } public void setTemp2(String temp2) { this.temp2 = temp2; } public String getWeather() { return weather; } public void setWeather(String weather) { this.weather = weather; } public String getImg1() { return img1; } public void setImg1(String img1) { this.img1 = img1; } public String getImg2() { return img2; } public void setImg2(String img2) { this.img2 = img2; } public String getPtime() { return ptime; } public void setPtime(String ptime) { this.ptime = ptime; }}编写ViewModel ...

December 9, 2021 · 3 min · jiezi

关于android:自定义View实战众人看了直呼666

前言在当初的App设计中,轮播根本成为了每个利用的“标配”,有了轮播,就天然须要有对应的指示器,代表以后轮播的进度,当初市面上指示器的款式大部分都是基于小圆点的模式,实现这个根本的成果网上也有很多轮子,本文次要是在实现根本成果的根底上,在切换圆点之间增加一个粘性过渡的动画成果。 成果预览 实现思路绘制圆点圆点的话基于画笔绘制,将控件宽度平分为N等份,且选中的圆点半径稍大。 圆点之间的联动滚动反对设置最多显示N个圆点,当圆点总数超过N个时,临时不显示在控件可见范畴内,直到左/右滚动到凑近边界时,主动平移所有圆点,从而让最新选中的圆点再次回到居中的地位。应用属性动画联合横坐标偏移实现。 圆点过渡动画圆点与圆点之间,如果单纯切换选中,会显得有些僵硬,所以要为这个过程增加一些过渡的动画成果,这里采纳当下常见的一种“粘性”成果,相似于咱们在QQ联系人列表长按拖动未读音讯数的成果: 这里基于贝塞尔曲线来实现,通过计算筹备过渡的两个圆点的地位,以及它们之间的中心点,能够绘制出高低两条贝塞尔曲线,再闭合起来即可。而后联合属性动画进行挪动,实现最终的过渡成果。 实现步骤1.计算控件宽高依照设计的成果,控件的宽高取决于小圆点的排列: 控件宽度 = 屏幕中可见的所有小圆点的宽度 * 可见小圆点的数量 + 小圆点之间的间距 * (可见小圆点的数量 - 1)控件高度 = 最大的小圆点的高度@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    int count = Math.min(totalCount, showCount);    int width = (int) ((count - 1) * smallDotWidth + (count - 1) * dotPadding + bigDotWidth);    int height = (int) bigDotWidth;    final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);    final int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);    final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);    final int heightSpecSize = MeasureSpec.getMode(heightMeasureSpec);    if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {        setMeasuredDimension(width, height);    } else if (widthSpecMode == MeasureSpec.AT_MOST) {        setMeasuredDimension(width, heightSpecSize);    } else if (heightSpecMode == MeasureSpec.AT_MOST) {        setMeasuredDimension(widthSpecSize, height);    } else {        setMeasuredDimension(width, height);    }}另外留神最大显示数量的管制,即 int count = Math.min(totalCount, showCount); 如果以后圆点总数超过屏幕可见数,则基于最大可见数来计算控件的宽度。 2.绘制小圆点在晓得小圆点的数量之后,只须要遍历顺次绘制即可。思考到选中的圆点与其余圆点款式上的区别,因而针对以后选中圆点独自设置宽度 bigDotWidth,独自设置色彩 selectColor,如下: @Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    float startX = curX;    float selectX = 0;    for (int i = 0; i < totalCount; i++) {        if (curIndex == i) {            //绘制选中圆点            paint.setColor(selectColor);            paint.setStyle(Paint.Style.FILL);            selectRectF.left = startX;            selectRectF.top = getHeight() / 2f - bigDotWidth / 2;            selectRectF.right = startX + bigDotWidth;            selectRectF.bottom = getHeight() / 2f + bigDotWidth / 2;            canvas.drawCircle(startX + (bigDotWidth) / 2, bigDotWidth / 2, (bigDotWidth) / 2, paint);            selectX = startX + bigDotWidth / 2;            startX += (bigDotWidth + dotPadding);        } else {            //绘制其它圆点            paint.setColor(defaultColor);            paint.setStyle(Paint.Style.FILL);            startX += smallDotWidth / 2;            canvas.drawCircle(startX, bigDotWidth / 2, (smallDotWidth) / 2, paint);            startX += (smallDotWidth / 2 + dotPadding);        }    }}3.左右平移动画从效果图能够看出,指示器平移的触发机会在于每一次的左右切换,具体须要满足如下条件: 1.以后圆点总数超过最大可见数2.以后筹备切换的下一个圆点在屏幕非两头的地位第一个条件,圆点总数超过最大可见数才可平移,这个很好了解。第二个是切换的下一个圆点在屏幕非两头地位,这个是平移的一个规定,比方上面的例子: 上图在切换之前,选中的是3,筹备切换到4的过程中,因为以后总数为7个,超过最大可见数5个,满足第一个条件,同时因为在切换之前4是处在非屏幕两头的地位,因而满足第二个条件,须要整体向左平移一个单位,使得切换之后,4变成了屏幕核心的地位,逻辑如下: public void setCurIndex(int index) {    if (index == curIndex) {        return;    }    //以后圆点总数超过最大可见数    if (totalCount > showCount) {        if (index > curIndex) {            //往左边滑动            int start = showCount % 2 == 0 ? showCount/2 - 1 : showCount / 2;            int end = totalCount - showCount / 2;            //判断是否须要先滚动            if (index > start && index < end) {                startScrollAnim(Duration.LEFT, () -> invalidateIndex(index));            } else {                invalidateIndex(index);            }        } else {            //往右边滑动            int start = showCount / 2;            int end = showCount % 2 == 0 ? totalCount - showCount / 2 + 1 : totalCount - showCount / 2;            //判断是否须要先滚动            if (index > start - 1 && index < end - 1) {                startScrollAnim(Duration.RIGHT, () -> invalidateIndex(index));            } else {                invalidateIndex(index);            }        }    } else {        invalidateIndex(index);    }}4.圆点过渡动画圆点之间的粘性动画,实质上是以前一个圆点作为基准地位,而后平移另外一个圆点的程度地位,使得它们之间的闭合曲线逐步变动,直到平移到与下一个圆点地位重合,如下: 由红色圆点切换到绿色圆点的过程中,以A点为起始点,连贯A点与C点绘制一条贝塞尔曲线,同样,底部B点和D点之间也绘制一条贝塞尔曲线,而后再把AB和CD也连接起来,四条门路造成一条闭合的曲线绘制进去,造成根本的形态。 而后再联合属性动画,使得C点和D点一直向右挪动,直到与绿色圆齐全重合。 如下: 设置粘性属性动画的起始和完结值: //以后选中的圆点的程度核心 作为粘性动画起始点float startValues = getCurIndexX() + bigDotWidth / 2;//依据方向设置动画的完结值if (index > curIndex) {    stickAnimator.setFloatValues(startValues, startValues + dotPadding + smallDotWidth);} else {    stickAnimator.setFloatValues(startValues, startValues - dotPadding - smallDotWidth);}监听动画一直刷新粘性过渡的动画值: ValueAnimator stickAnimator = new ValueAnimator();stickAnimator.setDuration(animTime);stickAnimator.addUpdateListener(animation -> {    stickAnimX = (float) animation.getAnimatedValue();    invalidate();});stickAnimator.removeAllListeners();stickAnimator.addListener(new AnimatorListenerAdapter() {    @Override    public void onAnimationEnd(Animator animation) {        isSwitchFinish = true;        invalidate();    }});stickAnimator.start();绘制粘性曲线: @Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    if (isSwitchFinish) {        //切换实现记得重置门路        stickPath.reset();    } else {        paint.setColor(selectColor);        //以以后选中的圆点为绘制起始点        float quadStartX = selectX;        float quadStartY = getHeight() / 2f - bigDotWidth / 2;        stickPath.reset();        //连贯4个点        stickPath.moveTo(quadStartX, quadStartY);        stickPath.quadTo(quadStartX + (stickAnimX - quadStartX) / 2, bigDotWidth / 2, stickAnimX, quadStartY);        stickPath.lineTo(stickAnimX, quadStartY + bigDotWidth);        stickPath.quadTo(quadStartX + (stickAnimX - quadStartX) / 2, bigDotWidth / 2, quadStartX, quadStartY + bigDotWidth);        //造成闭合曲线        stickPath.close();        //绘制过渡过程中的圆        canvas.drawCircle(stickAnimX, bigDotWidth / 2, (bigDotWidth) / 2, paint);        canvas.drawPath(stickPath, paint);    }}结语如果指定了最多显示多少个圆点,则当总数超过时会左右滚动,如果想要非滚动的模式也能够设置为最大圆点数。本控件次要还是通过贝塞尔曲线来制作粘性成果,让动画更为活泼,反对设置是否开启粘性成果、粘性动画时长、小圆点选中与非选中时的款式等,后续会再依据需要裁减其它细节。 github地址:https://github.com/GitHubZJY/...原文链接:https://www.jianshu.com/p/19a... 文末您的点赞珍藏就是对我最大的激励! 欢送关注我,分享Android干货,交换Android技术。 对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

December 9, 2021 · 1 min · jiezi

关于android:Android实现人脸识别动画效果

成果展现 实现步骤1.绘制圆圈遮罩这里咱们是用了混合模式来实现圆圈局部的扣除,这里咱们用到了PorterDuff.Mode.CLEAR /**     * 绘制圆圈遮罩     * @param canvas     */    private void drawCircleMask(Canvas canvas) {        canvas.save();        //指标图Dst        canvas.drawRect(new Rect(0,0,getWidth(),getHeight()), mPaint);        //设置混合模式        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));        //源图Src,重叠区域右下角局部        canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 3, mPaint);        //革除混合模式        mPaint.setXfermode(null);        canvas.restore();    }这时成果如下 2.绘制两个动画成果的圆圈咱们绘制的两个图片如下 咱们要做的就是通过计算将Bitmap缩放成与之前遮罩成果的圆圈一样大,因为咱们这里用的两张图片是一样大的,因而咱们只须要计算出内圆圈图片与遮罩圆圈的缩放比例即可,因为之前咱们给遮罩圆圈设置的半径为:控件宽度 / 3 因而咱们缩放后的圆圈Bitmap宽高该当是如下图所示的两头红线局部加两边蓝色局部的总长 其中两头红线局部就是:控件宽度 / 3 ,而蓝线局部能够通过PhotoShop等工具测量,而后依据与红线局部的比例求出,代码如下,其中mInnerCircleBitmap是内圆,mOutCircleBitmap是外圆 /**     * 画圆圈内部的圆圈图片     */    private void drawBitmapCircle(Canvas canvas) {        if(mInnerCircleBitmap == null){            int dstWidthAndHeight = (int) (getWidth() / 1.5f + getWidth() / 1.5f / 4);            mInnerCircleBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_checkface_innercircle);            mInnerCircleBitmap = Bitmap.createScaledBitmap(mInnerCircleBitmap,dstWidthAndHeight,dstWidthAndHeight,true);        }        if(mOutCircleBitmap == null){            int dstWidthAndHeight = (int) (getWidth() / 1.5f + getWidth() / 1.5f / 4);            mOutCircleBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_checkface_outcircle);            mOutCircleBitmap = Bitmap.createScaledBitmap(mOutCircleBitmap,dstWidthAndHeight,dstWidthAndHeight,true);        }        int left = (getWidth() - mInnerCircleBitmap.getWidth()) / 2;        int top = (int) (getWidth() / 2 - getWidth() / 3 - getWidth() / 1.5f / 8);        canvas.drawBitmap(mInnerCircleBitmap,left,top,mPaint);        canvas.drawBitmap(mOutCircleBitmap,left,top,mPaint);    }这时成果如下 3.实现旋转动画成果接下来咱们就能够通过ValueAnimator来实现圆圈的旋转成果了,从文章结尾的成果咱们能够看出两个圆圈的旋转方向是不一样的,因而咱们逻辑上也要留神一个是顺时针旋转另一个是逆时针旋转,代码如下 private float mDegress = 0;//旋转角度private void init() {        //定义动画        valueAnimator = ValueAnimator.ofFloat(0f, 360f);        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);        valueAnimator.setRepeatMode(ValueAnimator.RESTART);        valueAnimator.setInterpolator(new LinearInterpolator());        valueAnimator.setDuration(6000);        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                mDegress = (float) animation.getAnimatedValue();                postInvalidate();            }        });        valueAnimator.start();    }/**     * 画圆圈内部的圆圈图片     */    private void drawBitmapCircle(Canvas canvas) {        if(mInnerCircleBitmap == null){            int dstWidthAndHeight = (int) (getWidth() / 1.5f + getWidth() / 1.5f / 4);            mInnerCircleBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_checkface_innercircle);            mInnerCircleBitmap = Bitmap.createScaledBitmap(mInnerCircleBitmap,dstWidthAndHeight,dstWidthAndHeight,true);        }        if(mOutCircleBitmap == null){            int dstWidthAndHeight = (int) (getWidth() / 1.5f + getWidth() / 1.5f / 4);            mOutCircleBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_checkface_outcircle);            mOutCircleBitmap = Bitmap.createScaledBitmap(mOutCircleBitmap,dstWidthAndHeight,dstWidthAndHeight,true);        }        int left = (getWidth() - mInnerCircleBitmap.getWidth()) / 2;        int top = (int) (getWidth() / 2 - getWidth() / 3 - getWidth() / 1.5f / 8);        //顺时针旋转        canvas.save();        canvas.rotate(mDegress,getWidth() / 2, getWidth() / 2);        canvas.drawBitmap(mInnerCircleBitmap,left,top,mPaint);        canvas.restore();         //逆时针旋转        canvas.save();        canvas.rotate(-mDegress,getWidth() / 2, getWidth() / 2);        canvas.drawBitmap(mOutCircleBitmap,left,top,mPaint);        canvas.restore();    }这时成果如下 4.减少提醒文字绘制文字就比较简单了,次要是用于提醒用户须要进行的操作,代码如下 canvas.drawText("请把脸移入圈内",getWidth() / 2, (float) (getWidth() * 1.2),mTextPaint);最终成果如下 案例源码以上实现步骤中的源码我都是拆分开讲的,具体残缺的代码大家能够通过如下地址获取 https://gitee.com/itfitness/f... 原文链接:https://www.jianshu.com/p/938... 文末您的点赞珍藏就是对我最大的激励!欢送关注我,分享Android干货,交换Android技术。对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

December 9, 2021 · 1 min · jiezi

关于android:Jetpack-Compose架构优秀APP

前言Jetpack Compose是Android推出的新一代申明式UI框架,Compose库是用响应式编程的形式对View进行构建,用更少更直观的代码领有更弱小的性能,同时还能进步开发速度。随着alpha版本的公布,其API也逐步稳固,此时谁先把握这一项新技术,谁就能在这一行业中抢占先机。 但一些应用过Compose的小伙伴反馈说Compose的实现成果不好,其实是他们没有搭配框架应用,任何代码都是须要依靠于框架实现的,应用框架能够升高程序之间的依赖性和耦合性,使重复性达到最高。 那么Compose应该在哪个架构中实现呢? 目前市面上支流的几个架构有MVP、MVC、MVVM,那么在 Compose 我的项目中哪种架构最合适呢? 首先咱们先来理解一下各大架构的特点。 MVPMVP次要特点是presenter与View之间通过接口通信,presenter通过调用View的办法实现UI的更新。Compose无奈取得Presenter 持有一个 View 层对象的援用,因为用来创立 UI 的 Composable 必须要求返回 Unit。所以MVP这种依赖接口通信的解耦形式无奈在 Compose 我的项目中应用。 MVCMVC重要特点就是两种拆散:视图和数据模型的拆散和视图和体现逻辑(Controller)的拆散 长处:耦合性低;重用性高;生命周期成本低;部署快;可维护性高;无利软件工程化治理。 毛病:没有明确的定义;不适宜小型,中等规模的应用程序;减少系统结构和实现的复用性;视图与控制器间的过于严密的连贯;视图对模型数据的低效率拜访;个别高级的界面工具或结构器不反对模式。 MVVMMVVM(Model-View-ViewModel)其实就是MVC(Model-View-Controller)的增强版,实质上和MVC没有什么区别,只是代码的地位变动而已。 MVVM的的长处: 低耦合性重复使用性独立开发性可测试性  MVVM的呈现解决了: 1.开发者大量调用雷同的 DOM API时的繁琐,操作冗余。 2.大量的DOM操作会升高页面的渲染性能,导致加载的速度变慢,影响用户体验。 3.不论是用户的操作导致Model发生变化,还是Model频繁发生变化,开发者都须要被动更新将变动的数据同步更新,这样工作既繁琐又很难保护多变的数据状态。  总的来说与Compose最为符合的架构还是MVVM。MVVM凭借着Controller清晰简洁、不便测试、开发解耦等劣势深得各开发大佬的青眼。 如何疾速入门 Compose ?对于宽广开发者来说,Compose是Android UI的将来,现阶段你能够不会用,然而将来如果你还想留在Android平台的话,Compose就是你必不可少的技能之一。为了帮忙大家零碎的学习,在这里给大家分享一份谷歌大佬整顿的《Jetpack Compose 入门到精通》,心愿能够帮忙大家疾速入门Compose。 第一章初识 Jetpack Compose 1.为什么咱们须要一个新的UI 工具? 2.Jetpack Compose的着重点 3.API 设计 4.Compose API 的准则 5.深刻理解Compose 6.插槽API 第二章Jetpack Compose构建Android UI 1.Android Jetpack Compose 最全上手指南 2.深刻详解 Jetpack Compose | 优化 UI 构建 ...

December 8, 2021 · 1 min · jiezi

关于android:安卓逆向-35-安卓程序执行入口

AndroidManifest.xml介绍<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.xiaojianbang.hashmaptest"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.HashMapTest" android:name=".MyApplication"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>根节点是manifest,根节点的package属性指定包名,根节点下又有若干子节点user-permission 申明app运行须要的权限application 指定app本身属性 android:allowBackup 是否容许备份 android:icon 在手机屏幕上的图标 android:label 在手机屏幕上显示的名称 android:supportsRtl 是否反对从右往左的文字排列程序 android:theme 显示主题 android:name 可选,个别加固利用都会有这个,这里定义的类比activity先执行 application中还有若干字节点,比方四大组件的注册 <activity android:name=".MainActivity"> 示意这是一个界面,对应类名是以后包名门路下的MainActivity <category android:name="android.intent.category.LAUNCHER" /> 带有这一条的界面,是启动界面入口 带有 LAUNCHER 的activity 对应的类,先执行。如果后面有 android:name,那么它对应的类会优先执行。2. 事件执行程序Application staticApplication attachBaseContextApplication onCreate MainActivity staticMainActivity attachBaseContextMainActivity onCreate 3. Application的生命周期很长,能够用来传递一些全局变量public static HashMap<String, String> mInfoMap = new HashMap<>();定义动态变量,别的类中间接通过类名援用

December 8, 2021 · 1 min · jiezi

关于android:安卓逆向-34-AndroidStudio工程目录结构

gradle -> wrapper -> gradle-wrapper.properties 配置我的项目gradle版本build.gradle 形容工程整体的编译规定gradle.properties gradle配置文件,个别毋庸改变local.properties 本机环境配置,SDK、NDK门路等,个别毋庸改变settings.gradle 配置哪些模块在一起编译 include ':app' 只编译appAPPapp -> build.gradle 形容以后模块的编译规定app -> build -> outputs -> apk -> debug/release 生成的apk的寄存地位app -> build -> intermediates -> cmake -> debug/release -> obj 生成的so寄存地位libs 模块中应用了第三方jar的时候,会放这里src -> main -> cpp C/C++代码 java Java代码src -> proguard-rules.pro Java代码混同规定src -> main -> res -> drawable 用来放图片src -> main -> res -> layout 用来放布局文件src -> main -> res -> mipmap-hdpi 用来放利用图片,不同屏幕的适配图标src -> main -> res -> values strings.xml、public.xmlsrc -> main -> AndroidManifest.xml 清单文件,app的icon图标、四大组件的注册、权限申请、指明程序入口build.gradle 课件版本plugins { id 'com.android.application'}android { //指定编译用的SDK版本号 compileSdkVersion 30 //指定编译工具的版本号,结尾两位数字必须与compileSdkVersion统一。具体版本号可在sdk装置目录 SDK\build-tools 下找到 buildToolsVersion "30.0.3" defaultConfig { //app包名 applicationId "com.xiaojianbang.hashmaptest" //app适宜运行的最小SDK版本号 minSdkVersion 16 //指标设施的SDK版本号,即app心愿在哪个版本下运行 targetSdkVersion 30 //app利用版本号 versionCode 1 //app利用版本名称 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { //是否开启代码混同性能 minifyEnabled false //指定代码混同规定文件名 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }}//配置app编译所需依赖dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.1' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' api 'com.squareup.okhttp3:okhttp:3.10.0'}build.gradle 本人复制的plugins { id 'com.android.application'}android { //指定编译用的SDK版本号 compileSdk 31 defaultConfig { //app包名 applicationId "com.hengdi.hookdemo" //app适宜运行的最小SDK版本号 minSdk 21 //指标设施的SDK版本号,即app心愿在哪个版本下运行 targetSdk 31 //app利用版本号 versionCode 1 //app利用版本名称 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // c++ 我的项目 会有对应的c++版本 externalNativeBuild { cmake { cppFlags '-std=c++11' } } } buildTypes { release { //是否开启代码混同性能 minifyEnabled false //指定代码混同规定文件名 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } externalNativeBuild { cmake { // cmake 列表 path file('src/main/cpp/CMakeLists.txt') version '3.10.2' } } buildFeatures { viewBinding true }}//配置app编译所需依赖dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'}

December 8, 2021 · 2 min · jiezi

关于android:Android入门教程-WebView-二

自定义长按文字弹出的菜单长按 WebView 中的文字,会有抉择光标,并且弹出一个小菜单。 个别菜单里会有「复制」,「搜寻」等等选项。 这个菜单能够由开发者自定义。让弹出的菜单显示其余选项。 比方弹出「复制」「翻译」。 个别步骤: 设计监听器自定义WebView,复写办法定义Js接口须要新建2个 Java 文件,WebChooseActionListener,CustomClickWebView 定义监听器监听器 WebChooseActionListener,用来传输用户的抉择。 public interface WebChooseActionListener { /** * @param itemTitle 选项的题目 * @param txt 选中的文字 */ void onChosen(String itemTitle, String txt);}txt是用户在 WebView上选中的文字。如何获取到选中的文字呢? 本例中用 js 办法来获取。 自定义 WebViewjs 和 Java 的交互 定义 CustomJsInterface (本例中把它放在了 CustomClickWebView 里),其中给 chooseAction 增加注解 @JavascriptInterface,将其定义为沟通的桥梁。 js中会调用这个 chooseAction 办法。 为了获取用户在 WebView 上选中的文字,须要执行 js 代码,参考exeSelectTextJs 办法。 js代码中的RFJSInterface,和Java中addJavascriptInterface传入的名字必须统一。 addJavascriptInterface(new CustomJsInterface(this), "RFJSInterface");js代码中"RFJSInterface.chooseAction(txt,title);"就是在调用Java中定义的办法。 管制菜单 想要管制 WebView 中弹出的菜单,须要复写 startActionMode 办法,把默认的菜单替换成自定义的菜单。调用 Menu.add() 办法增加选项。 ...

December 8, 2021 · 3 min · jiezi

关于android:Android内存泄漏的原因解决办法以及如何避免

作为开发人员,在咱们的日常开发中,为了构建更好的应用程序,咱们须要思考很多事件以保障利用运行在正规上,其中之一是要确保咱们的应用程序不会解体。利用解体的一个常见起因是内存透露。这方面的问题能够以各种模式体现进去。在大多数状况下,咱们看到内存使用率稳步回升,直到应用程序不能调配更多的资源,并不可避免地解体。在Java中这往往导致一个OutOfMemoryException异样被抛出。在某些常见的状况下,泄露的类甚至能够勾留很长时间来接管已注册的回调,这会导致一些十分奇怪的谬误,并往往抛出臭名远扬的IllegalStateException异样。 为了帮忙别人在代码剖析上缩小破费工夫,我将介绍内存透露的几个例子,论述在Android Studio中如何查看它们,当然最重要的是如何将其解决。 申明在这篇文章中的代码示例的目标是为了促成大家对内存治理有更深的理解,特地是在java。其通用的体系结构,线程治理和代码示例的 HTTP 申请解决在实在的生产环境并不是现实的,这些示例仅仅为了阐明一个问题:在Android中,内存透露是一件要思考的事件。 监听器注册这真的不应该是个问题,但我常常看到各种注册办法的调用,但他们对应的登记办法却无处可寻。这是透露的潜在起源,因为这些办法明确设计成相互对消。如果没有调用登记办法,被援用的对象曾经被终止后,监听实例可能会持有该对象很长的工夫,从而导致透露内存。在Android中,如果该对象是一个Activity对象,是特地麻烦的,因为他们往往领有大量的数据。让我通知你,可能是什么样子。 public class LeaksActivity extends Activity implements LocationListener { private LocationManager locationManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_leaks); locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, TimeUnit.MINUTES.toMillis(5), 100, this); } // Listener implementation omitted}在这个例子中,咱们让Android的 LocationManager告诉咱们地位更新。咱们所须要做的就是获取零碎服务自身和设置一个回调来接管更新。在这里,咱们在Activity中实现了地位监听接口,这意味着LocationManager将持有该Activity的援用。当初,如果该设施被旋转,新的Activity将被创立并取代曾经注册地位更新接口的旧的Activity。因为零碎服务存活工夫必定比任何Activity都要长,LocationManager依然持有以前的Activity的援用,这使GC不可能回收依赖于以前的Activity的资源,从而导致内存透露。如果重复旋转设施,将导致大量的不可回收的Activity填满内存,最终导致OutOfMemoryException异样。 但为了解决内存透露,咱们首先必须要可能找到它。侥幸的是,Android Studio有一个叫做 Android Monitor的内置工具,咱们能够用它来 察看除利用内存应用状况。咱们须要做的仅仅是关上Android Monitor 并转到对应tab,看看应用了多少内存和内存实时分配情况。 1-3R36AJUjtdBArkIxPuED3g.png 任何导致资源分配的交互都在这里反映进去,使之成为跟踪应用程序的资源应用状况的现实场合。为了找到内存泄露,当咱们狐疑在某个工夫点内存被泄露时,咱们须要晓得在该工夫点蕴含了那些内存。对于这个非凡的例子,咱们所要做的就是启动咱们的应用程序,而后旋转设施一次,而后调用Dump Java Heap操作(在Memory的旁边,从右边数起第三个图标)。这将生成一个HPROF文件,其中蕴含咱们调用该操作时的一个内存快照。几秒钟后,Android Studio 会主动关上该文件,给咱们更易于剖析内存的直观示意。 我不会去深刻无关如何剖析微小的内存堆。相同,我会把你的注意力疏导到 Analyzer Tasks(上面截图中的右上角)。为了检测下面的例子中引入的内存透露,你所须要做的检测是查看泄露的Activity(Detect Leaked Activities),点击播放按钮而后在Analysis Results上面就会显示泄露的Activity状况。 Paste\_Image.png 如果咱们选中泄露的Activity,能够失去一个援用树,该援用树能够检测持有该Activity的援用。通过寻找深度为零的实例,咱们发现地位管理器中的实例mListener,是咱们的Activity不能被GC回收的起因。回到咱们的代码,咱们能够看到,这个援用是因为咱们在requestLocationsUpdates办法中设置Activity作为地位更新回调导致的。通过浏览地位管理器文档,问题很快变得清晰,为了勾销回调设置,咱们简略地调用removeUpdates办法就行了。在咱们的例子,因为咱们注册更新是在onCreate办法,显然要登记的中央在onDestroy办法。 public class LeaksActivity extends Activity implements LocationListener { private LocationManager locationManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_leaks); locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, TimeUnit.MINUTES.toMillis(5), 100, this); } @Override protected void onDestroy() { locationManager.removeUpdates(this); super.onDestroy(); } // Listener implementation omitted}从新构建程序并执行与上述雷同的内存剖析,无论旋转多少次设施,应该都不会导致Activity透露。 ...

December 8, 2021 · 3 min · jiezi

关于android:安卓逆向-33

1. 创立Native C++工程创立c++ 的 ,带 so 刚创立,会同步 gradle 。AndroidStudio界面介绍工具栏默认在左边,切显示不全面。将工具栏移到右边,并显示全面。我的项目应用jdk的配置app的编译 以下几种形式都能够编译Build -> Make ProjectMake Module 'HashMapTest.app'Build -> Build Bundle -> Build APKRun appClean ProjectRebuild Project 当我的项目遇到问题,能够清理我的项目,并Rebuild Project

December 8, 2021 · 1 min · jiezi

关于android:Android开发使用Kotlin协程自定义注解Retrofit的网络框架

对我的项目的根本介绍1.整个框架次要是给MVVM框架应用的,本人写完interface接口后,通过自定义的注解就能主动生成接口办法2.用Kotlin的Flow去代替Rxjava,因为我发现RxJava性能很弱小,然而大家都只是在Http层面应用了一下,既然要用Kotlin外面就曾经有Flow,那我还不如少增加一个库3.通过jetpack的Room数据库实现网络申请的存储,缓存策略也用过注解去实现。4.发动的网络申请是与宿主生命周期绑定的,在网络申请回来之前,宿主曾经销毁的话,网络申请也会中断的根本应用办法1.先定义接口类和Retrofit一样,须要定义一个接口类 其中@AutoApi,@AutoFlowApi,@NetStrategy是自定义的注解,前面会介绍到。 2.要先编译,会在你的接口类的文件夹下生成一个xxxRepository.class这是通过注解主动生成的文件,应用了kotlinpoet 并且这里apiService就是通过Retrofit拿到的接口代理 3.在viewmodel拿到对应Repository类的办法 4.在对应中央通过viewmodel调用调用接口,传入对应参数 在适合的中央察看 Retrofit的封装下面说到在Repository类的apiService就是通过Retrofit拿到的接口代理类。 所以先进去看看apiService好了 能够看到apiService是BaseRepository的变量 而咱们生成的Repository都是继承BaseRepository的 当咱们调ConfigRepository类中的办法时候,就会将ConfigRepository传入findNeedType 而findNeedType办法就会将ConfigRepository对应的ConfigService失去并且返回进来 所以apiService就相当于这样,如同有点Retrofit的create办法的样子了 var apiService: T = HttpProvider.defaultCreate(ConfigService) as Class<out T>)咱们持续进入 HttpProvider.defaultCreate 能够看到newRetrofit(),并且传入了一个HttpConfig,看到这个名字就晓得这是Http的配置 接着是newCreate(),接管了咱们的接口service类 首先看看newRetrofit办法这几行代码就是创立了一个Retrofit对象并且保存起来,最初返回进来。 然而他是怎么和HttpConfig分割起来的呢? 咱们能够看到这里将生成的Retrofit.Builder()传给了HttpConfig的办法build里,咱们进去看一看 能够看到这里就是咱们再相熟不过的Retrofit的配置环节 所以通过newRetrofit办法,咱们就将Retrofit对象配置好并且拿到Retrofit对象,还保存起来不便下次复用 在看看newCreate()办法这是个扩大函数,是Retrofit的扩大函数 将传入的ConfigService通过Retrofit.create()生成代理类,并且保存起来复用 注解的介绍1.AutoApi咱们从最简略的AutoApi做引子,开始介绍整个注解框架 只有你的接口类办法中应用了这个注解,就会生成suspend办法,十分的简略 接着咱们来看看他是怎么实现的 看一下这个注解是怎么定义的注解是反对有默认值的,因为kotlin的办法是能够在变量中间接赋初值的,这样调用就不必传值了,所以这里也做一个反对,让调用时候更加简洁 生成的流程 代码的剖析这里还是用ConfigService来剖析 1.首先流程图,咱们会遍历出应用这个注解的类,此时咱们就曾经拿到了ConfigService这个元素的所有信息了。2.接着咱们会对ConfigService将包装起来,将他存在RepositoryClass类中。RepositoryClass这个类会保留ConfigService的类名,包名,类型和所有办法等 3.会将ConfigService里的办法包装成AutoMethod(不同的注解会有不同的类型),存入RepositoryClass的method变量中 通过上述操作后,repositoryMap就存在所有应用过AutoAPi注解的类了,再将他做遍历,传入Repository类的生成器RepositoryClassBuilder 这个如果不增加startFuncBuild办法的话,这段代码就只会生成 open class ConfigRepository : BaseRepository<ConfigService>() { } 再来看看startFunBuild,依据你以后类中的办法应用的注解去抉择对应的办法处理器 ...

December 8, 2021 · 1 min · jiezi

关于android:这RecyclerView的特效看了直呼牛批

/  前言   /还是老套路,先来看看实现的成果! 在写这个成果之前,须要相熟Rv的回收复用机制,因为实现这个成果,须要自定义LayoutManager()… 家喻户晓,RecyclerView 是一个可滑动的View,那么他的回收/复用入口肯定是在onTouchEvent()事件中 滑动过程中响应的是MotionEvent.ACTION_MOVE事件,所以间接来这里找找看!! /   缓存机制   /onTouchEvent()入口#RecyclerView.java @Overridepublic boolean onTouchEvent(MotionEvent e) {    final int action = e.getActionMasked();     switch (action) {            ........................................               ........只展现代码思路,细节请自行查看........               ........................................            case MotionEvent.ACTION_MOVE: {            if (mScrollState == SCROLL_STATE_DRAGGING) {                    mLastTouchX = x - mScrollOffset[0];                    mLastTouchY = y - mScrollOffset[1];                    // 要害代码1                    if (scrollByInternal(                            canScrollHorizontally ? dx : 0,                            canScrollVertically ? dy : 0,                            vtev)) {                        getParent().requestDisallowInterceptTouchEvent(true);                    }                    if (mGapWorker != null && (dx != 0 || dy != 0)) {                        mGapWorker.postFromTraversal(this, dx, dy);                    }                }            }            break;    }} 接着找scrollByInternal(int x, int y, MotionEvent ev)办法 #RecyclerView.javaboolean scrollByInternal(int x, int y, MotionEvent ev) {     if (mAdapter != null) {            ........................................               ........只展现代码思路,细节请自行查看........               ........................................            if (x != 0) {                // 要害代码2 去到 LinearLayoutManager 执行fill办法                consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);                unconsumedX = x - consumedX;            }            if (y != 0) {                // 要害代码2 去到LinearLayoutManager 执行fill办法                consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);                unconsumedY = y - consumedY;            }        }        ....} 当初走到了mLayout.scrollHorizontallyBy(x, mRecycler, mState); 接着去LinearLayoutManager() 中去找scrollHorizontallyBy() 办法 #LinearLayoutManager.java    @Override    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,                                  RecyclerView.State state) {        if (mOrientation == HORIZONTAL) {            return 0;        }        // 要害代码3        return scrollBy(dy, recycler, state);    } scrollBy()-> #LinearLayoutManager.java int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {       ........................................     ........只展现代码思路,细节请自行查看........     ........................................     final int consumed = mLayoutState.mScrollingOffset                // 要害代码4                + fill(recycler, mLayoutState, state, false);} 接着找到fill()办法 #LinearLayoutManager.javaint fill(RecyclerView.Recycler recycler, LayoutState layoutState,             RecyclerView.State state, boolean stopOnFocusable) {        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {            // 要害代码19 缓存ViewHolder            recycleByLayoutState(recycler, layoutState);        }        // 循环调用        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {           // 要害代码5 [用来4级复用]            layoutChunk(recycler, state, layoutState, layoutChunkResult);                  ........................................                    ........只展现代码思路,细节请自行查看........                  ........................................        }    } 看到这里只须要记住以下两点即可: recycleByLayoutState(recycler, layoutState); 缓存ViewHolderlayoutChunk(recycler, state, layoutState, layoutChunkResult); 四级复用有人可能会问,这里为什么是四级?不是说的三级嘛? 其实三级和四级都无所谓,知识点是不会变的,只是层级越多,了解就越粗浅,越细罢了 间接进入到缓存的代码: #LinearLayoutManager.java private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {            // 要害代码21 缓存底部            recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);        } else {            // 要害代码20 缓存头部            recycleViewsFromStart(recycler, layoutState.mScrollingOffset);        }    } 这里如果是向下滑动,就会缓存头部那么就会执行到recycleViewsFromStart() 如果是向上滑动,就会缓存尾部那么就会执行到recycleViewsFromEnd() recycleViewsFromStart() 和 recycleViewsFromEnd() 轻易点开一个看看,外面代码都差不多一样. #LinearLayoutManager.java private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {        if (mShouldReverseLayout) {            for (int i = childCount - 1; i >= 0; i--) {            ...                    // 要害代码22                    recycleChildren(recycler, childCount - 1, i);                    return;            }        } else {            for (int i = 0; i < childCount; i++) {            ...                    // 要害代码23                    recycleChildren(recycler, 0, i);                    return;            }        }    }这里无论走哪一个if() 都会走到recycleChildren()办法 #LinearLayoutManager.javaprivate void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {        if (startIndex == endIndex) {            return;        }        if (endIndex > startIndex) {            for (int i = endIndex - 1; i >= startIndex; i--) {                // 移除View  要害代码23 [执行到RecyclerView.removeAndRecycleViewAt()]                removeAndRecycleViewAt(i, recycler);            }        } else {            for (int i = startIndex; i > endIndex; i--) {                removeAndRecycleViewAt(i, recycler);            }        }    } 接着这里会执行到RecyclerView的removeAndRecycleViewAt()办法 #RecyclerView.java        // 要害代码24        public void removeAndRecycleViewAt(int index, Recycler recycler) {            final View view = getChildAt(index);            removeViewAt(index);            // 要害代码25            recycler.recycleView(view);        } 持续往下执行 #RecyclerView.java public void recycleView(View view) {            .......            ViewHolder holder = getChildViewHolderInt(view);            // 缓存            recycleViewHolderInternal(holder);        } 接着继续执行recycleViewHolderInternal() #RecyclerView.javavoid recycleViewHolderInternal(ViewHolder holder) {            ........................................            ........只展现代码思路,细节请自行查看........            ........................................             boolean cached = false;            if (forceRecycle || holder.isRecyclable()) {                // mViewCacheMax = 缓存的最大值                 // mViewCacheMax = 2                // 如果viewHolder是有效、未被移除、未被标记的                if (mViewCacheMax > 0                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID                        | ViewHolder.FLAG_REMOVED                        | ViewHolder.FLAG_UPDATE                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {                    int cachedViewSize = mCachedViews.size();                    // 要害代码24                    // mViewCacheMax = 2                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {                        // 如果viewholder存满2个则移除第0个地位                         // 保障mCachedViews 最多能缓存2个ViewHolder                        recycleCachedViewAt(0);                        cachedViewSize--;                    }                    ....                    // 保留ViewHolder数据 [mCachedViews数据不会超过2个]                    mCachedViews.add(targetCacheIndex, holder);                    cached = true;                }                 if (!cached) {                    // 当ViewHolder不扭转时候(只有一个ViewHolder) 就会间接存到缓存池中                    addViewHolderToRecycledViewPool(holder, true);                    recycled = true;                }                ........................................                   ........只展现代码思路,细节请自行查看........                ........................................        } 通过 要害代码24 可知,mCachedViews 最多能保留2个ViewHolder 如果第三个ViewHolder降临的时候,就会先删除掉第0个,而后在 mCachedViews.add(targetCacheIndex, holder); 而后再来看看 recycleCachedViewAt(0)的细节! #RecyclerView.java    void recycleCachedViewAt(int cachedViewIndex) {            ...            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);            // 要害代码25            // 增加到ViewPool到缓存外面取            addViewHolderToRecycledViewPool(viewHolder, true);            // 将第0个ViewHolder移除            mCachedViews.remove(cachedViewIndex);        } 继续执行到 addViewHolderToRecycledViewPool()办法 将mCachedViews.get(0)中的ViewHolder获取进去,增加到缓存池中,并删除 #RecyclerView.javavoid addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {            .....            // 向缓存池中 保留ViewHolder 要害代码28            getRecycledViewPool().putRecycledView(holder);        } 点进来看看putRecycledView()办法 #RecyclerView.java// SparseArray 相似与 HashMap<int,ScrapData>// 特点: key雷同会保留最初一个,//      会依据key的int值排序(从小到大)SparseArray<ScrapData> mScrap = new SparseArray<>(); public void putRecycledView(ViewHolder scrap) {       // 获取ViewHolder布局类型      final int viewType = scrap.getItemViewType();      // 依据布局类型来获取ViewHolder       final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;       // 判断缓存池的大小       // mScrap.get(viewType).mMaxScrap 默认为 5       // 同一种ViewType 只保留5个ViewHolder        if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {           return;        }       // 清空ViewHolder记录        scrap.resetInternal();        //add        scrapHeap.add(scrap);} // 清空ViewHolder记录 void resetInternal() {            mFlags = 0;            mPosition = NO_POSITION;            mOldPosition = NO_POSITION;            mItemId = NO_ID;            mPreLayoutPosition = NO_POSITION;            mIsRecyclableCount = 0;            mShadowedHolder = null;            mShadowingHolder = null;            clearPayload();            mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;            mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;            clearNestedRecyclerViewIfNotNested(this);        }// 依据不同viewType 获取ViewHolder private ScrapData getScrapDataForType(int viewType) {            ScrapData scrapData = mScrap.get(viewType);            if (scrapData == null) {                scrapData = new ScrapData();                mScrap.put(viewType, scrapData);            }            return scrapData;        } 能够看出,缓存池,中最多保留5个同一类型的ViewHolder,并且ViewHolder是空的ViewHolder, 而且缓存池中保留的都是mCachedViews移除的数据!! [](https://upload-images.jianshu...) 小结mCachedViews 保留行将来到屏幕外的2个ViewHolder mRecyclerPool 缓存池中:同一种ItemViewType类型可能默认最多保留5个空数据的ViewHolder. ...

December 8, 2021 · 1 min · jiezi

关于android:android多线程AsyncTaskyi一

 明天剖析android的异步线程类HandlerThread与IntentService,它们都是android零碎独有的线程类,而android中还有另一个比拟重要的异步线程类AsyncTask。本文咱们就来剖析AsyncTask。 AsyncTask的惯例应用剖析以及案例实现AsyncTask在不同android版本的下的差别AsyncTask的工作原理流程一、AsyncTask的惯例应用剖析以及案例实现 AsyncTask是一种轻量级的异步工作类,它能够在线程池中执行后台任务,而后会把执行的进度和最终后果传递给主线程并更新UI。AsyncTask自身是一个抽象类它提供了Params、Progress、Result 三个泛型参数,其类申明如下: public abstract class AsyncTask<Params, Progress, Result> { 由类申明能够看出AsyncTask抽象类的确定义了三种泛型类型 Params,Progress和Result,它们别离含意如下: Params :启动工作执行的输出参数,如HTTP申请的URLProgress : 后台任务执行的百分比Result :后盾执行工作最终返回的后果类型 如果AsyncTask不须要传递具体参数,那么这三个泛型参数能够应用Void代替。咱们当初创立一个类继承自AsyncTask如下: package com.zejian.handlerlooper;import android.graphics.Bitmap;import android.os.AsyncTask;/** * Created by zejian * Time 16/9/4. * Description: */public class DownLoadAsyncTask extends AsyncTask<String,Integer,Bitmap> { /** * onPreExecute是能够选择性覆写的办法 * 在主线程中执行,在异步工作执行之前,该办法将会被调用 * 个别用来在执行后台任务前对UI做一些标记和筹备工作, * 如在界面上显示一个进度条。 */ @Override protected void onPreExecute() { super.onPreExecute(); } /** * 形象办法必须覆写,执行异步工作的办法 * @param params * @return */ @Override protected Bitmap doInBackground(String... params) { return null; } /** * onProgressUpdate是能够选择性覆写的办法 * 在主线程中执行,当后台任务的执行进度产生扭转时, * 当然咱们必须在doInBackground办法中调用publishProgress() * 来设置进度变动的值 * @param values */ @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } /** * onPostExecute是能够选择性覆写的办法 * 在主线程中执行,在异步工作执行实现后,此办法会被调用 * 个别用于更新UI或其余必须在主线程执行的操作,传递参数bitmap为 * doInBackground办法中的返回值 * @param bitmap */ @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); } /** * onCancelled是能够选择性覆写的办法 * 在主线程中,当异步工作被勾销时,该办法将被调用, * 要留神的是这个时onPostExecute将不会被执行 */ @Override protected void onCancelled() { super.onCancelled(); }} 如代码所示,咱们创立一个继承自AsyncTask的异步线程类,在泛型参数方面,传递String类型(Url) , Integer类型(显示进度),Bitmap类型作为返回值。接着重写了形象办法doInBackground(),以及覆写了onPreExecute()、onProgressUpdate()、onPostExecute()、onCancelled()等办法,它们的次要含意如下: ...

December 8, 2021 · 5 min · jiezi

关于android:程序员必知的Android-代码规范

前言尽管咱们我的项目的代码工夫并不长,也没通过太多人手,但代码的规范性仍然堪忧,目前存在较多的比拟自在的「代码标准」,这十分不利于我的项目的保护,代码可读性也不够高, 此外,客户端和后端的研发模式也齐全不同,后端研发根本都是基于 SOA 思维的,通常一个子系统 3 集体一起保护就曾经是很充沛的人力了,更多时候就是 1 个主力 + 1 个 backup 的人力配置。 而客户端却齐全不同,大家的代码都是互相穿插的,一个模块的代码可能要经验数十人的践踏,所以造成一个统一的开发标准火烧眉毛。 为什么须要统一的代码标准?外围还是缩小沟通老本,晋升咱们的 Code Review 效率,让咱们的代码更加易于保护。此外,一个统一的代码标准能够造成更少的 bug,也就意味着更节省时间和金钱。 当然,标准是约定的,本系列文字全是笔者多年来博采众长,积攒而成,所以有任何不同意见,欢送评论拍砖。 1. Android 的工具标准工欲善其事,必先利其器。 因为 Android 根本都基于 Android Studio 进行开发,所以工具标准全副以 Android Studio 为前提。 必须应用最新的稳固版本的 Android Studio 进行开发;编码格局必须对立为 UTF-8;删除多余的 import,缩小正告呈现,可利用 AS 的 Optimize Imports(Settings -> Keymap -> Optimize Imports)快捷键,设置本人的爱好。 编辑完 .java、.kt、.xml 等文件后必须格式化(须要在设置好以下几点的前提下)Reformat Code 的必要性,肯定须要保障 IDE 配置统一为前提,尽可能贴切于 Android Studio 默认。强烈建议对于比拟长的老代码部分格式化,不全局格式化每行字符数不得超过 160 字符,设置 Editor -> Code Style 全副设置为单门路援用,kotlinx.android.synthetic.main 除外。 ...

December 8, 2021 · 6 min · jiezi

关于android:android绘图-androidgraphics包

android:绘图 View:组件,了解为画布 Drawable:所有可见对象的形容,了解为:素材类 Bitmap:图片类 Canvas:画笔 Paint:画笔款式与色彩、特效的汇合 近期很多网友对Android用户界面的设计示意很感兴趣,对于Android UI开发自绘控件和游戏制作而言把握好绘图根底是必不可少的。本次专题分10节来讲述,无关OpenGL ES相干的可能将放到当前再走漏。本次次要波及以下四个包的相干内容: android.content.res 资源类 android.graphics 底层图形类 android.view 显示类 android.widget 控件类 一、android.content.res.Resources 对于Android平台的资源类android.content.res.Resources可能很多网友比拟生疏,一起来看看SDK上是怎么介绍的吧,Contains classes for accessing application resources, such as raw asset files, colors, drawables, media or other other files in the package, plus important device configuration details (orientation, input types, etc.) that affect how the application may behave.平时用到的二进制源文件raw、色彩colors、图形drawables和多媒体文件media的相干资源均通过该类来治理。 int getColor(int id) 对应res/values/colors.xml Drawable getDrawable(int id) 对应res/drawable/ XmlResourceParser getLayout(int id) 对应res/layout/ String getString(int id) 和CharSequence getText(int id) 对应res/values/strings.xml InputStream openRawResource(int id) 对应res/raw/ void parseBundleExtra (String tagName, AttributeSet attrs, Bundle outBundle) 对应res/xml/ ...

December 8, 2021 · 6 min · jiezi

关于android:Android模仿微博的LazyFragment懒加载

前言本文会从头开始一步一步带你去写一个LazyFragment,依据写的过程中一步一步记录,你也能够本人试一试,跟着一起写写。最初也依据遇到的问题去欠缺了,网上搜的都是不欠缺的,还是本人写一个吧! 懒加载是在加载啥?这个问题显得很愚昧。然而想一下,懒加载到底是加载数据和视图,还是数据呢??(一开始我也想过这个问题。。。)当然是数据啦,你怎么能阻止视图的加载呢!你是无奈阻止Fragment的生命周期函数的执行的,所以只能让以后显示的Fragment加载数据,不显示不加载数据 怎么能晓得以后页面是显示的这个Fragment呢?在源码中有两个办法能够应用 在viewpager和fragment模式应用的public void setUserVisibleHint(boolean isVisibleToUser) {}和 在add+show+hide模式应用public void onHiddenChanged(boolean hidden) {}那么就先简略的试试看这两个办法好了 我写了一个Viewpager+Fragment模式的,缓存数量offscreenPageLimit为1。 当关上app时 咱们能够看到主页BadHomeFragment的setUserVisibleHint()办法执行了两次!先返回false不可见,再返回true可见。而且不论在哪个Fragment中setUserVisibleHint()都跑得比生命周期快 当切换到第二个Fragment 切回去主页Fragment 当曾经跑过生命流程的Fragment再次显示时,只会走setUserVisibleHint()(先疏忽onHiddenChange办法) 这样的话,咱们大略能够想到,我依据setUserVisibleHint()的可见状态去加载数据不就完事了吗?所以咱们先这样写 在子Fragment中的loadData中调用接口获取数据,而后给控件赋值 后果就是!报错!!说你的控件为null,我都可见了为啥为null呢。因为在进入app时setUserVisibleHint在生命周期后面,Fragment都没有加载布局,当然为null啦 那我加个变量判断一下布局有没有加载好不就行了? 后果呢?主页Fragment没有执行onLoadData办法,点击TwoFragment却执行了,为什么呢,因为setUserVisibleHint更早啊,而TwoFragment曾经初始化过了,点击过来的时候能力失常加载数据。 所以咱们必须在生命周期中再散发一次加载数据的事件,把加载数据提成一个办法 再来看看从开启app到点击到第三个Fragment的日志,大家都失常的加载了数据,如同还挺失常的 那再试试从第一个页面跳转到最初一个页面吧,会发现两头有些页面莫名的就调用了StopLoadData函数???这显然有问题的,看来不能只是简略的用可见状态来散发了 咱们在好好想一下,LoadData是Fragment可见的时候调用, StopLoadData是Fragment不可见的时候调用。其实这样说并不齐全,应该是从不可见状态到可见状态的时候加载数据,可见状态到不可见状态的时候进行。所以咱们得用一个变量记录一下可见状态 能够看到在切换方面,散发的事件曾经是失常的了 留神要在onStop把`currentVisibleStatus状态重置! 以上就是咱们网上常见的懒加载了,然而会有两个问题 在跳转到其余页面回来时,会怎么样?并不会怎么,只是回来的时候不会去散发加载数据的事件而已啧 所以我加上这样一段代码,用一个变量去判断,不须要能够不写,然而我想想你跳转了别的页面,回来数据Fragment数据应该会变,所以散发一下 在子Fragment中又有一个viewpager+Fragment又会怎么样?这里去除掉了其余log,为了看起来分明点 我给TowFragment加了一个viewpager,外面有TowFragment1和2 进入App能够看到,爹都还没显示,儿子先加载了数据? 而且在之后的切换底部Tab,TowFragment外面的两个儿子都金石为开,只有刚关上App时加载了,连进行加载的办法都没调用到,基本就没有事件散发到儿子那里去 所以咱们必须要再做一些依据父亲和儿子的状态再做一些判断 先解决第一个问题,父Fragment还没显示,子Fragment就加载了数据。这个问题很简略,咱们只须要在散发加载数据的事件时,判断一下是否有父Fragment,且是否可见。就能够了 依据Fragment源码中的这个办法,能够解决下面的问题 比方,父Fragment不可见时间接退出办法,不准散发 然而还有一个问题存在,就是不管怎么切换,子Fragment都不会散发加载或进行加载数据的事件。这是为啥?? 从一开始咱们能够看到,在进入HomeFragment时,子Fragment也会别离调用两次setUserVisibleHint办法,在进入前就把本人的可见状态置为true。到底前面的散发事件有效了。 所以我决定在进入他的父Fragment时,再去调用一次散发事件,真正的加载子Fragment的数据,比方 能够看见切换都是失常的 最初明天的文章就到这里,感谢您的浏览,有问题能够在评论区留言探讨,期待与大家共同进步。喜爱的话不要忘了三连。大家的反对和认可,是我分享的最大能源。

December 8, 2021 · 1 min · jiezi

关于android:分享一款移动开发者必备神器

概述 这次次要是分享一款手机投屏工具,大家在开发过程中,特地是挪动端开发的童鞋们,真机调试程序是必不可少的,然而每编译运行一次程序都要低着头玩弄着调试机,每天都这么工作、与日俱增的,这对程序猿来说又是一个致命的残害啊(什么脖子酸、颈椎病等各种职业病啊),所以如果能将手机投屏到电脑屏幕上,那么就不必低着头调试程序,开发效率又进步,两全其美啊。 工具 在市面上的确是有很多投屏软件,但应用起来超级麻烦,还各种广告,太吃力了,这次分享的是一款收费的、在GitHub开源的工具,叫做scrcpy ,收费又好用,所以十分有必要安利下,地址 https://github.com/Genymobile... 用法 这工具反对Windows、Linux、macOS装置,对于装置的步骤可依据本人电脑的零碎依照官网装置步骤的命令行执行就能够。 最初就是这工具还反对WiFi形式投屏,电脑通过adb绑定手机端口5555,连贯手机ip,执行scrcpy命令即可辞别数据线连贯。

December 7, 2021 · 1 min · jiezi

关于android:手摸手教你做一个分段式进度条组件

Eason最近遇到一个需要,须要去展现分段式的进度条,为了给这个进度条想要的外观和感觉,在构建用户界面 (UI) 时,大家通常会依赖 SDK 提供的可用工具并尝试通过调整SDK来适配以后这个UI需要;但悲伤的是,大多数状况下它根本不合乎咱们的预期。所以Eason决定本人绘制它。 创立自定义视图在 Android 中要绘制自定义动图,大家须要应用Paint并依据Path对象疏导绘制到画布上。 咱们能够间接在画布Canvas中操作下面的所有对象View。更具体地说,所有图形的绘制都产生在onDraw()回调中。 class SegmentedProgressBar @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { override fun onDraw(canvas: Canvas) { // Draw something onto the canvas } }回到进度条,让咱们从开始对整个进度条的实现进行合成。 整体思路是:先绘制有一组显示不同角度的四边形,它们彼此间隔开并且具备没有空间的填充状态。最初,咱们有一个波浪动画与其填充进度同步。 在尝试满足上述所有这些要求之前,咱们能够从一个更简略的版本开始。不过不必放心。咱们会从根底的开始并逐渐深入浅出的! 绘制单段进度条第一步是绘制其最根本的版本:单段进度条。 临时抛开角度、间距和动画等简单元素。这个自定义动画整体来说只须要绘制一个矩形。咱们从调配 aPath和一个Paint对象开始。 private val segmentPath: Path = Path()private val segmentPaint: Paint = Paint( Paint.ANTI_ALIAS_FLAG )尽量不在onDraw()办法外部调配对象。这两个Path和Paint对象必须在其范畴之内创立。在View很多时候调用这个onDraw回调时将导致你内存逐步缩小。编译器中的 lint 音讯也会正告大家不要这样做。 要实现绘图局部,咱们可能要抉择Path的drawRect()办法。因为咱们将在接下来的步骤中绘制更简单的形态,所以更偏向于逐点绘制。 moveTo():将画笔搁置到特定坐标。 lineTo(): 在两个坐标之间画一条线。 这两种办法都承受Float值作为参数。 ...

December 7, 2021 · 7 min · jiezi

关于android:想进大厂这是你绕不过的门槛

你感觉这个门槛是什么? 不绕关子,就是数据结构与算法。 为什么是数据结构与算法?第一,数据结构与算法是科班程序员的必修课程,包含培训机构也有相干课程。 第二,程序员面试必考查数据结构与算法,尤其是大厂,因为算法和数据结构最能体现一个人的基本功,基本功扎实的人,无论是做工程还是去做算法,都不会差到哪里去。 第三,算法框架千千万,数据结构是最底层的撑持,你能够只用现成的框架和算法,但你不能不懂其中的基本原理。 如果你只停留在“应用”层面,连根本的算法和数据结构都不会,基本上属于比拟底层的程序员,这就意味着你只能拿比拟低的薪资以及只领有比拟差的竞争力,在技术高速更迭的互联网行业,注定是要被社会淘汰的。 不懂数据结构与算法仍旧能够找到工作?的确能够,基本没有那么多高级开发以及更高的岗位频繁用到数据结构和算法,小公司更不用说,只须要性能上线能用就完事。 但就如题目所说,想进大厂,数据结构与算法就是你绕不过的门槛,必定会有人反驳我,说“我不进大厂也能够好好的”,但咱们反诘一下,为什么大厂面试必问数据结构与算法? 大厂招聘以及造就的都是高尖人才,他们当然不容许本人的共事在交换技术的时候连“链表”、“堆”、“工夫复杂度”是什么都不晓得。 往大了说,互联网时不时就呈现一波裁员潮,“35岁危机”也始终存在,就算进不成大厂或者不进大厂也应该为本人的职业倒退做下思考,数据结构与算法就是程序员成长与倒退的基石。 光说不练假把式我这整顿了一份《2021年最新版数据结构与算法面试手册》,包含: JavaC++Golang相干的数据结构与算法题及解析,具体内容包含: 1.Java1.1 哈希 Java中的HashMap的工作原理是什么?什么是Hashmap?如何结构一致性哈希算法hashCode() 和equals() 办法的重要性体现在什么中央?Object作为HashMap的key的话,对Object有什么要求吗?hashset 存的数是有序的吗?1.2 二叉树 求二叉树的最大深度求二叉树的最小深度求二叉树中节点的个数求二叉树中叶子节点的个数求二叉树中第k层节点的个数判断二叉树是否是均衡二叉树判断二叉树是否是齐全二叉树两个二叉树是否完全相同两个二叉树是否互为镜像翻转二叉树or镜像二叉树求两个二叉树的最低公共先人节点二叉树的前序遍历二叉树的中序遍历二叉树的后序遍历前序遍历和后序遍历结构二叉树在二叉树中插入节点输出一个二叉树和一个整数,打印出二叉树中节点值的和等于输出整数所有的门路二叉树的搜寻区间二叉树的档次遍历二叉树内两个节点的最长距离不同的二叉树判断二叉树是否是非法的二叉查找树(BST)1.3 链表 谈一谈,bucket如果⽤链表存储,它的毛病是什么?有一个链表,奇数位升序偶数位降序,如何将链表变成升序?如何反转单链表当初有一个单向链表,谈一谈,如何判断链表中是否呈现了环随机链表的复制1.4 数组 写一个算法,能够将一个二维数组顺时针旋转90度一个数组,除一个元素外其它都是两两相等,求那个元素?找出数组中和为S的一对组合,找出一组就行求一个数组中间断子向量的最大和寻找一数组中前K个最大的数1.5 排序 用Java写一·个冒泡排序?排序都有哪几种办法?请列举进去归并排序的原理是什么?堆排序的原理是什么?如何失去一个数据流中的中位数?你晓得哪些排序算法,这些算法的工夫复杂度别离是多少,解释一下快排?1.6 堆与栈 内存中的栈(stack)、堆(heap) 和动态区(static area) 的用法heap和stack有什么区别最小的k个数滑动窗口最大值丑数前K个高频元素无效的括号最小栈柱状图中最大的矩形1.7 高级算法 LRU算法的实现原理为什么要设计后缀表达式,有什么益处?设计一个算法,用来压缩一段URLid全局惟一且自增,如何实现?最初一个单词的长度1.8 动静布局 斐波那契数列不同门路爬楼梯零钱兑换打家劫舍编辑间隔2.C++2.1 数组 Array&List, 数组和链表的区别一组有序数(从小到大排列),有负有正,找出绝对值最小值数组中反复的数字一个长度为N的整形数组,数组中每个元素的取值范畴是[0,n-1],判断该数组否有反复的数,请说一下你的思路并手写代码2.2 排序 手写一下快排代码介绍一下各种排序算法及其复杂度稳固排序有哪几种?问求第k大的数的办法以及各自的复杂度是怎么的?当有雷同元素时,还能够应用什么不同的办法求第k大的元素?海量数据如何去取最大的k个快排的工夫复杂度最差是多少?什么时候工夫最差什么是快排算法;以及什么是稳定性排序,快排是稳定性的吗;快排算法最差状况推导公式2.3 动静布局 手写代码:最长公共间断子序列手写代码:求一个字符串最长回文子串手写代码:求最大子序和2.4 链表 手写代码:如何合并两个有序链表手写代码:反转链表判断一个链表是否为回文链表,说出思路并手写代码什么是单链表,如何判断两个单向链表是否相交2.5 高级算法 什么是LRU缓存洗牌算法2.6 字符串 给你一个字符串,找出第一个不反复的字符,如“abbbabcd”,则第一个不反复就是c最长公共前缀无效的字母异位词3.Golang3.1 递归&回溯 手写代码:两数相加手写代码:括号生成手写代码:验证二叉搜寻树二叉树的最大深度二叉树的最近公共先人全排列3.2 并查集 手写代码:省份数量手写代码:岛屿数量手写代码:最长间断数列3.3 字符串 手写代码:转换成小写字母手写代码:最长公共前缀手写代码:无效的字母异位词最初程序员中有一个说法:不会数据结构与算法、网络、操作系统的都是伪程序员,你是吗?

December 7, 2021 · 1 min · jiezi

关于android:个推11周年庆最长90天消息推送VIP权益免费领

12月7日,个推行将迎来11岁生日 感恩每1份信赖 致谢每1次抉择 值个推十一周年之际 为回馈宽广开发者 个推重磅推出【创业者搀扶打算】 ——个推音讯推送VIP权利免费送! 厂商推送策略、后效剖析、行业大盘剖析等 数十款热门VIP性能通通收费用 最长可达90天! 满足肯定条件 还能继续收费应用推送VIP权利 (详情征询商务) 惊喜福利已退场! 还在犹豫什么 抓紧时间上车!

December 7, 2021 · 1 min · jiezi

关于android:Android入门教程-WebView-一

WebView 应用禁止 webview 解决长按事件 设置 OnLongClickListener 把长按事件生产掉。 web.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { return true; }});扭转字体大小 字体大小的枚举。 public static enum TextSize { SMALLEST(50), SMALLER(75), NORMAL(100), LARGER(125), LARGEST(150); int value; private TextSize(int var3) { this.value = var3; } }获取 WebSettings,而后设置字体大小。 WebSettings st = wv.getSettings();st.setTextSize(WebSettings.TextSize.LARGER);显示 html 文本示例:示例中用到的 html 文本与 TextView 显示 Html 雷同。 private static final String T1 = "<p>Tell me the <font color=\"red\">color</font></p>";private static final String H1 = "<p>Tell me the <text style=\"color: blue; font-weight: bold;\">color</text>.</p>";private static final String H1_FIX = "<p>Tell me the <b><font color=\"blue\">color</font></b>.</p>"; wb1.loadData(T1, "text/html", "UTF-8"); wb2.loadData(H1, "text/html", "UTF-8"); wb3.loadData(H1_FIX, "text/html", "UTF-8");layout 中搁置 WebView ...

December 7, 2021 · 3 min · jiezi

关于android:Kotlin开发中的一些Tips

作用域函数抉择目前有let、run、with、apply 和 also五个作用域函数。 官网文档有张表来阐明它们之间的区别: 总结一下有几点区别: 1、apply和also返回上下文对象。 2、let、run 和with返回lambda 后果。 3、let、also援用对象是it,其余是this。 1.let和run是我日常应用最多的两个,它们之间很相似。 private var textView: TextView? = nulltextView?.let { it.text = "Kotlin" it.textSize = 14f}textView?.run { text = "Kotlin" textSize = 14f}相比拟来说应用run显得比拟简洁,但let的劣势在于能够将it重命名,进步代码的可读性,也能够防止作用域函数嵌套时导致混同上下文对象的状况。 2.对于可空对象,应用let比拟不便。对于非空对象能够应用with。 3.apply和also也十分类似,文档给出的倡议是如果是对象配置操作应用apply,额定的解决应用also。例如: val numberList = mutableListOf<Double>()numberList.also { println("Populating the list") } .apply { add(2.71) add(3.14) add(1.0) } .also { println("Sorting the list") } .sort()简略说就是合乎单词的含意应用,进步代码可读性。 总的来说,这几种函数有许多重叠的局部,因而能够依据开发中的具体情况来应用。以上仅做参考。 Sequence咱们常常会应用到kotlin的汇合操作符,比方 map 和 filter 等。 list.map { it * 2}.filter { it % 3 == 0}老规矩,看一下反编译后的代码: ...

December 7, 2021 · 2 min · jiezi

关于android:Kotlin开发中的一些Tips

作用域函数抉择目前有let、run、with、apply 和 also五个作用域函数。 官网文档有张表来阐明它们之间的区别:   总结一下有几点区别: 1、apply和also返回上下文对象。 2、let、run 和with返回lambda 后果。 3、let、also援用对象是it ,其余是this。 1.let和run是我日常应用最多的两个,它们之间很相似。 private var textView: TextView? = nulltextView?.let { it.text = "Kotlin" it.textSize = 14f}textView?.run { text = "Kotlin" textSize = 14f}相比拟来说应用run显得比拟简洁,但let的劣势在于能够将it重命名,进步代码的可读性,也能够防止作用域函数嵌套时导致混同上下文对象的状况。 2.对于可空对象,应用let比拟不便。对于非空对象能够应用with。 3.apply和also也十分类似,文档给出的倡议是如果是对象配置操作应用apply,额定的解决应用also。例如: val numberList = mutableListOf<Double>()numberList.also { println("Populating the list") } .apply { add(2.71) add(3.14) add(1.0) } .also { println("Sorting the list") } .sort()简略说就是合乎单词的含意应用,进步代码可读性。 总的来说,这几种函数有许多重叠的局部,因而能够依据开发中的具体情况来应用。以上仅做参考。 Sequence咱们常常会应用到kotlin的汇合操作符,比方 map 和 filter 等。 list.map { it * 2}.filter { it % 3 == 0}老规矩,看一下反编译后的代码:  ...

December 7, 2021 · 2 min · jiezi

关于android:精彩回顾-2021-Android-开发者峰会

作者 / 产品治理副总裁 Sagar Kamdar 作为开发者,大家构建的利用体验以超乎设想的形式为寰球用户提供帮忙。在 Android 开发者峰会 上,咱们探讨了如何关注用户、理解他们的需要,构建令其称心的体验,并专一于 Android 的重要主题: 帮忙开发者构建优良的跨设施利用。咱们对 古代 Android 开发 (MAD) 产品进行了更新,助力大家进步工作效率,以便您专一于构建杰出的利用,进而将这些跨设施利用扩大到所有设施类型。其中一项重大更新是咱们在可折叠设施和平板电脑上隆重推出 12L 性能更新,这是一组专为 Android 12 大屏幕设施优化的性能。欢迎您持续浏览以理解更多信息!点击这里《2021 Android 开发者峰会主题演讲》视频。 优良利用: Jetpack Compose 现已反对 Material You在构建优良的利用上,咱们一直壮大的 古代 Android 开发 (MAD) 包含开发工具、API、语言和散发技术合集是您的左膀右臂,助您进步工作效率。同时,咱们在 MAD 中也推出了许多新性能,帮您达成这一目标,比方以下这项公布: 在 Jetpack Compose (Android 用于构建原生 UI 的古代工具包) 中引入 Material You 及其全新设计理念。 在往年早些时候举办的 Google I/O 大会上,咱们针对 Android 12 推出了 Material You,专一于提供适宜各种格调、满足各种需要并适应各种屏幕的个性化体验。当初咱们公布了 Compose Material 3 第一个 Alpha 版,该版本提供 Material Design 3 格调的组件和 主题,反对 Material You 个性化性能,如动静色调。咱们还公布了 Jetpack Compose 1.1 第一个 Beta 版,其中蕴含实用于 Android 12 的拉伸滚动、通过优化的触摸目标值、实验性提早布局动画等性能。Jetpack Compose 性能稳固且已可用于生产环境,咱们将持续增加大家所需的性能,助力您轻松疾速地为所有设施类型构建 Android UI,并针对 Wear OS 和构建主屏幕微件 (widget) 提供了全新反对。 ...

December 7, 2021 · 2 min · jiezi

关于android:鸿蒙应用上架全攻略含常见问题

开发利用应用鸿蒙开发工具HUAWEI DevEco Studio实现鸿蒙利用的开发。具体的开发过程能够参考HarmonyOS开发文档。 这部分由开发者自行实现。 创立利用鸿蒙利用开发好后,不论是调试还是公布利用,首先都须要在AGC创立鸿蒙利用。以后鸿蒙利用创立是受邀名单管控。 依据开发者账号状态,创立利用形式不同。 如果想申请申请鸿蒙利用受邀名单,可参考下文开发者已实名章节。 开发者未实名1.登录AGC会提醒如下页面,点击“去体验”后跳转到“我的项目设置 > HarmonyOS利用 > 治理HAP Provision Profile”页面: 2.点击“HarmonyOS利用”,在“增加利用”窗口中进行HarmonyOS利用创立。 开发者已实名1.如果曾经申请鸿蒙利用受邀名单,AGC页面“我的利用”下会呈现“HarmonyOS利用”页签。 选中“HarmonyOS利用”,即可创立鸿蒙利用,具体参见间接创立利用。也能够在“我的我的项目”下增加创立鸿蒙利用,具体参见在我的项目下增加利用。 2.如果未申请鸿蒙利用受邀名单,则只能在“我的我的项目”下增加创立鸿蒙利用。也只能进行利用的调试,无奈公布利用。 如您需退出受邀名单,请将开发者名称、申请背景、反对设施类型及Developer ID发送至agconnect@huawei.com,华为经营人员将在1-3个工作日内为您安顿对接人员。Developer ID查询方法请参见查问开发者帐号ID及我的项目ID。 留神点:1.AGC创立利用填写的包名必须全网惟一,鸿蒙利用包名也不能和安卓利用包名反复,如果提醒包名被占用,须要更换包名。 2.间接创立鸿蒙利用不须要填写包名,通过上传的软件包主动解析。在我的项目下增加鸿蒙利用必须填写包名。 3.在我的项目下能够增加已有的利用,抉择利用后不要批改利用名称,否则会认为是新建利用。 调测利用鸿蒙利用开发实现后,且曾经在AGC实现利用的创立,就能够开始进行调试了。如果仅仅在模拟器中调试,能够不配置签名信息;但如果须要在真机中调试,必须配置签名信息。华为提供了两种利用调试(签名)办法:自动化签名形式和手动签名形式。 主动签名间接在开发工具HUAWEI DevEco Studio中实现自动化签名并调试,具体参见自动化签名。 留神点:1.如果IDE没有主动签名选项,倡议降级IDE至最新版本。主动签名个性仅在DevEco Studio V2.1 Release及更高版本中反对。 2.自动化签名的调试形式会主动在AGC中创立用于调试的数字证书和Profile文件,但以后AGC调试证书最多仅反对2个,即最多同时只反对为两个利用进行自动化调试,如已达到下限,须要在“用户与拜访 > 证书治理”页面中“破除”多余的调试证书文件。 3.调试设施最多反对100个。 4.鸿蒙利用的config.json文件中的“bundleName”取值必须和AGC利用包名保持一致,如包名抵触,能够批改bundleName进行躲避。 5.如果报错连贯AGC网站失败,请查看是否应用代理等,能够断开代理重试。 手动签名通过登录AGC网站申请调试证书和调试Profile文件,下载到本地后再在IDE中进行签名,具体参见手动签名。 相干流程如下: 留神点:1.本地调试肯定要应用调试证书和调试Profile文件,并且调试证书和Profile文件要匹配。如果应用公布证书和公布Profile文件用于调试场景将会导致利用无奈装置。 2.以后AGC调试证书最多仅反对2个,如已达到下限,须要在“用户与拜访 > 证书治理”页面中“破除”多余的调试证书文件。 3.申请调试Profile文件的利用肯定要和本地调试的利用匹配。 公布利用本地调试结束后,能够打包HarmonyOS利用,在AGC提交上架。 申请公布证书和公布Profile文件为了确保HarmonyOS利用的完整性,HarmonyOS通过数字证书和Profile文件来对利用进行管控。上架到华为利用市场的HarmonyOS利用也必须通过签名才容许上架。因而须要应用公布证书和Profile文件对利用进行签名后能力公布。具体操作参考申请利用公布证书和申请公布Profile。 留神点:1.以后AGC最多可申请1个公布证书;最多可申请100个Profile文件。 2.公布利用肯定要应用公布证书和公布Profile文件,并且公布证书和Profile文件要匹配。如果应用调试证书和调试Profile文件用于公布场景将无奈上传软件包。 3.申请公布Profile文件的利用肯定要和公布利用匹配。 打包HarmonyOS利用打包HarmonyOS利用次要是在HUAWEI DevEco Studio中配置好在AGC下载好的公布证书和公布Profile,而后编译生成软件包。具体参考配置签名信息和编译构建APP。 留神点:1.上传的利用图标,截图,视频等须要满足利用市场要求,否则上传失败。 2.如果须要提前小范畴测试利用,能够抉择开放式测试版本公布。 3.上传素材或软件包须要纯公网环境,应用代理等须要断开连接。 4.降级利用时仅容许减少设施类型,不反对删除原有设施类型。如在架版本抉择反对手机和大屏,降级利用必须保留手机和大屏设施,但能够新增反对手表设施。 上传软件包失败FAQ1.HarmonyOS应用软件包上传后,提醒“您上传的软件包应用的HarmonyAppProvision和证书不匹配,要求批改后再从新上传”,如何解决? 起因:HarmonyOS应用软件包中的公布证书与公布Profile文件中的公布证书不匹配。 排查点:确认IDE打包时应用的公布证书是否与您申请公布Profile所应用的公布证书统一。 2.HarmonyOS应用软件包上传后,提醒“HarmonyOS hapAppProvision文件非法,请从新上传”,如何解决? 起因:HarmonyOS应用软件包中的公布Profile文件和以后上传软件包的利用不匹配。 排查点:确保软件包中应用的Profile文件是在以后利用下下载的。 常见谬误场景是软件包中的Profile文件应用的是在A利用下下载的,然而却在B利用下上传软件包。如下: 3.HarmonyOS应用软件包上传后,提醒“HarmonyOS软件包内不同文件包名不统一,请从新上传”,如何解决? 起因:HarmonyOS应用软件包内有.hap包的包名与公布Profile中的包名不统一。 排查点:确保HarmonyOS应用软件包内所有.hap包的包名一样,且与公布Profile中的包名统一。 ...

December 7, 2021 · 1 min · jiezi

关于android:Android事件分发机制

原文链接:https://juejin.im/post/5eb3e0... 这次说下Android中的事件散发机制从开始点击屏幕开始,就会产生从Activity开始到decorview始终到最里层的view一连串事件传递。每一层view或者viewgroup都会首先调用它的dispatchTouchEvent办法,而后判断是否就在以后一层生产掉事件view的事件散发首先上一段伪代码,是在书上看到的,也是我感觉总结的最好的 public boolean dispatchTouchEvent(MotionEvent event) {    boolean isConsume = false;     if (isViewGroup) {         if (onInterceptTouchEvent(event)) {            isConsume = onTouchEvent(event);         } else {             isConsume = child.dispatchTouchEvent(event);         }     } else {        //isView       isConsume = onTouchEvent(event);     }     return isConsume;}复制代码如果以后是viewgroup层级,就会判断 onInterceptTouchEvent 是否为true,如果为true,则代表事件要生产在这一层级,不再往下传递。接着便执行以后 viewgroup 的onTouchEvent办法。如果onInterceptTouchEvent为false,则代表事件持续传递到下一层级的 dispatchTouchEvent办法,接着一样的代码逻辑,始终到最外面一层的view。ok,还没完哦,到最外面一层就会间接执行onTouchEvent办法,这时候,view有没有权力回绝生产事件呢? 按情理view作为最底层的,应该是没有发言权才对。然而呢,秉着偏心公正准则,view也是能够回绝的,能够在onTouchEvent办法返回false,示意他不想生产这个事件。那么这个事件又会怎么解决呢?见上面一段伪代码: public void handleTouchEvent(MotionEvent event) {   if (!onTouchEvent(event)) {        getParent.onTouchEvent(event);    }}复制代码如果view的onTouchEvent办法返回false,那么它的父容器的onTouchEvent又会被调用,如果父容器的onTouchEvent又返回false,则又交给上一级。始终到最上层,也就是Activity的onTouchEvent被调用。 至此,生产流程结束然而,对于onTouch,onTouchEvent和onClick又是怎么样的调用关系呢?那就再来一段伪代码: public void consumeEvent(MotionEvent event) {     if (setOnTouchListener) {         onTouch();         if (!onTouch()) {             onTouchEvent(event);         }     } else {         onTouchEvent(event);     }    if (setOnClickListener) {        onClick();    }}复制代码当某一层view的onInterceptTouchEvent被调用,则代表以后层级要生产事件。如果它的onTouchListener被设置了的话,则onTouch会被调用,如果onTouch的返回值返回true,则onTouchEvent不会被调用。如果返回false或者没有设置onTouchListener,则会持续调用onTouchEvent。而onClick办法则是设置了onClickListener则会被失常调用。 这里用一张流程图总结下: <figcaption style="margin: 10px 0px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; line-height: inherit; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片形容</figcaption> 源码剖析一个触摸事件,首先是传到Activity层级,而后传到根view,通过一层层的viewgroup最终到底最外面一层的view,咱们来一层层解析Activity(dispatchTouchEvent)间接上代码 //Activity.java     public boolean dispatchTouchEvent(MotionEvent ev) {         if (ev.getAction() == MotionEvent.ACTION_DOWN) {             onUserInteraction();         }         if (getWindow().superDispatchTouchEvent(ev)) {             return true;         }         return onTouchEvent(ev);    }    public void onUserInteraction() {    }复制代码这里能够看到,onUserInteraction办法是空的,次要是调用了getWindow().superDispatchTouchEvent(ev)办法,返回true,就代表事件生产了。返回false,就代表上层没人解决,那就间接到了activity的onTouchEvent办法,这点跟之前的生产传递也是吻合的。持续看看superDispatchTouchEvent办法,而后就走到了PhoneWindow的superDispatchTouchEvent办法,以及DecorView的superDispatchTouchEvent,看看代码: ...

December 7, 2021 · 1 min · jiezi

关于android:Kotlin该如何实现多线程同步

问题背景需执行多线程工作:工作1、工作2并行执行;等全副执行实现后,执行工作3。 // 每个 工作 通过 sleep 模仿耗时val task1: () -> String = { sleep(2000) "Hello".also { println("task1 finished: $it") }}val task2: () -> String = { sleep(2000) "World".also { println("task2 finished: $it") }}val task3: (String, String) -> String = { p1, p2 -> sleep(2000) "$p1 $p2".also { println("task3 finished: $it") }}实现形式「多线程同步」。Kotlin实现多线程同步的形式次要包含:(含Java实现形式) 形式1:Thread.join形式2:线程锁:Synchronized、ReentrantLock、CountDownLatch、CyclicBarrier形式3:CAS形式4:Future(CompletableFuture)形式5:Rxjava形式6:协程Coroutine、Flow形式1:Thread.join()这是最简略的线程同步形式 @Testfun test_join() { lateinit var s1: String lateinit var s2: String val t1 = Thread { s1 = task1() } val t2 = Thread { s2 = task2() } t1.start() t2.start() t1.join() t2.join() task3(s1, s2)}形式2:线程锁次要包含:Synchronized、ReentrantLock、CountDownLatch、CyclicBarrier ...

December 7, 2021 · 3 min · jiezi

关于android:Android高手进阶教程三之Android-中自定义View的应用

大家好咱们明天的教程是在Android 教程中自定义View 的学习,对于初学着来说,他们习惯了Android 传统的页面布局形式,如下代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout\_width="fill\_parent" android:layout\_height="fill\_parent" > <TextView android:layout\_width="fill\_parent" android:layout\_height="wrap\_content" android:text="@string/hello" /> </LinearLayout>当然下面的布局形式能够帮忙咱们实现简略利用的开发了,然而如果你想写一个简单的利用,这样就有点牵强了,大家不信能够下源码都钻研看看,高手写的布局形式,如下面的布局高手通常是这样写的: <?xml version="1.0" encoding="utf-8"?> <A> <B></B> </A>其中A extends LinerLayout, B extends TextView.为了帮忙大家更容易了解,我写了一个简略的Demo ,具体步骤如下: 首先新建一个Android 工程 命名为ViewDemo . 而后自定义一个View 类,命名为MyView(extends View) .代码如下: package com.android.tutor; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Paint.Style; import android.util.AttributeSet; import android.view.View; public class MyView extends View { private Paint mPaint; private Context mContext; private static final String mString = "Welcome to Mr Wei's blog"; public MyView(Context context) { super(context); } public MyView(Context context,AttributeSet attr) { super(context,attr); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); mPaint = new Paint(); //设置画笔色彩 mPaint.setColor(Color.RED); //设置填充 mPaint.setStyle(Style.FILL); //画一个矩形,前俩个是矩形左上角坐标,前面俩个是右下角坐标 canvas.drawRect(new Rect(10, 10, 100, 100), mPaint); mPaint.setColor(Color.BLUE); //绘制文字 canvas.drawText(mString, 10, 110, mPaint); } }而后将咱们自定义的View 退出到main.xml 布局文件中,代码如下: ...

December 7, 2021 · 1 min · jiezi

关于android:Android开发往项目工程里面新引入工具包的步骤

前言在Android开发过程中,有些时候会依据理论须要,要往我的项目外面引入工具包,作为高级开发者或者刚开始入门的Android开发者来说会不太纯熟怎么引入,所以往我的项目工程外面新引入工具包也是必备技能。那么本篇博文就来分享一下给我的项目工程外面引入工具包的步骤,只分享给有须要的人。提前的筹备工作在往我的项目工程外面新引入工具包的时候,须要确认我的项目工程外面之前是否曾经导入过同样类型的工具包,确认无导入同款工具包之后,依据理论须要来找到对应工具包的包名链接,这里不再介绍,间接去网上搜寻查找到对于到链接即可。 具体的操作步骤1、首先,关上我的项目工程,找到我的项目左侧的文件列表,找到”build.gradle“名称的文件,而后鼠标单击进入; 2、接着,在build.gradle文件中,找到引入工具包的那个办法外面,而后把复制好的工具包链接粘贴进去,如图所示; 3、而后,引入工具包之后,须要更新编译一下我的项目,而后在文件的右上方会有一个提醒”Sync Now“,而后鼠标单击即可。 4、最初,在短暂的期待之后,而后再编译运行一下我的项目,如果我的项目没有报错,就能够了,而后引入新的工具包就功败垂成。 最初下面的操作步骤,能够很好的解决在Android开发中往我的项目工程外面新引入工具包的操作步骤,不便开发者查阅应用,尤其是对于高级开发者或者刚开始入门的Android开发者来说更为重要的操作步骤,切记要学会,这里不再赘述。 好了,明天的文章就到这里,感谢您的浏览,有问题能够在评论区留言探讨,期待与大家共同进步。喜爱的话不要忘了三连。大家的反对和认可,是我分享的最大能源。

December 7, 2021 · 1 min · jiezi

关于android:Android开发当前项目以Module形式引用别的项目的步骤

前言在Android开发过程中,有些时候会依据须要援用别的我的项目到以后我的项目外面,而且以Module模式援用。所以本篇博文就来分享一下怎么以Module模式援用别的我的项目到以后我的项目中,不便开发者查阅应用。举例说明,之前开发的我的项目,须要援用蓝牙的办法,就间接把另外一个我的项目的蓝牙局部做成一个demo而后间接以Module模式援用到以后我的项目外面。那么本篇博文就来介绍一下具体的援用办法的步骤,以备不便当前应用。 步骤一、在以后我的项目外面导入,Import Module… 二、抉择文件目录并抉择须要导入的文件 三、依据理论状况批改Module名称,如果没有重名能够不批改;而且须要勾选右侧的 “Import”选项,最初点击 “Finish”按钮 四、能够在我的项目左侧文件构造外面看到导入进去的地位 五、对Module进行我的项目关联,点击我的项目顶部工具菜单栏外面的“我的项目构造”图标,或者点击左侧顶部File—>Project Structure…间接进入1、点击我的项目顶部工具菜单栏外面的“我的项目构造”图标的办法 2、点击左侧顶部File—>Project Structure…间接进入的办法 六、弹出Project Structure主界面,而后点击“Dependencies”模块,找到第一行,而后抉择下拉弹框抉择中的“Module dependency”选项,点击“OK”按钮即可。 最初下面六步骤只是介绍了怎么正确的把我的项目通过Module的模式援用到另外一个我的项目外面,然而没有介绍援用之后会遇到的报错等问题,须要留神依据具体理论状况进行具体解决,尤其是错误信息的剖析,这里不再介绍引入之后的报错问题。 好了,明天的文章就到这里,感谢您的浏览,有问题能够在评论区留言探讨,期待与大家共同进步。喜爱的话不要忘了三连。大家的反对和认可,是我分享的最大能源。

December 7, 2021 · 1 min · jiezi

关于android:安卓逆向-32

root 1 装置Magisk下载 https://github.com/topjohnwu/Magisk/releases手机装置这个apk adb install D:\Android_study\Magisk-v23.0.apk2 拷贝boot.img从sailfish-qp1a.191005.007.a3\image-sailfish-qp1a.191005.007.a3.zip 中将 boot.img 拷贝进去 3 将 boot.img 推送到手机 sdcard 中adb push D:\Android_study\boot.img sdcard4 关上Magisk点击装置 此时,显示新的文件生成在如下门路 /storage/emulated/0/Download..... 此门路等同于 \sdcard\Downloadcd Download ls # magisk_patched-23000_joPeJ.imgexit C:\Users\Administrator>adb pull /sdcard/Download/magisk_patched-23000_joPeJ.img此时,文件被拉取到了C:\Users\Administrator> 中。此时,再次刷机 adb reboot bootloaderfastboot devicesfastboot flash boot D:\Android_study\magisk_patched-23000_joPeJ.img而后高低按音量键。抉择Start 。按电源键。手机重启。 再次adb shell 。 su 此时会提醒是否给与root 超级用户权限,抉择是。如果选错,再次su,不会再次提醒。 始终是禁止状态。点击此处,关上即可。 阐明装置frida的时候,须要敞开,否则无奈装置。

December 6, 2021 · 1 min · jiezi

关于android:安卓逆向-31

刷机教程: https://source.android.com/source/running.htmlhttps://mp.weixin.qq.com/s/1EySfXSucGdiuEBTfLsymA1 adb与fastboot手机通过USB连贯电脑,须要用到adb刷机包通过线刷刷入手机,须要用到fastboot这俩个货色在安卓SDK中自带,门路为 SDK\platform-tools 增加到零碎环境变量即可 2 开启开发者选项抉择 【设置 – 零碎 – 对于手机 – 版本号】,点击【版本号】7次开启 【开发者选项】。如果是英文零碎,自行翻译 3 开启USB调试抉择 【设置 – 零碎 – 开发者选项】,开启【USB调试】。通过数据线将手机连贯至电脑,手机端会弹出 USB 调试申请,容许即可开启胜利的标记,cmd中输出adb devices 4 Bootloader解锁要想刷机首先要Bootloader解锁,而要Bootloader解锁,又要先开启oem解锁。不过,如果你的手机是淘宝买的二手机,个别都是曾经解锁了的。那么能够跳过oem解锁与Bootloader解锁 1 手机先退出谷歌账号,勾销锁屏,指纹识别等 2 从手机设施中取出sim卡 3 开启开发者选项 4 开启USB调试 5 开启oem解锁 (这个选项也在开发者选项中,然而须要迷信上网) 6 手机连贯至电脑,进入Bootloader模式(进入形式在下文给出) 7 在cmd中输出fastboot oem unlock 或者 fastboot flashing unlock 8 Bootloader解锁界面中,用音量键 +/- 管制光标,抉择【Yes】并按下电源键进行 Bootloader解锁 9 确认结束后,稍作期待。通过 fastboot reboot 命令重启手机 胜利解锁Bootloader后,每次手机启动时,都会呈现黑底白字的英文正告页,提醒 “Your device software can’t be checked for corruption. Please lock the bootloader”。 ...

December 6, 2021 · 1 min · jiezi

关于android:安卓逆向-30

刷机形式的分类线刷 官网包(刷的比拟彻底,能够刷bootloader、radio)卡刷 lineage os(双清)谷歌手机工厂镜像https://developers.google.com...刷机包的分类 线刷包/工厂镜像包 OTA全量包/OTA增量包刷机包组成 bootloader、radio、android零碎下载对应的工厂镜像:之前的驱动为 Pixel binaries for Android 10.0.0 (QP1A.191005.007.A3) 链接为: https://dl.google.com/dl/android/aosp/sailfish-qp1a.191005.007.a3-factory-d4552659.zip下载后解压:

December 6, 2021 · 1 min · jiezi

关于android:Android入门教程-动画之补间动画

补间动画(Tween Animation)补间动画就是咱们只需指定开始、完结的“关键帧“,而变动中的其余帧由零碎来计算,不用本人一帧帧的去定义。Android 应用 Animation 代表形象动画,包含四种子类:AlphaAnimation (透明度动画)、 ScaleAnimation (缩放动画)、 TranslateAnimation (位移动画)、 RotateAnimation (旋转动画)个别都会采纳动画资源文件来定义动画,把界面与逻辑拆散定义好 anim 文件后,咱们能够通过 AnimationUtils 工具类来加载它们,加载胜利后返回一个 Animation。而后就能够通过 View 的 startAnimation(anim) 开始执行动画了。示例补间动画:通过应用 Animation 对单张图片执行一系列转换来创立动画。在 XML 中定义的动画,用于对图形执行旋转、淡出、挪动和拉伸等转换。动画文件放在res/anim/,该文件名将用作资源 ID。挪动示例 先用一个例子建设直观的意识。新建一个动画xml,实现从左到右的成果。 move_hor_1.xml <?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="200" android:toXDelta="-100%" /> <translate android:duration="400" android:startOffset="200" android:toXDelta="100%" /></set>要应用这个动画,须要用到 AnimationUtils.loadAnimation(getContext(), R.anim.move_hor_1); 来加载动画。失去对象 mAnimation  交给View.startAnimation(mAnimation); 来启动。 认真看一下动画 xml 里的内容: set 里有 2 个 translatetranslate 示意挪动动作duration 是这个动作的执行时长(毫秒)toXDelta 示意横行挪动startOffset 示意动画开始执行多久后再执行这个动作实际上,第二个 translate 周期是 400 毫秒,它“等了” 200 毫米才开始执行。 把 View 从右边挪动到了左边。 缩放示例除了位移,还能够执行缩放成果。 示例:先放大再放大回去。次要是用了android:repeatMode="reverse"和android:repeatCount="1"。 残缺动画 xml 如下: <?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"> <scale android:duration="600" android:fromXScale="1" android:fromYScale="1" android:pivotX="50%" android:pivotY="50%" android:repeatCount="1" android:repeatMode="reverse" android:toXScale="1.35" android:toYScale="1.35" /></set>scale 外面咱们遇到了几个元素 pivotX,repeatCount,repeatMode ...

December 6, 2021 · 2 min · jiezi

关于android:程序为何莫名卡顿内存泄漏都解决了那内存抖动呢

我敢说还有绝大一部分Android老开发还不晓得为什么要性能优化! 这相对不是在危言耸听, 不晓得大家有没有在平时的开发工作中遇到过这样的状况: 为什么这些按钮工作的那么慢,我却找不到起因! 很多人高高兴兴写完性能之后,运行到设施上测试发现什么问题都没有,可唯独这个按钮“慢的飞起”,界面之间的跳转也不够晦涩,然而代码看过来看过来也没看出什么故障。 始终以来Android开发的性能优化总被局部人忘记,很多守业公司基本上都不会去进行性能测试,他们会更重视功能测试和稳定性测试,而在一些中小型公司,局部测试人员却爱莫能助。性能优化始终是大厂面试中被提及最多的问题之一,如果你想进入大厂那么性能优化是你必不可少的技能之一。 为什么大厂看重性能优化呢?有很多工作了一两年的老开发都还在问,性能优化真的有用么? 一些在小厂工作的敌人,他们说本人素来没有做过性能调优,测试完就间接上线了,也没有呈现什么问题,那为什么还要性能优化呢?作为一个Android开发工程师,每天都须要解决各种各样的技术问题,其中性能优化是Android开发工作中最为考验技术的工作之一,更是Android开发工程师必备的技能。 性能优化尽管说容易学、好上手,但还有绝大多数做业务方面的老开发对性能优化也只停留在“据说过”或者“简略应用过工具”的阶段。如果想要吃透性能优化,光会利用工具是远远不够的 ,必须要对底层原理理解透彻,例如:App启动速度优化、内存优化、UI晦涩度优化、apk瘦身、电量优化等等。其中与性能优化有间接关联的绝大多数都是内存问题。 什么是内存优化?内存不优化会呈现哪些问题?先看看上面这张图 内存抖动(Memory Churn)是因为在短时间内大量的对象被创立又马上被开释。霎时大量产生的对象会把内存区域大量占用,当达到阀值,残余空间不够的时候,GC会被触发从而导致刚产生的对象又很快被回收。 即便每次调配的对象都只占用很少的内存,然而他们叠加在一起导致Heap的压力减少,导致其余类型的GC被触发。此时帧率有可能会被影响到,从而使得用户感知到性能问题,带来不好的用户体验。 上面我给大家列举几个面试常见的内存问题: 内存透露与内存溢出的区别?如何防止内存抖动?为什么内存抖动会导致 OOM?OOM产生的起因有哪些?内存抖动的危害产生内存抖动应该如何解决?Android内存优化是咱们性能优化工作中比拟重要的一环, 也是Android高开面试最常问的点之一,如果不把内存优化吃透,那么技术面上会很吃亏,然而想要深刻学习性能优化不是那么容易的事件。 上面我给大家分享一些内存优化相干的学习材料,有须要的小伙伴文末有支付形式,材料是收费分享的。 ANR问题解析 ANR原理剖析及解决ANR源码剖析 卡顿监控-ANR底层源码剖析四大组件启动超时ANR Input响应超时ANR crash监控计划 监控死锁 监控存活周期 监控CPU占用率 ANR问题启动速度与执行效 率优化我的项目实战解析 卡顿检测,卡顿起因,卡顿优化 微信为什么越滑越卡 异步、非阻塞式Android启动任务调度库 Android App启动页白屏疾速优化计划实际 内存优化 罕用的Android内存优化工具 Memory Profiler应用解析 LeakCanary应用解析 如何应用MAT 内存透露剖析及检测工具 耗电优化 网络优化的三个要点 网络优化的两个维度 三个线下测试工具 线上监控的三个要点 网络申请品质优化 因为篇幅无限,就不一一展现余下内容,这份材料总共408页,简直涵盖性能优化全副知识点,大家能够先参考一下目录: 其实性能优化根本知识点都是非亲非故的,所以学习起来是有技巧的,肯定要把握根本的优化计划,再去探讨更深的原理问题。这份《Android性能优化-实战全解析》肯定能够帮忙到大家,有须要的小伙伴能够 评论 或者 私信 888,我收费发给大家,最初祝大家都能找到本人心仪的工作! 相干视频举荐:【2021最新版】Android studio装置教程+Android(安卓)零基础教程视频(适宜Android 0根底,Android初学入门)含音视频_哔哩哔哩_bilibili 【 Android进阶教程】——App启动速度的优化_哔哩哔哩_bilibili Android进阶零碎学习——高级UI卡顿性能优化_哔哩哔哩_bilibili

December 6, 2021 · 1 min · jiezi