跳转至

琐碎知识点

CopyOnWriteArrayList

CopyOnWriteArrayList支持并发读写。

RemoteCallbackList

RemoteCallbackList支持跨进程删除listener,原理是ArrayMap<IBinder, Callback>。IPC时Parcelable对象会序列化、反序列化,因此不会是同一个对象,但底层IBinder不会变。 它同时具有以下特点: 1. 客户端进程终止后,能够自动移除客户端注册的listener 2. RemoteCallbackList内部自动实现了线程同步功能,所以使用它来进行注册、取消注册时不需要做额外的线程同步工作。

android:windowSoftInputMode

EditText禁止进入时弹出键盘:

android:windowSoftInputMode="stateHidden"
键盘弹出时禁止顶动View:
android:windowSoftInputMode="adjustPan"

常用Intent

跳转拨号盘

new Intent(Intent.ACTION_DIAL, Uri.parse("tel:12306"));
直接拨打电话
new Intent(Intent.ACTION_CALL, Uri.parse("tel:12306"));
发送短信
new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:12306"));

Collections.sort

Java Bean实体进行排序

Collections.sort(mRowsBeanList, new Number2zComparator());

private class Number2zComparator implements Comparator<T> {
    @Override
    public int compare(T o1, T o2) {
        boolean o1IsHot = o1.getHotFlag() == 1;
        boolean o2IsHot = o2.getHotFlag() == 1;
        String o1Letter = PinyinUtils.ccs2Pinyin(o1.getPlatformName());
        String o2Letter = PinyinUtils.ccs2Pinyin(o2.getPlatformName());

        if (o1IsHot && o2IsHot) {
            return o1Letter.compareToIgnoreCase(o2Letter);
        } else if (o1IsHot) {
            return -1;
        } else if (o2IsHot) {
            return 1;
        }

        return o1Letter.compareToIgnoreCase(o2Letter);
    }
}
o1与o2进行比较,如果o1要排在o2前面,返回-1;
如果两者相等,返回0;
如果o1排在o2后面,返回1。

Gif加载

Gif加载
在尝试Glide加载(不卡,但是图片错乱了)、帧动画实现(超级耗内存,30帧,每帧8k原图,耗内存大约20M)、android-gif-drawable库(Android O上非常卡顿,耗内存大约10M)加载失败之后,找到了一种新奇的思路:每隔一段时间调用setBackgroundResource,内存消耗基本不计。

代码设置drawable

setCompoundDrawablesWithIntrinsicBounds

response.body().string()只能调用一次

OkHttp访问网络成功的回调中,Response responseresponse.body().string()只能调用一次,否则

E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
                  Process: yorek.demoandtest, PID: 24671
                  java.lang.IllegalStateException: closed

因为这是一个流,使用过后就被关闭了:

  public final String string() throws IOException {
    BufferedSource source = source();
    try {
      Charset charset = Util.bomAwareCharset(source, charset());
      return source.readString(charset);
    } finally {
      Util.closeQuietly(source);
    }
  }

Glide加载圆形图片

Glide.with(mContext)
    .load(item.platformLogo)
    .bitmapTransform(CropCircleTransformation(mContext))
    .into(iv_logo)

其中,.bitmapTransform(CropCircleTransformation(mContext))是其中的重点, CropCircleTransformation使用了jp.wasabeef:glide-transformations:2.0.2这个lib

compile 'jp.wasabeef:glide-transformations:2.0.2'

Glide v4已经内置了圆形、圆角等常用的Trasform了。上面是Glide v3时的做法。

好人好信Tab3下拉冲突问题

SwipeRefreshLayoutCollapsingToolbarLayout和充满RecyclerViewViewPager联用出现的滑动冲突问题

解决办法是自定义SwipeRefreshLayout,在AppBarLayout元素没有到顶时允许child向上滑,到顶后不允许滑动,这样就触发了下拉刷新。

public class CreditFragmentSwipeRefreshLayout extends SwipeRefreshLayout {
    private AppBarLayout targetView;

    public CreditFragmentSwipeRefreshLayout(Context context) {
        super(context);
    }

    public CreditFragmentSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        targetView = (AppBarLayout) findViewById(R.id.abl);
    }

    @Override
    public boolean canChildScrollUp() {
        if (targetView != null) {
            return targetView.getTop() != 0;
        } else {
            return super.canChildScrollUp();
        }
    }
}

Android中字体的加载

app/src/main/assets下面放置字体文件,然后使用下面的代码加载

class KTypeface {
    companion object {
        val DIN_PRO_MIDIUM =
            Typeface.createFromAsset(MyApp.getInstance().assets, "DINPro-Medium.otf")
        val ROBOTO_MIDIUM =
            Typeface.createFromAsset(MyApp.getInstance().assets, "Roboto-Medium.ttf")
    }
}

这么使用:

tvMyScore.typeface = KTypeface.DIN_PRO_MIDIUM

Android中字体最好加载一次之后缓存起来,因为每次加载需要消耗时间。

判断应用通知权限是否打开

/**
 * 只能检查KITKAT及以上的系统,以下会返回true
 */
public static boolean isNotificationEnabled(Context context) {
    NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
    return notificationManagerCompat.areNotificationsEnabled();
}

ViewPager实现画廊效果

实现原理很简单

  1. ViewPager的父布局需要设置clipChildren = false,同时最好设置背景色(pageMargin的部分需要背景色填充)
  2. ViewPager也需要设置clipChildren = false
  3. ViewPager宽度为MATCH_PARENT,但需要为其设置leftMarginrightMargin,这样才有画廊的效果
  4. 两张卡片之间的间距由ViewPagerpageMargin控制

ViewPager里面的子元素距离边框最好不要有padding以及margin,这样不利于控制效果

全面屏Splash以及广告页适配指北

SplashActivity适配

  1. 新建drawable-xxhdpi-2016x1080文件夹,里面放入为全面屏准备的开屏页(一般是1080x2160)
  2. SplashActivity的主题中加入<item name="android:windowBackground">@drawable/img_splash</item>(所谓程序秒开主要指这个)

上述直接放图片的方式不太适用了,因为手机高宽比越来越大,这样总会被拉长的。如果图片底部是纯色的话,可以使用layer-list包裹一下图片,其他位置用纯色就好了。

广告页适配(我家广告页就是与SplashActivity style一样的第二屏)
这个位置麻烦的地方就是需要显示从网络下载的图片,而且有多种尺寸,所以为了在全面屏上显示更好,我们需要加载一张1080x2160大小的图片然后显示。
所以,我们会先判断是不是全面屏,如果是就下载1080x2160大小的图片,否则一律下载1080x1920大小的图片。

// 下载图片的判断逻辑
String screenType = "1080x1920";
Point point = HRScreenUtils.Companion.getScreenSize(this);
if (HRScreenUtils.Companion.isFullScreen(point.x, point.y)) {
    screenType = "1080x2160";
}
Call<FestivalImgRes> call = HttpHelper.getApiService().getFestivalHead("android", screenType);

// ------------------------------
// HRScreenUtils.kt
class HRScreenUtils {
    companion object {
        fun isFullScreen(width: Int, height: Int) : Boolean = (1.0f * height / width) >= 1.86f

        fun getScreenSize(context: Context): Point {
            val point = Point()
            val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                windowManager.defaultDisplay.getRealSize(point)
            } else {
                windowManager.defaultDisplay.getSize(point)
            }

            return point
        }
    }
}

同样,这里需要考虑一下手机高宽比越来越大的情况。

震动反馈

不调用Vibrator实现震动反馈的两种方式 - 在ViewOnLongClickListener中返回true - 调用View#performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);方法

