开始使用Kotlin协程

节选自:开始使用Kotlin协程


本文主要介绍协程的用法, 以及使用协程能带来什么好处. 另外, 也会粗略提一下协程的大致原理.
本文的意义可能仅仅是让你了解一下协程, 并愿意开始使用它.
如果想彻底理解协程, 请查看官方文档, 官方文档链接将在文章的结尾给出.

如果你以前在别的语言里学习过协程, 如Python的yield, 那请你先忘记它们, 毕竟还是有些区别, 等你弄懂了Kotlin的协程, 再去作对比, 否则, 可能会有一些先入为主的思路来阻碍你理解, 我就吃过这个亏.

初识协程:

首先我们来瞄一眼协程是长啥样的, 以下引用(copy)了官网的一个例子:

fun main(args: Array<String>) {
    launch(CommonPool) {
        delay(1000L) 
        println("World!") 
    }
    println("Hello,")
    Thread.sleep(2000L)
}

/* 
运行结果: ("Hello,"会立即被打印, 1000毫秒之后, "World!"会被打印)
Hello, 
World!
*/

姑且不管里面具体的细节, 上面代码大体的运行流程是这样的:

A. 主流程:

1.调用系统的launch方法启动了一个协程, 跟随的大括号可以看做是协程体.
(其中的CommonPool暂且理解成线程池, 指定了协程在哪里运行)
2.打印出”Hello,”
3.主线程sleep两秒
(这里的sleep只是保持进程存活, 目的是为了等待协程执行完)

B. 协程流程:

1.协程延时1秒
2.打印出”World!”

解释一下delay方法:
在协程里delay方法作用等同于线程里的sleep, 都是休息一段时间, 但不同的是delay不会阻塞当前线程, 而像是设置了一个闹钟, 在闹钟未响之前, 运行该协程的线程可以被安排做了别的事情, 当闹钟响起时, 协程就会恢复运行.

Kotlin/JVM 协程实现原理

节选自:Kotlin/JVM 协程实现原理


目录

前言

在文章正式上干货之前,先说一点背景吧;我是 Kotlin 协程官方文档的译者,大家在 Kotlin 中文官网上看到的绝大多数协程的中文官方文档都是我翻译的。

官方文档可以说是比较全面的介绍了协程的使用,但是就我的感觉来说,这些文档分布的比较散乱,甚至还有三篇分布在协程的官方 Github 的 project 中,很多协程的初学者对这些文档的阅读顺序也常常感到摸不到头脑。这里我将一共 15 篇文档的学习顺序做一个整理,如果你还不了解如何使用协程,可以参考我如下的列举:

首先,如果您不了解什么是协程,以及不清楚如何将协程引入您的项目,你可以按顺序阅读这两篇教程:

接下来两篇官方文档类似于导读或目录,简述了一下协程的理念,以及给出了一些干货的链接,这两篇导读本身倒是没啥干货:

然后就是大量的正餐了,如下九篇官方文档介绍了协程使用的方方面面,一定要读懂:

上面九篇文档读完,再配以大量的实践,你应该已经掌握了协程的基本用法,并开始思考使用它的场景,你可能想知道如何使用协程编写 UI 应用程序,亦或是你可能对协程和响应式流(例如 RxJava)之间的异同和关系有疑问,那么可以参考下面两篇被刊登在官方 Github 上的指南:

现在你应该已经掌握协程在绝大多数场景下的用法,于是你可能好奇于它的实现原理,那么可以阅读这篇官方 Keep:

目前这就是协程全部的官方资料,两篇指南和一篇 Keep,都是刊登在 Github 上的;目前 Kotlin 中文站的站长是灰蓝天际老哥,所以上面给出的指向 Github 的地址是指向他 Fork 的版本,以上所有文档的英文原版,都可以在 Kotlin 的英文官网,以及官方的 Github 上找到。

Kotlin 目前是一门多平台语言,虽然协程的设计思想是统一的,但它们在底层的实现原理上会有所不同,例如,在 JVM 和 Android 上,协程的实现要基于线程池的 API,但是在 JS 平台上,由于 JS 本身不支持多线程,所以协程这时必定就不会产生并行。作为一名 Android 工程师,本文将致力于阐述协程在 Android 平台和 JVM 平台的原理,而 JS 平台以及众多的 Native 平台则暂不讨论。

本文将会先介绍一些协程的设计思想,然后详细讲解一下协程的编译相关以及标准库等内容,然后根据源码深入到协程调度器的底层实现细节(调度器这一部分我认为是最值得去看的)。

【转】Android EditText设置负数以及小数

转载自:Android Edittext设置负数以及小数


