LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

M.O.T

Magician.of.Technique

Shell脚本执行需要 root 权限的命令

Shell 2017/6/20

直接上代码,命令如下:

echo "passwd"|sudo -S command

passwd替换为你的root权限需要的密码,command 替换为需要执行的命令即可。

example:
比如 test.sh 脚本内容如下

#!/bin/bash
echo "123456"|sudo -S cp -rf  hosts.txt /etc/hosts
阅读全文

Mac上如何配置周期任务和 easyhosts 的使用

Mac 2017/6/20

easyhosts

该项目基于 Github 项目整合的远程 Hosts 直链,适配多种规则、终端,每30分钟自动同步一次Github最新可用项目并提供打包下载。所以我们可以替换本地的hosts文件,已达到访问Google,Facebook,Instagram等网站的目的。

这里,我们只需要周期执行脚本,更新我们的hosts文件就可以了。

首先为们clone一下工程

git clone https://github.com/forkgood/easyhosts.git

然后将我们的更新脚本放到clone的目录中。

比如更新hosts文件的脚本叫schedule-host.sh,其内容如下:

#!/bin/bash
git pull
# 将 pwd 替换为你电脑的登陆密码,也就是 root 权限需要的密码。
# mac 中需要使用 hosts.txt 文件
echo "pwd"|sudo -S cp -rf  hosts.txt /etc/hosts

接下来就是周期执行脚本了。

Mac上周期执行任务

主要有两种方式:

  • launchctl
  • Linux 上的 crontab

本次主要讲一下launchctl的使用。

1.创建plist文件

比如文件名为im.wangchao.schedulehosts.plist,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>im.wangchao.schedulehosts</string>   <!-- 名称 -->
    <key>ProgramArguments</key>
    <array>
        <string>/Users/xxx/xxx/easyhosts/schedule-host.sh</string>  <!-- 需要执行的脚本,绝对路径,这里直接使用上面创建的更新 hosts 的脚本 -->
    </array>
    <key>StartInterval</key>
      <integer>18000</integer>  <!-- 执行的周期间隔,单位为秒 -->
</dict>
</plist>

为了确保我们的plist文件正确,我们可以使用plutil -lint对文件进行校验。

2.保存plist文件

将我们刚刚创建的plist文件保存到/Library/LaunchAgents目录下即可。

  • ~/Library/LaunchAgents 由用户自己定义的任务项
  • /Library/LaunchAgents 由管理员为用户定义的任务项
  • /Library/LaunchDaemons 由管理员定义的守护进程任务项
  • /System/Library/LaunchAgents 由Mac OS X为用户定义的任务项
  • /System/Library/LaunchDaemons 由Mac OS X定义的守护进程任务项

其中/System/Library/Library~/Library目录的区别?

  • /System/Library目录是存放Apple自己开发的软件
  • /Library目录是系统管理员存放的第三方软件
  • ~/Library/是用户自己存放的第三方软件

LaunchDaemonsLaunchAgents的区别?

  • LaunchDaemons是用户未登陆前就启动的服务(守护进程)
  • LaunchAgents是用户登陆后启动的服务(守护进程)
3.加载plist文件

相关命令如下:

# 加载任务
launchctl load -w im.wangchao.schedulehosts.plist
# 卸载任务
launchctl unload -w im.wangchao.schedulehosts.plist
# 查看任务列表
launchctl list | grep 'im.wangchao.schedulehosts'
阅读全文

Https证书配置

Web 2017/1/19

这几天把自己的博客配置了Https证书,这里主要记录一下我的配置过程。

首先采用的是Let’s Encrypt颁发的免费证书,其次我是使用acme.sh配置的,这里主要说一下acme.sh的安装以及使用。

1.安装

官方方法使用如下命令即可:

curl  https://get.acme.sh | sh

如果执行失败,可以下载master分之最新代码,解压之后进入目录执行如下命令:

./acme.sh --install

安装成功后,acme.sh脚本在~/.acme.sh目录中,所以如果想直接执行脚本,那么需要设置环境变量,后文中因为配置过了环境变量所以直接执行了acme.sh脚本。

2.配置

安装成功后,我们需要创建一个存放Let’s Encrypt验证文件的文件夹(最好不要存放到root目录中),比如我们创建如下目录:

mkdir -p /www/acme-challenges
阅读全文

Android 5.0以下TLS1.x SSLHandshakeException

Android 2016/11/30

最近把App的所有请求都换成Https,在测试的时候,部分手机发现请求失败,失败的异常信息如下:

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x783e8e70: Failure in SSL library, usually a protocol error
error:14077102:SSL routines:SSL23_GET_SERVER_HELLO:unsupported protocol (external/openssl/ssl/s23_clnt.c:714 0x71a20cf8:0x00000000)