震动反馈能否发生还取决与系统的震动反馈设置,通过performHapticFeedback重载方法实现震动反馈可以忽略系统设置。

Android 5.0 平台共享元素动画

  1. 为两个Activity中需要进行动画的View取上相同的transitionName,若需要同时进行多个元素的动画,每个元素的transitionName都要不同
  2. 跳转Activity时,
  3. 若只有一个元素,可以调用startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, tv_right, getString(R.string.transition_name)).toBundle())
  4. 若多个元素,如下
    ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(getActivity(),
          new Pair<>(view.findViewById(R.id.iv_folder_icon), getContext().getString(R.string.transition_task_icon)),
          new Pair<>(view.findViewById(R.id.tv_folder_progress_value), getContext().getString(R.string.transition_task_progress_value)),
          new Pair<>(view.findViewById(R.id.pb_folder_progress), getContext().getString(R.string.transition_task_progress)),
          new Pair<>(view.findViewById(R.id.tv_folder_count), getContext().getString(R.string.transition_task_count)));
    Intent intent = new Intent(getContext(), TodoFolderActivity.class);
    getContext().startActivity(intent, activityOptions.toBundle());
    

由B返回A时,一般情况都也会默认进行共享元素动画返回,特殊情况下可以调用finishAfterTransition()进行共享元素动画返回

共享元素动画执行的过程也有监听方法,SharedElementCallback里面方法比较多,具体可以看源码注释

  • Activity#setEnterSharedElementCallback(SharedElementCallback)
  • Activity#setExitSharedElementCallback(SharedElementCallback)
  • Fragment#setEnterSharedElementCallback(SharedElementCallback)
  • Fragment#setExitSharedElementCallback(SharedElementCallback)
  • SharedElementCallback
  • onSharedElementStart
  • onSharedElementEnd
  • onRejectSharedElements
  • onMapSharedElements
  • onSharedElementsArrived

向WebView注入本地JS并调用

1.定义本地JS文件(test.js),放到assets目录下

'use strict';

function test() {
  $(".fake-box input").val('1');
  angular.element(document.getElementById('pwd-input')).scope().$apply('verify_code = "123458"');
}

2.定义webview注入本地js文件的方法

private void injectScriptFile(WebView view, String scriptFile) {
    InputStream input;
    try {
        input = getAssets().open(scriptFile);
        byte[] buffer = new byte[input.available()];
        input.read(buffer);
        input.close();

        // String-ify the script byte-array using BASE64 encoding !!!
        String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
        view.loadUrl("javascript:(function() {" +
                "var parent = document.getElementsByTagName('head').item(0);" +
                "var script = document.createElement('script');" +
                "script.type = 'text/javascript';" +
                // Tell the browser to BASE64-decode the string into your script !!!
                "script.innerHTML = window.atob('" + encoded + "');" +
                "parent.appendChild(script)" +
                "})()");
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

3.注入并调用

injectScriptFile(webView, "test.js");                   // 注入
webView.loadUrl("javascript:setTimeout(test(), 500)");  // 调用

NavigationView中的菜单添加分割线,只要给每个group添加id即可。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="navigation_view">

    <group
        android:id="@+id/group_all"
        android:checkableBehavior="single">
        <item
            android:id="@+id/nav_all"
            android:icon="@drawable/ic_menu_lightbulb"
            android:title="@string/nav_menu_all"
            android:checked="true"/>
    </group>

    <item android:title="@string/nav_menu_labels">
        <menu>
            <group android:checkableBehavior="single">
                <item
                    android:id="@+id/nav_new_label11"
                    android:icon="@drawable/ic_menu_lightbulb"
                    android:title="测试1"/>
                <item
                    android:id="@+id/nav_new_label"
                    android:icon="@drawable/ic_menu_lightbulb"
                    android:title="@string/nav_menu_create_new_label" />
            </group>
        </menu>
    </item>

    <group
        android:id="@+id/group_collection"
        android:checkableBehavior="single">
        <item
            android:id="@+id/nav_archive"
            android:icon="@drawable/ic_menu_lightbulb"
            android:title="@string/nav_menu_archive" />
        <item
            android:id="@+id/nav_trash"
            android:icon="@drawable/ic_menu_lightbulb"
            android:title="@string/nav_menu_trash" />
    </group>

    <group
        android:id="@+id/group_settings"
        android:checkableBehavior="single">
        <item
            android:id="@+id/nav_settings"
            android:icon="@drawable/ic_menu_lightbulb"
            android:title="@string/nav_menu_settings" />
        <item
            android:id="@+id/nav_help"
            android:icon="@drawable/ic_menu_lightbulb"
            android:title="@string/nav_menu_help_and_feedback" />
    </group>
</menu>

CardView背景色

CardView添加背景色要使用app:cardBackgroundColor,不然会导致app:cardCornerRadius不生效

从attr中提取资源id

从attr中提取资源id,可以使用TypedValue

val typedValue = TypedValue()
theme.resolveAttribute(android.R.attr.colorControlNormal, typedValue, true)
val resourceId = typedValue.resourceId

跳微信扫一扫

try {
    val intent = mActivity.packageManager.getLaunchIntentForPackage("com.tencent.mm")
    intent.putExtra("LauncherUI.From.Scaner.Shortcut", true)
    startActivity(intent)
} catch (e: Exception) {
    ToastUtils.showLongToast("无法跳转到微信,请检查您是否安装了微信!")
}

RadioGroup互斥

RadioGroup里面RadioButton最好都设置id,不然给其中一个设置为默认选中后,其他的RadioButton不会互斥。

不flatDir使用aar包

使用aar包不用flatDir,一句代码就好

implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])

自定义资源目录

android {
    sourceSets {
        main {
            java.srcDirs += 'src/main/kotlin'
            res.srcDirs = [
                    'src/main/res',
                    'src/main/res_overlay'
            ]
        }
    }
}

修改gradle输出包的名字

// -----------------------------------------------------------------------------------------
// build script
android {
    applicationVariants.all { variant ->
        variant.outputs.all { output ->
            def newName
            def timeNow
            if ("true" == IS_JENKINS) {
                // Jenkins编译
                newName = APP_NAME + '-' + variant.buildType.name + '-' + BUILD_TIME + '.apk'
                outputFileName = new File("../../../../..", newName)
            } else {
                // Android Studio编译
                timeNow = new Date().format("yyyyMMddHHmm")
                newName = APP_NAME + "-v" + variant.versionName + '-' + variant.buildType.name + '-' + timeNow + '.apk'
                outputFileName = newName
            }
        }
    }
}
// -----------------------------------------------------------------------------------------

gradle copy函数

android.libraryVariants.all {
    it.outputs.all {
        outputFileName = "hruilib-${version}-${it.name}.aar"
    }
}

project.afterEvaluate {
    android.libraryVariants.each {
        String variantName = it.name.capitalize()
        if (variantName == 'Release') {
            def assembleTask = project.tasks.getByName("assemble${variantName}")
            assembleTask.doLast {
                copy {
                    from('build/outputs/aar/')
                    into('../app/libs/')
                    include("*-release.aar")
                }
            }
        }
    }
}

ScrollView始终显示scrollbar

android:scrollbars="vertical"
android:fadeScrollbars="false"

实践效果:当可以滑动的时候会显示scollbar,不能滑动的时候不会显示。

AlertDialog消息换行

AlertDialog中\n不换行,调用create().show()就可以了。

accentColor

基础库中UI的style最好不要加上accentColor属性,不然上层可能TextView等各种崩溃

zip/unzip

zip -q -r \<zip_file_name> *

unzip \<zip_file_name> -d \<path>