1,设置Edittext只可以输入数字,只需要给EditText这样设置

xml:
android:inputType="number"
代码中设置:
edit.setInputType(InputType.TYPE_CLASS_NUMBER);

2,设置Edittext可以输入正负数字,需要给EditText这样设置

xml:
android:inputType="numberSigned"
代码中设置:
edit.setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_NUMBER_FLAG_SIGNED);

3,设置Edittext可输入带小数点的数字,需要给EditText这样设置

xml:
android:inputType="numberDecimal"
代码中设置:
edit.setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_NUMBER_FLAG_DECIMAL);

4,设置Edittext可输入正负带小数点的数字,需要给EditText这样设置

xml:
android:inputType="numberSigned|numberDecimal"
代码中设置:
edit.setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_NUMBER_FLAG_SIGNED|InputType.TYPE_NUMBER_FLAG_DECIMAL);

Java注解代码生成

节选自:Java注解代码生成


1、简介
在我们常用的框架中注解和自动生成代码的身影很常见,因为注解和自动生成的配合,从而简化和统一代码,使框架使用简单且容易扩展,典型且最熟悉的就是ButterKnife,主要功能利用注解省略了findViewById的过程,当然也提供了其他的监听、绑定等很多强大的操作符,熟悉ButterKnife源码的应该知道,ButterKnife的实现就是利用我们今天的要讲的内容,我们在文章的最后也会尝试编写一个简单的ButterKnife;

IjkPlayer 学习笔记

节选自:IjkPlayer 学习笔记


笔记可能微乱,但大致清晰,可能会对他人有所帮助,故分享出来。

×××××××××××××××目录×××××××××××××××
ijk概述
mediacodec相关
OpenGL相关
filter相关
setOption配置相关
metadata相关
h264编码器特有的设置域
线程相关
消息机制
音频输出
声道切换
SDL_CreateCond 与 SDL_CreateThreadEx
如何暂停
×××××××××××××××××××××××××××××××××

手机设备信息与 Android build.prop 文件

节选自:手机设备信息与 Android build.prop 文件


什么是 build.prop 文件

路径: /system/build.prop

说明: build.prop 是 Android 系统中的一个重要的属性文件,记录了系统的设置和改变。

主要信息类型ro.build.*(编译信息),ro.product.*(设备信息),ro.config.*(默认设置信息),dalvik.vm.*(虚拟机信息)等。

获取 Android 设备信息的命令

一种是通过访问 /system/build.prop 文件进行查看

# 打印 /system/build.prop 所有内容
adb shell cat /system/build.prop

# 通过 grep 筛选特定内容
adb shell cat /system/build.prop | grep ro.product

也可以用 ADB 的命令查看

# 显示所有信息
adb shell getprop

# 显示特定信息
adb shell getprop ro.product.model

# 筛选信息
adb shell getprop | grep product

部分 build.prop 信息说明

参数 说明 例子
ro.build.id 编译标识 ro.build.id=KTU84P
ro.build.display.id 显示标识 ro.build.display.id=KTU84P release-keys
ro.build.version.sdk 编译时使用的SDK版本 ro.build.version.sdk=19

【转】Android 开发之ActivityLifecycleCallback

转载自:Android 开发之ActivityLifecycleCallback


一. 简介

ActivityLifecycleCallbacks是Application里的一个接口,在Android4.0(API level 14)中新加。用于监听应用中所有Activity的生命周期的调用情况,callback中的方法会在Activity回调方法之前调用,可以通过实现这个接口来完成一些特殊需求,比如检测APP是否处于前台。

二. 使用

以检测APP是否处于前台为例介绍使用。

1. 实现ActivityLifecycleCallbacks接口

public class ActivityLifecycleCallbackWrapper implements Application.ActivityLifecycleCallbacks {
    private static final String TAG = "LifecycleCallback";
    private int count; 
    private boolean isForeground; 

    public boolean isForeground() {
        return isForeground; 
    }

    @Override 
    public void onActivityCreated(Activity activity, Bundle bundle) {
        //to do 
    }

    @Override 
    public void onActivityStarted(Activity activity) { 
        count ++; 
    }

    @Override 
    public void onActivityResumed(Activity activity) {
        //to do 
    }

    @Override 
    public void onActivityPaused(Activity activity) {
        //to do 
    }

    @Override 
    public void onActivityStopped(Activity activity) { 
        count --; 
        if(count == 0) {
            isForeground = true; 
        }
    }

    @Override 
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
        //to do 
    }

    @Override 
    public void onActivityDestroyed(Activity activity) {
        //to do 
    }
}