该异常为握手失败,但是为什么有的手机可以成功有的手机又失败了呢,首先查看我们服务端接口TLS支持的版本为1.x,后来发现失败的手机都是5.x以下的版本,推测应该是和这个有关,然后查阅官方文档,SSLSocket中有提到TLS版本和Android SDK版本的对应表,如下:

Protocol Supported (API Levels) Enabled by default (API Levels)
SSLv3 1+ 1+
TLSv1 1+ 1+
TLSv1.1 16+ 20+
TLSv1.2 16+ 20+

通过这个表看到,TLSv1.x(1.1,1.2)Android默认从API16开始支持,而从API20开始默认可用,这就可以解释之前为什么5.x以下手机在进行请求时失败了。

阅读全文

Android 7.0 适配

Android 2016/11/10

Android 7.0 也出一阵子了,在这里分享一些关于 Android 7.0 适配的心得体会。

首先你要知道 Android 7.0 强制执行了StrictMode API 政策,目录被限制了访问。所以在我们日常开发中建议开启StrictMode

在 Android 7.0 手机上,当我们调用相机照相,或者传递 file:// Uri 到其它 App 时,我们会遇到FileUriExposedException异常,这是因为强制执行StrictMode后,禁止向其它 App 公开 file:// Uri,也就是说当一个包含 file:// Uri 的的 Intent 离开当前 App 时候就会出现FileUriExposedException异常。

在 Android 7.0 上我们对应的解决方案就是使用FileProvider,获取一个content:// Uri 来完成我们的操作(当然低于7.0的版本,我们还用原来的方法即可)。

FileProvider

官方API

首先创建自己的 FileProvider,为什么要创建自己的FileProvider,当我们的工程引入第三方库时,难免会引入已经注册android.support.v4.content.FileProvider的库,那么我们AndroidManifest.xml就是合并失败,为了避免我们可以定义自己的FileProvider,如下:

package com.xxx.test.TestFileProvider;

...

public class TestProvider extends FileProvider {
    public static final String AUTHORITY = "com.xxx.test.fileprovider";

    public static Uri compatUriFromFile(Context context, File file){
        return compatUriFromFile(context, file, null);
    }

    public static Uri compatUriFromFile(Context context, File file, Intent intent) {
        if (!needUseProvider()){
            return Uri.fromFile(file);
        }

        if (intent != null){
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }

        return getUriForFile(context, AUTHORITY, file);
    }

    public static boolean needUseProvider(){
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
    }
}

然后在AndroidManifest.xml中注册我们的FileProvider,如下:

<provider
    android:name="com.xxx.test.TestProvider"
    android:authorities="com.xxx.test.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"/>
</provider>

注意exported:要求必须为false,若为true则会报安全异常grantUriPermissions:true,表示授予 URI 临时访问权限

接下来在res目录中创建xml目录,并在里面创建file_paths.xml文件。该文件中指定可以访问目录,如下:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.xxx.test/files/Pictures" />
</paths>

我们主要看里面的节点,可以为files-pathcache-pathexternal-pathexternal-files-pathexternal-cache-path

  • files-path 代表的根目录为:Context.getFilesDir()
  • cache-path 代表的根目录为:Context.getCacheDir()
  • external-path 代表的根目录为:Environment.getExternalStorageDirectory()
  • external-files-path 代表的根目录为:Context#getExternalFilesDir(String)
  • external-cache-path 代表的根目录为:Context.getExternalCacheDir()

节点的path属性代表相对目录,如果该属性设置为"",那么代表可以访问根目录的所有文件,如果像上面那样配置,那么则可以访问/storage/emulated/0/Android/data/com.xxx.test/files/Pictures目录。

配置完之后,我们就可以使用我们创建的FileProvider了,只需要将之前的Uri.fromFile()替换成TestProvider.compatUriFromFile()即可,比如调起照相机:

Intent photoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = null;
try {
    // 创建文件
    file = createImageFile();
} catch (IOException e) {
    e.printStackTrace();
}
if (file != null){
    photoIntent.putExtra(MediaStore.EXTRA_OUTPUT, TestProvider.compatUriFromFile(this, file, photoIntent));
    startActivityForResult(photoIntent, CAPTURE_CUSTOM_FILE_REQUEST_CODE);
}

其他

低电耗模式

在低电耗模式下,当用户设备未插接电源、处于静止状态且屏幕关闭时,该模式会推迟 CPU 和网络活动,从而延长电池寿命。
Android7.0通过在设备未插接电源且屏幕关闭状态下、但不一定要处于静止状态(例如用户外出时把手持式设备装在口袋里)时应用部分 CPU 和网络限制,进一步增强了低电耗模式。(也就是说,Android7.0会在手机屏幕关闭的状态下,限时应用对CPU以及网络的使用。)