Mac调整Launcher行列数

Mac调整Launcher一屏显示多少行、多少列,比如7行10列。

defaults write com.apple.dock springboard-rows -int 7
defaults write com.apple.dock springboard-columns -int 10
defaults write com.apple.dock ResetLaunchPad -bool TRUE;killall Dock

OkHttp下载文件

@Suppress("DEPRECATION")
private var progressDialog: ProgressDialog? = null
private var apkFile: File? = null
private fun doDownloadApk(versionRes: VersionRes) {
    apkFile = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "${BuildConfig.APPLICATION_ID}_${versionRes.version}.apk")
    // check file is exists or not
    if (apkFile?.exists() == true) {
        val deleted = apkFile?.delete()
        Logger.dTag(TAG_APP, "[SplashActivity] [doDownloadApk] apkFile exists, delete=$deleted")
    }

    // check download url is illegal
    val apkUrl = versionRes.channelUrl
    if (apkUrl.isNullOrEmpty()) {
        Logger.eTag(TAG_APP, "[SplashActivity] [doDownloadApk] apkUrl isNullOrEmpty >>>>>> ")
        viewModel.checkSwitchEnable()
        return
    }

    // show progress dialog
    @Suppress("DEPRECATION")
    progressDialog = ProgressDialog(this, R.style.HddAlertDialog).apply {
        setMessage(getString(R.string.download_apk_title))
        setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
        setCancelable(false)
        setCanceledOnTouchOutside(false)
        show()
    }

    // begin download
    Schedulers.newThread().createWorker().schedule {
        try {
            Logger.dTag(TAG_APP, "[SplashActivity] [doDownloadApk] download begin")
            val request = Request.Builder()
                .url(apkUrl)
                .build()
            val response = OkHttpClient.Builder().build().newCall(request).execute()

            // check response body
            val responseBody = response.body()
            if (responseBody == null) {
                viewModel.checkSwitchEnable()
                return@schedule
            }
            val inputStream = responseBody.byteStream()
            // get file size
            val contentLength = responseBody.contentLength()
            // set progress dialog max progress (shown in the bottom-right corner)
            // percent will be calculated by system
            progressDialog?.max = contentLength.toInt()
            Logger.dTag(TAG_APP, "[SplashActivity] [doDownloadApk] contentLength=$contentLength")

            // ready write files
            val fileOutputStream = FileOutputStream(apkFile)
            val bis = BufferedInputStream(inputStream)
            val buffer = ByteArray(1024)
            var length: Int
            var downloaded = 0

            // write files
            do {
                length = bis.read(buffer)
                if (length != -1) {
                    fileOutputStream.write(buffer, 0, length)
                    // update progress
                    downloaded += length
                    val percent = (downloaded * 100F / contentLength).toInt()
                    progressDialog?.progress = downloaded
                    Logger.dTag(TAG_APP, "[SplashActivity] [onProgress] progress = $percent")
                }
            } while (length != -1)
            fileOutputStream.flush()
            fileOutputStream.close()
            bis.close()
            inputStream.close()
            Logger.dTag(TAG_APP, "[SplashActivity] [doDownloadApk] download end")

            // write files done
            progressDialog?.dismiss()
            // install apk
            if (FileUtils.isFileExists(apkFile)) {
                @Suppress("DEPRECATION")
                AppUtils.installApp(this@SplashActivity, apkFile, Constants.FILE_PROVIDER, REQUEST_INSTALL_APP)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            runOnUiThread {
                YLToastUtils.showToast(
                    getString(R.string.download_apk_error_try_again, e.message),
                    duration = Toast.LENGTH_LONG
                )
                viewModel.checkSwitchEnable()
            }
        }
    }
}

monkey测试:

adb shell monkey -p <package-name> --throttle 100 --pct-touch 50 --pct-motion 50 -v -v <count> > <file_path>

App物料尺寸

app icon的尺寸:

logo (3:4:6:8:12:16)

name scale 普通 adaptive icon
ldip 0.75 108 81
mdpi 1 144 108
hdpi 1.5 192 162
xhdpi 2 256 216
xxhdpi 3 384 324
xxxhdpi 4 512 432

adaptive icon直接采用Android Studio中Image Asset工具进行生成即可。

两者可以共存:

  1. 在mipmap-*dpi中放入普通的icon,取名为ic_launcher
  2. 使用Image Asset工具生成adaptive icon,在Legacy选项卡中去除不需要的icon。这样会生成会在mipmap-*dpi目录下生成一套ic_launcher_foreground和一套ic_launcher_background,同时会在mipmap-anydpi-v26中生成一个ic_launcher.xml文件,里面会引用ic_launcher_foreground和ic_launcher_background资源。

引导页 - 2160×1080(适配全面屏) - 1920x1080 - 720x1280

屏幕快照 - 480x800 (vivo) - 1080x1920

StatusBar和NavigationBar完全透明

代码方式:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    window.statusBarColor = Color.TRANSPARENT
    window.navigationBarColor = Color.TRANSPARENT
}
window.setFlags(
    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
)
StatusBarUtil.setTransparent(this, false)

From StackOverflow

查看签名证书信息

keytool -v -list -keystore <keystore>

然后输入密码即可。

Dynamic Permission

Request App Permissions

Android在6.0及后对于危险权限需要动态申请,目前(2019-06-24)动态权限有以下10组共计26个权限,表格如下:

Permission groups

Dangerous permissions and permission groups.
Permission Group Permissions
CALENDAR READ_CALENDAR
WRITE_CALENDAR
CALL_LOG READ_CALL_LOG
WRITE_CALL_LOG
PROCESS_OUTGOING_CALLS
CAMERA CAMERA
CONTACTS READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATION ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATE
READ_PHONE_NUMBERS
CALL_PHONE
ANSWER_PHONE_CALLS
ADD_VOICEMAIL
USE_SIP
SENSORS BODY_SENSORS
SMS SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE

:您的应用仍需要明确请求其需要的每项权限,即使用户已向应用授予该权限组中的其他权限。此外,权限分组在将来的 Android 版本中可能会发生变化。您的代码不应依赖特定权限属于或不属于相同组这种假设。

在 Android 11 上,大部分权限都被移动到了 android.permission-group.UNDEFINED 这个权限组下面;但是实际表现上来说,权限还是和上面列表中的认知一样。这就导致我们判断权限组出现了很大的问题,我们无法运行时判断出哪些权限属于哪个组。
权限组的获取可以使用下面的命令: adb shell pm list permissions -d -g
代码中可以使用 packageManager.getAllPermissionGroups(PackageManager.GET_META_DATA) 获取权限组,然后根据权限组的 name 调用 packageManager.queryPermissionsByGroup(groupName, PackageManager.GET_META_DATA) 去获取权限组下面的权限。 系统权限请求框对权限的归类: https://cs.android.com/android/platform/superproject/+/master:packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java;drc=master;l=148

运行时权限的处理涉及到以下四个方法:

  • ContextCompat.checkSelfPermission
    检查权限是否已经授予
  • ActivityCompat.shouldShowRequestPermissionRationale 在用户已经拒绝某项权限时,用来向用户解释为什么需要权限。
    如果用户没有申请过、或者第二次或以上拒绝时选择了Don't ask again选项,此方法会返回false
    如果用户拒绝了权限请求,此方法会返回true
  • ActivityCompat.requestPermissions
    申请权限
  • onRequestPermissionsResult
    申请权限的回调

动态权限申请流程代码片段如下:

private fun permission() {
    val thisActivity = this

    // Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.

            // @@@ add by author
            showRequestPermissionRationale()
            // @@@
        } else {
            // No explanation needed, we can request the permission.
            ActivityCompat.requestPermissions(thisActivity,
                arrayOf(Manifest.permission.READ_CONTACTS),
                MY_PERMISSIONS_REQUEST_READ_CONTACTS)

            // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    } else {
        // Permission has already been granted
    }
}

override fun onRequestPermissionsResult(requestCode: Int,
                                        permissions: Array<String>, grantResults: IntArray) {
    when (requestCode) {
        MY_PERMISSIONS_REQUEST_READ_CONTACTS -> {

            // If request is cancelled, the result arrays are empty.
            if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                // permission was granted, yay! Do the
                // contacts-related task you need to do.
            } else {
                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return
        }

        // Add other 'when' lines to check for other
        // permissions this app might request.
        else -> {
            // Ignore all other requests.
        }
    }
}

private fun showRequestPermissionRationale() {
    AlertDialog.Builder(this)
        .setTitle("RequestPermissionRationale")
        .setMessage("This is the message")
        .setPositiveButton(android.R.string.ok) { _, _ ->
            ActivityCompat.requestPermissions(this,
                arrayOf(Manifest.permission.READ_CONTACTS),
                MY_PERMISSIONS_REQUEST_READ_CONTACTS)
        }.setNegativeButton(android.R.string.cancel, null)
        .create()
        .show()
}

companion object {
    private const val MY_PERMISSIONS_REQUEST_READ_CONTACTS = 0x1111
}

动态权限流程图

BTW,在权限请求回调的权限拒绝的分支中,此时我们再次调用ActivityCompat.shouldShowRequestPermissionRationale方法,如果返回true表示用户拒绝了权限;如果返回了false,表示用户拒绝了权限且勾选了不再提醒,此时我们弹框提醒用户。
可以参考PermissionDispatcher中生成的辅助代码——MainActivity.kt对应的辅助代码

Google Chrome强制禁用darkmode

defaults write com.google.Chrome NSRequiresAquaSystemAppearance -bool YES

ANR的判定

  1. Activity超过5秒无响应
  2. BroadcastReceiver超过10秒无响应
  3. Service超过20秒无响应

RecyclerView是否到顶部

RecyclerView.canScrollVertically(-1)

  • true 表示还没有到顶部
  • false 表示到顶部了

判断程序是否运行在主进程

私有多进程下,pid相等的进程可能是主进程或者子进程。获取当前进程id对应的进程,判断进程名是否是包名即可。

private fun isMainProcess(context: Context) : Boolean {
    val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager

    activityManager?.runningAppProcesses?.find {
        it.pid == Process.myPid()
    }?.let {
        return it.processName == context.packageName
    }

    return false
}

Drawable2Bitmap

通过Canvas将Drawable画到空白的Bitmap上。

private fun getBitmapFromDrawableResourceId(context: Context, @DrawableRes drawableId: Int): Bitmap {
    val drawable = ContextCompat.getDrawable(context, drawableId)!!
    val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
    drawable.draw(canvas)

    return bitmap
}

Gravity的妙用

在一个小红点需求中,由于想封装的更完整一点,所以设计了一个包裹普通View的小红点FrameLayout,该FrameLayout监听对应的小红点事件,从而显示或隐藏小红点。这样业务上的View一般情况无需处理小红点的相关逻辑。
所以在设计BadgeFrameLayout时,需要事先设置好小红点需要显示的位置,这里参考了gravity的一些实现:

BadgeFrameLayout使用下面属性来指定小红点的位置:

<declare-styleable name="BadgeFrameLayout">
    <!-- badge的方位 -->
    <attr name="badgeGravity" format="flags">
        <flag name="top" value="0x30" />
        <flag name="bottom" value="0x50" />
        <flag name="left" value="0x03" />
        <flag name="right" value="0x05" />
        <flag name="center_vertical" value="0x10" />
        <flag name="center_horizontal" value="0x01" />
        <flag name="center" value="0x11" />
    </attr>
    <!-- badge在X-Y方向上的偏移量,follow gravity -->
    <attr name="badgeXAdj" format="dimension" />
    <attr name="badgeYAdj" format="dimension" />
    ...
</declare-styleable>

在代码中这样来确定小红点的rect:

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)

    // 容器的rect
    mBadgeContainer.set(0, 0, measuredWidth, measuredHeight)
    // mBadgeRadius为小红点的半径,mBadgeRect就是计算出来的小红点的rect
    Gravity.apply(mGravity,
        mBadgeRadius shl 1,
        mBadgeRadius shl 1,
        mBadgeContainer,
        mBadgeXAdj,
        mBadgeYAdj,
        mBadgeRect
    )
}

Gravity.apply方法可以避免绘制超过边界,不需要我们自己做一些麻烦的运算,还是相当方便的。方法解释见: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/Gravity.java;l=186?q=Gravity.apply

9patch

长期不用还是会忘记,具体来说可以参考Google官网

左边上边的线条决定哪些区域可以被缩放,右边下边的线条决定哪个区域显示内容。

nm命令查看so里面的函数

 ~/Downloads/lib  nm -D libjavacore.so            
         U BN_CTX_free
         U BN_CTX_new
         U BN_add
         U BN_add_word
         U BN_bin2bn
         U BN_bn2bin
         U BN_bn2dec
         U BN_bn2hex
         U BN_bn2le_padded
         U BN_cmp
         U BN_copy
         U BN_dec2bn
         U BN_div
         U BN_exp
         U BN_free
         U BN_gcd
         U BN_generate_prime_ex
         U BN_get_u64
         U BN_hex2bn
         U BN_init
         U BN_is_bit_set
         U BN_is_negative
         U BN_is_pow2
         U BN_is_zero
         U BN_le2bn
         U BN_lshift
         U BN_mod_exp
         U BN_mod_inverse
         U BN_mod_word
         U BN_mul
         U BN_mul_word
         U BN_new
         U BN_nnmod
         U BN_nnmod_pow2
         U BN_num_bits
         U BN_num_bytes
         U BN_primality_test
         U BN_rshift
         U BN_set_negative
         U BN_set_u64
         U BN_sub
         U BN_zero
         U ERR_clear_error
         U ERR_error_string_n
         U ERR_get_error
0000efb0 T JNI_OnLoad
0000f130 T JNI_OnUnload
         U OPENSSL_free
         U UCNV_FROM_U_CALLBACK_SKIP_60
         U UCNV_FROM_U_CALLBACK_STOP_60
         U UCNV_TO_U_CALLBACK_STOP_60
         U XML_ErrorString
         U XML_ExternalEntityParserCreate
         U XML_GetCurrentColumnNumber
         U XML_GetCurrentLineNumber
         U XML_GetErrorCode
         U XML_Parse
         U XML_ParserCreate
         U XML_ParserCreateNS
         U XML_ParserFree
         U XML_SetCdataSectionHandler
         U XML_SetCharacterDataHandler
         U XML_SetCommentHandler
         U XML_SetDoctypeDeclHandler
         U XML_SetElementHandler
         U XML_SetExternalEntityRefHandler
         U XML_SetNamespaceDeclHandler
         U XML_SetNotationDeclHandler
         U XML_SetProcessingInstructionHandler
         U XML_SetReturnNSTriplet
         U XML_SetUnparsedEntityDeclHandler
         U XML_SetUserData
000313c0 T _Z11longValueOfP7_JNIEnvx
0000eef0 T _Z11setBlockingib
00031490 T _Z12booleanValueP7_JNIEnvP8_jobject
00015350 T _Z12lowestSetBitPy
00031220 T _Z13doubleValueOfP7_JNIEnvd
00016010 T _Z13floatExponentf
00015fe0 T _Z13floatMantissaf
000152b0 T _Z13highestSetBitPy
000212b0 W _Z13toStringArrayI13VectorCounter12VectorGetterEP13_jobjectArrayP7_JNIEnvPT_PT0_
         U _Z13toStringArrayP7_JNIEnvPKPKc