2. 创建自定义Application,并在Application中注册callback。

public class MyApplication extends Application {
    ActivityLifecycleCallbacks callbacks; 

    @Override 
    public void onCreate() {
        super.onCreate(); 
        callbacks = new ActivityLifecycleCallbackWrapper();

        registerActivityLifecycleCallbacks(callbacks);  // 注册Callback
    }
}

利用反射获取ViewBinding

ViewBindingUtil

import android.view.LayoutInflater;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.viewbinding.ViewBinding;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Objects;

public class ViewBindingUtil {

    public static <Binding extends ViewBinding> Binding create(Class<?> clazz, LayoutInflater inflater) {
        return create(clazz, inflater, null);
    }

    public static <Binding extends ViewBinding> Binding create(Class<?> clazz, LayoutInflater inflater, ViewGroup root) {
        return create(clazz, inflater, root, false);
    }

    @SuppressWarnings("unchecked")
    @NonNull
    public static <Binding extends ViewBinding> Binding create(Class<?> clazz, LayoutInflater inflater, ViewGroup root, boolean attachToRoot) {
        Class<?> bindingClass = getBindingClass(clazz);
        Binding binding = null;
        if (bindingClass != null) {
            try {
                Method method = bindingClass.getMethod("inflate", LayoutInflater.class, ViewGroup.class, boolean.class);
                binding = (Binding) method.invoke(null, inflater, root, attachToRoot);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return Objects.requireNonNull(binding);
    }

    private static Class<?> getBindingClass(Class<?> clazz) {
        ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
        Type[] types = Objects.requireNonNull(parameterizedType).getActualTypeArguments();
        Class<?> bindingClass = null;
        for (Type type : types) {
            if (type instanceof Class<?>) {
                Class<?> temp = (Class<?>) type;
                if (ViewBinding.class.isAssignableFrom(temp)) {
                    bindingClass = temp;
                }
            }
        }
        return bindingClass;
    }
}

BaseActivity

import android.os.Bundle;
import android.view.LayoutInflater;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewbinding.ViewBinding;

public abstract class BaseActivity<Binding extends ViewBinding> extends AppCompatActivity {
    protected Binding binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ViewBindingUtil.create(getClass(), LayoutInflater.from(this));
        setContentView(binding.getRoot());
    }
}

参考链接:

利用ViewBinding和反射封装的基类,从此再也不用findViewById了

【转】Windows 安装 SourceTree3.3.8 跳过注册

转载自:windows 安装sourceTree3.3.8 install 时无反应,如何跳过注册安装


我的电脑是windows7, sourceTree是3.3.8

1、访问官网下载最新版本的sourceTree ,官网连接:

https://www.sourcetreeapp.com/

2、先安装sourceTree, 他会提示缺少.NET Framework 4.7.1, 少什么就安装什么,安装成功后, 需要重启电脑,然后执行步骤3; 如果没有缺少,请忽略,直接执行步骤3

3、选择bitbucket的界面退出

4、打开我的电脑, 在地址栏, 输入

%LocalAppData%\Atlassian\SourceTree\

5、在此目录下新建名为accounts.json文件, 写入以下内容

[
  {
    "$id": "1",
    "$type": "SourceTree.Api.Host.Identity.Model.IdentityAccount, SourceTree.Api.Host.Identity",
    "Authenticate": true,
    "HostInstance": {
      "$id": "2",
      "$type": "SourceTree.Host.Atlassianaccount.AtlassianAccountInstance, SourceTree.Host.AtlassianAccount",
      "Host": {
        "$id": "3",
        "$type": "SourceTree.Host.Atlassianaccount.AtlassianAccountHost, SourceTree.Host.AtlassianAccount",
        "Id": "atlassian account"
      },
      "BaseUrl": "https://id.atlassian.com/"
    },
    "Credentials": {
      "$id": "4",
      "$type": "SourceTree.Model.BasicAuthCredentials, SourceTree.Api.Account",
      "Username": "",
      "Email": null
    },
    "IsDefault": false
  }
]

6、打开 %LocalAppData%\Atlassian,进入“SourceTree.exe_Url_iayhtc13zv3obzuz5vchezjs1az2q5ef”(注:该目录可能和版本相关,不同版本的路径可能不完全一样。)

进入 3.3.8.3848 目录, 打开user.config 加入这几句话

<setting name="AgreedToEULA" serializeAs="String">
    <value>True</value>
</setting>
<setting name="AgreedToEULAVersion" serializeAs="String">
    <value>20160201</value>
</setting>

7、重新点击seourceTree.exe 安装, 弹框选择最后一项安装即可