具体规则如下:

  1. 当设备处于充电状态且屏幕已关闭一定时间后,设备会进入低电耗模式并应用第一部分限制: 关闭应用网络访问、推迟作业和同步。
  2. 如果进入低电耗模式后设备处于静止状态达到一定时间,系统则会对 PowerManager.WakeLock、AlarmManager、GPS 和 Wi-Fi 扫描应用余下的低电耗模式限制。 无论是应用部分还是全部低电耗模式限制,系统都会唤醒设备以提供简短的维护时间窗口,在此窗口期间,应用程序可以访问网络并执行任何被推迟的作业/同步。

后台优化

官方文档
Android 7.0中删除了三项隐式广播,以帮助优化内存使用和电量消耗。

  1. 在 Android 7.0上 应用不会收到 CONNECTIVITY_ACTION 广播,即使你在manifest清单文件中设置了请求接受这些事件的通知。 但在前台运行的应用如果使用BroadcastReceiver请求接收通知,则仍可以在主线程中侦听CONNECTIVITY_CHANGE
  2. 在 Android 7.0上应用无法发送或接收 ACTION_NEW_PICTUREACTION_NEW_VIDEO 类型的广播。

Android 框架提供多个解决方案来缓解对这些隐式广播的需求。 例如,JobScheduler API提供了一个稳健可靠的机制来安排满足指定条件(例如连入无限流量网络)时所执行的网络操作。 您甚至可以使用 JobScheduler API 来适应内容提供程序变化。

移动设备会经历频繁的连接变更,例如在 Wi-Fi 和移动数据之间切换时。 目前,可以通过在应用清单中注册一个接收器来侦听隐式 CONNECTIVITY_ACTION 广播,让应用能够监控这些变更。 由于很多应用会注册接收此广播,因此单次网络切换即会导致所有应用被唤醒并同时处理此广播。

阅读全文

Android Activity启动模式与影响任务栈的故事

Android 2016/11/10

引言

Android开发中,Activity的使用是不可或缺的,在使用Activity的过程中通常会给特殊的Activity设置不同的启动模式,而这些启动模式也影响着Activity所在的任务栈,今天就先从Activity的启动模式说起,然后总结一些关于影响Activity任务栈的那些事。

Activity启动模式

1.standard

Activity标准启动模式,也是默认启动模式。在这种模式下启动的Activity可以被多次实例化,也就是说在同一个任务栈中可以存在多个Activity的实例,每个实例都会处理一个Intent对象。

比如,我们有一个MainActivity,MainActivity的代码如下

public class MainActivity extends AppCompatActivity {
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.mTestBtn).setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, MainActivity.class);
                //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
            }
        });
        setTitle(getClass().getSimpleName());
        Log.e("wcwcwc", getClass().getSimpleName() + " onCreate Task ID : " + getTaskId());
    }
}

执行点击按钮跳转MainActivity,此时控制台的日志如下:

11-09 14:54:16.528 21613-21613/im.wangchao.launchemode E/wcwcwc: MainActivity onCreate Task ID : 5634
11-09 14:54:18.198 21613-21613/im.wangchao.launchemode E/wcwcwc: MainActivity onCreate Task ID : 5634

可以看出创建的多个MainActivity的实例,并且它们是在同一个任务栈中。

阅读全文

Java,Android动态代理你需要知道的一些事

Java 2016/10/19

代理模式

首先,什么是代理,拿出你的手机,打开微信朋友圈,看看朋友圈的微商,这就是代理。代理就是一个对象代表代表另一个对象做其需要做的事,就像刚才说的微商,你的朋友就是代理了原厂商。现在大概知道了代理是什么,那么Android中有哪些代理呢,举个最简单的例子,我们都知道__Context__,__Context__就是使用了代理模式,__ContextImpl__实现了__Context__的所有功能,ContextWrapper__即为代理类,也实现了__Context__类,里面包含的__Context__引用为__ContextImpl,所以__ContextWrapper__调用的方法都是__ContextImpl__中的实现,这就是典型的代理模式。

阅读全文

Electron 打包

Electron 2016/10/12

最近简单看了下Electron,Electron是什么?引用官网的一句话__Build cross platform desktop apps with JavaScript, HTML, and CSS__。简单的看了一下API然后做了一个小Demo,但是最后打包的时候还是查了一些资料,下面对打包做一个简单的介绍。

阅读全文

android.os.BadParcelableException

Android 2016/10/12

异常说明

今天在测试工程的时候,出现如下异常:

Caused by android.os.BadParcelableException: ClassNotFoundException when unmarshalling:
...
android.os.Parcel.readParcelableCreator (Parcel.java:2203)
android.view.View$BaseSavedState. (View.java:18696)
...
阅读全文

Android Bolts Task 从入门到放弃(二)

Android 2016/9/10

上一篇文章关于__Bolts Task__的使用做了简单的说明,这次我们注意解析一下__Bolts Task__的源码,来看看它具体是怎么实现的。

阅读全文
头像
Wang Chao
正しさなんてもの
人のモノサシによって変わる