00031150 T _Z14booleanValueOfP7_JNIEnvh
00015fc0 T _Z14doubleExponentd
00015f80 T _Z14doubleMantissad
000312f0 T _Z14integerValueOfP7_JNIEnvi
         U _Z14newStringArrayP7_JNIEnvj
00014a20 T _Z16addHighPrecisionPyiS_i
0000f470 T _Z17toNativeZipStreamx
00015580 T _Z20compareHighPrecisionPyiS_i
0000e050 T _Z21fromStringEnumerationP7_JNIEnvR10UErrorCodePKcPN6icu_6017StringEnumerationE
0000eeb0 T _Z21inetAddressToSockaddrP7_JNIEnvP8_jobjectiR16sockaddr_storageRi
00014e00 T _Z21multiplyHighPrecisionPyiS_iS_i
0000e4c0 T _Z21sockaddrToInetAddressP7_JNIEnvRK16sockaddr_storagePi
00014b50 T _Z21subtractHighPrecisionPyiS_i
00015660 T _Z21toDoubleHighPrecisionPyi
0000e2b0 T _Z22maybeThrowIcuExceptionP7_JNIEnvPKc10UErrorCode
000149a0 T _Z22simpleAddHighPrecisionPyiy
0000e450 T _Z23jniThrowSocketExceptionP7_JNIEnvi
0000e410 T _Z24jniThrowOutOfMemoryErrorP7_JNIEnvPKc
0001ab70 T _Z24register_libcore_icu_ICUP7_JNIEnv
000310e0 T _Z24register_sun_misc_UnsafeP7_JNIEnv
000154d0 T _Z25lowestSetBitHighPrecisionPyi
00021d30 T _Z25register_libcore_io_LinuxP7_JNIEnv
000153e0 T _Z26highestSetBitHighPrecisionPyi
0000e3a0 T _Z26jniThrowExceptionWithErrnoP7_JNIEnvPKci
0002cba0 T _Z26register_libcore_io_MemoryP7_JNIEnv
0000f1e0 T _Z26throwExceptionForZlibErrorP7_JNIEnvPKciP15NativeZipStream
0001aef0 T _Z26unregister_libcore_icu_ICUP7_JNIEnv
000182b0 T _Z27register_java_math_NativeBNP7_JNIEnv
00015ac0 T _Z27timesTenToTheEHighPrecisionPyii
00015080 T _Z28simpleShiftLeftHighPrecisionPyii
0000e840 T _Z29inetAddressToSockaddrVerbatimP7_JNIEnvP8_jobjectiR16sockaddr_storageRi
00016030 T _Z31register_java_lang_StringToRealP7_JNIEnv
00019b40 T _Z32register_java_util_regex_MatcherP7_JNIEnv
0001a7e0 T _Z32register_java_util_regex_PatternP7_JNIEnv
000215f0 T _Z34register_libcore_icu_TimeZoneNamesP7_JNIEnv
0000f480 T _Z35register_android_system_OsConstantsP7_JNIEnv
00017ab0 T _Z35register_java_lang_invoke_VarHandleP7_JNIEnv
0001f620 T _Z36register_libcore_icu_NativeConverterP7_JNIEnv
00015020 T _Z37simpleAppendDecimalDigitHighPrecisionPyiy
000179f0 T _Z38register_java_lang_invoke_MethodHandleP7_JNIEnv
0002e820 T _Z43register_org_apache_harmony_xml_ExpatParserP7_JNIEnv
00021cb0 T _Z44register_libcore_io_AsynchronousCloseMonitorP7_JNIEnv
0002e7b0 T _Z46register_libcore_util_NativeAllocationRegistryP7_JNIEnv
000315f0 T _Z51register_org_apache_harmony_dalvik_NativeTestTargetP7_JNIEnv
00031540 T _Z8intValueP7_JNIEnvP8_jobject
0000e040 T _ZN11ExecStrings3getEv
0000de30 T _ZN11ExecStringsC1EP7_JNIEnvP13_jobjectArray
0000de30 T _ZN11ExecStringsC2EP7_JNIEnvP13_jobjectArray
0000df20 T _ZN11ExecStringsD1Ev
0000df20 T _ZN11ExecStringsD2Ev
         U _ZN12JniConstants11doubleClassE
         U _ZN12JniConstants11stringClassE
         U _ZN12JniConstants12booleanClassE
         U _ZN12JniConstants12integerClassE
         U _ZN12JniConstants13structIfaddrsE
         U _ZN12JniConstants14byteArrayClassE
         U _ZN12JniConstants15charsetICUClassE
         U _ZN12JniConstants15localeDataClassE
         U _ZN12JniConstants15structStatClassE
         U _ZN12JniConstants16inetAddressClassE
         U _ZN12JniConstants16structFlockClassE
         U _ZN12JniConstants16structUcredClassE
         U _ZN12JniConstants17gaiExceptionClassE
         U _ZN12JniConstants17inet6AddressClassE
         U _ZN12JniConstants17structLingerClassE
         U _ZN12JniConstants17structPasswdClassE
         U _ZN12JniConstants17structPollfdClassE
         U _ZN12JniConstants18structStatVfsClassE
         U _ZN12JniConstants18structTimevalClassE
         U _ZN12JniConstants18structUtsnameClassE
         U _ZN12JniConstants19errnoExceptionClassE
         U _ZN12JniConstants19fileDescriptorClassE
         U _ZN12JniConstants19structAddrinfoClassE
         U _ZN12JniConstants19structGroupReqClassE
         U _ZN12JniConstants19structTimespecClassE
         U _ZN12JniConstants22inetAddressHolderClassE
         U _ZN12JniConstants22inetSocketAddressClassE
         U _ZN12JniConstants22unixSocketAddressClassE
         U _ZN12JniConstants23inet6AddressHolderClassE
         U _ZN12JniConstants24packetSocketAddressClassE
         U _ZN12JniConstants25netlinkSocketAddressClassE
         U _ZN12JniConstants27patternSyntaxExceptionClassE
         U _ZN12JniConstants28inetSocketAddressHolderClassE
         U _ZN12JniConstants4initEP7_JNIEnv
         U _ZN12JniConstants9longClassE
0000f300 T _ZN15NativeZipStream13setDictionaryEP7_JNIEnvP11_jbyteArrayiib
0000f3d0 T _ZN15NativeZipStream8setInputEP7_JNIEnvP11_jbyteArrayii
0000f250 T _ZN15NativeZipStreamC1Ev
0000f250 T _ZN15NativeZipStreamC2Ev
0000f2b0 T _ZN15NativeZipStreamD1Ev
0000f2b0 T _ZN15NativeZipStreamD2Ev
         U _ZN24AsynchronousCloseMonitor20signalBlockedThreadsEi
         U _ZN24AsynchronousCloseMonitor4initEv
         U _ZN24AsynchronousCloseMonitorC1Ei
         U _ZN24AsynchronousCloseMonitorD1Ev
         U _ZN6icu_6010UnicodeSetC1Ev
         U _ZN6icu_6010UnicodeSetD1Ev
         U _ZN6icu_6012NumberFormat14createInstanceERKNS_6LocaleE18UNumberFormatStyleR10UErrorCode
         U _ZN6icu_6012RegexMatcher18useAnchoringBoundsEa
         U _ZN6icu_6012RegexMatcher20useTransparentBoundsEa
         U _ZN6icu_6012RegexMatcher4findEv
         U _ZN6icu_6012RegexMatcher4findExR10UErrorCode
         U _ZN6icu_6012RegexMatcher5resetEP5UText
         U _ZN6icu_6012RegexMatcher6regionExxR10UErrorCode
         U _ZN6icu_6012RegexMatcher7matchesER10UErrorCode
         U _ZN6icu_6012RegexMatcher9lookingAtER10UErrorCode
         U _ZN6icu_6012RegexPattern7compileERKNS_13UnicodeStringEjR11UParseErrorR10UErrorCode
         U _ZN6icu_6013BreakIterator22createSentenceInstanceERKNS_6LocaleER10UErrorCode
         U _ZN6icu_6013TimeZoneNames14createInstanceERKNS_6LocaleER10UErrorCode
         U _ZN6icu_6013UnicodeString10setToBogusEv
         U _ZN6icu_6013UnicodeString19getTerminatedBufferEv
         U _ZN6icu_6013UnicodeString5setToEaNS_14ConstChar16PtrEi
         U _ZN6icu_6013UnicodeString7toLowerERKNS_6LocaleE
         U _ZN6icu_6013UnicodeString7toTitleEPNS_13BreakIteratorERKNS_6LocaleEj
         U _ZN6icu_6013UnicodeString7toUpperERKNS_6LocaleE
         U _ZN6icu_6013UnicodeString8fromUTF8ENS_11StringPieceE
         U _ZN6icu_6013UnicodeStringC1EPKciNS0_10EInvariantE
         U _ZN6icu_6013UnicodeStringC1ERKS0_
         U _ZN6icu_6013UnicodeStringD1Ev
         U _ZN6icu_6013UnicodeStringaSERKS0_
         U _ZN6icu_6017DateFormatSymbolsC1ERKNS_6LocaleER10UErrorCode
         U _ZN6icu_6017DateFormatSymbolsD1Ev
         U _ZN6icu_6018UStringEnumeration5snextER10UErrorCode
         U _ZN6icu_6018UStringEnumerationC1EP12UEnumeration
         U _ZN6icu_6018UStringEnumerationD1Ev
         U _ZN6icu_6020DecimalFormatSymbolsC1ERKNS_6LocaleER10UErrorCode
         U _ZN6icu_6020DecimalFormatSymbolsD1Ev
         U _ZN6icu_6024DateTimePatternGenerator14createInstanceERKNS_6LocaleER10UErrorCode
         U _ZN6icu_6024DateTimePatternGenerator14getBestPatternERKNS_13UnicodeStringER10UErrorCode
         U _ZN6icu_606Locale10getDefaultEv
         U _ZN6icu_606Locale10setDefaultERKS0_R10UErrorCode
         U _ZN6icu_606Locale10setToBogusEv
         U _ZN6icu_606Locale14createFromNameEPKc
         U _ZN6icu_606Locale15getISOCountriesEv
         U _ZN6icu_606Locale15getISOLanguagesEv
         U _ZN6icu_606LocaleC1Ev
         U _ZN6icu_606LocaleD1Ev
         U _ZN6icu_606LocaleaSERKS0_
         U _ZN6icu_607UMemorydlEPv
         U _ZN6icu_608ByteSink15GetAppendBufferEiiPciPi
         U _ZN6icu_608ByteSink5FlushEv
         U _ZN6icu_608ByteSinkD2Ev
         U _ZN6icu_608Calendar14createInstanceERKNS_6LocaleER10UErrorCode
         U _ZN6icu_608Calendar6getNowEv
         U _ZN6icu_608TimeZone14getCanonicalIDERKNS_13UnicodeStringERS1_R10UErrorCode
         U _ZN6icu_608TimeZone16getTZDataVersionER10UErrorCode
         U _ZN7android22ClearJniConstantsCacheEv
00033320 T _ZN7android4base10LogMessage6streamEv
00033bc0 T _ZN7android4base10LogMessage7LogLineEPKcjNS0_5LogIdENS0_11LogSeverityES3_
00033870 T _ZN7android4base10LogMessage7LogLineEPKcjNS0_5LogIdENS0_11LogSeverityES3_S3_
00033330 T _ZN7android4base10LogMessageC1EPKcjNS0_5LogIdENS0_11LogSeverityES3_i
00033430 T _ZN7android4base10LogMessageC1EPKcjNS0_5LogIdENS0_11LogSeverityEi
00033330 T _ZN7android4base10LogMessageC2EPKcjNS0_5LogIdENS0_11LogSeverityES3_i
00033430 T _ZN7android4base10LogMessageC2EPKcjNS0_5LogIdENS0_11LogSeverityEi
00033470 T _ZN7android4base10LogMessageD1Ev
00033470 T _ZN7android4base10LogMessageD2Ev
00032a70 T _ZN7android4base10LogdLoggerC1ENS0_5LogIdE
00032a70 T _ZN7android4base10LogdLoggerC2ENS0_5LogIdE
00032a90 T _ZN7android4base10LogdLoggerclENS0_5LogIdENS0_11LogSeverityEPKcS5_jS5_
000331c0 T _ZN7android4base10SetAborterEONSt3__18functionIFvPKcEEE
00035730 T _ZN7android4base10StartsWithERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPKc
00035780 T _ZN7android4base10StartsWithERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_
00031ec0 T _ZN7android4base10WriteFullyEiPKvj
00032b10 T _ZN7android4base11InitLoggingEPPcONSt3__18functionIFvNS0_5LogIdENS0_11LogSeverityEPKcS8_jS8_EEEONS4_IFvS8_EEE
00032790 T _ZN7android4base12KernelLoggerENS0_5LogIdENS0_11LogSeverityEPKcS4_jS4_
00032960 T _ZN7android4base12StderrLoggerENS0_5LogIdENS0_11LogSeverityEPKcS4_jS4_
00034a10 T _ZN7android4base12StringPrintfEPKcz
000325e0 T _ZN7android4base13GetDefaultTagEv
000326a0 T _ZN7android4base13SetDefaultTagERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
00034a80 T _ZN7android4base13StringAppendFEPNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPKcz
00034920 T _ZN7android4base13StringAppendVEPNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPKcPc
00032a40 T _ZN7android4base14DefaultAborterEPKc
00031770 T _ZN7android4base14ReadFdToStringEiPNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
00031910 T _ZN7android4base15WriteStringToFdERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEi
00035a90 T _ZN7android4base16EqualsIgnoreCaseERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_
00031860 T _ZN7android4base16ReadFileToStringERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPS7_b
000321f0 T _ZN7android4base17GetExecutablePathEv
00031dc0 T _ZN7android4base17ReadFullyAtOffsetEiPvjx
00033c30 T _ZN7android4base17ScopedLogSeverityC1ENS0_11LogSeverityE
00033c30 T _ZN7android4base17ScopedLogSeverityC2ENS0_11LogSeverityE
00033c60 T _ZN7android4base17ScopedLogSeverityD1Ev
00033c60 T _ZN7android4base17ScopedLogSeverityD2Ev
00031c50 T _ZN7android4base17WriteStringToFileERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_b
000319a0 T _ZN7android4base17WriteStringToFileERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_tjjb
00035990 T _ZN7android4base18EndsWithIgnoreCaseERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPKc
00035a10 T _ZN7android4base18EndsWithIgnoreCaseERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_
00031f30 T _ZN7android4base18RemoveFileIfExistsERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPS7_
000357e0 T _ZN7android4base20StartsWithIgnoreCaseERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPKc
00035830 T _ZN7android4base20StartsWithIgnoreCaseERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_
00033300 T _ZN7android4base21GetMinimumLogSeverityEv
00033c00 T _ZN7android4base21SetMinimumLogSeverityENS0_11LogSeverityE
000322b0 T _ZN7android4base22GetExecutableDirectoryEv
00034f10 W _ZN7android4base4JoinINSt3__16vectorINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS7_IS9_EEEERKS9_EES9_RKT_T0_
00034ae0 W _ZN7android4base4JoinINSt3__16vectorINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS7_IS9_EEEEcEES9_RKT_T0_
00035170 W _ZN7android4base4JoinINSt3__16vectorIPKcNS2_9allocatorIS5_EEEERKNS2_12basic_stringIcNS2_11char_traitsIcEENS6_IcEEEEEESD_RKT_T0_
00034d10 W _ZN7android4base4JoinINSt3__16vectorIPKcNS2_9allocatorIS5_EEEEcEENS2_12basic_stringIcNS2_11char_traitsIcEENS6_IcEEEERKT_T0_
000355b0 T _ZN7android4base4TrimERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
00035390 T _ZN7android4base5SplitERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_
000323b0 T _ZN7android4base7DirnameERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
00032410 T _ZN7android4base8BasenameERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
00035890 T _ZN7android4base8EndsWithERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPKc
00035910 T _ZN7android4base8EndsWithERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_
00032010 T _ZN7android4base8ReadlinkERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPS7_
00032170 T _ZN7android4base8RealpathERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPS7_
00031d40 T _ZN7android4base9ReadFullyEiPvj
00033080 T _ZN7android4base9SetLoggerEONSt3__18functionIFvNS0_5LogIdENS0_11LogSeverityEPKcS6_jS6_EEE
         U _ZNK24AsynchronousCloseMonitor11wasSignaledEv
         U _ZNK6icu_6010UnicodeSet11containsAllERKS0_
         U _ZNK6icu_6012RegexMatcher10groupCountEv
         U _ZNK6icu_6012RegexMatcher10requireEndEv
         U _ZNK6icu_6012RegexMatcher3endEiR10UErrorCode
         U _ZNK6icu_6012RegexMatcher5startEiR10UErrorCode
         U _ZNK6icu_6012RegexMatcher6hitEndEv
         U _ZNK6icu_6012RegexPattern19groupNumberFromNameERKNS_13UnicodeStringER10UErrorCode
         U _ZNK6icu_6012RegexPattern7matcherER10UErrorCode
         U _ZNK6icu_6013UnicodeString6toUTF8ERNS_8ByteSinkE
         U _ZNK6icu_6013UnicodeString7extractENS_9Char16PtrEiR10UErrorCode
         U _ZNK6icu_6013UnicodeString8doEqualsERKS0_i
         U _ZNK6icu_6013UnicodeString9doCompareEiiPKDsii
         U _ZNK6icu_6017DateFormatSymbols11getWeekdaysERiNS0_13DtContextTypeENS0_11DtWidthTypeE
         U _ZNK6icu_6017DateFormatSymbols14getAmPmStringsERi
         U _ZNK6icu_6017DateFormatSymbols7getErasERi
         U _ZNK6icu_6017DateFormatSymbols9getMonthsERiNS0_13DtContextTypeENS0_11DtWidthTypeE
         U _ZNK6icu_6018UStringEnumeration5countER10UErrorCode
         U _ZNK6icu_606Locale11getBaseNameEv
         U _ZNK6icu_606Locale14getISO3CountryEv
         U _ZNK6icu_606Locale15getISO3LanguageEv
         U _ZNK6icu_606Locale16getDisplayScriptERKS0_RNS_13UnicodeStringE
         U _ZNK6icu_606Locale17getDisplayCountryERKS0_RNS_13UnicodeStringE
         U _ZNK6icu_606Locale17getDisplayVariantERKS0_RNS_13UnicodeStringE
         U _ZNK6icu_606Locale18getDisplayLanguageERKS0_RNS_13UnicodeStringE
         U _ZNK6icu_608Calendar17getFirstDayOfWeekEv
         U _ZNK6icu_608Calendar25getMinimalDaysInFirstWeekEv
00033d10 W _ZNKSt3__110__function6__funcIN7android4base10LogdLoggerENS_9allocatorIS4_EEFvNS3_5LogIdENS3_11LogSeverityEPKcSA_jSA_EE7__cloneEPNS0_6__baseISB_EE
00033cd0 W _ZNKSt3__110__function6__funcIN7android4base10LogdLoggerENS_9allocatorIS4_EEFvNS3_5LogIdENS3_11LogSeverityEPKcSA_jSA_EE7__cloneEv
00033e80 W _ZNKSt3__110__function6__funcIPFvPKcENS_9allocatorIS5_EES4_E7__cloneEPNS0_6__baseIS4_EE
00033e40 W _ZNKSt3__110__function6__funcIPFvPKcENS_9allocatorIS5_EES4_E7__cloneEv
         U _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEcj
00034680 W _ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv
         U _ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv
         U _ZNKSt3__121__basic_string_commonILb1EE20__throw_length_errorEv
         U _ZNKSt3__16locale9use_facetERNS0_2idE
         U _ZNKSt3__18ios_base6getlocEv
00033d50 W _ZNSt3__110__function6__funcIN7android4base10LogdLoggerENS_9allocatorIS4_EEFvNS3_5LogIdENS3_11LogSeverityEPKcSA_jSA_EE18destroy_deallocateEv
00033d40 W _ZNSt3__110__function6__funcIN7android4base10LogdLoggerENS_9allocatorIS4_EEFvNS3_5LogIdENS3_11LogSeverityEPKcSA_jSA_EE7destroyEv
00033d80 W _ZNSt3__110__function6__funcIN7android4base10LogdLoggerENS_9allocatorIS4_EEFvNS3_5LogIdENS3_11LogSeverityEPKcSA_jSA_EEclEOS7_OS8_OSA_SF_OjSF_
00033ec0 W _ZNSt3__110__function6__funcIPFvPKcENS_9allocatorIS5_EES4_E18destroy_deallocateEv
00033eb0 W _ZNSt3__110__function6__funcIPFvPKcENS_9allocatorIS5_EES4_E7destroyEv
00033ef0 W _ZNSt3__110__function6__funcIPFvPKcENS_9allocatorIS5_EES4_EclEOS3_
         U _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc
         U _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcj
         U _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKc
         U _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKcj
         U _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6resizeEjc
         U _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEj
         U _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc
         U _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_
         U _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_jjRKS4_
         U _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_
         U _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev
         U _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
         U _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
         U _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED2Ev
         U _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4syncEv
         U _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
         U _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5uflowEv
         U _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6setbufEPci
         U _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsgetnEPci
         U _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsputnEPKci
         U _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9showmanycEv
         U _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2Ev
         U _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED2Ev
00034220 W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE7seekoffExNS_8ios_base7seekdirEj
000344b0 W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE8overflowEi
00034450 W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9pbackfailEi
00034410 W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9underflowEv
         U _ZNSt3__115recursive_mutex4lockEv
         U _ZNSt3__115recursive_mutex6unlockEv
         U _ZNSt3__115recursive_mutexC1Ev
0001a4b0 W _ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_j
         U _ZNSt3__15ctypeIcE2idE
         U _ZNSt3__15mutex4lockEv
         U _ZNSt3__15mutex6unlockEv
         U _ZNSt3__16localeD1Ev
0002c510 W _ZNSt3__16vectorI5iovecNS_9allocatorIS1_EEE21__push_back_slow_pathIRKS1_EEvOT_
00021150 W _ZNSt3__16vectorINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS4_IS6_EEE21__push_back_slow_pathIRKS6_EEvOT_
00035ae0 W _ZNSt3__16vectorINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS4_IS6_EEE21__push_back_slow_pathIS6_EEvOT_
0002caa0 W _ZNSt3__16vectorIP13ScopedBytesRONS_9allocatorIS2_EEE21__push_back_slow_pathIS2_EEvOT_
0002c410 W _ZNSt3__16vectorIP13ScopedBytesRWNS_9allocatorIS2_EEE21__push_back_slow_pathIS2_EEvOT_
0002be90 W _ZNSt3__16vectorIP24AsynchronousCloseMonitorNS_9allocatorIS2_EEE21__push_back_slow_pathIS2_EEvOT_
00032470 W _ZNSt3__16vectorIcNS_9allocatorIcEEE8__appendEj
         U _ZNSt3__18ios_base4initEPv
         U _ZNSt3__18ios_base5clearEj
         U _ZNSt3__19basic_iosIcNS_11char_traitsIcEEED2Ev
         U _ZSt7nothrow
00044540 V _ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE
00044524 V _ZTTNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
         U _ZTVN6icu_6013UnicodeStringE
00044480 V _ZTVN6icu_6014StringByteSinkINSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEEE
000444b4 V _ZTVNSt3__110__function6__funcIN7android4base10LogdLoggerENS_9allocatorIS4_EEFvNS3_5LogIdENS3_11LogSeverityEPKcSA_jSA_EEE
000444d8 V _ZTVNSt3__110__function6__funcIPFvPKcENS_9allocatorIS5_EES4_EE
00044568 V _ZTVNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
000444fc V _ZTVNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
         U _ZTv0_n12_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
         U _ZTv0_n12_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
         U _ZdaPv
         U _ZdlPv
         U _Znaj
         U _ZnajRKSt9nothrow_t
         U _Znwj
         U __android_log_buf_print
         U __android_log_print
00045f98 A __bss_start
         U __cxa_atexit
         U __cxa_finalize
         U __cxa_guard_acquire
         U __cxa_guard_release
         U __errno
         U __libc_current_sigrtmax
         U __libc_current_sigrtmin
         U __memcpy_chk
         U __open_2
         U __pread_chk
         U __read_chk
         U __register_atfork
         U __stack_chk_fail
         U __stack_chk_guard
         U __strcat_chk
         U __strcpy_chk
         U __strlen_chk
         U __umask_chk
         U __vsnprintf_chk
00045f98 A _edata
00046418 A _end
         U abort
         U accept
         U access
         U android_getaddrinfofornet
         U android_set_abort_message
         U basename
         U bind
         U capget
         U capset
         U chmod
         U chown
         U clock_gettime
         U close
         U connect
         U deflateSetDictionary
         U dirname
         U dup
         U dup2
         U environ
         U execv
         U execve
         U fchmod
         U fchown
         U fcntl
         U fdatasync
         U fprintf
         U free
         U freeaddrinfo
         U freeifaddrs
         U fstat
         U fstatvfs
         U fsync
         U ftruncate64
         U gai_strerror
         U getegid
         U getenv
         U geteuid
         U getgid
         U getifaddrs
         U getnameinfo
         U getpeername
         U getpgid
         U getpid
         U getppid
         U getprogname
         U getpwnam_r
         U getpwuid_r
         U getrlimit
         U getsockname
         U getsockopt
         U gettid
         U getuid
         U getxattr
         U if_indextoname
         U if_nametoindex
         U inet_pton
         U inflateSetDictionary
         U ioctl
         U isatty
         U isspace
         U jniCreateFileDescriptor
         U jniCreateString
         U jniGetFDFromFileDescriptor
         U jniRegisterNativeMethods
         U jniSetFileDescriptorOfFD
         U jniStrError
         U jniThrowException
         U jniThrowExceptionFmt
         U jniThrowNullPointerException
         U kill
         U lchown
         U link
         U listen
         U listxattr
         U localtime_r
         U lseek64
         U lstat
         U madvise
         U malloc
         U memcpy
         U memmove
         U memset
         U mincore
         U mkdir
         U mkfifo
         U mlock
         U mmap
         U mmap64
         U msync
         U munlock
         U munmap
         U open
         U pipe2
         U poll
         U posix_fallocate64
         U pow
         U prctl
         U pread
         U pread64
         U pwrite64
         U read
         U readlink
         U readv
         U realpath
         U recvfrom
         U remove
         U removexattr
         U rename
         U sendfile
         U sendto
         U setegid
         U setenv
         U seteuid
         U setgid
         U setpgid
         U setregid
         U setreuid
         U setsid
         U setsockopt
         U setuid
         U setxattr
         U shutdown
         U socket
         U socketpair
         U splice
         U stat
         U statvfs
         U stderr
         U strcasecmp
         U strchr
         U strcmp
         U strcpy
         U strdup
         U strerror
         U strftime
         U strlen
         U strncasecmp
         U strncmp
         U strncpy
         U strrchr
         U strsignal
         U strtok_r
         U symlink
         U sysconf
         U tcdrain
         U tcsendbreak
         U time
         U u_cleanup_60
         U u_errorName_60
         U u_getUnicodeVersion_60
         U u_getVersion_60
         U u_init_60
         U u_strncpy_60
         U u_versionToString_60
         U ubrk_countAvailable_60
         U ubrk_getAvailable_60
         U ucal_countAvailable_60
         U ucal_getAvailable_60
         U ucnv_cbFromUWriteBytes_60
         U ucnv_cbToUWriteUChars_60
         U ucnv_close_60
         U ucnv_countAliases_60
         U ucnv_countAvailable_60
         U ucnv_fromUnicode_60
         U ucnv_getAlias_60
         U ucnv_getAvailableName_60
         U ucnv_getCanonicalName_60
         U ucnv_getFromUCallBack_60
         U ucnv_getInvalidChars_60
         U ucnv_getInvalidUChars_60
         U ucnv_getMaxCharSize_60
         U ucnv_getMinCharSize_60
         U ucnv_getStandardName_60
         U ucnv_getSubstChars_60
         U ucnv_getToUCallBack_60
         U ucnv_getUnicodeSet_60
         U ucnv_openStandardNames_60
         U ucnv_open_60
         U ucnv_resetFromUnicode_60
         U ucnv_resetToUnicode_60
         U ucnv_setFromUCallBack_60
         U ucnv_setToUCallBack_60
         U ucnv_toUnicode_60
         U ucol_countAvailable_60
         U ucol_getAvailable_60
         U ucurr_getDefaultFractionDigits_60
         U ucurr_getName_60
         U ucurr_getNumericCode_60
         U ucurr_isAvailable_60
         U ucurr_openISOCurrencies_60
         U udat_countAvailable_60
         U udat_getAvailable_60
         U udata_setCommonData_60
         U udata_setFileAccess_60
         U uloc_addLikelySubtags_60
         U uloc_countAvailable_60
         U uloc_getAvailable_60
         U uloc_getParent_60
         U ulocdata_getCLDRVersion_60
         U uname
         U unlink
         U unsetenv
         U unum_countAvailable_60
         U unum_getAvailable_60
         U ures_close_60
         U ures_getByIndex_60
         U ures_getByKey_60
         U ures_getStringByIndex_60
         U ures_getStringByKey_60
         U ures_getString_60
         U ures_getType_60
         U ures_openDirect_60
         U ures_open_60
         U utext_close_60
         U utext_openUChars_60
         U waitpid
         U write
         U writev
         U zError

将三方库上传到jcenter

./gradlew clean build bintrayUpload

jcenter已经废弃,现在使用jitpack

评论