-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 254 KB
/
content.json
1
{"meta":{"title":"Hexo | Kavan","subtitle":"","description":"","author":"Kavan","url":"https://laujiangtao.github.io","root":"/"},"pages":[{"title":"404 Not Found:该页无法显示","date":"2021-05-15T15:50:05.820Z","updated":"2021-05-15T15:50:05.820Z","comments":false,"path":"/404.html","permalink":"https://laujiangtao.github.io/404.html","excerpt":"","text":""},{"title":"书单","date":"2021-05-15T15:50:07.204Z","updated":"2021-05-15T15:50:07.204Z","comments":false,"path":"books/index.html","permalink":"https://laujiangtao.github.io/books/index.html","excerpt":"","text":""},{"title":"关于","date":"2021-05-15T15:50:07.204Z","updated":"2021-05-15T15:50:07.204Z","comments":false,"path":"about/index.html","permalink":"https://laujiangtao.github.io/about/index.html","excerpt":"","text":"本博所言,仅代表个人观点"},{"title":"分类","date":"2021-05-15T15:50:07.204Z","updated":"2021-05-15T15:50:07.204Z","comments":false,"path":"categories/index.html","permalink":"https://laujiangtao.github.io/categories/index.html","excerpt":"","text":""},{"title":"外部链接","date":"2021-05-15T15:50:07.204Z","updated":"2021-05-15T15:50:07.204Z","comments":false,"path":"links/index.html","permalink":"https://laujiangtao.github.io/links/index.html","excerpt":"","text":""},{"title":"音乐","date":"2021-05-12T09:52:04.000Z","updated":"2021-05-25T23:54:31.844Z","comments":false,"path":"music/index.html","permalink":"https://laujiangtao.github.io/music/index.html","excerpt":"","text":""},{"title":"标签","date":"2021-05-15T15:50:07.205Z","updated":"2021-05-15T15:50:07.205Z","comments":false,"path":"tags/index.html","permalink":"https://laujiangtao.github.io/tags/index.html","excerpt":"","text":""},{"title":"Repositories","date":"2021-05-15T15:50:07.205Z","updated":"2021-05-15T15:50:07.205Z","comments":false,"path":"repository/index.html","permalink":"https://laujiangtao.github.io/repository/index.html","excerpt":"","text":""}],"posts":[{"title":"Android MQTT实现","slug":"Android-MQTT实现","date":"2021-09-23T09:09:20.000Z","updated":"2021-09-24T00:14:50.907Z","comments":false,"path":"2021/09/23/android-mqtt/","link":"","permalink":"https://laujiangtao.github.io/2021/09/23/android-mqtt/","excerpt":"","text":"一、Apache Apollo服务器下载Apache Apollo并解压 下载地址 解压 1tar -zxvf 压缩文件名.tar.gz 服务搭建方式 1、命令行进入解压后bin目录(例:E:>cd E:\\MQTT\\apache-apollo-1.7.1\\bin)。 2、输入./apollo create xxx(xxx为创建的服务器实例名称,例:./apollo create ~/apche-apollo/mybroker),之后会在用户目录下创建名称文件夹。 You can now start the broker by executing: “/Users/kavan/apche-apollo/mybroker/bin/apollo-broker” run Or you can run the broker in the background using: “/Users/kavan/apche-apollo/mybroker/bin/apollo-broker-service” start 在bin目录下运行./apollo-broker-service start开启服务,在浏览器中输入http://127.0.0.1:61680打开登录页面 XXX文件夹下etc\\apollo.xml文件下是配置服务器信息的文件。 etc\\users.properties文件包含连接MQTT服务器时用到的用户名和密码,默认为admin=password,即账号为admin,密码为password,可自行更改。 二、Android端实现添加mqtt依赖所在的maven仓库 1maven { url "https://repo.eclipse.org/content/repositories/paho-releases/" } 导入依赖 12implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.1' AndroidManifest 123456<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.WAKE_LOCK" /><!-- Mqtt Service --><service android:name="org.eclipse.paho.android.service.MqttService" /><service android:name=".mqtt.MqttService" /> Android Mqtt Service 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207import android.app.Service;import android.content.Context;import android.content.Intent;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.os.Binder;import android.os.IBinder;import android.util.Log;import android.widget.Toast;import org.eclipse.paho.android.service.MqttAndroidClient;import org.eclipse.paho.client.mqttv3.IMqttActionListener;import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;import org.eclipse.paho.client.mqttv3.IMqttToken;import org.eclipse.paho.client.mqttv3.MqttCallback;import org.eclipse.paho.client.mqttv3.MqttConnectOptions;import org.eclipse.paho.client.mqttv3.MqttException;import org.eclipse.paho.client.mqttv3.MqttMessage;public class MqttService extends Service { public static final String TAG = MqttService.class.getSimpleName(); public static MqttAndroidClient client; private MqttConnectOptions mqttConnectOptions; // 服务器地址(协议+地址+端口号) private String host = "tcp://10.0.2.2:61613"; private String userName = "admin"; private String passWord = "password"; private static String topic = "MqttTestTopic";//要订阅的主题 private String clientId = "AndroidId";//客户端标识 private IMessageCallBack iMessageCallBack; public MqttService() { } @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate"); init(); } public static void publish(String msg) { Integer qos = 0; Boolean retained = false; try { if (client != null) { client.publish(topic, msg.getBytes(), qos.intValue(), retained.booleanValue()); } } catch (MqttException e) { e.printStackTrace(); } } private void init() { client = new MqttAndroidClient(this, host, clientId); // 设置MQTT监听并且接受消息 client.setCallback(mqttCallback); mqttConnectOptions = new MqttConnectOptions(); // 清除缓存 mqttConnectOptions.setCleanSession(true); // 设置超时时间,单位:秒 mqttConnectOptions.setConnectionTimeout(10); // 心跳包发送间隔,单位:秒 mqttConnectOptions.setKeepAliveInterval(20); // 用户名 mqttConnectOptions.setUserName(userName); // 密码 mqttConnectOptions.setPassword(passWord.toCharArray()); //将字符串转换为字符串数组 // last will message boolean doConnect = true; String message = "{\\"clientId\\":\\"" + clientId + "\\"}"; Log.i(TAG, "message:" + message); Integer qos = 0; Boolean retained = true; // MQTT本身就是为信号不稳定的网络设计的,所以难免一些客户端会无故的和Broker断开连接。 //当客户端连接到Broker时,可以指定LWT,Broker会定期检测客户端是否有异常。 //当客户端异常掉线时,Broker就往连接时指定的topic里推送当时指定的LWT消息。 try { mqttConnectOptions.setWill(topic, message.getBytes(), qos.intValue(), retained.booleanValue()); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, e.toString()); doConnect = false; iMqttActionListener.onFailure(null, e); } if (doConnect) { doConnection(); } } @Override public void onDestroy() { stopSelf(); try { client.disconnect(); } catch (MqttException e) { e.printStackTrace(); } super.onDestroy(); } /** * 连接MQTT服务器 */ private void doConnection() { if (!client.isConnected() && isConnectIsNormal()) { try { client.connect(mqttConnectOptions, null, iMqttActionListener); } catch (MqttException e) { e.printStackTrace(); } } } /** * MQTT连接状态监听 */ private final IMqttActionListener iMqttActionListener = new IMqttActionListener() { @Override public void onSuccess(IMqttToken arg0) { Log.i(TAG, "连接成功 "); try { // 订阅topic话题 client.subscribe(topic, 1); } catch (MqttException e) { e.printStackTrace(); } } @Override public void onFailure(IMqttToken arg0, Throwable arg1) { Log.i(TAG, "连接失败"); arg1.printStackTrace(); // 连接失败,重连 } }; /** * MQTT消息监听 */ private final MqttCallback mqttCallback = new MqttCallback() { @Override public void messageArrived(String topic, MqttMessage message) throws Exception { String str = new String(message.getPayload()); Log.i(TAG, "messageArrived: " + str); if (iMessageCallBack != null) { iMessageCallBack.setMessage(str); } Toast.makeText(MqttService.this, str, Toast.LENGTH_SHORT).show(); Log.i(TAG, topic + "\\n qos: " + message.getQos() + "\\n retained: " + message.isRetained()); } @Override public void deliveryComplete(IMqttDeliveryToken arg0) { Log.i(TAG, "deliveryComplete: "); } @Override public void connectionLost(Throwable arg0) { // 失去连接,重连 Log.i(TAG, "connectionLost: "); } }; /** * 判断网络是否连接 */ private boolean isConnectIsNormal() { ConnectivityManager connectivityManager = (ConnectivityManager) this.getApplicationContext() .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = connectivityManager.getActiveNetworkInfo(); if (info != null && info.isAvailable()) { String name = info.getTypeName(); Log.i(TAG, "当前网络名称:" + name); return true; } else { Log.i(TAG, "没有可用网络"); return false; } } @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind"); return new MqttBinder(); } public void setMessageCallBack(IMessageCallBack iMessageCallBack) { this.iMessageCallBack = iMessageCallBack; } public class MqttBinder extends Binder { public MqttService getService() { return MqttService.this; } }} Android MqttServiceConnection 12345678910111213141516171819202122232425262728293031import android.content.ComponentName;import android.content.ServiceConnection;import android.os.IBinder;/** * @author jiangtao on 2021/9/23 */public class MqttServiceConnection implements ServiceConnection { private MqttService mqttService; private IMessageCallBack iMessageCallBack; @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mqttService = ((MqttService.MqttBinder)iBinder).getService(); mqttService.setMessageCallBack(iMessageCallBack); } @Override public void onServiceDisconnected(ComponentName componentName) { } public MqttService getMqttService(){ return mqttService; } public void setMessageCallBack(IMessageCallBack iMessageCallBack){ this.iMessageCallBack = iMessageCallBack; }} 消息回调接口 123456/** * @author jiangtao on 2021/9/23 */public interface IMessageCallBack { public void setMessage(String message);} Android Activity实现 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869import androidx.appcompat.app.AppCompatActivity;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import cn.ljt.myapplication.R;import cn.ljt.myapplication.inject.butterknife.BindView;import cn.ljt.myapplication.inject.butterknife.InjectView;import cn.ljt.myapplication.inject.click.InjectClick;import cn.ljt.myapplication.inject.click.OnClick;public class MqttTestActivity extends AppCompatActivity implements IMessageCallBack { @BindView(R.id.text) public TextView textView; @BindView(R.id.btn) public Button button; private MqttServiceConnection serviceConnection; private MqttService mqttService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mqtt_test); InjectView.inject(this); InjectClick.inject(this); serviceConnection = new MqttServiceConnection(); serviceConnection.setIGetMessageCallBack(this); Intent intent = new Intent(this, MqttService.class); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } @OnClick(R.id.btn) public void onClick(View view) { switch (view.getId()) { case R.id.btn: Log.i("TAG", "onClick: 测试"); MqttService.publish("Android自己的消息"); break; } } @Override public void setMessage(String message) { String trim = textView.getText().toString().trim() + "\\n"; textView.setText(trim + message); mqttService = serviceConnection.getMqttService(); } @Override public void onPointerCaptureChanged(boolean hasCapture) { Log.i("TAG", "onPointerCaptureChanged: " + hasCapture); } @Override protected void onDestroy() { unbindService(serviceConnection); super.onDestroy(); }} 三、Java服务端实现pom文件导入依赖 123456<!-- https://repo.eclipse.org/content/repositories/paho-releases/org/eclipse/paho/org.eclipse.paho.client.mqttv3/ --><dependency> <groupId>org.eclipse.paho</groupId> <artifactId>org.eclipse.paho.client.mqttv3</artifactId> <version>1.1.1</version></dependency> Server 程序入口 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475import org.eclipse.paho.client.mqttv3.MqttClient;import org.eclipse.paho.client.mqttv3.MqttConnectOptions;import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;import org.eclipse.paho.client.mqttv3.MqttException;import org.eclipse.paho.client.mqttv3.MqttMessage;import org.eclipse.paho.client.mqttv3.MqttPersistenceException;import org.eclipse.paho.client.mqttv3.MqttTopic;import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;import java.util.Scanner;public class Main { public static final String HOST = "tcp://localhost:61613"; public static final String TOPIC = "MqttTestTopic"; private static final String clientId = "kavan_server"; private MqttClient client; private MqttTopic topic; private String userName = "admin"; private String passWord = "password"; private MqttMessage message; public Main() throws MqttException { //MemoryPersistence设置clientId的保存形式,默认为以内存保存 client = new MqttClient(HOST, clientId, new MemoryPersistence()); connect(); } private void connect() { MqttConnectOptions options = new MqttConnectOptions(); options.setCleanSession(true); options.setUserName(userName); options.setPassword(passWord.toCharArray()); // 设置超时时间 options.setConnectionTimeout(60); // 设置会话心跳时间 options.setKeepAliveInterval(20); try { client.setCallback(new PushCallback()); client.connect(options); topic = client.getTopic(TOPIC); } catch (Exception e) { e.printStackTrace(); } } public void publish(MqttMessage message) throws MqttPersistenceException, MqttException { MqttDeliveryToken token = topic.publish(message); token.waitForCompletion(); System.out.println("isComplete: " + token.isComplete()); } public static void main(String[] args) throws MqttException { // write your code here Main server = new Main(); server.message = new MqttMessage(); server.message.setQos(1); server.message.setRetained(true); server.message.setPayload("这是Server发送给Android的消息".getBytes()); while (true) { System.out.println("输入send发送消息:\\n"); Scanner scanner = new Scanner(System.in); if (scanner.hasNext()) { String str1 = scanner.next(); if ("send".equals(str1)) { server.publish(server.message); System.out.println("retained:" + server.message.isRetained()); } } } }} 回调函数 PushCallback 12345678910111213141516171819202122232425262728import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;import org.eclipse.paho.client.mqttv3.MqttCallback;import org.eclipse.paho.client.mqttv3.MqttMessage;public class PushCallback implements MqttCallback { @Override public void connectionLost(Throwable arg0) { // 连接丢失后,一般在这里面进行重连 System.out.println("连接断开,可以做重连"); } @Override public void deliveryComplete(IMqttDeliveryToken token) { // publish后会执行到这里 System.out.println("deliveryComplete: "+ token.isComplete()); } @Override public void messageArrived(String topic, MqttMessage message) throws Exception { // subscribe后得到的消息会执行到这里面 System.out.println("订阅的字符串:"+topic); System.out.println("消息内容:"+message.toString()); }} 四、测试启动Apollo服务 将Android App和Java后台程序启动,链接上服务器,如图: 启动服务端程序,发送信息,如图: 手机端接收到信息,如图:","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"MQTT","slug":"MQTT","permalink":"https://laujiangtao.github.io/tags/MQTT/"}]},{"title":"仿butterknife注解","slug":"仿butterknife注解","date":"2021-09-05T04:14:01.000Z","updated":"2021-09-05T09:26:04.965Z","comments":false,"path":"2021/09/05/my-butter-knife/","link":"","permalink":"https://laujiangtao.github.io/2021/09/05/my-butter-knife/","excerpt":"","text":"View注解1、声明变量上面的注解,约束注解使用范围2、添加注解保留级别为运行时,使得注解能被JVM或其他使用反射机制的代码所读取和使用 12345678910import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface BindView { int value() default -1;} 注入方法实现1、创建注解类,传入当前activity,后续会使用到activity里面的findviewbyid方法2、获取到activity里面的所有变量属性并遍历(注解消耗性能就在这里)3、判断变量属性上面是否存在上面声明的注解4、判断变量是否为public,不为public5、如果存在则取出注解并获取注解里面的值6、获取到findViewById方法,通过反射给属性赋值 12345678910111213141516171819202122232425262728293031323334import android.app.Activity;import java.lang.reflect.Field;import java.lang.reflect.Method;public class InjectView { public static void inject(Activity activity) { if (null == activity) return; Class<? extends Activity> activityClass = activity.getClass(); Field[] declaredFields = activityClass.getDeclaredFields(); for (Field field : declaredFields) { if (field.isAnnotationPresent(BindView.class)) { //public 1 private 2 protected 4 不写是0 if (field.getModifiers() != 1) { throw new IllegalArgumentException(field + " should be public."); } BindView bindView = field.getAnnotation(BindView.class); int value = bindView.value(); try { Method findViewById = activityClass.getMethod("findViewById", int.class); findViewById.setAccessible(true); Object view = findViewById.invoke(activity, value); field.set(activity, view); } catch (Exception e) { e.printStackTrace(); } } } }} onClick注解点击时间常用的有两种,onClick和onLongClick,所以要区分这两种注解,需要给注解添加原注解以区分 1234567891011import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.ANNOTATION_TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface ClickType { Class clickType(); String methodNameString();} 然后声明两个点击时间的注解 123456789101112131415161718192021222324252627import android.view.View;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@ClickType(clickType = View.OnClickListener.class, methodNameString = "setOnClickListener")public @interface OnClick { int[] value();}import android.view.View;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@ClickType(clickType = View.OnLongClickListener.class, methodNameString = "setOnLongClickListener")public @interface OnLongClick { int[] value();} 注入方法实现1、获取activity内的方法2、获取方法上面的注解3、判断注解类型是否为ClickType4、如果是,则取出注解内的值(点击事件类型和需要反射的方法名)5、判断方法是否为onLongClickListener,如果是,方法返回值必须为boolean型6、获取直接内的value方法,并获取里面的值7、声明点击事件动态代理,并为每一个注解中元素对应的view设置点击代理 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869import android.app.Activity;import android.view.View;import java.lang.annotation.Annotation;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class InjectClick { public static void inject(Activity activity) { Class<? extends Activity> activityClass = activity.getClass(); Method[] declaredMethods = activityClass.getDeclaredMethods(); for (Method method : declaredMethods) { Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { Class<? extends Annotation> annotationType = annotation.annotationType(); if (annotationType.isAnnotationPresent(ClickType.class)) { ClickType eventType = annotationType.getAnnotation(ClickType.class); Class clickType = eventType.clickType(); String methodNameString = eventType.methodNameString(); if (clickType == View.OnLongClickListener.class && method.getReturnType() != boolean.class) { throw new RuntimeException(method + " returned value should be boolean."); } try { //生命的注解值必须为values才能匹配 Method valueMethod = annotationType.getDeclaredMethod("value"); //获取注解是ClickType类型的注解的值 int[] viewIds = (int[]) valueMethod.invoke(annotation); //没有添加注解值(响应点击时间的id没有设置) if (viewIds == null || viewIds.length == 0) { return; } method.setAccessible(true); //点击时间的动态代理 Object clickProxy = Proxy.newProxyInstance(clickType.getClassLoader(), new Class[]{clickType}, new ClickInvocationHandler(activity, method)); for (int viewId : viewIds) { View view = activity.findViewById(viewId); Method setter = view.getClass().getMethod(methodNameString, clickType); setter.invoke(view, clickProxy); } } catch (Exception e) { e.printStackTrace(); } } } } } static class ClickInvocationHandler implements InvocationHandler { private final Method method; private final Activity activity; public ClickInvocationHandler(Activity activity, Method method) { this.activity = activity; this.method = method; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return this.method.invoke(activity, args); } }} 同理,可以使用这种思想应用到其他地方,比如activity之间的intent传值 值传递定义存放key的注解 12345678910import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Autowired { String value() default "";} 注入实现 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.os.Parcelable;import java.lang.reflect.Field;import java.util.Arrays;public class InjectValues { public static void inject(Activity activity) { Class<? extends Activity> activityClass = activity.getClass(); Bundle extras = activity.getIntent().getExtras(); if (extras == null) { return; } Field[] declaredFields = activityClass.getDeclaredFields(); for (Field field : declaredFields) { if (field.isAnnotationPresent(Autowired.class)) { Autowired autowired = field.getAnnotation(Autowired.class); String key; if ("".equals(autowired.value())) { key = field.getName(); } else { key = autowired.value(); } if (extras.containsKey(key)) { Object o = extras.get(key); //特殊处理Parcelable o = getParcelableObject(o, field); field.setAccessible(true); try { field.set(activity, o); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } private static Object getParcelableObject(Object o, Field field) { Class<?> componentType = field.getType().getComponentType(); if (field.getType().isArray() && Parcelable.class.isAssignableFrom(componentType)) { Object[] objs = (Object[]) o; o = Arrays.copyOf(objs, objs.length, (Class<? extends java.lang.Object[]>) field.getType()); } return o; }}","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"java","slug":"java","permalink":"https://laujiangtao.github.io/tags/java/"}]},{"title":"C语言","slug":"C语言","date":"2021-05-26T01:42:04.000Z","updated":"2021-05-26T09:24:03.868Z","comments":false,"path":"2021/05/26/c-language/","link":"","permalink":"https://laujiangtao.github.io/2021/05/26/c-language/","excerpt":"","text":"一步编译gcc -o hello.exe hello1.c hello2.c 分步编译 预处理:gcc -E hello.c -o hello.i 展开宏定义 展开头文件 条件编译 删除注释 编译:gcc -S hello.i -o hello.s 检查语法 将C语言转成汇编语言 汇编:gcc -c hello.s -o hello.o 将汇编语言转成机器语言 链接:gcc hello.o -o hello.exe 将C语言的库文件链接到可执行程序中 选项 含义 -E 只进行预处理 -S 只进行预处理和编译 -c 只进行预处理、编译和汇编 -o file 指定生成的文件名为file 文件后缀 含义 .c C语言文件 .i 预处理后的C语言文件 .s 编译后的汇编文件 .o 编译后的目标文件 转义字符 转义字符 意义 ASCII码值(十进制) \\a 响铃(BEL) 007 \\b 退格(BS) ,将当前位置移到前一列 008 \\f 换页(FF),将当前位置移到下页开头 012 \\n 换行(LF) ,将当前位置移到下一行开头 010 \\r 回车(CR) ,将当前位置移到本行开头 013 \\t 水平制表(HT) (跳到下一个TAB位置) 009 \\v 垂直制表(VT) 011 \\ 代表一个反斜线字符’’' 092 ' 代表一个单引号(撇号)字符 039 " 代表一个双引号字符 034 ? 代表一个问号 063 \\0 空字符(NUL) 000 \\ddd 1到3位八进制数所代表的任意字符 三位八进制 \\xhh 十六进制所代表的任意字符 十六进制 ASCII打印字符对照表 DEC OCT HEX BIN 缩写/符号 HTML实体 描述 0 000 00 00000000 NUL � Null char (空字符) 1 001 01 00000001 SOH  Start of Heading (标题开始) 2 002 02 00000010 STX  Start of Text (正文开始) 3 003 03 00000011 ETX  End of Text (正文结束) 4 004 04 00000100 EOT  End of Transmission (传输结束) 5 005 05 00000101 ENQ  Enquiry (请求) 6 006 06 00000110 ACK  Acknowledgment (收到通知) 7 007 07 00000111 BEL  Bell (响铃) 8 010 08 00001000 BS  Back Space (退格) 9 011 09 00001001 HT 	 Horizontal Tab (水平制表符) 10 012 0A 00001010 LF 
 Line Feed (换行键) 11 013 0B 00001011 VT  Vertical Tab (垂直制表符) 12 014 0C 00001100 FF  Form Feed (换页键) 13 015 0D 00001101 CR 
 Carriage Return (回车键) 14 016 0E 00001110 SO  Shift Out / X-On (不用切换) 15 017 0F 00001111 SI  Shift In / X-Off (启用切换) 16 020 10 00010000 DLE  Data Line Escape (数据链路转义) 17 021 11 00010001 DC1  Device Control 1 (设备控制1) 18 022 12 00010010 DC2  Device Control 2 (设备控制2) 19 023 13 00010011 DC3  Device Control 3 (设备控制3) 20 024 14 00010100 DC4  Device Control 4 (设备控制4) 21 025 15 00010101 NAK  Negative Acknowledgement (拒绝接收) 22 026 16 00010110 SYN  Synchronous Idle (同步空闲) 23 027 17 00010111 ETB  End of Transmit Block (传输块结束) 24 030 18 00011000 CAN  Cancel (取消) 25 031 19 00011001 EM  End of Medium (介质中断) 26 032 1A 00011010 SUB  Substitute (替补) 27 033 1B 00011011 ESC  Escape (溢出) 28 034 1C 00011100 FS  File Separator (文件分割符) 29 035 1D 00011101 GS  Group Separator (分组符) 30 036 1E 00011110 RS  Record Separator (记录分离符) 31 037 1F 00011111 US  Unit Separator (单元分隔符) 32 040 20 00100000   Space (空格) 33 041 21 00100001 ! ! Exclamation mark 34 042 22 00100010 “ " Double quotes 35 043 23 00100011 # # Number 36 044 24 00100100 $ $ Dollar 37 045 25 00100101 % % Procenttecken 38 046 26 00100110 & & Ampersand 39 047 27 00100111 ‘ ' Single quote 40 050 28 00101000 ( ( Open parenthesis 41 051 29 00101001 ) ) Close parenthesis 42 052 2A 00101010 * * Asterisk 43 053 2B 00101011 + + Plus 44 054 2C 00101100 , , Comma 45 055 2D 00101101 - - Hyphen 46 056 2E 00101110 . . Period, dot or full stop 47 057 2F 00101111 / / Slash or divide 48 060 30 00110000 0 0 Zero 49 061 31 00110001 1 1 One 50 062 32 00110010 2 2 Two 51 063 33 00110011 3 3 Three 52 064 34 00110100 4 4 Four 53 065 35 00110101 5 5 Five 54 066 36 00110110 6 6 Six 55 067 37 00110111 7 7 Seven 56 070 38 00111000 8 8 Eight 57 071 39 00111001 9 9 Nine 58 072 3A 00111010 : : Colon 59 073 3B 00111011 ; ; Semicolon 60 074 3C 00111100 < < Less than 61 075 3D 00111101 = = Equals 62 076 3E 00111110 > > Greater than 63 077 3F 00111111 ? ? Question mark 64 100 40 01000000 @ @ At symbol 65 101 41 01000001 A A Uppercase A 66 102 42 01000010 B B Uppercase B 67 103 43 01000011 C C Uppercase C 68 104 44 01000100 D D Uppercase D 69 105 45 01000101 E E Uppercase E 70 106 46 01000110 F F Uppercase F 71 107 47 01000111 G G Uppercase G 72 110 48 01001000 H H Uppercase H 73 111 49 01001001 I I Uppercase I 74 112 4A 01001010 J J Uppercase J 75 113 4B 01001011 K K Uppercase K 76 114 4C 01001100 L L Uppercase L 77 115 4D 01001101 M M Uppercase M 78 116 4E 01001110 N N Uppercase N 79 117 4F 01001111 O O Uppercase O 80 120 50 01010000 P P Uppercase P 81 121 51 01010001 Q Q Uppercase Q 82 122 52 01010010 R R Uppercase R 83 123 53 01010011 S S Uppercase S 84 124 54 01010100 T T Uppercase T 85 125 55 01010101 U U Uppercase U 86 126 56 01010110 V V Uppercase V 87 127 57 01010111 W W Uppercase W 88 130 58 01011000 X X Uppercase X 89 131 59 01011001 Y Y Uppercase Y 90 132 5A 01011010 Z Z Uppercase Z 91 133 5B 01011011 [ [ Opening bracket 92 134 5C 01011100 \\ \ Backslash 93 135 5D 01011101 ] ] Closing bracket 94 136 5E 01011110 ^ ^ Caret - circumflex 95 137 5F 01011111 _ _ Underscore 96 140 60 01100000 ` ` Grave accent 97 141 61 01100001 a a Lowercase a 98 142 62 01100010 b b Lowercase b 99 143 63 01100011 c c Lowercase c 100 144 64 01100100 d d Lowercase d 101 145 65 01100101 e e Lowercase e 102 146 66 01100110 f f Lowercase f 103 147 67 01100111 g g Lowercase g 104 150 68 01101000 h h Lowercase h 105 151 69 01101001 i i Lowercase i 106 152 6A 01101010 j j Lowercase j 107 153 6B 01101011 k k Lowercase k 108 154 6C 01101100 l l Lowercase l 109 155 6D 01101101 m m Lowercase m 110 156 6E 01101110 n n Lowercase n 111 157 6F 01101111 o o Lowercase o 112 160 70 01110000 p p Lowercase p 113 161 71 01110001 q q Lowercase q 114 162 72 01110010 r r Lowercase r 115 163 73 01110011 s s Lowercase s 116 164 74 01110100 t t Lowercase t 117 165 75 01110101 u u Lowercase u 118 166 76 01110110 v v Lowercase v 119 167 77 01110111 w w Lowercase w 120 170 78 01111000 x x Lowercase x 121 171 79 01111001 y y Lowercase y 122 172 7A 01111010 z z Lowercase z 123 173 7B 01111011 { { Opening brace 124 174 7C 01111100 | | Vertical bar 125 175 7D 01111101 } } Closing brace 126 176 7E 01111110 ~ ~ Equivalency sign (tilde) 127 177 7F 01111111  Delete 128 200 80 10000000 € € Euro sign 129 201 81 10000001 130 202 82 10000010 ‚ ‚ Single low-9 quotation mark 131 203 83 10000011 ƒ ƒ Latin small letter f with hook 132 204 84 10000100 „ „ Double low-9 quotation mark 133 205 85 10000101 … … Horizontal ellipsis 134 206 86 10000110 † † Dagger 135 207 87 10000111 ‡ ‡ Double dagger 136 210 88 10001000 ˆ ˆ Modifier letter circumflex accent 137 211 89 10001001 ‰ ‰ Per mille sign 138 212 8A 10001010 Š Š Latin capital letter S with caron 139 213 8B 10001011 ‹ ‹ Single left-pointing angle quotation 140 214 8C 10001100 Œ Œ Latin capital ligature OE 141 215 8D 10001101 142 216 8E 10001110 Ž Ž Latin capital letter Z with caron 143 217 8F 10001111 144 220 90 10010000 145 221 91 10010001 ‘ ‘ Left single quotation mark 146 222 92 10010010 ’ ’ Right single quotation mark 147 223 93 10010011 “ “ Left double quotation mark 148 224 94 10010100 ” ” Right double quotation mark 149 225 95 10010101 • • Bullet 150 226 96 10010110 – – En dash 151 227 97 10010111 — — Em dash 152 230 98 10011000 ˜ ˜ Small tilde 153 231 99 10011001 ™ ™ Trade mark sign 154 232 9A 10011010 š š Latin small letter S with caron 155 233 9B 10011011 › › Single right-pointing angle quotation mark 156 234 9C 10011100 œ œ Latin small ligature oe 157 235 9D 10011101 158 236 9E 10011110 ž ž Latin small letter z with caron 159 237 9F 10011111 Ÿ Ÿ Latin capital letter Y with diaeresis 160 240 A0 10100000   Non-breaking space 161 241 A1 10100001 ¡ ¡ Inverted exclamation mark 162 242 A2 10100010 ¢ ¢ Cent sign 163 243 A3 10100011 £ £ Pound sign 164 244 A4 10100100 ¤ ¤ Currency sign 165 245 A5 10100101 ¥ ¥ Yen sign 166 246 A6 10100110 ¦ ¦ Pipe, Broken vertical bar 167 247 A7 10100111 § § Section sign 168 250 A8 10101000 ¨ ¨ Spacing diaeresis - umlaut 169 251 A9 10101001 © © Copyright sign 170 252 AA 10101010 ª ª Feminine ordinal indicator 171 253 AB 10101011 « « Left double angle quotes 172 254 AC 10101100 ¬ ¬ Not sign 173 255 AD 10101101 ­ Soft hyphen 174 256 AE 10101110 ® ® Registered trade mark sign 175 257 AF 10101111 ¯ ¯ Spacing macron - overline 176 260 B0 10110000 ° ° Degree sign 177 261 B1 10110001 ± ± Plus-or-minus sign 178 262 B2 10110010 ² ² Superscript two - squared 179 263 B3 10110011 ³ ³ Superscript three - cubed 180 264 B4 10110100 ´ ´ Acute accent - spacing acute 181 265 B5 10110101 µ µ Micro sign 182 266 B6 10110110 ¶ ¶ Pilcrow sign - paragraph sign 183 267 B7 10110111 · · Middle dot - Georgian comma 184 270 B8 10111000 ¸ ¸ Spacing cedilla 185 271 B9 10111001 ¹ ¹ Superscript one 186 272 BA 10111010 º º Masculine ordinal indicator 187 273 BB 10111011 » » Right double angle quotes 188 274 BC 10111100 ¼ ¼ Fraction one quarter 189 275 BD 10111101 ½ ½ Fraction one half 190 276 BE 10111110 ¾ ¾ Fraction three quarters 191 277 BF 10111111 ¿ ¿ Inverted question mark 192 300 C0 11000000 À À Latin capital letter A with grave 193 301 C1 11000001 Á Á Latin capital letter A with acute 194 302 C2 11000010   Latin capital letter A with circumflex 195 303 C3 11000011 à à Latin capital letter A with tilde 196 304 C4 11000100 Ä Ä Latin capital letter A with diaeresis 197 305 C5 11000101 Å Å Latin capital letter A with ring above 198 306 C6 11000110 Æ Æ Latin capital letter AE 199 307 C7 11000111 Ç Ç Latin capital letter C with cedilla 200 310 C8 11001000 È È Latin capital letter E with grave 201 311 C9 11001001 É É Latin capital letter E with acute 202 312 CA 11001010 Ê Ê Latin capital letter E with circumflex 203 313 CB 11001011 Ë Ë Latin capital letter E with diaeresis 204 314 CC 11001100 Ì Ì Latin capital letter I with grave 205 315 CD 11001101 Í Í Latin capital letter I with acute 206 316 CE 11001110 Î Î Latin capital letter I with circumflex 207 317 CF 11001111 Ï Ï Latin capital letter I with diaeresis 208 320 D0 11010000 Ð Ð Latin capital letter ETH 209 321 D1 11010001 Ñ Ñ Latin capital letter N with tilde 210 322 D2 11010010 Ò Ò Latin capital letter O with grave 211 323 D3 11010011 Ó Ó Latin capital letter O with acute 212 324 D4 11010100 Ô Ô Latin capital letter O with circumflex 213 325 D5 11010101 Õ Õ Latin capital letter O with tilde 214 326 D6 11010110 Ö Ö Latin capital letter O with diaeresis 215 327 D7 11010111 × × Multiplication sign 216 330 D8 11011000 Ø Ø Latin capital letter O with slash 217 331 D9 11011001 Ù Ù Latin capital letter U with grave 218 332 DA 11011010 Ú Ú Latin capital letter U with acute 219 333 DB 11011011 Û Û Latin capital letter U with circumflex 220 334 DC 11011100 Ü Ü Latin capital letter U with diaeresis 221 335 DD 11011101 Ý Ý Latin capital letter Y with acute 222 336 DE 11011110 Þ Þ Latin capital letter THORN 223 337 DF 11011111 ß ß Latin small letter sharp s - ess-zed 224 340 E0 11100000 à à Latin small letter a with grave 225 341 E1 11100001 á á Latin small letter a with acute 226 342 E2 11100010 â â Latin small letter a with circumflex 227 343 E3 11100011 ã ã Latin small letter a with tilde 228 344 E4 11100100 ä ä Latin small letter a with diaeresis 229 345 E5 11100101 å å Latin small letter a with ring above 230 346 E6 11100110 æ æ Latin small letter ae 231 347 E7 11100111 ç ç Latin small letter c with cedilla 232 350 E8 11101000 è è Latin small letter e with grave 233 351 E9 11101001 é é Latin small letter e with acute 234 352 EA 11101010 ê ê Latin small letter e with circumflex 235 353 EB 11101011 ë ë Latin small letter e with diaeresis 236 354 EC 11101100 ì ì Latin small letter i with grave 237 355 ED 11101101 í í Latin small letter i with acute 238 356 EE 11101110 î î Latin small letter i with circumflex 239 357 EF 11101111 ï ï Latin small letter i with diaeresis 240 360 F0 11110000 ð ð Latin small letter eth 241 361 F1 11110001 ñ ñ Latin small letter n with tilde 242 362 F2 11110010 ò ò Latin small letter o with grave 243 363 F3 11110011 ó ó Latin small letter o with acute 244 364 F4 11110100 ô ô Latin small letter o with circumflex 245 365 F5 11110101 õ õ Latin small letter o with tilde 246 366 F6 11110110 ö ö Latin small letter o with diaeresis 247 367 F7 11110111 ÷ ÷ Division sign 248 370 F8 11111000 ø ø Latin small letter o with slash 249 371 F9 11111001 ù ù Latin small letter u with grave 250 372 FA 11111010 ú ú Latin small letter u with acute 251 373 FB 11111011 û û Latin small letter u with circumflex 252 374 FC 11111100 ü ü Latin small letter u with diaeresis 253 375 FD 11111101 ý ý Latin small letter y with acute 254 376 FE 11111110 þ þ Latin small letter thorn 255 377 FF 11111111 ÿ ÿ Latin small letter y with diaeresis","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"C","slug":"C","permalink":"https://laujiangtao.github.io/tags/C/"}]},{"title":"Android自动任务探索","slug":"Android定时自动任务探索","date":"2021-05-19T08:15:08.000Z","updated":"2021-05-25T23:54:31.843Z","comments":false,"path":"2021/05/19/android-auto-task/","link":"","permalink":"https://laujiangtao.github.io/2021/05/19/android-auto-task/","excerpt":"","text":"若要手机自动执行任务,首要做的就是保活自己的app,避免被系统把进程杀掉。 网络上有许多app保活方式,也有许多靠谱的操作,更多的则是不寻常的路子,而且还不稳定,容易被系统回收资源。 下面列出三种自己探索过的app保活方式 方案一:通知保活在通知栏创建一个常驻通知,创建的时候将自己应用的上下文传入,目前个中音乐app就是用这种方式实现长时间驻留后台的,Google官方也推荐这种方式。 定义执行任务的服务,在服务开启的时候显示通知栏的通知保活 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687import android.app.*import android.content.Contextimport android.content.Intentimport android.net.Uriimport android.os.Buildimport android.os.IBinderimport android.util.Logimport cn.ljt.clock.openimport cn.ljt.clock.playAudioimport com.tencent.mmkv.MMKVimport java.text.SimpleDateFormatimport java.util.*import kotlin.concurrent.fixedRateTimerclass NotificationService : Service() { val channelId: String = "ChannelId" lateinit var timer: Timer override fun onCreate() { super.onCreate() startTimer() } override fun onBind(intent: Intent): IBinder? { return null } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, packageName + "打卡", NotificationManager.IMPORTANCE_LOW ) notificationManager.createNotificationChannel(channel) } startForeground(1, getNotification()) return START_STICKY } private fun getNotification(): Notification { val contentIntent = Intent() contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) contentIntent.action = "android.settings.APPLICATION_DETAILS_SETTINGS" contentIntent.data = Uri.fromParts("package", packageName, null) val builder: Notification.Builder = Notification.Builder(this) .setSmallIcon(android.R.mipmap.sym_def_app_icon) .setContentIntent(PendingIntent.getActivity(this, 0, contentIntent, 0)) .setContentTitle("\\"$packageName\\"正在运行") .setContentText("触摸即可了解详情或停止应用") //设置Notification的ChannelID,否则不能正常显示 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId(channelId) } return builder.build() } override fun onDestroy() { super.onDestroy() stopTimer() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { stopForeground(STOP_FOREGROUND_REMOVE) } stopForeground(true) } private fun startTimer() { timer = fixedRateTimer("", false, 0, period * 1000) { try { // TODO: 根据条件执行任务 } catch (e: Exception) { Log.i("TAG", "$e") } } } private fun stopTimer() { timer.cancel() Log.d("TAG", "stopTimer") }} 方案二:闹钟唤醒实现方式时到点发送一个PendingIntent,自己应用声明一个对应的receiver,在receiver中处理事件。 AndroidManifest.xml文件添加receiver 点我展开 1234<receiver android:name=".ui.alarm.AlarmReceiver" android:enabled="true" android:exported="true" /> 定义receiver类 点我展开 123456789101112class AlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action.equals("${context.packageName}.alarm")) { try { // TODO: 根据条件执行任务 } catch (e: Exception) { e.printStackTrace() } } }} 定义闹钟并开启 点我展开 1234567891011121314151617181920212223242526val intent = Intent("$packageName.alarm")intent.setClass(this, AlarmReceiver::class.java)val pi = PendingIntent.getBroadcast(this, 0, intent, 0) //设置一个PendingIntent对象,发送广播val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager //获取AlarmManager对象if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { val nextAlarmClock = alarmManager.nextAlarmClock if (nextAlarmClock != null) { val showIntent = nextAlarmClock.showIntent val triggerTime = nextAlarmClock.triggerTime Log.i("TAG", "init: $showIntent") Log.i("TAG", "init: $triggerTime") }}//AlarmManager.ELAPSED_REALTIME: 闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3; //AlarmManager.ELAPSED_REALTIME_WAKEUP: 闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2; //AlarmManager.RTC: 闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1; //AlarmManager.RTC_WAKEUP: 表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0; //AlarmManager.POWER_OFF_WAKEUP: 表示闹钟在手机关机状态下也能正常进行提示功能,所以是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持;// 重复执行,倒数第二参数为间隔时间,单位为毫秒alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, intervalMillis * 1000, pi)//时间到时,执行PendingIntent,只执行一次alarmManager[AlarmManager.RTC_WAKEUP, calendar.timeInMillis] = pi 方案三:屏保保活系统的屏保是一个特殊的service,可以设置屏保的UI,在屏保显示的过程中,app一直存活。同样屏保消失的时候,service也会被销毁。 AndroidManifest.xml 中添加屏保服务,一定要添加android.permission.BIND_DREAM_SERVICE权限 点我展开 1234567891011121314151617<service android:name=".service.MyDream" android:enabled="true" android:exported="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:permission="android.permission.BIND_DREAM_SERVICE"> <intent-filter> <action android:name="android.service.dreams.DreamService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.service.dream" android:resource="@xml/my_dream" /></service> meta-data指向的资源文件里面声明了屏保的设置页面 点我展开 123<?xml version="1.0" encoding="utf-8"?><dream xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="cn.ljt.clock.ui.dreamsetting.MyDreamActivity" /> 屏保服务对应的实体类,继承DreamService,并设置屏保的UI 点我展开 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980import android.content.Contextimport android.os.SystemClockimport android.service.dreams.DreamServiceimport android.util.Logimport cn.ljt.clock.Rimport com.tencent.mmkv.MMKVimport java.util.*import kotlin.concurrent.fixedRateTimerclass MyDream : DreamService() { private val TAG: String? = MyDream::class.java.name var boolean: Boolean? = false lateinit var timer: Timer /** * 初始化设置,在这里可以调用 setContentView() */ override fun onAttachedToWindow() { super.onAttachedToWindow() Log.i(TAG, "onAttachedToWindow: ") // Exit dream upon user touch isInteractive = false // Hide system UI isFullscreen = true //设置为false会降低屏幕亮度 isScreenBright = false // Set the dream layout setContentView(R.layout.my_day_dream) boolean = MMKV.defaultMMKV()?.getBoolean("dream_task_enable", false) } /** * 在这里回收前面调用的资源(比如 handlers 和 listeners) */ override fun onDetachedFromWindow() { super.onDetachedFromWindow() Log.i(TAG, "onDetachedFromWindow: ") } /** * 互动屏保已经启动,这里可以开始播放动画或者其他操作 */ override fun onDreamingStarted() { super.onDreamingStarted() Log.i(TAG, "onDreamingStarted: ") if (boolean == true) { startTimer() } } /** * 在停止 onDreamingStarted() 里启动的东西 */ override fun onDreamingStopped() { super.onDreamingStopped() Log.i(TAG, "onDreamingStopped: ") if (boolean == true) { stopTimer() } } private fun startTimer() { timer = fixedRateTimer("fixedRateTimer", true, period * 1000, period * 1000) { try { // TODO: 根据条件执行任务 } catch (e: Exception) { Log.i("TAG", "$e") } } } private fun stopTimer() { timer.cancel() Log.d("TAG", "stopTimer") }} 避开屏幕锁以上方法可以保活,但是屏幕锁定之后无法开锁。 尝试多种方式,在高版本的手机上,依然无法自动解锁。 在上述三种方案中,第三种屏保的方式可以阻止手机进入锁屏状态,故从它入手。 正常状态下,屏保开启后,轻触屏幕,屏保会自动退出并点亮屏幕。 所以,只需执行代码,模拟轻触屏保的动作即可。 以下提供三种方式,在屏保开启后,试图唤醒页面 调用View的performClick()方法(失败) 调用DreamService的wakeUp()方法(失败) 模拟屏幕点击(成功) 点我展开 1234567891011121314151617181920212223private fun click() { val inst = Instrumentation() inst.sendPointerSync( MotionEvent.obtain( SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 240f, 400f, 0 ) ) inst.sendPointerSync( MotionEvent.obtain( SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 240f, 400f, 0 ) )}","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"Android","slug":"Android","permalink":"https://laujiangtao.github.io/tags/Android/"}]},{"title":"线性伸缩圆角矩形indicator","slug":"线性伸缩圆角矩形indicator","date":"2021-03-27T12:27:00.000Z","updated":"2021-05-15T15:54:28.863Z","comments":false,"path":"2021/03/27/linear-telescopic-fillet-rectangle-indicator/","link":"","permalink":"https://laujiangtao.github.io/2021/03/27/linear-telescopic-fillet-rectangle-indicator/","excerpt":"","text":"UI给了一个轮播图的交互 网上找了一圈,没有很合适的轮子,趁周末自己写了一下 UI显示是ViewPager + Indicator,那么,Indicator和ViewPager的联动,就用比较流行的MagicIndicator去处理,主要关心的是怎样自己定义一个符合UI设计的Indicator 需求是,当向左滑动ViewPager的时候,当前逐渐Indicator变小,下一个逐渐Indicator变大,切换完成之后效果类似两个Indicator交换位置。 效果如下 点我展开代码 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356import android.annotation.SuppressLint;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PointF;import android.graphics.RectF;import android.os.Build;import android.util.SparseArray;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.view.animation.Interpolator;import android.view.animation.LinearInterpolator;import net.lucode.hackware.magicindicator.NavigatorHelper;import net.lucode.hackware.magicindicator.abs.IPagerNavigator;import java.util.ArrayList;import java.util.List;/** * 线性伸缩圆角矩形indicator * Created by laujiangtao on 2021/3/27. */public class ScaleLinearRoundedRectangleNavigator extends View implements IPagerNavigator, NavigatorHelper.OnNavigatorScrollListener { private int mRadius;//小圆点半径 private int mNormalCircleColor = Color.LTGRAY; private int mSelectedCircleColor = Color.GRAY; private int mCircleSpacing;//圆点之间的空隙 private int mCircleCount;//indicator个数 private int mRectWidth;//圆角矩形宽,看作横向伸缩的圆 private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //存放各个圆心位置 private List<PointF> mCirclePoints = new ArrayList<PointF>(); //稀疏数组存放各个圆的半径 private SparseArray<Float> mCircleRadiusArray = new SparseArray<Float>(); // 事件回调 private boolean mTouchable; private ScaleLinearRoundedRectangleNavigator.OnCircleClickListener mCircleClickListener; private float mDownX; private float mDownY; private int mTouchSlop; // 是否跟随手指滑动 private boolean mFollowTouch = true; private NavigatorHelper mNavigatorHelper = new NavigatorHelper(); //使用线性插补器 private Interpolator mStartInterpolator = new LinearInterpolator(); public ScaleLinearRoundedRectangleNavigator(Context context) { super(context); init(context); } private void init(Context context) { mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mRadius = UIUtil.dip2px(context, 3); mCircleSpacing = UIUtil.dip2px(context, 18); mRectWidth = UIUtil.dip2px(context, 30); mNavigatorHelper.setNavigatorScrollListener(this); mNavigatorHelper.setSkimOver(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } private int measureWidth(int widthMeasureSpec) { int mode = MeasureSpec.getMode(widthMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int result = 0; switch (mode) { case MeasureSpec.EXACTLY: result = width; break; case MeasureSpec.AT_MOST: case MeasureSpec.UNSPECIFIED: if (mCircleCount <= 0) { result = getPaddingLeft() + getPaddingRight(); } else { //一个圆角矩形的横向宽 + (n-1)*小圆直径 + (n-1)*圆之间的空隙 + 左右边距 result = mRectWidth + (mCircleCount - 1) * mRadius * 2 + (mCircleCount - 1) * mCircleSpacing + getPaddingLeft() + getPaddingRight(); } break; default: break; } return result; } private int measureHeight(int heightMeasureSpec) { int mode = MeasureSpec.getMode(heightMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int result = 0; switch (mode) { case MeasureSpec.EXACTLY: result = height; break; case MeasureSpec.AT_MOST: case MeasureSpec.UNSPECIFIED: //小圆的直径 + 上下边距 result = mRadius * 2 + getPaddingTop() + getPaddingBottom(); break; default: break; } return result; } @Override protected void onDraw(Canvas canvas) { //遍历画每个位置上的View for (int i = 0, j = mCirclePoints.size(); i < j; i++) { //获取圆心位置 PointF point = mCirclePoints.get(i); //从稀疏数组中获取当前位置圆的半径 float radius = mCircleRadiusArray.get(i, (float) mRadius); //颜色渐变处理 mPaint.setColor(eval((radius - mRadius) / (mRectWidth / 2 - mRadius), mNormalCircleColor, mSelectedCircleColor)); @SuppressLint("DrawAllocation") RectF rectF = new RectF(); if (radius == (float) mRadius || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { //如果是小圆或者当前SDK小于Android5,直接画小圆 rectF.left = point.x - mRadius; rectF.right = point.x + mRadius; } else { //宽度渐变,算法同颜色渐变 //权重:当前变化量与最大变化量之比 => (radius - mRadius) / (mRectWidth / 2 - mRadius) //当前变化位置 => 权重 * 横向变化量 => 权重 *(结束宽度 - 开始宽度)=> weight * (end - start) //从小圆变为圆角矩形,那么半径从 mRadius 变为(mRectWidth / 2),即开始半径为 mRadius,结束时半径为(mRectWidth / 2) //左边变化之后位置为 最大变化位置 - mRadius //右边变化之后位置为 最大变化位置 + mRadius int ff = -mRadius + (int) ((radius - mRadius) / (mRectWidth / 2 - mRadius) * (mRadius - mRectWidth / 2)); rectF.left = point.x - ff; rectF.right = point.x + ff; } rectF.top = point.y - mRadius; rectF.bottom = point.y + mRadius; canvas.drawRoundRect(rectF, radius, radius, mPaint); } } private void prepareCirclePoints() { mCirclePoints.clear(); if (mCircleCount > 0) { int y = Math.round(getHeight() / 2.0f); //圆心间距 => 一个小圆 + 圆的间隙 int centerSpacing = mRadius * 2 + mCircleSpacing; //开始是圆心位置 => 左间距 + 圆角矩形宽的一半 int startX = getPaddingLeft() + mRectWidth / 2; for (int i = 0; i < mCircleCount; i++) { PointF pointF = new PointF(startX, y); mCirclePoints.add(pointF); startX += centerSpacing; } } } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (mTouchable) { mDownX = x; mDownY = y; return true; } break; case MotionEvent.ACTION_UP: if (mCircleClickListener != null) { if (Math.abs(x - mDownX) <= mTouchSlop && Math.abs(y - mDownY) <= mTouchSlop) { float max = Float.MAX_VALUE; int index = 0; for (int i = 0; i < mCirclePoints.size(); i++) { PointF pointF = mCirclePoints.get(i); float offset = Math.abs(pointF.x - x); if (offset < max) { max = offset; index = i; } } mCircleClickListener.onClick(index); } } break; default: break; } return super.onTouchEvent(event); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mNavigatorHelper.onPageScrolled(position, positionOffset, positionOffsetPixels); } @Override public void onPageSelected(int position) { mNavigatorHelper.onPageSelected(position); } @Override public void onPageScrollStateChanged(int state) { mNavigatorHelper.onPageScrollStateChanged(state); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { prepareCirclePoints(); } @Override public void notifyDataSetChanged() { prepareCirclePoints(); requestLayout(); } @Override public void onAttachToMagicIndicator() { } @Override public void onDetachFromMagicIndicator() { } public void setMinRadius(int minRadius) { mRadius = minRadius; prepareCirclePoints(); invalidate(); } public void setNormalCircleColor(int normalCircleColor) { mNormalCircleColor = normalCircleColor; invalidate(); } public void setSelectedCircleColor(int selectedCircleColor) { mSelectedCircleColor = selectedCircleColor; invalidate(); } public void setCircleSpacing(int circleSpacing) { mCircleSpacing = circleSpacing; prepareCirclePoints(); invalidate(); } public void setRectWidth(int rectWidth) { this.mRectWidth = rectWidth; prepareCirclePoints(); invalidate(); } public void setStartInterpolator(Interpolator startInterpolator) { mStartInterpolator = startInterpolator; if (mStartInterpolator == null) { mStartInterpolator = new LinearInterpolator(); } } public void setCircleCount(int count) { mCircleCount = count; // 此处不调用invalidate,让外部调用notifyDataSetChanged mNavigatorHelper.setTotalCount(mCircleCount); } public void setTouchable(boolean touchable) { mTouchable = touchable; } public void setFollowTouch(boolean followTouch) { mFollowTouch = followTouch; } public void setSkimOver(boolean skimOver) { mNavigatorHelper.setSkimOver(skimOver); } public void setCircleClickListener(OnCircleClickListener circleClickListener) { if (!mTouchable) { mTouchable = true; } mCircleClickListener = circleClickListener; } @Override public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { if (mFollowTouch) { //进入时,半径从小变大,mRadius => mRectWidth / 2 //mStartInterpolator.getInterpolation(enterPercent) 即为权重 float radius = mRadius + (mRectWidth / 2 - mRadius) * mStartInterpolator.getInterpolation(enterPercent); mCircleRadiusArray.put(index, radius); invalidate(); } } @Override public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { if (mFollowTouch) { //离开时,半径从大变小,mRectWidth / 2 => mRadius float radius = mRectWidth / 2 - (mRectWidth / 2 - mRadius) * mStartInterpolator.getInterpolation(leavePercent); mCircleRadiusArray.put(index, radius); invalidate(); } } @Override public void onSelected(int index, int totalCount) { if (!mFollowTouch) { mCircleRadiusArray.put(index, (float) mRectWidth / 2); invalidate(); } } @Override public void onDeselected(int index, int totalCount) { if (!mFollowTouch) { mCircleRadiusArray.put(index, (float) mRadius); invalidate(); } } public interface OnCircleClickListener { void onClick(int index); } private int eval(float fraction, int startValue, int endValue) { int startA = (startValue >> 24) & 0xff; int startR = (startValue >> 16) & 0xff; int startG = (startValue >> 8) & 0xff; int startB = startValue & 0xff; int endA = (endValue >> 24) & 0xff; int endR = (endValue >> 16) & 0xff; int endG = (endValue >> 8) & 0xff; int endB = endValue & 0xff; int currentA = (startA + (int) (fraction * (endA - startA))) << 24; int currentR = (startR + (int) (fraction * (endR - startR))) << 16; int currentG = (startG + (int) (fraction * (endG - startG))) << 8; int currentB = startB + (int) (fraction * (endB - startB)); return currentA | currentR | currentG | currentB; } public int dip2px(Context context, double dpValue) { float density = context.getResources().getDisplayMetrics().density; return (int) (dpValue * density + 0.5); }}","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"自定义View","slug":"自定义View","permalink":"https://laujiangtao.github.io/tags/%E8%87%AA%E5%AE%9A%E4%B9%89View/"}]},{"title":"禅道项目管理软件部署","slug":"禅道项目管理软件部署","date":"2021-03-09T15:01:55.000Z","updated":"2022-07-10T12:06:31.620Z","comments":false,"path":"2021/03/09/zentao-project-management-software-deployment/","link":"","permalink":"https://laujiangtao.github.io/2021/03/09/zentao-project-management-software-deployment/","excerpt":"","text":"Windows连接远程Linux部署项目管理软件 复制安装包到远程Linux 1scp E:\\Download\\ZenTaoPMS.12.5.3.zbox_64.tar.gz androidserver@10.10.2.98:/home/androidserver/Desktop 解压到opt目录下 1tar -xzf ZenTaoPMS.12.5.3.zbox_64.tar.gz -C /opt 修改禅道自带apache、mysql端口 修改禅道自带的apache端口: 1/opt/zbox/zbox -ap 9000 修改禅道自带的mysql端口: 1/opt/zbox/zbox -mp 9001 启动禅道服务 1/opt/zbox/zbox start 关闭禅道 1/opt/zbox/zbox stop 重启禅道 1/opt/zbox/zbox restart 浏览器访问http://10.10.2.98:9000/zentao/,管理员登录的默认账号和密码:admin、123456","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[]},{"title":"Jekins构建Flutter项目","slug":"Jekins构建Flutter项目","date":"2021-02-24T02:55:42.000Z","updated":"2021-05-15T15:54:19.814Z","comments":false,"path":"2021/02/24/jekins-build-flutter-project/","link":"","permalink":"https://laujiangtao.github.io/2021/02/24/jekins-build-flutter-project/","excerpt":"","text":"构建面板 构建命令 以登录方式执行bash命令,进入项目目录下,清除flutter缓存,构建对应服务器的Android apk,构建完成,将apk复制到tomcat公开目录下,发送钉钉消息。其他同Android项目构建 1234#!/bin/sh -lcd ~/.jenkins/workspace/Flutterflutter cleanflutter build apk --obfuscate --split-debug-info=/Flutter/build --target lib/main_${FLAVOR_NAME}.dart","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"jekins","slug":"jekins","permalink":"https://laujiangtao.github.io/tags/jekins/"}]},{"title":"Jekins构建Android项目","slug":"Jekins构建Android项目","date":"2021-02-24T02:21:33.000Z","updated":"2021-05-15T15:54:14.075Z","comments":false,"path":"2021/02/24/jekins-build-android-project/","link":"","permalink":"https://laujiangtao.github.io/2021/02/24/jekins-build-android-project/","excerpt":"","text":"构建面板 属性配置 gradle task任务 12app:cleanapp:assemble${PRODUCT_FLAVORS}${BUILD_TYPE} 构建完成,复制apk包到tomcat配置的公开目录下 1cp /home/androidserver/.jenkins/workspace/Android/app/build/outputs/apk/${PRODUCT_FLAVORS}/${BUILD_TYPE}/${APP_NAME}-${PRODUCT_FLAVORS}-${BUILD_TYPE}-${APP_BUILD_TIME}.apk /home/androidserver/Public/mmmiddle/${APP_NAME}-${PRODUCT_FLAVORS}-${BUILD_TYPE}-${APP_BUILD_TIME}.apk 钉钉消息配置,发送tomcat公开目录下的新apk文件地址 http://192.168.2.98:8080/public/mmmiddle/${APP_NAME}-${PRODUCT_FLAVORS}-${BUILD_TYPE}-${APP_BUILD_TIME}.apk ${DESCRIPTION} 钉钉消息样式 插件","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"jekins","slug":"jekins","permalink":"https://laujiangtao.github.io/tags/jekins/"}]},{"title":"Linux安装Flutter开发环境","slug":"Linux安装Flutter开发环境","date":"2021-02-19T03:13:53.000Z","updated":"2021-05-15T15:54:08.483Z","comments":false,"path":"2021/02/19/linux-install-flutter-development-environment/","link":"","permalink":"https://laujiangtao.github.io/2021/02/19/linux-install-flutter-development-environment/","excerpt":"","text":"转到下载页 在Windows电脑上通过ssh连接局域网内Linux打包服务器,在命令行安装flutter开发环境 获取Flutter SDK1wget https://storage.googleapis.com/flutter_infra/releases/stable/linux/flutter_linux_1.22.5-stable.tar.xz 将文件解压到合适目录下1tar xf flutter_linux_1.22.5-stable.tar.xz 如果你不想安装安装包的补丁,你可以跳过步骤 1 或步骤 2,直接获取 Github 上 Flutter 仓库 的源码并执行以下命令: 1git clone https://github.com/flutter/flutter.git 你也可以按你的需要切换分支或者tag。例如,你可以使用 stable 版本的分支: 1git clone https://github.com/flutter/flutter.git -b stable --depth 1 配置环境变量 编辑.profile文件 1vi .profile 添加以下行并 123export PUB_HOSTED_URL=https://pub.flutter-io.cn //国内用户需要设置export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //国内用户需要设置export PATH=[flutter dir]/flutter/bin:$PATH 运行source .profile使得环境变量生效 执行命令初始化环境 运行flutter自动下载dart SDK 运行flutter doctor检查工具链 运行flutter doctor --android-licenses同意相关协议","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"flutter","slug":"flutter","permalink":"https://laujiangtao.github.io/tags/flutter/"}]},{"title":"MySQL Note","slug":"MySQL-Note","date":"2021-02-11T00:01:04.000Z","updated":"2021-05-15T15:53:56.903Z","comments":false,"path":"2021/02/11/mysql-note/","link":"","permalink":"https://laujiangtao.github.io/2021/02/11/mysql-note/","excerpt":"","text":"(1) 基本命令 登录 12345// -h 指定连接的主机// -P 指定端口// -u 指定用户// -p 指定密码mysql -h localhost -P 3306 -u root -p root1234 退出 1exit 查看所有数据库 1show databases; 打开某个数据库 1use sys; 显示数据库内的表 1show tables; 显示指定数据库内的表 1show tables from mysql; 查看当前所在数据库 1select database(); 创建数据库 1create database test; 创建表 1create table user_info(id int, name varchar(20)); 查看表结构 1desc user_info; 插入数据 1insert into user_info(id,name) value(1,'Kavan'); 查看表中的数据 1select * from user_info; 修改表内的数据 1update user_info set name='KavanLiu' where id=1; 删除数据 1delete from user_info where id=1; 查看数据库版本 1select version(); 命令行查看版本 12mysql --versionmysql -V (2) 查询表结构 departments employees jobs locations job_grades 基础查询1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950USE myemployees;/*语法:SELECT 查询的东西 FROM 表名;特点:1、可以查询表中的字段、常量值、表达式、函数2、查询结果是虚拟表格*/# 1、查询单个字段SELECT last_name FROM employees;# 2、查询多个字短SELECT last_name,salary,email FROM employees;# 3、查询所有字段SELECT * FROM employees;# 4、查询常量值(字符型和日期型必须用引号引起来)SELECT 100;SELECT 'jhon';# 5、查询表达式SELECT 100*98;SELECT 100%98;# 6、查询函数SELECT VERSION();# 7、为字段起别名SELECT 100%98 AS Result;SELECT last_name AS 姓,first_name AS 名 FROM employees;SELECT last_name 姓,first_name 名 FROM employees;SELECT last_name AS "姓 名" FROM employees;# 8、去重(只能针对单个字段去重,多个字段不支持。假如支持,第一个字段去重后,第二个字段查询出来可能不完整)SELECT DISTINCT department_id FROM employees;# 9、+ 的作用(只能当作运算符,不能当作字符串的连接符)SELECT '100'+6; # 会试图将字符转换成数值型进行运算,SELECT 'jhon'+6; # 转换失败,字符取值为0SELECT null+6; # 只要存在null,结果就为null# 连接多个字段使用concat函数SELECT CONCAT(last_name,first_name) AS 姓名 FROM employees;# 10、IFNULL(expr1,expr2) 如果值为null,使用第二个参数代替SELECT IFNULL(commission_pct,0) FROM employees;# 11、ISNULL() 判断字段或表达式是否为null,是返回1,否则返回0SELECT ISNULL(commission_pct),commission_pct FROM employees; 条件查询123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051USE myemployees;/*语法:SELECT 查询的东西 FROM 表名 WHERE 筛选条件;分类:1、按条件表达式('>', '<', '=', '!=', '<>', '>=', '<='>)筛选2、按逻辑表达式('&&', '||', '!', 'AND', 'OR', 'NOT')筛选3、模糊('LIKE', 'BETWEEN AND', 'IN', 'IS NULL', 'IS NOT NULL')筛选*/# 按条件表达式SELECT * FROM employees WHERE salary>12000;SELECT last_name,department_id FROM employees WHERE department_id<>90;# 按逻辑表达式SELECT last_name,salary,commission_pct FROM employees WHERE salary>=10000 AND salary<=10000;SELECT * FROM employees WHERE department_id<90 OR department_id>100 OR salary>15000;SELECT * FROM employees WHERE NOT(department_id>=90 AND department_id<=100) OR salary>15000);# 模糊查询(一般和通配符搭配使用)# 通配符:# % 任意多个字符,包含0个字符# _ 任意单个字符# 名字中包含字符'a'的员工信息SELECT * FROM employees WHERE last_name LIKE '%a%';# 名字中第三个字符为e,第五个字符为a的员工名字和工资SELECT last_name,salary FROM employees WHERE last_name LIKE '___e_a%';# 名字中第二个字符为下划线的员工SELECT * FROM employees WHERE last_name LIKE '_\\_%';# 指定转移字符SELECT * FROM employees WHERE last_name LIKE '_$_%' ESCAPE '$';# between and (包含临界值)SELECT * FROM employees WHERE department_id BETWEEN 90 AND 100;# IN 列表内不能使用通配符SELECT last_name,job_id FROM employees WHERE job_id IN('IT_PROT', 'AD_VP', 'AD_PRES');# IS NULL / IS NOT NULL (IS 不能代替'='使用)# 没奖金的员工SELECT last_name,commission_pct FROM employees WHERE commission_pct IS NULL;# 有奖金的员工SELECT last_name,commission_pct FROM employees WHERE commission_pct IS NOT NULL;# 安全等于 <=># 没奖金的员工SELECT last_name,commission_pct FROM employees WHERE commission_pct <=> NULL;SELECT last_name,salary FROM employees WHERE salary <=> 12000; 排序查询123456789101112131415161718192021/*语法:SELECT 查询的东西 FROM 表名 WHERE 筛选条件 ORDER BY 排序的列表 ASC|DESC;ASC 代表升序DESC 代表降序默认不写代表升序*/# 公司从高到低排序SELECT * FROM employees ORDER BY salary DESC;# 按年薪排序SELECT *,salary*12*(1+IFNULL(commission_pct,0)) AS 年薪 FROM employees ORDER BY salary*12*(1+IFNULL(commission_pct,0)) ASC;# 支持别名SELECT *,salary*12*(1+IFNULL(commission_pct,0)) AS 年薪 FROM employees ORDER BY 年薪 ASC;# 按名字长度排序SELECT LENGTH(last_name) AS 名字长度,last_name FROM employees ORDER BY 名字长度 DESC;# 先按工资排序,再按员工编号排序SELECT * FROM employees ORDER BY salary ASC,employee_id DESC; (3) 单行函数 语法:SELECT 函数名(实参列表) FROM 表名; 字符函数123456789101112131415161718192021222324252627282930# LENGTH() 返回字节长度SELECT LENGTH('jhon'); # 返回4SELECT LENGTH('张三'); # 返回6SELECT LENGTH(last_name) FROM employees;# CONCAT()SELECT CONCAT(last_name,first_name) FROM employees;# UPPER() LOWER()SELECT UPPER('jhon');SELECT CONCAT(UPPER(last_name),first_name) FROM employees;# SUBSTR() SUBSTRING()SELECT SUBSTR('王二麻子',2); #返回 二麻子,索引从1开始# 第三个参数指字符长度SELECT SUBSTR('大河弯弯向东流',3,2); # 返回 弯弯# INSTR()SELECT INSTR('abcdefg',bcd); #返回2,返回起始索引,第一次出现的索引,找不到返回0#TRIM()SELECT TRIM(' 张三 ');SELECT TRIM('a' FROM 'aaaaa张a三aaa'); # 返回 张a三#LPAD() RPAD() # 左填充,右填充,用指定字符填充到指定长度SELECT LPAD('李二狗子',10,'*'); # 最后总字符为10,SELECT LPAD('李二狗子',2,'*'); # 返回 李二#REPLACE()SELECT REPLACE('李二狗子','二','三'); #返回 李三狗子 数学函数12345678910111213141516171819# ROUND() 四舍五入SELECT ROUND(1.45); #返回1SELECT ROUND(1.65); #返回2SELECT ROUND(-1.65); #返回-2SELECT ROUND(1.567,2); #返回1.57 #CEIL() 向上取整,返回>=该参数的最小整数SELECT CEIL(1.02); #返回2SELECT CEIL(-1.02); #返回-1#FLOOR() 向下取整,返回<=该参数的最大整数SELECT FLOOR(1.02); #返回1SELECT FLOOR(-1.02); #返回-2#TRUNCATE() 截断SELECT TRUNCATE(1.65,1); #小数点后保留1位,返回1.6#MOD() 取余 MOD(a,b) => a-a/b*bSELECT MOD(10,3); # 返回1 日期函数1234567891011121314151617# NOW() 返回系统时间SELECT NOW(); #返回 2021-01-31 12:02:29# CURDARE();SELECT CURDARE(); #返回 2021-01-31# CURTIME();SELECT CURTIME(); #返回 12:04:24# 获取日期指定部分# YEAR() MONTH() MONTHNAME() DAY() HOUR() .....SELECT YEAR(NOW());SELECT YEAR('2021-1-31');SELECT YEAR(hiredate) FROM employees;# STR_TO_DATE() 将日期格式的字符转化成日期类型SELECT STR_TO_DATE('9-13-2021','%m-%d-%Y'); # 返回 2021-09-13 123456# 通过日期查询SELECT * FROM employees WHERE hiredate = '2021-1-31'SELECT * FROM employees WHERE hiredate = STR_TO_DATE('1-31 2021','%m-%d %Y');# DATE_FORMAT()DATE_FORMAT('2021.1.31','%Y年%m月%d日') 其他函数123SELECT VERSION();SELECT DATABASE();SELECT USER(); 流程控制函数1234567891011121314151617181920212223242526272829303132# IF(expr1,expr2,expr3); 表达式expr1成立返回expe2否则返回expr3# CASE 表达式CASE 要判断的字段或表达式WHEN 常量1 THEN 要显示的语句1或值1(语句后需要带分号 ;)WHEN 常量2 THEN 要显示的语句2或值2(语句后需要带分号 ;)WHEN 常量3 THEN 要显示的语句3或值3(语句后需要带分号 ;)ELSE 默认要显示的语句或值ENDSELECT salary AS 原始工资,department_id,CASE department_idWHEN 30 THEN salary*1.1WHEN 40 THEN salary*1.2WHEN 40 THEN salary*1.3ELSE salaryEND AS 新工资 FROM employees;CASEWHEN 条件1 THEN 要显示的语句1或值1(语句后需要带分号 ;)WHEN 条件2 THEN 要显示的语句2或值2(语句后需要带分号 ;)WHEN 条件3 THEN 要显示的语句3或值3(语句后需要带分号 ;)ELSE 默认要显示的语句或值ENDSELECT salary,CASEWHEN salary>20000 THEN 'A'WHEN salary>15000 THEN 'B'WHEN salary>10000 THEN 'C'ELSE 'D'END AS 工资级别 FROM employees; (4) 分组函数123456789101112SELECT SUM(salary) FROM employees;SELECT AVG(salary) FROM employees;SELECT MAX(salary) FROM employees;SELECT MIN(salary) FROM employees;SELECT COUNT(salary) FROM employees; #非空值个数SELECT SUM(DISTINCT salary) FROM employees;SELECT COUNT(*) FROM employees; # 统计行数SELECT COUNT(1) FROM employees; # 统计行数SELECT DATEDIFF('2021-2-11','2021-1-31'); # 日期之间相差天数 (5) 分组查询 语法:SELECT column_name, function(column_name)FROM table_nameWHERE column_name operator valueGROUP BY column_name; 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556SELECT job_id, MAX(salary)FROM employeesGROUP BY job_id;SELECT COUNT(*),location_idFROM departmentsGROUP BY location_id;# 分组前筛选SELECT department_id,AVG(salary)FROM employeesWHERE email LIKE '%a%'GROUP BY department_id;SELECT MAX(salary),manager_idFROM employeesWHERE commission_pct IS NOT NULLGROUP BY manager_id;# Q:哪个部门员工数>2# 查询每个部门员工数SELECT COUNT(*),department_idFROM employeesGROUP BY department_id;# 分组之后再筛选SELECT COUNT(*),department_idFROM employeesGROUP BY department_idHAVING COUNT(*) > 2;# 查询每个工种有奖金的员工且最高工资>12000的工种编号和最高工资SELECT MAX(salary),job_idFROM employeesWHERE commission_pct IS NOT NULLGRPOP BY job_idHAVING MAX(salary) > 12000;# 查询领导编号>102的每个领导手下员工最低工资>5000的领导编号,以及其最低工资SELECT manager_id,MIN(salary)FROM employeesWHERE manager_id > 102GROUP BY manager_id; HAVING MIN(salary) > 5000;# 按函数分组SELECT COUNT(*) count,LENGTH(last_name) nameFROM employeesGROUP BY nameHAVING count > 5;# 按多个字段分组# 每个部门每个工种的平均工资 (部门和工种相同的为一组)SELECT AVG(salary),department_id,job_idFROM employeesGROUP BY department_id,job_id; (6) 多表查询(连接查询/多表连接) 年代: sql92标准:仅支持内连接 sql99标准:支持 内连接+外连接(左外、右外)+全外连接 功能: 1、内连接 等值连接 非等值连接 自连接 2、外连接 左外连接 右外连接 全外连接 3、交叉连接 sql92 等值连接 1234567891011121314151617181920212223242526# 查询男女对应的名字SELECT `name`,boyNameFROM boys,beautyWHERE beauty.boyfriend_id = boys.id;# 查询员工名和对应的部门名SELECT last_name,department_nameFROM employees,departmentsWHERE employees.department_id = departments.department_id;# 查询员工名,工种号,工种名(起别名,起别名后,原来的表名失效)SELECT last_name,e.job_id,job_titleFROM employees AS e,jobs AS jWHERE e.job_id = j.job_id;# 加入筛选SELECT last_name,department_nameFROM employees AS e,departments AS dWHERE e.department_id = d.department_idAND e.department_id ID NOT NULL;# 三表查询SELECT last_name,department_name,cityFROM employees AS e,departments AS d,locations AS lWHERE e.department_id = d.department_idAND d.location_id = l.location_id; 非等值连接 1234# 查询员工工资和工资级别SELECT salary,grade_levelFROM employees e,job_grades gWHERE salary BETWEEN g.lowest_sal AND g.highest_sal; 自连接 1234# 查询员工名以及他上级名称SELECT e.employee_id,e.last_name,m.employee_id,m.last_nameFROM employees e,employees m,WHERE e.manager_id = m.employee_id; sql99 语法:1234567SELECT 查询列表FROM 表1 别名 [连接类型] 表2 别名ON 连接条件[WHERE 筛选条件][GROUP BY 分组][HAVING 筛选条件][ORDER BY 排序列表] 内连接 INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。 等值连接 123456789101112131415161718192021222324252627282930313233343536# 查询员工名和部门名SELECT last_name,department_nameFROM employees AS eINNER JOIN departments AS dON e.department_id = d.department_id;# 名字中包含e的员工名和工种名SELECT last_name,job_nameFROM employees AS eINNER JOIN jobs AS jON e.job_id = j.job_idWHERE e.last_name LIKE '%e%';# 部门个数>3的城市名和部门个数SELECT city,COUNT(*)FROM locations AS lINNER JOIN departments AS dON l.location_id = d.location_idGROUP BY cityHAVING COUNT(*) > 3;# 查询哪个部门的员工个数>3的部门名和员工个数,并按个数降序SELECT department_name,COUNT(*)FROM departments AS dINNER JOIN employees AS eON e.department_id = d.department_idGROUP BY department_nameHAVING COUNT(*) > 3ORDER BY COUNT(*) DESC;# 查询员工名、部门名、工种名,并按照部门名降序SELECT last_name,department_name,job_titleFROM employees AS eINNER JOIN departments AS d ON d.department_id = e.department_idINNER JOIN jobs AS j ON j.job_id = e.job_idORDER BY department_name DESC; 非等值连接 1234SELECT salary,grade_levelFROM employees AS eINNER JOIN job_grades AS gON e.salary BETWEEN g.lowest_sal AND g.highest_sal; 自连接 12345# 查询员工名字和上级名字SELECT e.last_name,m.last_nameFROM employees AS eINNER JOIN employees AS mON e.manager_id = m.employee_id; 外连接 LEFT [OUTER] JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。RIGHT [OUTER] JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。FULL [OUTER] JOIN 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950# 左连接# 查询没男朋友的女生SELECT b.*,bo.idFROM beauty AS bLEFT OUTER JOIN boys AS boON b.boyfriend_id = bo.idWHERE bo.id IS NULL;# 左连接# 查询哪个部门没有员工SELECT d.*,e.employee_idFROM departments AS dLEFT OUTER JOIN employees AS eON e.department_id = d.department_idWHERE d.employee_id IS NULL;# 右连接# 查询哪个部门没有员工SELECT d.*,e.employee_idFROM employees AS eRIGHT OUTER JOIN departments AS dON e.department_id = d.department_idWHERE d.employee_id IS NULL;# 交叉连接SELECT b.*,bo.*FROM boys AS boCROSS OUTER JOIN beauty AS b;eg.# 查询编号>3的女生所对应男生信息SELECT b.*,bo,idFROM beauty AS bLEFT OUTER JOIN boys AS boON b.boyfrienf_id = b.idWHERE b.id > 3;# 查询哪个城市没有部门SELECT city,d.department_idFROM locations AS lLEFT OUTER JOIN departments AS dON l.location_id = d.location_idWHERE d.department_id IS NULL;# 查询[部门名]为SAL和IT的员工信息SELECT e.*,department_nameFROM employees AS eRIGHT JOIN departments AS dON e.department_id = d.department_idWHERE department_name IN('SAL','IT'); (7) 子查询(内查询) 出现在其他语句中的SELECT语句,叫做子查询或者内查询。外部的查询叫做主查询或外查询。子查询可以在使用表达式的任何地方使用,并且必须在括号中关闭。 WHERE子句中(1)1234567891011121314151617181920212223242526272829303132333435# 在位于美国(USA)的办公室工作的员工。SELECT lastName, firstNameFROM employeesWHERE officeCode IN (SELECT officeCode FROM offices WHERE country = 'USA');# 查询找到其付款大于平均付款的客户SELECT customerNumber, checkNumber, amountFROM paymentsWHERE amount > (SELECT AVG(amount) FROM payments);# 查找没有下过任何订单的客户SELECT customerNameFROM customersWHERE customerNumber NOT IN (SELECT DISTINCT customerNumber FROM orders); FROM子句中(1) 从子查询返回的结果集将用作临时表,该表称为派生表或物化子查询。 123456789# 查询将查找订单表中的最大,最小和平均数SELECT MAX(items), MIN(items), FLOOR(AVG(items))FROM (SELECT orderNumber, COUNT(orderNumber) AS items FROM orderdetails GROUP BY orderNumber) AS lineitems; 以上sql语句来自易百教程 WHERE子句中(2)1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950# 谁的工资比Abel高SELECT *FROM employeesWHERE salary > ( SELECT salary FROM employees AS e WHERE e.last_name = 'Abel');# 返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id,和工资SELECT last_name,job_id,salaryFROM employeesWHERE job_id = ( SELECT job_id FROM employees WHERE employee_id = '141')AND salary > ( SELECT salary FROM employees WHERE employee_id = '143');# 返回公司工资最少的员工的last_name,job_id,salarySELECT last_name,job_id,salaryFROM employeesWHERE salary = ( SELECT MIN(salary) FROM employees);# 最低工资大于50号部门最低工资的部门id和其最低工资# 错误语句SELECT department_id,MIN(salary)FROM employeesWHERE MIN(salary) > ( SELECT MIN(salary) FROM employees WHERE department_id = '50');# 正确语句SELECT department_id,MIN(salary)FROM employeesGROUP BY department_idHAVING MIN(salary) > ( SELECT MIN(salary) FROM employees WHERE department_id = '50'); where 子句的作用是对查询结果进行分组前,将不符合where条件的行去掉,即在分组之前过滤数据,where条件中不能包含聚组函数,使用where条件过滤出特定的行。having 子句的作用是筛选满足条件的组,即在分组之后过滤数据,条件中经常包含聚组函数,使用having 条件过滤出特定的组,也可以使用多个分组标准进行分组。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566# 返回location_id是1400和1700部门中所有员工姓名SELECT last_nameFROM employeesWHERE department_id IN( SELECT department_id FROM departments WHERE location_id = 1400 OR location_id = 1700);# 返回其他工种中比job_id为'IT_PROG'的部门任意工资低的员工的工号,姓名,job_id,salarySELECT last_name,job_id,salary,employee_idFROM employeesWHERE salary < ANY( SELECT salary FROM employees WHERE job_id = 'IT_PROG')AND job_id <> 'IT_PROG';# 或者SELECT last_name,job_id,salary,employee_idFROM employeesWHERE salary < MAX( SELECT salary FROM employees WHERE job_id = 'IT_PROG')AND job_id <> 'IT_PROG';# 返回其他部门中比job_id为'IT_PORG'的部门所有工资都低的员工的工号,姓名,job_id,salarySELECT last_name,job_id,salary,employee_idFROM employeesWHERE salary < ALL( SELECT salary FROM employees WHERE job_id = 'IT_PROG')AND job_id <> 'IT_PROG';#或者SELECT last_name,job_id,salary,employee_idFROM employeesWHERE salary < ( SELECT MIN(salary) FROM employees WHERE job_id = 'IT_PROG')AND job_id <> 'IT_PROG';# 查询员工编号最小而且工资最高的员工信息SELECT *FROM employeesWHERE employee_id = ( SELECT MIN(employee_id) FROM employees)AND salary = ( SELECT MAX(salary) FROM employees);# 或者SELECT *FROM employeesWHERE (employee_id,salary) = ( SELECT MIN(employee_id),MAX(salary) FROM employees); SELECT子句中1234567# 查询每个部门员工个数SELECT d.*,( SELECT COUNT(*) FROM employees AS e WHERE e.department_id = d.department_id) AS 员工个数FROM departments AS d; FROM子句中(2)1234567891011121314151617181920212223# 每个部门平均工资的工资等级SELECT *,g.levelFROM ( SELECT AVG(salary) AS avg,department_id FROM employees AS e GROUP BY e.department_id) AS ag_depINNER JOIN job_grades AS gON ag_dep.avgBETWEEN g.lowest_salAND g.heigth_sal;# 比较SELECT *,g.levelFROM ( SELECT AVG(salary) AS avg,department_id FROM employees AS e GROUP BY e.department_id) AS ag_depLEFT OUTER JOIN job_grades AS gON ag_dep.avgBETWEEN g.lowest_salAND g.heigth_sal; EXISTS子句中(相关子查询) 判断子查询中有没有值 12345678910111213141516171819SELECT EXISTS( SELECT * FROM employees);# 查询有员工的部门名SELECT department_nameFROM departments AS dWHERE ( SELECT * FROM employees AS e WHERE e.department_id = d.department_id);# 或者SELECT department_nameFROM departments AS dWHERE d.department_id IN( SELECT e.department_id FROM employees AS e); 子查询景点场景 查询工资最低的员工信息123456SELECT *FROM employeesWHERE salary = ( SELECT MIN(salary) FROM employees); 查询平均工资最低的部门信息12345678910111213141516171819202122232425SELECT d.*FROM departments AS dWHERE d.department_id = ( SELECT department_id FROM employees GROUP BY department_id HAVING AVG(salary) = ( SELECT MIN(ag) FROM ( SELECT AVG(salary) AS ag,department_id FROM employees GROUP BY department_id ) AS ag_dep ) );# 或者SELECT *FROM departmentsWHERE department_id = ( SELECT department_id FROM employees GROUP BY department_id ORDER BY AVG(salary) ASC LIMIT 0,1); 查询平均工资最低的部门信息和该部门平均工资12345678910SELECT d.*,agFROM departments AS dJOIN ( SELECT AVG(salary) AS ag,department_id FROM employees GROUP BY department_id ORDER BY AVG(salary) ASC LIMIT 0,1) AS ag_depON d.department_id = ag_dep.department_id; 查询平均工资最高的job信息 123456789SELECT *FROM jobsWHERE job_id = ( SELECT job_id FROM employees GROUP BY job_id ORDER BY AVG(salary) DESC LIMIT 0,1); 查询平均工资高于公司平均工资的部门1234567SELECT AVG(salary),department_idFROM employeesGROUP BY department_idHAVING AVG(salary) > ( SELECT AVG(salary) FROM employees); 查询公司所有manager的详细信息123456SELECT *FROM employeesWHERE employee_id IN( SELECT DISTINCT manager_id FROM employees); 查询各部门中最高工资 中最低的那个部门,最低工资123456789select MIN(salary),department_idFROM employeesWHERE department_id = ( SELECT department_id FROM employees GROUP BY department_id ORDER BY MAX(salary) ASC LINIT 0,1); 查询平均工资最高的部门的manager详细信息1234567891011SELECT *FROM employees AS eINNER JOIN departments AS dON e.employee_id = d.manager_idWHERE d.department_id = ( SELECT department_id FROM employees GROUP BY department_id ORDER BY AVG(salary) DESC LIMIT 0,1); (8) 分页查询 SELECT * FROM 表 LIMIT (page - 1) * size, size; 123456789101112# 查询前五条员工信息SELECT * FROM employees LIMIT 0, 5;# 查询11-25条员工信息SELECT * FROM employees LIMIT 10, 15;# 有奖金的员工信息,工资较高的前10名SELECT *FROM employeesWHERE commission_pct IS NOT NULLORDER BY salary DESCLIMIT 0, 10; (9) 联合查询 将多条语句的查询结果合并成一个结果,多用于多个表没有连接关系时使用要求多条语句查询列数一样,顺序一样,会去掉重复项目,不去重使用UNION ALL 123456789101112131415# 部门编号>90或者邮箱中包含a的员工信息# 普通查询SELECT *FROM employeesWHERE email LIKE '%a%'OR department_id > 90;# 联合查询SELECT *FROM employeesWHERE email LIKE '%a%'UNIONSELECT *FROM employeesWHERE department_id > 90; (10) 插入语句 语法1:INSERT INTO table_name ( field1, field2,…fieldN )VALUES( value1, value2,…valueN ); 语法2:INSERT INTO table_nameSETfield1 = value1, field2 = value2,… fieldN = valueN; 12INSERT INTO boysSET id=19,name='Kavan',phone='180xxxxxxxx'; 语法1 支持插入多条数据 123INSERT INTO beautyVALUES(13,'小唐','女','1990-4-23','18888888888',NULL,2),(14,'小张','女','1991-4-23','18988888888',NULL,2); 语法1 支持子查询 1234567INSERT INTO beauty(id,name,phone)SELECT 26,'小宋','15888888888';INSERT INTO beauty(id,name,phone)SELECT id,name,phoneFROM boysWHERE id=26; (11) 更新语句 语法:UPDATE table_name SET field1=new-value1, field2=new-value2[WHERE Clause] 1234# 修改beauty表中姓唐的女生电话为15888888888UPDATE beautySET phone='15888888888'WHERE name LIKE '唐%' 多表更新语法:UPDATE table_1INNER | LEFT | RIGHT JOIN table_2ON 连接条件SET field1=new-value1, field2=new-value2[WHERE Clause] 12345678910111213# 修改张无忌女友手机号18989898989UPDATE boys AS bINNER JOIN beauty AS buON bu.boyfriend_id = b.idSET bu.phone = '18989898989'WHERE b.name = '张无忌';# 修改没有男友的女生的男友编号为张飞UPDATE boys AS bRIGHT JOIN beauty AS buON bo.boyfriend_id = b.idSET bu.boyfriend_id = 2WHERE b.id IS NULL; (12) 删除语句 语法1:DELETE FROM table_name [WHERE Clause]; 多表删除语法:DELETE 表一的别名,表二的别名FROM 表一 AS 别名INNER | LEFT | RIGHT JOIN 表二 AS 别名ON 连接条件WHERE 筛选条件; 12345678910111213# 删除张无忌女友信息DELETE bFROM boys AS bINNER JOIN beauty AS buON bu.boyfriend_id = b.idWHERE b.boyName = '张无忌';# 删除黄晓明以及其女友记录DELETE b,buFROM boys AS bINNER JOIN beauty AS buON bu.boyfriend_id = b.idWHERE b.boyName = '黄晓明'; 语法2:(自增长列归位,相当于刚创建的空表,删除语句没有返回值,删除后不能回滚)TRUNCATE TABLE table_name; (13) 表和库的管理 操作创建:CREATE修改:ALTER删除:DROP 库 创建 CREATE DATABASE database_name;创建之前加判断CREATE DATABASE IF NOT EXISTS database_name; 修改一般不修改,之前有个RENAME关键字修改数据库名字,后来由于安全原因废弃了,可以直接到数据库存储文件位置修改文件夹名字 改数据库的字符集ALTER DATABASE database_name CHARACTER SET gbk; 删除 DROP DATABASE database_name;删除之前判断DROP IF EXISTS database_name; 表 创建 CREATE TABLE table_name (column_name column_type []); 123456789101112131415161718192021CREATE TABLE book( id INT, bname VARCHAR(20), price DOUBLE, authorId INT, publishDate DATETIME,);# 或者CREATE TABLE IF NOT EXISTS book( id INT, bname VARCHAR(20), price DOUBLE, authorId INT, publishDate DATETIME,);CREATE TABLE author( id INT, au_name VARCHAR(20), nation VARCHAR(10)); 修改 可以修改列名,类型,约束,添加列,删除列,修改表名 123456789101112131415161718# 修改列名ALTER TABLE book CHANGE COLUMN publishDate pubDate DATETIME;# 修改列的类型ALTER TABLE book MODIFY COLUMN pubDate TIMESTAMP;# 添加新列ALTER TABLE author ADD COLUMN annual DOUBLE;# 插入第一列ALTER TABLE author ADD COLUMN annual DOUBLE FIRST;# 在某一列后面插入ALTER TABLE author ADD COLUMN annual DOUBLE AFTER au_name;# 删除列ALTER TABLE author DROP COLUMN annual;# 修改表名ALTER TABLE author RENAME TO book_author; 删除1234DROP TABLE book_author;# 删除之前判断DROP TABLE IF EXISTS book_author; 复制 仅复制表结构1CREATE TABLE copy_author LIKE author; 复制结构和数据12CREATE TABLE copy_author2SELECT * FROM author; 复制结构和部分数据1234CREATE TABLE copy_author3SELECT id,au_nameFROM authorWHERE nation = '中国'; 仅复制部分结构123456789CREATE TABLE copy_author4SELECT id,au_nameFROM authorWHERE 1=2;# 或者CREATE TABLE copy_author4SELECT id,au_nameFROM authorWHERE 0; (14) 约束 约束是一种限制,为了保证表中数据准确可靠 六种约束 NOT NULL:非空,用于保证该字段值不能为空 DEFAULT:默认,用于保证该字段有默认值 PRIMARY KEY:主键,保证该字段的值唯一且非空 UNIQUE:唯一,用于保证该字段值唯一,可以为空 CHECK:检查约束,mysql不支持 FOREIGN KEY:外键,用于限制两个表的关系,保证该字段的值必须来自主表关联列的值,在从表添加外键约束,用于引用主表中某列的值 约束在创建表和修改表是可以使用 关于主键和唯一的比较: 外键: 须在从表设置外键关系 从表外键列的类型和主表关联列类型一致或兼容 主表中关联列必须是一个key(一般是主键或唯一) 插入数据时,先插入主表再插入从表,删除数据时,先删除从表再删除主表 列级约束1234567891011121314151617CREATE DATABASE students;CREATE TABLE stuinfo ( id INT PRIMARY KEY,#主键 stuName VARCHAR ( 20 ) NOT NULL,#非空 gender CHAR ( 1 ) CHECK ( gender = '男' OR gender = '女' ),#检查 seat INT UNIQUE,#唯一 age INT DEFAULT 18,#默认约束 majorId INT REFERENCES major ( id ) #外键,对列级约束没作用 );CREATE TABLE major ( id INT PRIMARY KEY, majorName VARCHAR ( 20 ) );DESC stuinfo; 表级约束1234567891011121314151617181920212223242526272829303132333435363738394041424344DROP TABLE IF EXISTS stuinfo;CREATE TABLE stuinfo ( id INT, stuName VARCHAR ( 20 ), gender CHAR ( 1 ), seat INT, age INT, majorId INT, CONSTRAINT pk PRIMARY KEY ( id ),#主键 CONSTRAINT uq UNIQUE ( seat ),#唯一键 CONSTRAINT ck CHECK ( gender = '男' OR gender = '女' ),#检查 CONSTRAINT fk_stuinfo_major FOREIGN KEY ( majorId ) REFERENCES major ( id ) #外键);SHOW INDEX FROM stuinfo;# 或者DROP TABLE IF EXISTS stuinfo;CREATE TABLE stuinfo ( id INT, stuName VARCHAR ( 20 ), gender CHAR ( 1 ), seat INT, age INT, majorId INT, PRIMARY KEY ( id ),#主键 UNIQUE ( seat ),#唯一键 CHECK ( gender = '男' OR gender = '女' ),#检查 FOREIGN KEY ( majorId ) REFERENCES major ( id ) #外键);SHOW INDEX FROM stuinfo;# 一般写法CREATE TABLE IF NOT EXISTS stuinfo ( id INT PRIMARY KEY,#主键 stuName VARCHAR ( 20 ) NOT NULL,#非空 gender CHAR ( 1 ) CHECK ( gender = '男' OR gender = '女' ),#检查 seat INT UNIQUE,#唯一 age INT DEFAULT 18,#默认约束 majorId INT, CONSTRAINT fk_stuinfo_major FOREIGN KEY ( majorId ) REFERENCES major ( id ) #外键); 修改表时添加约束 添加列级约束ALTER TABLE 表名 MODIFY COLUMN 字段名 字段类型 新约束;添加表级约束ALTER TABLE 表名 ADD [CONSTRAINT 约束名] 约束类型(字段名); 1234567891011121314151617181920212223242526DROP TABLE IF EXISTS stuinfo;CREATE TABLE stuinfo ( id INT, stuName VARCHAR ( 20 ), gender CHAR ( 1 ), seat INT, age INT 18, majorId INT);# 添加非空约束ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) NOT NULL;# 添加默认约束ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;# 添加主键ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;ALTER TABLE stuinfo ADD PRIMARY KEY(id);# 添加唯一键ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;ALTER TABLE stuinfo ADD UNIQUE(seat);# 添加外键ALTER TABLE stuinfo ADD FOREIGN KEY ( majorId ) REFERENCES major ( id ) ; 修改表时删除约束1234567891011121314# 删除非空约束ALTER TABLE stuinfo MODIFY COLUMN stuName VARCHAR(20) NULL;# 删除默认约束ALTER TABLE stuinfo MODIFY COLUMN age INT;# 删除主键ALTER TABLE stuinfo DROP PRIMARY KEY;# 删除唯一键ALTER TABLE stuinfo DROP INDEX seat;# 删除外键ALTER TABLE stuinfo DROP FOREIGN KEY majorId; (15) 标示列(自增长列) 可以不用手动插入值,系统提供默认值序列标识列须是一个key每个表最多能有一个自增长列标识列类型只能是数值型标示列可以通过SET AUTO_INCREMENT_INCREMENT = 3设置步长,也可以通过手动插入值,设置起始值 12345678910# 创建表时设置标示列DROP TABLE IF EXISTS tab_identity; CREATE TABLE tab_identity( id INT PRIMARY KEY AUTO_INCREMENT,);# 修改表时设置标示列ALTER TABLE tab_identity MODIFY COLUMN id PRIMARY KEY AUTO_INCREMENT;# 修改表时删除标示列ALTER TABLE tab_identity MODIFY COLUMN id; (16) 事物 一个或者一组sql语句组成一个执行单元,执行这个单元要么全部执行,要么全部不执行 查看存储引擎1SHOW ENGINES; 事务的ACID属性 原子性(Atomicity)原子性是指事务是一个不可分割的单位,事物中的操作要么都发生,要么都不发生。 一致性(Consistency)事务必须使数据库从一个一致性状态变到另一个一致性状态。 隔离性(Isolation)一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能相互干扰。 持久性(Durability)一个事务一旦被提交,它对数据库中数据的改变是永久性的,接下来的其他操作和数据库故障应该对其有任何影响。 事务的创建 隐式的事务:没有明显的开启和关闭标记 insert、update、delete语句都是隐式事务 显示事务:具有明显的开始和结束标记,使用前必须先设置自动提交功能关闭 SET AUTOCOMMIT = 0; 步骤: 12345678# 1. 开启事务SET AUTOCOMMIT = 0;START TRANSACTION; # 可选的# 2. 编写事物中的sql语句增删改查语句(select、insert、update、delete)# 3. 结束事务commit; # 提交rollback; # 回滚 事务并发问题 脏读:对于两个事务,T1、T2,T1读取了T2已经更改但是没有提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的。 不可重复读:对于两个事务,T1、T2,T1读取了一个字段,然后T2更新了这个字段之后,T1再读取这个字段,,值就不一样了。 幻读:对于两个事务,T1、T2,T1从一个表中读取了一个字段,T2在这个表中插入了新的行之后,如果T1再读这个表,就会多处几行数据。 隔离级别mysql中默认第三种隔离级别oracle默认第二种 查看当前隔离级别1SELECT @@TX_ISOLATION 设置隔离级别1234# 当前sql语句SET TRANSACTION ISOLATION LEVEL read committed;# 数据库系统SET GLOBAL TRANSACTION ISOLATION LEVEL read committed; 回滚点(savepoint)123456SET AUTOCOMMIT = 0;START TRANSACTION;DELETE FROM account WHERE id=25;SAVEPOINT a;DELETE FROM account WHERE id=28;ROLLBACK TO a; (17) 存储过程 一组预先编译好的SQL语句合集,类似批处理语句适合做一些批量插入更新等操作 创建存储过程 CREATE PROCEDURE 存储过程名(参数列表)BEGIN 存储过程体(一组合法有效的SQL语句)END Note参数列表包含3部分(参数模式 参数名 参数类型)如果存储过程体中只有一条SQL语句,那么 BEGIN 和 END 可以省略存储过程体中每条语句必须加分号,结尾可以用 DELIMITER 重新设置 参数模式 IN :该参数可以作为输入(需要调用方传值进来) OUT : 该参数可以作为输出(参数可以作为返回值) INOUT : 该参数可以作为输入输出(既需要传入值,又能返回值) 调用存储过程 CALL 存储过程名(参数列表); 空参列表 1234567891011121314# 插入admin中5条记录DELIMITER $CREATE PROCEDURE myp1()BEGIN INSERT INTO admin(username,`password`) VALUES('username1','password1'), VALUES('username2','password2'), VALUES('username3','password3'), VALUES('username4','password4'), VALUES('username5','password5');END $# 调用CALL myp1()$ IN 模式参数存储过程 123456789101112131415161718192021222324252627282930# 根据女生名查询对应的男生信息DELIMITER $CREATE PROCEDURE myp2(IN beautyName VARCHAR(20))BEGIN SELECT bo.* FROM boys AS bo RIGHT JOIN beauty AS b ON bo.id = b.boyfirend_id WHERE b.name = beautyName;END $# 调用CALL myp2('小昭')$# 创建存储过程,验证用户登录是否成功DELIMITER $CREATE PROCEDURE myp3(IN username VARCHAR(20), IN password VARCHAR(20))BEGIN DECLARE result INT DEFAULT 0; SELECT COUNT(*) INTO result FROM admin WHERE admin.username = username AND admin.password = password; SELECT if(result>0,'成功','失败');END $# 调用CALL myp3('','')$ OUT 模式参数存储过程 123456789101112131415# 根据女生名返回对应的男生名DELIMITER $CREATE PROCEDURE myp4(IN beautyName VARCHAR(20), OUT boyName VARCHAR(20))BEGIN SELECT bo.boyName INTO boyName FROM boys AS bo INNER JOIN beauty AS b ON bo.id = b.boyfirend_id WHERE b.name = beautyNameEND $# 调用SET @bName$CALL myp5('小昭',@bName);SELECT @bName$ INOUT 模式参数存储过程 12345678910111213# 传入a和b两个值,要求a和b都翻倍并返回DELIMITER $CREATE PROCEDURE myp5(INOUT a INT, INOUT b INT)BEGIN SET a = a * 2; SET b = b * 2;END $# 调用SET @m = 10$SET @n = 20$CALL myp5(@m,@n)$SELECT @m,@n$ 存储过程删除 DROP PROCEDURE 存储过程名; 存储过程查看 SHOW CREATE PROCEDURE 存储过程名; (18) 函数 类似存储过程,但更适合做一些处理数据结果后返回一个结果 函数创建 CREATE FUNCTION 函数名(参数列表) RETURNS 返回类型BEAGIN 函数体END Note参数列表包含2部分(参数名 参数类型)函数体肯定会包含返回语句,不包含会报错,如果return不在最后也不会报错函数体中只有一条语句则可以省略,结尾可以用 DELIMITER 重新设置 调用函数 SELECT 函数名(参数列表); 无参 123456789101112# 返回公司员工个数DELIMITER $CREATE FUNCTION myf1() RETURNS INTBEGIN DECLARE c INT DEFAULT 0; SELECT COUNT(*) INTO c FROM employees; RETURN c;END $# 调用SELECT myf1()$ 有参有返回 12345678910111213141516171819202122232425262728293031# 根据员工名返回工资DELIMITER $CREATE FUNCTION myf2(empName VARCHAR(20)) RETURNS DOUBLEBEGIN SET @sal = 0; SELECT salary INTO @sal FROM employees WHERE last_name = empName; RETURN @sal;END $# 调用SELECT myf2('kochhar')$# 根据部门名返回该部门的平均工资DELIMITER $CREATE FUNCTION myf3(deptName VARCHAR(20)) RETURNS DOUBLEBEGIN DECLARE sal DOUBLE DEFAULT 0; SELECT AVG(salary) INTO sal FROM employees AS e INNER JOIN departments AS d ON e.department_id = d.department_id WHERE d.department_name = deptName; RETURN sal;END $# 调用SELECT myf3('IT')$ 查看函数 SHOW CREATE FUNCTION 函数名; 删除函数 DROP FUNCTION 函数名; 1234567891011# 求和函数DELIMITER $CREATE FUNCTION test_fun_add(num1 FLOAT,num2 FLOAT) RETURNS FLOATBEGIN DECLARE sum FLOAT DEFAULT 0; SET sum = num1 + num2; RETURN sum;END $# 调用SELECT test_fun_add(1,2)$ (19) 流程控制 if else12345678910# 根据传入成绩显示等级DELIMITER $CREATE FUNCTION test_if(score INT) RETURNS CHARBEGIN IF score >= 90 AND score <= 100 THEN RETURN 'A'; ELSEIF score >= 80 THEN RETURN 'B'; ELSEIF score >= 60 THEN RETURN 'C'; ELSE RETURN 'D'; END IF;END $ 循环 while loop repeatiterate 类似 continueleave 类似 break while [标签:] while 循环条件 do 循环体;end while [标签]; 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647# 批量插入,没有循环控制DELIMITER $CREATE PROCEDURE while1(IN insertCount INT)BEGIN DECLARE i INT DEFAULT 1; WHILE i < insertCount DO INSERT INTO admin(username,`password`) VALUES(CONCAT('rose', i),'666'); SET i = i + 1; END WHILE;END $# 调用CALL while1(100)$# 批量插入,有循环控制(如果次数 >20 就停止)DELIMITER $CREATE PROCEDURE while2(IN insertCount INT)BEGIN DECLARE i INT DEFAULT 1; a:WHILE i < insertCount DO INSERT INTO admin(username,`password`) VALUES(CONCAT('rose', i),'666'); IF i >= 20 THEN LEAVE a; END IF; SET i = i + 1; END WHILE a;END $# 调用CALL while2(100)$# 批量插入,有循环控制(只插入偶数数据)DELIMITER $CREATE PROCEDURE while3(IN insertCount INT)BEGIN DECLARE i INT DEFAULT 0; a:WHILE i < insertCount DO SET i = i + 1; IF MOD(i,2) != 0 THEN ITERATE a; END IF; INSERT INTO admin(username,`password`) VALUES(CONCAT('rose', i),'666'); END WHILE a;END $# 调用CALL while3(100)$ loop [标签:] loop 循环体;end loop [标签]; repeat [标签:] repeat 循环体;until 结束条件end repeat [标签];","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"mysql","slug":"mysql","permalink":"https://laujiangtao.github.io/tags/mysql/"}]},{"title":"Windows VScode 配置 C++ 环境","slug":"Windows-VScode-配置-C++-环境","date":"2021-01-28T12:39:37.000Z","updated":"2022-07-10T11:53:23.905Z","comments":false,"path":"2021/01/28/windows-vscode-configures-the-cpp-environment/","link":"","permalink":"https://laujiangtao.github.io/2021/01/28/windows-vscode-configures-the-cpp-environment/","excerpt":"","text":"Windows 安装 MinGW-w64 官网下载地址 Windows点击 MingW-W64-builds 选项下载 下载后安装 安装过程,下载文件 在国内下载过程非常慢,我下载了两天,最终失败。所以直接去下载安装包了。 下载安装包 解压文件到本地目录并复制bin的路径 将路径添加到环境变量 在命令行检查环境变量是否配置成功 123gcc --versiong++ --versiongdb --version VSCode 配置C++ vscode 安装C++插件,在vscode插件中搜索C++ 配置编译器 按快捷键Ctrl+Shift+P调出命令面板,输入C/C++,选择Edit Configurations(UI)进入配置。 配置两个选项: 编译器路径 IntelliSense 模式 配置完成后,此时在侧边栏可以发现多了一个.vscode文件夹,并且里面有一个c_cpp_properties.json文件,内容如下,说明上述配置成功。 点我展开 c_cpp_properties.json 1234567891011121314151617181920{ "configurations": [ { "name": "Win32", "includePath": [ "${workspaceFolder}/**" ], "defines": [ "_DEBUG", "UNICODE", "_UNICODE" ], "compilerPath": "D:/Program Files (x86)/mingw-w64/mingw64-x86_64-8.1.0-release-posix-seh-rt_v6-rev0/mingw64/bin/g++.exe", "cStandard": "gnu17", "cppStandard": "gnu++14", "intelliSenseMode": "gcc-x64" } ], "version": 4} 现在可以通过 Ctrl + < + ` 快捷键打开内置终端并进行编译运行了。 配置构建任务 使用vscode的默认构建任务工具,调用g++编译器,编译源代码,创建可执行文件。 同样,按快捷键Ctrl+Shift+P调出命令面板,输入tasks,选择Tasks:Configure Default Build Task 再选择C/C++: g++.exe build active file,会出现一个tasks.json配置文件: 点我展开 tasks.json 123456789101112131415161718192021222324252627{ "version": "2.0.0", "tasks": [ { "type": "cppbuild", "label": "C/C++: g++.exe build active file", "command": "D:/Program Files (x86)/mingw-w64/mingw64-x86_64-8.1.0-release-posix-seh-rt_v6-rev0/mingw64/bin/g++.exe", "args": [ "-g", "${file}", "-o", "${fileDirname}\\\\${fileBasenameNoExtension}.exe" ], "options": { "cwd": "D:/Program Files (x86)/mingw-w64/mingw64-x86_64-8.1.0-release-posix-seh-rt_v6-rev0/mingw64/bin" }, "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true }, "detail": "compiler: \\"D:/Program Files (x86)/mingw-w64/mingw64-x86_64-8.1.0-release-posix-seh-rt_v6-rev0/mingw64/bin/g++.exe\\"" } ]} 配置调试设置 点击菜单运行->启动调试 选择C++(GDB/LLDB) 在选择g++.exe - 生成和调试活动文件 会在.vscode文件夹里面生成一个launch.json文件 点我展开 launch.json 1234567891011121314151617181920212223242526272829{ // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "g++.exe - 生成和调试活动文件", "type": "cppdbg", "request": "launch", "program": "${fileDirname}\\\\${fileBasenameNoExtension}.exe", "args": [], "stopAtEntry": false, "cwd": "D:/Program Files (x86)/mingw-w64/mingw64-x86_64-8.1.0-release-posix-seh-rt_v6-rev0/mingw64/bin", "environment": [], "externalConsole": false, "MIMode": "gdb", "miDebuggerPath": "D:\\\\Program Files (x86)\\\\mingw-w64\\\\mingw64-x86_64-8.1.0-release-posix-seh-rt_v6-rev0\\\\mingw64\\\\bin\\\\gdb.exe", "setupCommands": [ { "description": "为 gdb 启用整齐打印", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "preLaunchTask": "C/C++: g++.exe build active file" } ]} 然后,可以点击右下角添加配置...按钮,添加相应配置 或者复制别人的配置进来。 至此,.vscode文件夹下共有三个文件c_cpp_properties.json、launch.json、tasks.json。 手动创建这三个文件,将上述相应内容复制进去,也可以完成相应配置。 现在可以按F5调试一下hello.cpp试试","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"C++","slug":"C","permalink":"https://laujiangtao.github.io/tags/C/"}]},{"title":"旧时光","slug":"旧时光","date":"2021-01-18T07:37:58.000Z","updated":"2021-05-15T15:53:32.385Z","comments":false,"path":"2021/01/18/old-time/","link":"","permalink":"https://laujiangtao.github.io/2021/01/18/old-time/","excerpt":"","text":"于2013年9月4日,用联想A360录制中央人民广播电台,「千里共良宵」节目 您的浏览器不支持 audio 标签。 《心曲,散落于旧时光》 作者:一朵怜幽 [初夏,一米阳光] 初夏,日光倾城。 诗行里于樱花的咏叹未曾落定,被温透的诗情还未风干,街左右浅粉的花瓣已摇曳着曼妙的舞姿,纷纷落下。那身影,让人怜惜,其时花开,即时花败,终敌不过荏苒时光。春,让多少花儿为其献了身? 岸边的柳,依靠了哪夜皓月的肩,让绿盈满了天空,朦胧了脸庞?那些风景由近至远处妖娆,葱翠的绿浪蔓延成了远方的地平线。 束起短的发,游弋于这江南小城的拐角。让心成为一个跳动的音符,融在五月的隽永里翩翩而舞。光,有透明的暖意,亦多了一丝炙热,照在背上,洒了一地的影子。 初夏,是我们相识的时节,风声穿越记忆,一遍遍回忆,你是怎样把我从忧郁的冬眠中唤醒,理顺了二十多年的心结,翻走了那淡灰的日子。 用一支曲的时间,回眸相拥走过的日子,那次,为你写的文,你阅过,说感动得要哭,我竟不相信。你始终是个刚毅的男子,钟情经济学,怎会有感那儿女情长的唏嘘。 暮地害怕起来,我有多了解你,你又对我有多了解? 此一时,想裁一片天空,借它的蓝,放在此文中,澄澈你的心,亦让你明白我的情。却也只是空幻。 货船从眼前掠过,渐行渐远,皱了河的心思,日落的余晖伏在面上,泛着金黄的光。 这河,缓缓吞了热情的太阳,即将吐出幽幽的星月。 这光,能否借一抹,予我升腾,朝着你所在的方向,凝眸…… [幽思心曲] 盛夏,阳光都是纯白色。 在氤氲香气的清晨,遵循着花飞花落的旧路,穿过鹅卵石小道,踏过古色的木廊,漫步到第八座桥。 有轻柔的风从身旁拂过,翕动了我的睫毛。依着桥栏,独对清碧,那些水香弥漫,濛濛的一片,有朦胧的美丽。拈一朵素洁小花,在太阳抛出暧昧眼神的霎那,抛入在水中央,荡起的圈圈涟漪便拥有了花朵的独特清芬,漾着一抹欢愉与感激。 一叶小舟,不知泅渡了多少人由此岸到彼岸,此时,被废弃在一隅,盛满了无数碎末的叹息。那斑驳的容颜,都是光阴流逝的见证。 静静地,溢出绚彩如烟的往事,隐遁了,前世今生。临岸,一佝偻阿婆,气定神闲,推着轮椅上的阿翁,于木阁亭小歇,未闻交谈,仅那几个细微的动作与温情的眼神,亦羡煞许多人。 那日,见到一个有关爱与被爱的投票,于是投了爱一票,闺蜜问:被爱不是要幸福些么?我答:正因这样,才让我爱的人感受幸福。她反问:你不是说过,被爱,有时也会成为负担么?我,无言以对。 抑或,待到桑榆暮景时,方能悟出爱的真谛吧? 此一时,只静静而立,远离千葩百卉,开成一朵含苞的白色小花,一朵就好。等候下个女子的柔指,拈花而笑,之后,绽在她的耳畔…… [夏日呓语] 小城,暮色已临,如是地想着,夜,必定是斑斓的。 将心放在闲处,凭栏感受清风徐来,清浅的柔波里,剪影凄美,迷幻了眼睛。你说我宛如黛玉,独自莫凭栏,会沾染孤寂。怎奈独爱上这凭栏的心,寂寥的景,不可自制。 《琵琶语》,余音袅袅,融在空气里悬浮于空间,久久不肯落下。聆听此曲时,常幻想一雪衣女子:素手弄琵琶,信手低眉,续续弹,弹尽心中无限事。一声叹息,穿越千年寻觅,叹尽人间的哀怨与孤寂。 《琵琶语》源于《一个陌生女人的来信》,茨威格的直译本读过数次,泪亦流过数次。而后,一度认为那只是个梦,连卡门与爱丝美拉达亦都活在纸上,何况他们。 七月,祝福很多人:七月初始,顺安。可是,竟然忘了祝福自己,于是,一度被人误解,从来不是个善于言辞之人,不惯于解释。因为一直相信,解释不如沉默,一切自当水落石出。 自忖,你们不信我,但我,信时间,可是依旧希望你们都安好。误解冰释时,我愿一笑而过,之后,风轻云淡。 有人问我:“我若英年早殇,能否有你一滴清泪。” 他不知,我是怎样个爱落泪的女子,常常看着别人的故事,流自己的眼泪。仅你这一句话,已让我的眼中便蒙上异彩,已然成殇。那么,请你好好活着,不为他人,只为自己,只为我眼中的这滴泪,不要落下。 近来,旧疾复发,身心俱疲,不想再过问其他,唯想书写一笺丝缕分明且温润馨香的诗,纪念此一时。然,无芳姿纤笔,便不能落笔成花,只能静静地,任残章驮着白色的忧伤,让夕阳一点一点西沉,落在心上…… [采一瓣阳光,置在心房] 初冬,时光温然,阳光静沉。 午后的闲散时分,径自踱在疏影憧憬的青石巷内,有江南古韵的屋舍在小巷左右排列着延伸。尽头有一方莲塘,那些残荷映在日光下,水中的倒影凄美里亦是分外妖饶。无论哪一隅,都有江南水墨画的气息神韵。时常在静好的时光,静默的风景里,醉去,迟迟不能醒来。 葱郁墨绿的梧桐叶边缘被初冬的风点缀了些橙红,宛如羞红了脸的少女。那三两片已枯黄的乘着风蹁跹而下,落地轻吟。大片的阳光穿梭过梧桐叶,被切割成细细碎碎的光块,斑斓地洒在地上。我立在那里,如痴如醉地淋了一场阳光梧桐雨。 摊开掌心,让一瓣阳光停留,看她恬静的样子,感知她的温度。这阳光,有声有韵,有形有态,有馥有香。 这一瓣,从天际流淌下来,一路欢愉地低吟浅唱。穿过梧桐,到达我掌心时有有微微的破碎声,如柔匀的吐息。她轻声对我诉说,在红尘里与万物邂逅的美丽瞬间。 这一瓣,柔谐婉转地随着风变动成各异的形态,倾斜着,有不规则的美,如一个舞者随着音律不停地变换姿步。那一份绝韵,让人不觉对着她,凝眸,唯恐错过某个清丽而绰约的刹那。 这一瓣,有明媚的轻香浮溢,还聚敛着风尘的味道,不似花却也清清淡淡将你俘获。这香味,随着风声穿过记忆,变成来日灿烂缤纷的思恋印在往事里。 这一刻,所有的阳光,都属于今日的时光。清晨从潋滟的海面而来,黄昏从幽静的山谷隐褪。我用最虔诚的姿态恳求此时的光阴,可否将这一瓣赠予我。我会温柔地捧她在手心,迈着缓缓的步调,不至于颠簸到她,携领着回家,珍存于挚爱的水晶瓶中。 在云淡风轻的夜,携着她到幽静的露台,凭栏而望,望她从未见过的月色。一瓣日光与月光相遇,那该是怎样的景致呢?我定会被她们那炫美的邂逅感动,然后摘一朵花浸酒,庆贺这无双的一对。 或是将她悬挂在我清冷的案台,细致地描抹下她的颜容,她有娇柔的形态,暖暖的颜色。我们共赏一曲箫声、再赏一曲琵琶语。这些忧伤如泣的音乐常常让我柔软的心疼痛得无以复加。有她在身边,那些暖光定全浸润所有的忧伤,让疼痛也变得温柔而温暖。 或是让她住进我的文字里,游弋于字里行间,隐约我所有的愁绪。那篇文字定会饱满而深情,成为历来最能打动自我的一文。在后日,翻阅记忆,轻易便因为它忆起那一日的自己。 我还会征得她的应允,置放在你、我、他、所有需要温暖的人的心房。让她在心中,生根、发芽、再开出一朵又一朵温馨的永不凋零的阳光之花…… 《蓦然回首,那些旧时光已不在》 文/迅雷 看到这四个字,让人很容易想到,暮然回首此人就在灯火阑珊处。但意境跟它不一样。 ——题记 一、无泪 刚出来时,总是很天真的认为只要努力,只要你用心的去做了,那就是好的。当第一次被生产线的老大骂的狗血淋头的时候,当被人家故意嫁祸的时候,当被冤枉百口莫辩的时候,当被别人认为是新来的就呼来唤去的时候,才发现这就是社会。这就是生活中极小极小的潜规则,这只是生活的一角。那时眼泪是天性,总是以眼泪来抗拒,总是认为委屈,以为哭泣是最好的宣泄,是解决的办法。 当人们用嫌恶和嘲笑来看待时,才发现泪水是那么的不值钱。眼泪是懦弱的表现,哭了才代表你输了。所以无泪的网名成了我的固定。以此来告诫自己不许哭,不能哭,要坚强。当要流泪时,就偷偷的躲起来,偷偷的哭,不能让别人看到自己的懦弱。总认为这样的自己很坚强,很厉害很了不起,当后来看到一句话,提醒自己坚强是弱者的表现。只有脆弱的人才会需要用坚强来提醒、麻痹自己。 现在的自己,奇怪的是依然爱哭,依然偷偷的哭,依然以为这样的自己最坚强。其实自己才知道,自己有多脆弱。 二、朋友 熟话说,在家靠父母,出外靠朋友。这句话一句也不假,试问身在异乡的我们,哪个没有一两个知心朋友,哪个没被朋友帮助过?我爱回忆,突然回忆起我生命中那些可爱的朋友。 王飞艳:她是第一个主动关心我的人,她是我的第一个知心朋友,可以说心里话,不必担心被人告密,不必担心会添油加醋的去陷害你。她是我第一个去放开心去交的朋友。同样爱唱歌的我们,曾傻傻的在马路上大声的歌唱过,不在乎别人异样的眼光。曾一起诉说过自己那些不开心的童年时光,一起动情的哭过,一起谈这个社会,一起谈我们的梦想,一起努力去实现梦想。曾经说下豪言说要让中国的每寸土地都留下我们的脚印。曾一起找工作被PK过,曾一起在大马路上被打劫过……那些曾经的曾经我们一起面对过。现在,我变了,不再是那个说道就能做到的我了,她生病了没去看,她要我帮的忙我一件也没帮上,不是不愿意,是无可奈何。现在再也没有人可以陪我一起唱歌了,再也没有人跟我和了。再也没有人被我一个笑话笑得眼泪都掉出来了。再也没有人鼓励我为自己的梦想去奋斗了。因为我们离得远了。不知是距离,心的距离也远了。 邵宁霞:她是第一个教会我如何打扮的人,她是第一个给我画眼影用睫毛膏的人。她是第一个告诉我不要轻易的去相信一个人,第一个告诉我这个世界有多可怕多肮脏。第一个教会我做人做事都要留个心眼,可惜现在仞没学会。她是第一个在我偷偷哭泣躲起来时,打电话大声的骂我是笨蛋,问我有没有把她当朋友的人。她是在我生病时苦等我几小时的人,我却没有珍惜。现在,依然记得我们在莲湖打打闹闹的情景,依然记得我生日时一起唱歌的情景,依然记得你最爱听的是那首《白狐》、你喜欢那忧伤的格调。依然记得你最爱跳舞,依然记得我们最爱吃的伊利奶片,可惜现在涨价啊。 真真:负债后我的第一个朋友。第一个听我唧唧呱呱不停讲家里事的人。第一个即使不会唱歌,只要我说去唱歌,就一定会陪我去的人。第一个在大年30晚上一起想家一起去网吧通宵的人。第一个送我衣服的人。还好,现在的你依然还在,我不知道几年之后,我们又会是什么样子,在现有这一刻,我只知道我该珍惜,生活中会有许多的朋友,但社会,或者说是生存环境给它的定义又是不同的,握个手,我们也是朋友了,但可信度有有几成呢,我们得去考虑。但我知道,我们是真心的朋友。 郝有晴:第一个我想去帮助的人,在她身上,看到刚出来时的自己。她是第一个让我感觉原来真诚的微笑也可以这样的美丽自然,第一个傻乎乎的问我许多问题的人,第一个你给她一点好处,她就会感恩戴德的人,第一个在2010年大年30因想家抱着我大声痛哭的人。一有事就会找我诉说的人。第一个让我觉得做人,我有愧的人。单纯干净得让你羞愧。第一个让我觉得,即使没有出众的外貌,我们依然可以活得快乐的人。她让我知道微笑其实可以很简单。 这些曾经的现在的朋友,或许以前我们有过争吵,或许我们已不再联系,或许我们已不再亲近,但是偶尔闲暇时,就请回忆我们的旧时光,你会发现,那些曾经的美好让你释然,那些一起感动一起哭过、笑过、累过、痛过、闹过、打过、骂过、也怨过的日子,也是我们美好的回忆,因为生命只有一次,有些人,或许你这一辈子都不会再相见,开心的不开心的,你们相处的时间就会定格在你们最后见面的那一秒,说过的话也一样。所以有时话不可太重,相处的机会很轻也很短。我们该珍惜。 暮然回首,我们的那些旧时光已不在,我想你们了。 《指尖流逝的美好旧时光呵》 文/墨黑、纸白 双手握在眼前,朦胧与现实的距离,悄悄地在心中,虔诚地祷告,向指缝、指尖流逝的美好旧时光,说一声,因为虚度而抱歉。 ——题记 指尖流逝的美好旧时光呵,你是那样的柔细,比风沙还细软的身躯,用你的温柔,抚摸着我日渐变长的黑发,划过我日益成熟的脸庞,亲吻我因成长而流落的泪珠,那份爱意,浓浓的让人迷失了自我。 【一】 指尖流逝的美好旧时光,我错过了你多少次的提醒? 几度春风拂面,几经花开花落,几何冰封融化,渐行渐远的时光,若隐若现的美好,它不经意间,出现在我不用看便可以敲打出文字的键盘,也让我想起初次用这键盘时的生涩。它悄然之间,出现在我那几本不算新妆的书本之上,落了厚厚的灰尘讲述着不为人知的偷偷阅览。它默然回首,降临在我那本厚厚的同学录上,泛黄了的笔记,远去的照片中的微笑,又在追忆着那些年前的美好。它随夜潜入梦,赶走了胡思乱想,却带来了一个个的曾经画面,还有那年那个惘然的少年,看着前方的问号继续惘然着。它随着双手插在衣袋中,包裹着的不单纯再是双手,还是父母的关怀与责备,还是几位老师的教导与鼓励,还是无数朋友们的支持与呐喊,还是更多的忠言逆耳的言论…… 不敢回忆,而今已是弱冠之年,没有多少细软在手,没有多少资本细数,没有多少学识于胸,有的只是一片充满希望的梦想,此刻人生若泥,只有梦想主义的灌溉,永远无法让人生刻画成精美的瓷器,只能在一地叹息之中,沦落了这一场青春永无休止的争论。 坐在空无一人的教室,虽然还有一年多的时光在等待着消磨或是奋斗,但是那些渐行渐远的光阴,还是让人无限的后怕。他们都说:最是一年好时光。朱自清先生也说:一年之计在于春。而今年的初春、晚雪又在讲述着多少变故?一直在安排着自己的时光,希望可以更好的,更合理的将人生安排,最终却发现自己一直被人生安排着,无法战胜自己的安排,只能是一种妄想,而非一种踏踏实实的实践。 闹钟每天都在嘈闹地提醒着人们,一天的时光到来了,而睡意却一直在拖着人们的身体,在讲述着睡眠的美妙,到最后也闹不清楚到底是闹钟失败在睡意的强大之中,还是睡意败在了闹钟嘹亮的呐喊之中。只知道,每天温习着昨天的日子,而时光便在此刻指尖纤细中,继续着它的流逝,一去不再复返。 春天,唤醒了多少人的迷惘。春天,讲述了多少人的起步。春天,释放了多少人的压抑。数不清楚,也不应该去数,因为我们还有事情要做,那便是如何让自己对得起这一场充满希望的春之中,不失这场希望的施舍,不辜负这场春的恩泽! 【二】 指缝逝去的美好旧时光,我擦肩了你多少次的际遇? 暖暖的风,扑面而来。炙燥的天,夏蝉鸣叫。热的马路,柳絮纷飞。悄悄溜走的时光,似有若无的美好,它忽而幻化为红色的蜻蜓,缓缓地朝我飞来,又瞬间转折方向而去,让我不断回忆着,也不断忘却着。它忽而转换成树叶上的露珠,露珠爬在树叶之上,又悄然间消失于太阳的初光之中,转换的不是被蒸发了的身体,而是瞬间即逝的光阴。它又像天空之云,像是谁都可以捏成的形状,忽而龙状,忽然虫状,捏成的不是变化的躯体,而是明白却不懂珍惜的时光。还有那瀑布,还有那流水,还有那屋檐下的燕子,还有那不曾被发现的毛毛虫…… 不敢思索,现在再回头看那片段,总觉得自己在断桥处,凭栏独望的不是美丽的风景,而是繁华被没落而代替。此时人生若浮云,只有现实主义的厚重,永远无法让这片充满力量的云飞翔于空中,成为天之骄子,只能在声声无奈之中,落幕了这一场年少的懊悔。 置身于山水之间,聆听涧边的瀑布,臭着夏天的凉爽,偶有梵呗入耳,又闻松下琴声,耳畔此刻如此丰富。流水,沟浍,还有那片片的翠绿,倒影在脑海中的是该如何拥抱这些美景。其实用现实去拥抱这些美景,倒不如用自己的眼帘去记住这点点滴滴,永远封存在记忆之中,现实永远不可能随你的任性而选择改变,那么就天马行空的随现实的改变而改变吧,人生的哲学不过如此,即使再清高,总得继续着人情世故吧? 夏蝉总会在我打盹的那刻,此起彼伏。当然也没有能力去分辨,是睡虫肆虐了夏蝉的嘈杂?还是夏蝉打跑了爱寻事非的睡虫。依稀记得,手缝这点空隙,还不足以留住时光的流逝,越想去捧住时光的脚丫子,她也是逝去的更快。 夏天,用燥热批判了多少人的懒惰?夏天,用蝉鸣喊乱了多少人的忏悔?夏天,用暖风柳絮惊觉了多少人的美梦?没有去细数这些细节,因为我们还有我们要做的事情,只有去做了,才对得起这场夏雨般的赦书,它赦免的不是你的忏悔,而是你勇往直上的心! 【三】 被秋风吹落的美好旧时光,我该怎样去接才不会摔伤你? 秋风萧瑟,卷席了一切努力后的成果。秋意盎然,带回了多少诗意的画卷。秋雨紧慢,模糊了多少人的记忆。我就坐在,那个早已被红木代替的老藤椅上,看不到古城旧砖,看不到四合杂院,也体会不到那场皮影戏,手捏泥人的欢乐。它们像院子里厚厚的落叶一般,安静的躺在我的脚下。还有那薄如蝉翼的时光,它更像正在被风摇晃着,摇摇欲坠的剩叶,我就坐在它的下面,用不灵便的双手,放在它也许会掉落的位置上,静静地看着,不知道何时,它会悄然的掉落,而我能不能用自己的小心翼翼去接住它,怕它被风的那股劲,还有地上的凉,摔坏了,就像怕一个孩子跌倒一般的心疼,其实我明白,孩子既然要学习走路,必然要跌倒,而我们既然要经历人生,那么就注定了凋落,我唯一不甘的是,怕这场人生平平淡淡,毫无意义的过去,那该是多么悲凉的结局? 秋叶讲述着它曾经的红,还述说着它曾经染红过的故事。破浪拍打着它曾经的花,还形容这它曾经托起过的豪艇。秋雨洗礼着它的世界,也轻轻地叹道,曾经随风而落的冲劲。屋檐下的滴答声,正是它最爱听的声音,而今可能再也听不到了,因为它也在时光的交换中,幻化为冰冷的雪。 秋树看着自己周遭的凄惨,看着自己突兀的树枝,不禁黯然泪下,轻声道:已经这个样子了,我还能看到下一个春的样子吗?秋风摇摆着秋树不再伟岸的身影,安慰道:每个时期必然有每个时期的兴盛,相对而言,每个时期亦必然有没有时期的败落,懂得珍惜,不愧对曾经的拥有即可,叹息不是你该做的事,你要做的就是聚集力量,待到来年花开时,也是你朝气蓬勃时。秋树挺直了腰板,仿佛刚刚什么都没发生一样。 摔伤了旧的美好时光,便真的无法再言拥有了。那么就在你明白的时候,好好去疼爱这段美好的旧的时光,这样便会让自己内疚的心,轻轻地哼唱着流金岁月。秋雨若挽歌,此话果真不假,当到了秋的季节,人们成熟也便随之而来了,当懂得了很多,便会明白,秋雨呢喃地挽歌,在轻轻地对你诉说着,成长方能自由! 【四】 被冬雪覆盖的美好旧时光,我该怎样觅寻你当初的翠绿? 漫漫冬夜,似霜月光,凌乱枯叶,不经意间与雪合而为一。冬天,听雪拍落的声音,无疑是一种从心底里悦耳动听。那么在雪中望月,更该是一种莫名的悸动吧,所谓新月恨其易沉,缺月恨其迟上,那此时的雪,又会怎样看到这轮明月,面对不同的自己,所感叹不同的言辞呢? 或而有友相伴,雪月下说剑,畅谈远去历史中的荆轲,赵云,肝胆相照的岂止忠义?更有情谊!雪月下论诗,遥想当年大小李杜之交好,李白,杜甫,李商隐,杜牧,风致幽雅的岂止诗歌?更有我中华之文化博大精深。只是这段美好的旧时光呵,你太快的便离去了,谁还会坐在月下说剑论诗?都被这嘈杂的世界,引领上了所谓的市场经济,为吃口饭,不得不忙忙碌碌,即使不愁衣食,那些人还要惦记着娱乐万千哩。 想给远去的美好旧时光写封信,开头未知,结尾惘然,最终在一封苍白的信纸上,毫无点墨,只好将白的信纸装进信封,没有了字里行间,没有了万千愁绪,没有了缠缠绵绵,有的便是内心深处对没有珍惜的逝去道声珍重,也提醒自己及诸君能够缅怀逝去,珍惜现在。 在小小的两人世界中,我看到了分秒的珍惜,当然我无意讽刺所谓的爱情带来的珍惜时光效应。我真的是在他们洋溢着幸福的脸庞上,看到了他们对彼此深深地依恋及珍惜,舍不得一分一秒离开彼此的脸颊,舍不得眨眼,怕逝去了便不再复返。他们漫步在小亭中,游走在浮桥上,爬过了山坡,飞跃了云海,我在想还会有谁像他们一样如此珍惜美好的旧的时光的逝去吗?答案:未知。 我试图在想,猜,也许与小孩子猜一元硬币这种兴趣应该略同,一切缘起美好的旧时光,在这场旧时光中寻找乐趣所在,这便是我们逝去的,也是我们想要永远拥有的吧!雪又开始了游历,而我们的人生也走在下一个春的路上,路途不远,只是能不能让自己好好地珍惜,这段逝去的美好的旧时光? 东风之中,斟酌小酒,酣畅淋漓,浑不觉冷,东风便若姜芥一般,辛辣了你的神经,也让你从刺骨之中醒来,来尝尝这杯小酒,也许你会精神百倍! 【尾声】 谁也无法追回从指缝、指尖溜走的美好旧的时光,那么就请从这段逝去中,学会拥有,拥有现在的美好的时光,如此便是最好的道歉,否则在道歉中忏悔,那样依然会让现在的美好时光,转化了昨日的美好旧时光。 朋友们,我们的时光真的不多,我们不能用任性去挽留她,那么就让自己用珍惜去感动她,她会在你的岁月变迁中,在变白你的双鬓,轻轻地对你说一声:你曾经来过,你的人生曾经充满了意义,这便足够了! 《陌上花开,聆听那一曲旧时光的伤》 文/锦瑟柠檬 流年的风景划过眼前,陌上的花儿已开,陌上人却早已不在。只能躲在角落聆听一首旧时光里的伤。 ——题记 【一】 我一直以为,既是被上苍安排到了尘世,生而为人,就免不了在人间应景。这世上应景的,又何止是人,凡尘万物皆如此。草木山石、飞禽虫蚁,都有其无法推卸的使命。它们的到来,也许有前世今生之约,为了某个人,为了某种生物。我相信,每一段缘分,每一个故事,都意义非凡,耐人寻味。 时常生出一种预感,今生,定然有那么一座深深庭院,属于我。院子里长满了植物,有一池莲,有梅骨雕刻的桌案,摆放一张琴。院内无生人,爬满青藤的木门终年落锁。世间风尘就这样被关在院外,无惊无扰。而我甘愿,和流光执手,缓慢老去,不言沧桑。 也许每个人,都没有属于自己的归宿。每个人,都是浮云一朵。明明有过交集,转身又成了陌路。我想,我是船,静静地漂浮在时间的河上。人海茫茫,也曾为了一段萍水相逢,迷失过最初的方向。隔岸灯火已阑珊,而我打捞着一轮水中的月亮,止不住内心无尽的荒凉。在时间的河上,已然忘记那些落花无言的过往。当年的承诺,是我对青春撒下的谎。走过千回百转的岁月,不要问我,是否饮尽了尘世的风霜。走过百转千回的山水,如果你真的慈悲,那么就别再去打听,谁失踪了,谁又去了哪里? 一时无句,心中留白。或许每个人的内心,都有几处不为人知的暗伤,只能等待时光去将之复原。 【二】 时光如水,物转星移,许多人事都以分道扬镳,不明下落。而缘分是一条神奇的河流,我们划着桨橹漂浮在其中,朝着各自的方向驶去。在没有约定的未来,却终有一天会不期而遇。就像花开是有情,花落是无意。来者是缘起,去者是缘灭。三千世界,每一天都会有擦肩,每一天都会有重逢。就像一段前朝往事,一出经年的戏曲,一本古老的书,被五味杂陈的烟火浸染,被悲欢冷暖的世情冲洗,繁芜中,依然有种地老天荒的安宁。 有人说,许多的风景,总是在对岸。如果相逢总在山水外,莫如,在人间四月安然地等待。看一纸诗书,如何将世间聚散的缘分,重新安排。给我一段老时光,独坐在绿苔滋长的木窗下,泡一壶闲茶。不去管,那南飞燕子,何日才可以返家。不去问,那一叶小舟,又会放逐到哪里的天涯。不去想,那些走过的岁月,到底多少是真,多少是假。 古人云:“人到无求品自高。”无求即是无欲,人若能无欲,品格自然高尚,而苦恼也会消散。但能够做到无欲无求的人,又岂是等闲之辈。在风尘弥漫的人世间修炼,有人痴迷惊艳华丽的风景,有人独恋似曾相识的旧物。辽阔似海的人心啊,该用什么来填满,平凡的物质或是精神,真的足够吗? 在那条叫做轮回的老巷里,多少人,在那里曾寻找散落的过往。其实,故事早已改写了当初的模样,可流年,为什么还要这样叫人神伤。一定有些什么,被我不小心遗忘。否则,转角处的灯火,不会那样的荒凉。否则,昨天留下的,不会只是淡淡迷惘。如果支付了一生的时光,那么,是否就可以拥有,我想要的地久天长? 世上总有许多执迷不悟的人,为了一溪云、一帘梦、一出戏,交换心性,倾注深情。而痴情本身就是一个寂寞的旅程,倘若无法承担其间的清冷与凉薄,莫如不要开始。其实有时候,做一个有情有义的人,会比一个寡淡漠然的人更疲累。 多想擦去昨天的记忆,漫步在红尘的烟火里。多想流光飞作雪,世味煮成茶。想来谁都愿意做一个闲散的人,日子纯净简单,生活并无别事。有大把时光,用来虚度,而不去担心流年似水,转瞬白头。只是这世上,有多少人可以清醒自持,敢于承担光阴所带来的消耗,敢于接受命运所带来的仓促变幻。 曾经那样仓促地背上行囊,想用年华,换取一段如水的过往。走过红尘陌上,品过浮世清欢,才知道,人生不过戏梦一场。假如爱过的人,可以遗忘;犯下的错,值得原谅。就许我,用剩余的时光,重新和这里的一草一木、一瓦一檐,诉说衷肠。 即使没有世事洞明的宽厚与气度,却有着落尽尘埃的简净与从容。岁月河山瞬息万变,就算有未卜先知的能力,结局也难免有所偏失。回首前尘,聚散离合,不过镜花水月一场。守着当下,不去寻找曾经预约的风景,不再许下迟早要违背的诺言。愿与草木,随遇而安。 如果可以,我只想做一株遗世的梅花,守着寂寞的年华,在老去的渡口,静看日落烟霞。","categories":[{"name":"生活","slug":"生活","permalink":"https://laujiangtao.github.io/categories/%E7%94%9F%E6%B4%BB/"}],"tags":[{"name":"audio","slug":"audio","permalink":"https://laujiangtao.github.io/tags/audio/"}]},{"title":"Kotlin的lambda表达式","slug":"Kotlin的lambda表达式","date":"2021-01-18T02:19:22.000Z","updated":"2021-05-15T15:53:41.057Z","comments":false,"path":"2021/01/18/kotlin-lambda-expression/","link":"","permalink":"https://laujiangtao.github.io/2021/01/18/kotlin-lambda-expression/","excerpt":"","text":"在lambda表达式,只支持单抽象方法模型,即设计的接口里面只有一个抽象的方法,才符合lambda表达式的规则,多个回调方法不支持。 1、用Java代码实现一个接口的回调。 123456mView.setEventListener(new EventListener(){ public void onSuccess(Data data){ //TODO }}); 2、在Kotlin中的实现一个接口的回调,不使用lambda表达式(这种方式非常适用于kotlin中对于一个接口中含有多个回调方法)。 123456mView.setEventListener(object: EventListener{ public void onSuccess(Data data){ //TODO }}); 3、如果在Kotlin中的对于接口只有一个回调的方法,就符合使用lambda函数,我们可以把以上代码简化成这样。 1234567891011mView.setEventListener({ data: Data -> //TODO})//或者可以直接省略Data,借助kotlin的智能类型推导mView.setEventListener({ data -> //TODO}) 4、如果以上代码中的data参数没有使用到的话,可以直接把data去掉 123mView.setEventListener({ //TODO}) 5、以上代码还可以做个调整,由于setEventListener函数最后一个参数是一个函数的话,可以直接把括号的实现提到圆括号外面 123mView.setEventListener(){ //TODO} 6、由于setEventListener这个函数只有一个参数,可以直接省略圆括号 123mView.setEventListener{ //TODO}","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"Kotlin","slug":"Kotlin","permalink":"https://laujiangtao.github.io/tags/Kotlin/"}]},{"title":"Flutter Question","slug":"Flutter-Question","date":"2021-01-18T01:32:33.000Z","updated":"2021-05-15T15:53:37.079Z","comments":false,"path":"2021/01/18/flutter-question/","link":"","permalink":"https://laujiangtao.github.io/2021/01/18/flutter-question/","excerpt":"","text":"安装完flutter环境,执行flutter doctor命令,检查flutter开发工具链。 按照提示安装对应插件即可,但在Android Studio 4.1以上版本,插件目录更换,导致之前在Android Studio版本里面安装到饿插件找不到。 老版本插件目录:/Library/Application\\ Support/AndroidStudio4.1 新版本插件目录:/Library/Application Support/Google/AndroidStudio4.1/plugins flutter使用的老版本Android Studio插件目录查找插件,产生错误,虽然目前bug已经修复,但是还没有release出来。 我发现解决方法有两种: 第一种如网上所说,在终端执行ln -s ~/Library/Application\\ Support/Google/AndroidStudio4.1/plugins ~/Library/Application\\ Support/AndroidStudio4.1,在新的插件目录下创建之前插件目录的软连接,通过软连接找到插件。 第二种切换flutter sdk的分支到dev(dev分支已经修复此bug)。","categories":[{"name":"Question","slug":"Question","permalink":"https://laujiangtao.github.io/categories/Question/"}],"tags":[{"name":"flutter","slug":"flutter","permalink":"https://laujiangtao.github.io/tags/flutter/"}]},{"title":"Code Tools","slug":"Code-Tools","date":"2021-01-07T07:16:45.000Z","updated":"2021-05-10T16:00:00.000Z","comments":false,"path":"2021/01/07/code-tools/","link":"","permalink":"https://laujiangtao.github.io/2021/01/07/code-tools/","excerpt":"","text":"将View保存为图片 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081import android.app.Activityimport android.content.Intentimport android.graphics.Bitmapimport android.graphics.Canvasimport android.graphics.Colorimport android.net.Uriimport android.util.DisplayMetricsimport android.util.Logimport android.view.Viewimport android.view.WindowManagerimport java.io.Fileimport java.io.FileOutputStreamobject ViewUtils { private const val TAG = "ViewUtils" /** * 保存view为图片 * * @param activity Activity * @param view View * @param savePathName 保存的文件路径及文件名 */ @Throws(Exception::class) fun saveView(activity: Activity, view: View, savePathName: String) { //计算设备分辨率 val manager: WindowManager = activity.windowManager val outMetrics = DisplayMetrics() manager.defaultDisplay.getMetrics(outMetrics) val width: Int = outMetrics.widthPixels val height: Int = outMetrics.heightPixels // 整个View的大小 参数是左上角 和右下角的坐标 view.layout(0, 0, width, height) val measuredWidth: Int = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY) val measuredHeight: Int = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST) //测量,布局View view.measure(measuredWidth, measuredHeight) view.layout(0, 0, view.measuredWidth, view.measuredHeight) view.isDrawingCacheEnabled = true view.drawingCacheQuality = View.DRAWING_CACHE_QUALITY_HIGH view.drawingCacheBackgroundColor = Color.WHITE // 把一个View转换成图片 val cacheBmp: Bitmap = viewConversionBitmap(view) val file = File(savePathName) val fos = FileOutputStream(file) cacheBmp.compress(Bitmap.CompressFormat.PNG, 90, fos) fos.flush() fos.close() view.destroyDrawingCache() //发送广播更新相册 val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) val uri: Uri = Uri.fromFile(file) intent.data = uri activity.sendBroadcast(intent) } /** * view转bitmap * * @param v View * @return Bitmap */ private fun viewConversionBitmap(v: View): Bitmap { val w: Int = v.width val h: Int = v.height Log.e(TAG, "width: $w height: $h") val bmp: Bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) val c = Canvas(bmp) c.drawColor(Color.WHITE) /** 如果不设置canvas画布为白色,则生成透明 */ v.layout(0, 0, w, h) v.draw(c) return bmp }} 扫描文件 点我展开 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162import java.io.File;import java.util.HashSet;import java.util.LinkedList;import java.util.Queue;import java.util.Set;/** * @author Kavan */public final class ScanFileHelper { private static ScanFileHelper scanFileHelper; public static ScanFileHelper getInstance() { if (scanFileHelper == null) { scanFileHelper = new ScanFileHelper(); } return scanFileHelper; } private ScanFileHelper() { } private OnFileScanListener fileScanListener; public void setOnFileScanListener(OnFileScanListener fileScanListener) { this.fileScanListener = fileScanListener; } interface OnFileScanListener { void onFileScan(String path); } public void traverseFolderBFS(File node) { Set<File> visited = new HashSet<>(); Queue<File> q = new LinkedList<>(); q.offer(node); while (!q.isEmpty()) { File currNode = q.poll(); if (visited.contains(currNode) || null == currNode) { continue; } if (fileScanListener != null) { fileScanListener.onFileScan(currNode.getAbsolutePath()); } else { throw new IllegalArgumentException("FileScanListener should be not null"); } visited.add(currNode); File[] files = currNode.listFiles(); if (null == files) { continue; } for (File file : files) { q.offer(file); } } } public void traverseFolderBFS(File node, OnFileScanListener l) { setOnFileScanListener(l); traverseFolderBFS(node); }} 读取assets文件 点我展开 123456789101112131415161718192021222324/** * 读取assets📁下json📃 * @param fileName * @param context * @return */fun getJsonStringFromAssetsFile(context: Context, fileName: String?): String { val stringBuilder = StringBuilder() try { val assetManager: AssetManager = context.assets val bf = BufferedReader( InputStreamReader( assetManager.open(fileName!!) ) ) var line: String? while (bf.readLine().also { line = it } != null) { stringBuilder.append(line) } } catch (e: IOException) { e.printStackTrace() } return stringBuilder.toString()} 获取应用签名 点我展开 1234567891011121314151617181920212223242526/** * 获取应用签名 * @param pkgname * @param context * @return */public String getSignature(Context context, String pkgname) { boolean isEmpty = TextUtils.isEmpty(pkgname); if (isEmpty) { Toast.makeText(this, "应用程序的包名不能为空!", Toast.LENGTH_SHORT).show(); } else { try { @SuppressLint("PackageManagerGetSignatures") PackageInfo packageInfo = context.getPackageManager().getPackageInfo(pkgname, PackageManager.GET_SIGNATURES); Signature[] signatures = packageInfo.signatures; StringBuilder builder = new StringBuilder(); for (Signature signature : signatures) { builder.append(signature.toCharsString()); } return builder.toString(); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } return null;} SPHelper.java 点我展开 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071import android.content.Context;import android.content.SharedPreferences;/** * Created by jiangtao on 2016/12/25 12:22 * E-mail:3305727299@qq.com */public class SPHelper { /** * 保存在手机里面的文件名 */ private static final String FILE_NAME = "share_date"; /** * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法 * * @param context * @param key * @param object */ public static void setParam(Context context, String key, Object object) { String type = object.getClass().getSimpleName(); SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sp.edit(); if ("String".equals(type)) { editor.putString(key, (String) object); } else if ("Integer".equals(type)) { editor.putInt(key, (Integer) object); } else if ("Boolean".equals(type)) { editor.putBoolean(key, (Boolean) object); } else if ("Float".equals(type)) { editor.putFloat(key, (Float) object); } else if ("Long".equals(type)) { editor.putLong(key, (Long) object); } editor.apply(); } /** * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值 * * @param context * @param key * @param defaultObject * @return */ public static Object getParam(Context context, String key, Object defaultObject) { String type = defaultObject.getClass().getSimpleName(); SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); if ("String".equals(type)) { return sp.getString(key, (String) defaultObject); } else if ("Integer".equals(type)) { return sp.getInt(key, (Integer) defaultObject); } else if ("Boolean".equals(type)) { return sp.getBoolean(key, (Boolean) defaultObject); } else if ("Float".equals(type)) { return sp.getFloat(key, (Float) defaultObject); } else if ("Long".equals(type)) { return sp.getLong(key, (Long) defaultObject); } return null; }} Logger.java 点我展开 12345678910111213141516171819202122232425262728293031323334353637383940import android.util.Log;/** * Created by jiangtao on 2017/2/7. */public class Logger { private static boolean isDebug = true; private static String TAG = "TAG"; public static void v(String msg) { if (isDebug) { Log.v(TAG, msg); } } public static void d(String msg) { if (isDebug) { Log.d(TAG, msg); } } public static void i(String msg) { if (isDebug) { Log.i(TAG, msg); } } public static void w(String msg) { if (isDebug) { Log.w(TAG, msg); } } public static void e(String msg) { if (isDebug) { Log.e(TAG, msg); } }} 键盘覆盖WebView输入框 点我展开 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071import android.app.Activity;import android.graphics.Rect;import android.view.View;import android.view.ViewTreeObserver;import android.widget.FrameLayout;/** * Created by jiangtao on 2017/4/26 16:36 * E-mail:3305727299@qq.com */public class KeyBoardListener { private Activity activity; private View mChildOfContent; private int usableHeightPrevious; private FrameLayout.LayoutParams frameLayoutParams; private static KeyBoardListener keyBoardListener; public static KeyBoardListener getInstance(Activity activity) { keyBoardListener = new KeyBoardListener(activity); return keyBoardListener; } public KeyBoardListener(Activity activity) { super(); this.activity = activity; } public void init() { FrameLayout content = (FrameLayout) activity .findViewById(android.R.id.content); mChildOfContent = content.getChildAt(0); mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { public void onGlobalLayout() { possiblyResizeChildOfContent(); } }); frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent .getLayoutParams(); } private void possiblyResizeChildOfContent() { int usableHeightNow = computeUsableHeight(); if (usableHeightNow != usableHeightPrevious) { int usableHeightSansKeyboard = mChildOfContent.getRootView() .getHeight(); int heightDifference = usableHeightSansKeyboard - usableHeightNow; if (heightDifference > (usableHeightSansKeyboard / 4)) { frameLayoutParams.height = usableHeightSansKeyboard - heightDifference; } else { frameLayoutParams.height = usableHeightSansKeyboard; } mChildOfContent.requestLayout(); usableHeightPrevious = usableHeightNow; } } private int computeUsableHeight() { Rect r = new Rect(); mChildOfContent.getWindowVisibleDisplayFrame(r); return (r.bottom - r.top); }} 根据经纬度计算两点间的距离 点我展开 123456789101112131415161718/** * Created by jiangtao on 2017/4/19 15:01 * E-mail:3305727299@qq.com */public static double getDistance(double long1, double lat1, double long2, double lat2) { double a, b, R; R = 6378137; lat1 = lat1 * Math.PI / 180.0; lat2 = lat2 * Math.PI / 180.0; a = lat1 - lat2; b = (long1 - long2) * Math.PI / 180.0; double d; double sa2, sb2; sa2 = Math.sin(a / 2.0); sb2 = Math.sin(b / 2.0); d = 2 * R * Math.asin(Math.sqrt(sa2 * sa2 + Math.cos(lat1) * Math.cos(lat2) * sb2 * sb2)); return d;} Base64Utils.java 点我展开 12345678910111213141516171819202122232425262728293031323334import java.nio.charset.StandardCharsets;import java.util.Base64;/** * @version Base64Utils, v0.1 2020/7/7 10:28 */public class Base64Utils { final static Base64.Encoder encoder = Base64.getEncoder(); final static Base64.Decoder decoder = Base64.getDecoder(); /** * 给字符串加密 * * @param text * @return */ public static String encode(String text) { byte[] textByte = text.getBytes(StandardCharsets.UTF_8); return encoder.encodeToString(textByte); } /** * 将加密后的字符串进行解密 * * @param encodedText * @return */ public static String decode(String encodedText) { return new String(decoder.decode(encodedText), StandardCharsets.UTF_8); }} File2Base64.java 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142import android.util.Base64;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;/** * Created by jiangtao on 2017/3/20 11:10 * E-mail:3305727299@qq.com */public class File2Base64 { /** * 将文件转成base64 字符串 * * @param path 文件路径 * @return 编码后的字符串 * @author 3305727299@qq.com */ public static String encodeBase64File(String path) throws Exception { File file = new File(path); FileInputStream inputFile = new FileInputStream(file); byte[] buffer = new byte[(int) file.length()]; inputFile.read(buffer); inputFile.close(); return Base64.encodeToString(buffer, Base64.DEFAULT); } /** * 将base64字符解码保存文件 * * @param base64Code 编码后的字串 * @param savePath 文件保存路径 * @author 3305727299@qq.com */ public static void decoderBase64File(String base64Code, String savePath) throws Exception { byte[] buffer = Base64.decode(base64Code, Base64.DEFAULT); FileOutputStream out = new FileOutputStream(savePath); out.write(buffer); out.close(); }} Apk下载 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188import android.app.NotificationManager;import android.content.Context;import android.content.Intent;import android.net.Uri;import android.os.AsyncTask;import android.os.Environment;import android.support.v7.app.NotificationCompat;import android.util.Log;import java.io.BufferedInputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;import java.util.List;import java.util.Map;import java.util.Set;/** * Created by jiangtao on 2017/3/13 13:04 * E-mail:3305727299@qq.com */public final class Download extends AsyncTask<String, Integer, String> { private File file; private Context context; private String fileName; private NotificationCompat.Builder builder; private NotificationManager notificationManager; public Download(Context context) { this.context = context; notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); builder = new NotificationCompat.Builder(context); builder.setTicker("下载开始了...") .setContentTitle("Download") .setSmallIcon(R.mipmap.ic_launcher); } @Override protected String doInBackground(String... params) { URL url; HttpURLConnection conn; BufferedInputStream bis = null; FileOutputStream fos = null; try { url = new URL(params[0]); Log.i("TAG", url.toString()); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); int fileLength = conn.getContentLength(); bis = new BufferedInputStream(conn.getInputStream()); fileName = getFileName(params[0]); builder.setContentTitle(fileName); String fileDir = Environment.getExternalStorageDirectory().getPath() + "/Download/" + fileName; file = new File(fileDir); if (!file.exists()) { if (!file.getParentFile().exists()) { if (file.getParentFile().mkdirs()) { file.createNewFile(); } } } else { return "文件已存在"; } fos = new FileOutputStream(file); byte data[] = new byte[4 * 1024]; long total = 0; int count; while ((count = bis.read(data)) != -1) { total += count; Log.i("TAG", "total:" + total); publishProgress((int) (total * 100 / fileLength)); fos.write(data, 0, count); fos.flush(); } fos.flush(); return "下载成功"; } catch (IOException e) { e.printStackTrace(); } finally { try { if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (bis != null) { bis.close(); } } catch (IOException e) { e.printStackTrace(); } } return "网络错误"; } @Override protected void onProgressUpdate(Integer... progress) { super.onProgressUpdate(progress); if (progress[0] == 100) { builder.setTicker("下载完成!"); } builder.setProgress(100, progress[0], false); builder.setContentText("正在下载..." + progress[0] + "%"); notificationManager.notify(1, builder.build()); } @Override protected void onPostExecute(String s) { super.onPostExecute(s); builder.setContentText(s); builder.setProgress(0, 0, false); notificationManager.notify(1, builder.build()); if (fileName.endsWith(".apk")) { openFile(file); } } public static String getFileName(String url) { String filename = ""; boolean isOk = false; // 从UrlConnection中获取文件名称 try { URL myURL = new URL(url); URLConnection conn = myURL.openConnection(); if (conn == null) { return null; } Map<String, List<String>> hf = conn.getHeaderFields(); if (hf == null) { return null; } Set<String> key = hf.keySet(); if (key == null) { return null; } for (String stringKey : key) { List<String> values = hf.get(stringKey); for (String value : values) { String result; try { result = new String(value.getBytes("ISO-8859-1"), "GBK"); int location = result.indexOf("filename"); if (location >= 0) { result = result.substring(location + "filename".length()); filename = result.substring(result.indexOf("=") + 1); isOk = true; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); }// ISO-8859-1 UTF-8 gb2312 } if (isOk) { break; } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 从路径中获取 if (filename == null || "".equals(filename)) { filename = url.substring(url.lastIndexOf("/") + 1); } return filename; } private void openFile(File file) { Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); context.startActivity(intent); }} 根据日期和经纬度,计算日出日落时间 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313#include<iostream>#include<math.h>#include<sstream>#define PI 3.1415926using namespace std;//定义全局变量int days_of_month_1[]= {31,28,31,30,31,30,31,31,30,31,30,31};int days_of_month_2[]= {31,29,31,30,31,30,31,31,30,31,30,31};long double h=-0.833;//判断是否为闰年:若为闰年,返回1;若非闰年,返回0int leap_year(int year){ if(((year%400==0) || (year%100!=0) && (year%4==0))) return 1; else return 0;}//求从格林威治时间公元2000年1月1日到计算日天数daysint days(int year, int month, int date){ int i,a=0; for(i=2000; i<year; i++) { if(leap_year(i)) a=a+366; else a=a+365; } if(leap_year(year)) { for(i=0; i<month-1; i++) { a=a+days_of_month_2[i]; } } else { for(i=0; i<month-1; i++) { a=a+days_of_month_1[i]; } } a=a+date; return a;}//求格林威治时间公元2000年1月1日到计算日的世纪数tlong double t_century(int days, long double UTo){ return ((long double)days+UTo/360)/36525;}//求太阳的平黄径long double L_sun(long double t_century){ return (280.460+36000.770*t_century);}//求太阳的平近点角long double G_sun(long double t_century){ return (357.528+35999.050*t_century);}//求黄道经度long double ecliptic_longitude(long double L_sun,long double G_sun){ return (L_sun+1.915*sin(G_sun*PI/180)+0.02*sin(2*G_sun*PI/180));}//求地球倾角long double earth_tilt(long double t_century){ return (23.4393-0.0130*t_century);}//求太阳偏差long double sun_deviation(long double earth_tilt, long double ecliptic_longitude){ return (180/PI*asin(sin(PI/180*earth_tilt)*sin(PI/180*ecliptic_longitude)));}//求格林威治时间的太阳时间角GHAlong double GHA(long double UTo, long double G_sun, long double ecliptic_longitude){ return (UTo-180-1.915*sin(G_sun*PI/180)-0.02*sin(2*G_sun*PI/180)+2.466*sin(2*ecliptic_longitude*PI/180)-0.053*sin(4*ecliptic_longitude*PI/180));}//求修正值elong double e(long double h, long double lat, long double sun_deviation){ return 180/PI*acos((sin(h*PI/180)-sin(lat*PI/180)*sin(sun_deviation*PI/180))/(cos(lat*PI/180)*cos(sun_deviation*PI/180)));}//求日出时间long double UT_rise(long double UTo, long double GHA, long double lng, long double e){ return (UTo-(GHA+lng+e));}//求日落时间long double UT_set(long double UTo, long double GHA, long double lng, long double e){ return (UTo-(GHA+lng-e));}//判断并返回结果(日出)long double result_rise(long double UT, long double UTo, long double lng, long double lat, int year, int month, int date){ long double d; if(UT>=UTo) d=UT-UTo; else d=UTo-UT; if(d>=0.1) { UTo=UT; UT=UT_rise(UTo, GHA(UTo, G_sun(t_century(days(year, month, date), UTo) ),ecliptic_longitude(L_sun(t_century(days(year, month, date), UTo) ), G_sun(t_century(days(year, month, date), UTo) ) ) ), lng, e(h, lat, sun_deviation(earth_tilt(t_century(days(year, month, date), UTo) ), ecliptic_longitude(L_sun(t_century(days(year, month, date), UTo) ), G_sun(t_century(days(year, month, date), UTo) ) ) ) ) ); result_rise(UT,UTo,lng,lat,year,month,date); } return UT;}//判断并返回结果(日落)long double result_set(long double UT, long double UTo, long double lng, long double lat, int year, int month, int date){ long double d; if(UT>=UTo) d=UT-UTo; else d=UTo-UT; if(d>=0.1) { UTo=UT; UT=UT_set(UTo, GHA(UTo, G_sun(t_century(days(year, month, date), UTo)), ecliptic_longitude(L_sun(t_century(days(year, month, date), UTo)), G_sun(t_century(days(year, month, date), UTo) ) ) ), lng, e(h, lat, sun_deviation(earth_tilt(t_century(days(year, month, date), UTo) ), ecliptic_longitude(L_sun(t_century(days(year, month, date), UTo) ), G_sun(t_century(days(year, month, date), UTo) ) ) ) ) ); result_set(UT,UTo,lng,lat,year,month,date); } return UT;}//求时区int Zone(long double lng){ if(lng>=0) return (int)((int)(lng/15.0)+1); else return (int)((int)(lng/15.0)-1);}//打印结果string output(long double rise, long double set, long double lng){ string hour; string mine; string sunrise_result; string sunset_result; if((int)(60*(rise/15+Zone(lng)-(int)(rise/15+Zone(lng))))<10) { stringstream ss_hour; ss_hour<<(int)(rise/15+Zone(lng)); ss_hour>>hour; stringstream ss_mine; ss_mine<<(int)(60*(rise/15+Zone(lng)-(int)(rise/15+Zone(lng)))); ss_mine>>mine; sunrise_result = hour + ":0" + mine; //cout<<"The time at which the sun rises is "<<(int)(rise/15+Zone(lng))<<":0"<<(int)(60*(rise/15+Zone(lng)-(int)(rise/15+Zone(lng))))<<" .\\n"; } else { stringstream ss_hour; ss_hour<<(int)(rise/15+Zone(lng)); ss_hour>>hour; stringstream ss_mine; ss_mine<<(int)(60*(rise/15+Zone(lng)-(int)(rise/15+Zone(lng)))); ss_mine>>mine; sunrise_result = hour + ":" + mine; //cout<<"The time at which the sun rises is "<<(int)(rise/15+Zone(lng))<<":"<<(int)(60*(rise/15+Zone(lng)-(int)(rise/15+Zone(lng))))<<" .\\n"; } if((int)(60*(set/15+Zone(lng)-(int)(set/15+Zone(lng))))<10) { stringstream ss_hour; ss_hour<<(int)(set/15+Zone(lng)); ss_hour>>hour; stringstream ss_mine; ss_mine<<(int)(60*(set/15+Zone(lng)-(int)(set/15+Zone(lng)))); ss_mine>>mine; sunset_result = hour + ": " + mine; //cout<<"The time at which the sun sets is "<<(int)(set/15+Zone(lng))<<": "<<(int)(60*(set/15+Zone(lng)-(int)(set/15+Zone(lng))))<<" .\\n"; } else { stringstream ss_hour; ss_hour<<(int)(set/15+Zone(lng)); ss_hour>>hour; stringstream ss_mine; ss_mine<<(int)(60*(set/15+Zone(lng)-(int)(set/15+Zone(lng)))); ss_mine>>mine; sunset_result = hour + ":" + mine; //cout<<"The time at which the sun sets is "<<(int)(set/15+Zone(lng))<<":"<<(int)(60*(set/15+Zone(lng)-(int)(set/15+Zone(lng))))<<" .\\n"; } return sunrise_result + "," + sunset_result;}string get_sunrise_and_sunset_time(int year,int month,int date,double lat,double lng){ long double UTo=180.0; int c[3] = {year,month,date}; long double rise,set; rise=result_rise(UT_rise(UTo,GHA(UTo,G_sun(t_century(days(year,month,date),UTo)),ecliptic_longitude(L_sun(t_century(days(year,month,date),UTo)),G_sun(t_century(days(year,month,date),UTo)))),lng,e(h,lat,sun_deviation(earth_tilt(t_century(days(year,month,date),UTo)),ecliptic_longitude(L_sun(t_century(days(year,month,date),UTo)),G_sun(t_century(days(year,month,date),UTo)))))),UTo,lng,lat,year,month,date); set=result_set(UT_set(UTo,GHA(UTo,G_sun(t_century(days(year,month,date),UTo)),ecliptic_longitude(L_sun(t_century(days(year,month,date),UTo)),G_sun(t_century(days(year,month,date),UTo)))),lng,e(h,lat,sun_deviation(earth_tilt(t_century(days(year,month,date),UTo)),ecliptic_longitude(L_sun(t_century(days(year,month,date),UTo)),G_sun(t_century(days(year,month,date),UTo)))))),UTo,lng,lat,year,month,date); return output(rise,set,lng);}int main(){ string s; s = get_sunrise_and_sunset_time(2017,12,12,36.01666,106.2833); cout << "日出日落时间:" << s; return 0;} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241/** * * @author jiangtao * @date 2017/12/13 */public class SunriseAndSunsetTime { //定义全局变量 private double PI = 3.1415926; private int daysOfMonth1[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; private int daysOfMonth2[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; private double h = -0.833; private String sunrise; private String sunset; public SunriseAndSunsetTime() { } public SunriseAndSunsetTime(int year, int month, int date, double lat, double lng) { setTimeAndLoc(year,month,date,lat,lng); } public String getSunrise() { return sunrise; } public String getSunset() { return sunset; } //判断是否为闰年:若为闰年,返回1;若非闰年,返回0 private boolean leapYear(int year) { return ((year % 400 == 0) || (year % 100 != 0) && (year % 4 == 0)); } //求从格林威治时间公元2000年1月1日到计算日天数days private int days(int year, int month, int date) { int i, a = 0; for (i = 2000; i < year; i++) { if (leapYear(i)) { a = a + 366; } else { a = a + 365; } } if (leapYear(year)) { for (i = 0; i < month - 1; i++) { a = a + daysOfMonth2[i]; } } else { for (i = 0; i < month - 1; i++) { a = a + daysOfMonth1[i]; } } a = a + date; return a; } //求格林威治时间公元2000年1月1日到计算日的世纪数t private double tCentury(int days, double uto) { return ((double) days + uto / 360) / 36525; } //求太阳的平黄径 private double lSun(double tCentury) { return (280.460 + 36000.770 * tCentury); } //求太阳的平近点角 private double gSun(double tCentury) { return (357.528 + 35999.050 * tCentury); } //求黄道经度 private double eclipticLongitude(double lSun, double gSun) { return (lSun + 1.915 * Math.sin(gSun * PI / 180) + 0.02 * Math.sin(2 * gSun * PI / 180)); } //求地球倾角 private double earthTilt(double tCentury) { return (23.4393 - 0.0130 * tCentury); } //求太阳偏差 private double sunDeviation(double earthTilt, double eclipticLongitude) { return (180 / PI * Math.asin(Math.sin(PI / 180 * earthTilt) * Math.sin(PI / 180 * eclipticLongitude))); } //求格林威治时间的太阳时间角GHA private double gha(double uto, double gSun, double eclipticLongitude) { return (uto - 180 - 1.915 * Math.sin(gSun * PI / 180) - 0.02 * Math.sin(2 * gSun * PI / 180) + 2.466 * Math.sin(2 * eclipticLongitude * PI / 180) - 0.053 * Math.sin(4 * eclipticLongitude * PI / 180)); } //求修正值e private double e(double h, double lat, double sunDeviation) { return 180 / PI * Math.acos((Math.sin(h * PI / 180) - Math.sin(lat * PI / 180) * Math.sin(sunDeviation * PI / 180)) / (Math.cos(lat * PI / 180) * Math.cos(sunDeviation * PI / 180))); } //求日出时间 private double utRise(double uto, double gha, double lng, double e) { return (uto - (gha + lng + e)); } //求日落时间 private double utSet(double uto, double gha, double lng, double e) { return (uto - (gha + lng - e)); } //判断并返回结果(日出) private double resultRise(double ut, double uto, double lng, double lat, int year, int month, int date) { double d; if (ut >= uto) { d = ut - uto; } else { d = uto - ut; } if (d >= 0.1) { uto = ut; ut = utRise(uto, gha(uto, gSun(tCentury(days(year, month, date), uto)), eclipticLongitude(lSun(tCentury(days(year, month, date), uto)), gSun(tCentury(days(year, month, date), uto)))), lng, e(h, lat, sunDeviation(earthTilt(tCentury(days(year, month, date), uto)), eclipticLongitude( lSun(tCentury(days(year, month, date), uto)), gSun(tCentury(days(year, month, date), uto)))))); resultRise(ut, uto, lng, lat, year, month, date); } return ut; } //判断并返回结果(日落) private double resultSet(double ut, double uto, double lng, double lat, int year, int month, int date) { double d; if (ut >= uto) { d = ut - uto; } else { d = uto - ut; } if (d >= 0.1) { uto = ut; ut = utSet(uto, gha(uto, gSun(tCentury(days(year, month, date), uto)), eclipticLongitude(lSun(tCentury(days(year, month, date), uto)), gSun(tCentury(days(year, month, date), uto)))), lng, e(h, lat, sunDeviation(earthTilt(tCentury(days(year, month, date), uto)), eclipticLongitude( lSun(tCentury(days(year, month, date), uto)), gSun(tCentury(days(year, month, date), uto)))))); resultSet(ut, uto, lng, lat, year, month, date); } return ut; } //求时区 private int zone(double lng) { if (lng >= 0) { return ((int) (lng / 15.0) + 1); } else { return ((int) (lng / 15.0) - 1); } } //打印结果 private void output(double rise, double set, double lng) { int hour; int min; if ((int) (60 * (rise / 15 + zone(lng) - (int) (rise / 15 + zone(lng)))) < 10) {// cout << "the sun rises is " << (int) (rise / 15 + zone(lng)) << ":0"// << (int) (60 * (rise / 15 + zone(lng) - (int) (rise / 15 + zone(lng)))) << " .\\n"; hour = (int) (rise / 15 + zone(lng)); min = (int) (60 * (rise / 15 + zone(lng) - (int) (rise / 15 + zone(lng)))); sunrise = hour + ":0" + min; } else {// cout << "the sun rises is " << (int) (rise / 15 + zone(lng)) << ":"// << (int) (60 * (rise / 15 + zone(lng) - (int) (rise / 15 + zone(lng)))) << " .\\n"; hour = (int) (rise / 15 + zone(lng)); min = (int) (60 * (rise / 15 + zone(lng) - (int) (rise / 15 + zone(lng)))); sunrise = hour + ":" + min; } if ((int) (60 * (set / 15 + zone(lng) - (int) (set / 15 + zone(lng)))) < 10) {// cout << "the sun sets is " << (int) (set / 15 + zone(lng)) << ": "// << (int) (60 * (set / 15 + zone(lng) - (int) (set / 15 + zone(lng)))) << " .\\n"; hour = (int) (set / 15 + zone(lng)); min = (int) (60 * (set / 15 + zone(lng) - (int) (set / 15 + zone(lng)))); sunset = hour + ": " + min; } else {// cout << "the sun sets is " << (int) (set / 15 + zone(lng)) << ":"// << (int) (60 * (set / 15 + zone(lng) - (int) (set / 15 + zone(lng)))) << " .\\n"; hour = (int) (set / 15 + zone(lng)); min = (int) (60 * (set / 15 + zone(lng) - (int) (set / 15 + zone(lng)))); sunset = hour + ":" + min; } } public void setTimeAndLoc(int year, int month, int date, double lat, double lng) { double uto = 180.0; int[] c = {year, month, date}; double rise, set; rise = resultRise(utRise(uto, gha(uto, gSun(tCentury(days(year, month, date), uto)), eclipticLongitude( lSun(tCentury(days(year, month, date), uto)), gSun(tCentury(days(year, month, date), uto)))), lng, e(h, lat, sunDeviation( earthTilt(tCentury(days(year, month, date), uto)), eclipticLongitude(lSun(tCentury(days(year, month, date), uto)), gSun(tCentury(days(year, month, date), uto)))))), uto, lng, lat, year, month, date); set = resultSet(utSet(uto, gha(uto, gSun(tCentury(days(year, month, date), uto)), eclipticLongitude( lSun(tCentury(days(year, month, date), uto)), gSun(tCentury(days(year, month, date), uto)))), lng, e(h, lat, sunDeviation(earthTilt(tCentury(days(year, month, date), uto)), eclipticLongitude( lSun(tCentury(days(year, month, date), uto)), gSun(tCentury(days(year, month, date), uto)))))), uto, lng, lat, year, month, date); output(rise, set, lng); }} android 5 新 UI 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout 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="ljt.appbarlayout.MainActivity" tools:layout_editor_absoluteY="81dp"> <!-- 对LinearLayout的封装,方向为垂直方向 --> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <!-- android.support.v7.widget.Toolbar 的扩展,layout_scrollFlags设置滚动样式 --> <!-- CollapsingToolbarLayout是用来对Toolbar进行再次包装的ViewGroup,主要是用于实现折叠(其实就是看起来像伸缩~)的App Bar效果。它需要放在AppBarLayout布局里面,并且作为AppBarLayout的直接子View。CollapsingToolbarLayout主要包括几个功能(参照了官方网站上内容,略加自己的理解进行解释): (1) 折叠Title(Collapsing title):当布局内容全部显示出来时,title是最大的,但是随着View逐步移出屏幕顶部,title变得越来越小。你可以通过调用setTitle函数来设置title。 (2)内容纱布(Content scrim):根据滚动的位置是否到达一个阀值,来决定是否对View“盖上纱布”。可以通过setContentScrim(Drawable)来设置纱布的图片. (3)状态栏纱布(Status bar scrim):根据滚动位置是否到达一个阀值决定是否对状态栏“盖上纱布”,你可以通过setStatusBarScrim(Drawable)来设置纱布图片,但是只能在LOLLIPOP设备上面有作用。 (4)视差滚动子View(Parallax scrolling children):子View可以选择在当前的布局当时是否以“视差”的方式来跟随滚动。(PS:其实就是让这个View的滚动的速度比其他正常滚动的View速度稍微慢一点)。将布局参数app:layout_collapseMode设为parallax (5)将子View位置固定(Pinned position children):子View可以选择是否在全局空间上固定位置,这对于Toolbar来说非常有用,因为当布局在移动时,可以将Toolbar固定位置而不受移动的影响。 将app:layout_collapseMode设为pin。 --> <!-- app:layout_scrollFlags: (1) scroll:值设为scroll的View会跟随滚动事件一起发生移动。 (2) enterAlways:值设为enterAlways的View,当ScrollView往下滚动时,该View会直接往下滚动。而不用考虑ScrollView是否在滚动。 (3) exitUntilCollapsed:值设为exitUntilCollapsed的View,当这个View要往上逐渐“消逝”时,会一直往上滑动,直到剩下的的高度达到它的最小高度后,再响应ScrollView的内部滑动事件。 (4) enterAlwaysCollapsed:是enterAlways的附加选项,一般跟enterAlways一起使用,它是指,View在往下“出现”的时候,首先是enterAlways效果,当View的高度达到最小高度时,View就暂时不去往下滚动,直到ScrollView滑动到顶部不再滑动时,View再继续往下滑动,直到滑到View的顶部结束。 --> <android.support.design.widget.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" app:expandedTitleMarginEnd="64dp" app:expandedTitleMarginStart="48dp" app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed" app:statusBarScrim="@android:color/transparent"> <!-- 头部差值滚动图片 --> <ImageView android:layout_width="match_parent" android:layout_height="300dp" android:scaleType="centerCrop" android:src="@mipmap/title_bar" app:layout_collapseMode="parallax" /> <!-- 真实的toolbar --> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" /> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <!-- 把ScrollView和AppBarLayout作为CoordinateLayout的子View,然后编写一个Behavior,在这个Behavior里面判断当前的操作是应该让ScrollView时刻保持在AppBarLayout之下(即只要改变AppBarLayout的位置就可以一起滑动),还是应该让ScrollView内部滚动而不让AppBarLayout位置发生变化等等这些需求,都是可以在Behavior里面处理的。你可以去针对你的ScrollView编写Behavior。然而,我们看到我们的AppBarLayout事先的功能比较复杂,如果我们自己去定义这样的效果,代码非常复杂,还要考虑很多方面,好在Android帮我们写好啦,我们直接用就是了,这个ScrollView就是NestedScrollView,请注意,它并没有继承ScrollView,它继承的是FrameLayout,但是它实现的效果把它可以看成是ScrollView。 --> <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/test_str" /> </android.support.v4.widget.NestedScrollView></android.support.design.widget.CoordinatorLayout> MD5 点我展开 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.security.MessageDigest;import org.apache.commons.codec.binary.Hex;import org.apache.commons.codec.digest.DigestUtils;/** * MD5计算工具 */public class Md5CaculateUtil { /** * 获取一个文件的md5值(可处理大文件) * * @return md5 value */ public static String getMD5(File file) { FileInputStream fileInputStream = null; try { MessageDigest MD5 = MessageDigest.getInstance("MD5"); fileInputStream = new FileInputStream(file); byte[] buffer = new byte[102400]; int length; while ((length = fileInputStream.read(buffer)) != -1) { MD5.update(buffer, 0, length); } return new String(Hex.encodeHex(MD5.digest())); } catch (Exception e) { e.printStackTrace(); return null; } finally { try { if (fileInputStream != null) { fileInputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 求一个字符串的md5值 * * @param target 字符串 * @return md5 value */ public static String MD5(String target) { return DigestUtils.md5Hex(target); } public static void main(String[] args) { long beginTime = System.currentTimeMillis(); File file = new File("D:/1/pdi-ce-7.0.0.0-24.zip"); String md5 = getMD5(file); long endTime = System.currentTimeMillis(); System.out.println("MD5:" + md5 + "\\n 耗时:" + ((endTime - beginTime) / 1000) + "s"); }} 文件重命名 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748import java.io.File;public class ChangeFileName { public static String[] getFileName(String path) { File file = new File(path); String[] fileName = file.list(); return fileName; } public static void renameFile(String path, String oldname) { System.out.println(path + "\\\\" + oldname); String newname = oldname; newname = newname.replace("[在线]", ""); newname = newname.replace("+", ""); newname = newname.replace("~", ""); if (!oldname.equals(newname)) {//新的文件名和以前文件名不同时,才有必要进行重命名 File oldfile = new File(path + "\\\\" + oldname); File newfile = new File(path + "\\\\" + newname); if (!oldfile.exists()) { return;//重命名文件不存在 } if (newfile.exists())//若在该目录下已经有一个文件和新文件名相同,则不允许重命名 System.out.println(newname + "已经存在!"); else { boolean b = oldfile.renameTo(newfile); if (b) { System.out.println("更名成功"); } else { System.out.println("更名失败"); } } } else { System.out.println("新文件名和旧文件名相同..."); } } public static void main(String[] args) { int page = 111; while (page <= 130) { String[] fileName = getFileName("G:\\\\slb\\\\" + page);//<span style="font-family: Arial, Helvetica, sans-serif;">此处修改为你的本地路径</span> for (int i = 0; i < fileName.length; i++) { renameFile("G:\\\\slb\\\\" + page, fileName[i]);//cx修改为你要修改的文件名格式 } page++; } }} okhttp3上传图片 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262import android.content.Context;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import java.io.File;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Objects;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import okhttp3.Headers;import okhttp3.MediaType;import okhttp3.MultipartBody;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.RequestBody;import okhttp3.Response;/** * @Created by jiangtao on 2019/8/26 */public class UpLoadImgThread { private static UpLoadImgThread sImgThread; private Context mContext; private UpLoadImgThread(Context context) { mContext = context.getApplicationContext(); } public static synchronized UpLoadImgThread getInstance(Context context) { if (sImgThread == null) { sImgThread = new UpLoadImgThread(context); } return sImgThread; } public void upLoad(String url, String fileKey, List<String> pathList, Map<String, String> header, Map<String, Object> params, final CallBackListener mCallBackListener) { MyHandler handler = new MyHandler(); handler.setCallBackListener(mCallBackListener, pathList.size()); ExecutorService singlePool = Executors.newSingleThreadExecutor(); for (int i = 0; i < pathList.size(); i++) { singlePool.execute(new UploadTask(url, fileKey, pathList.get(i), header, params, handler)); } } public void upLoadNet(String url, String fileKey, List<String> pathList, List<Boolean> isNet, Map<String, Object> params, final CallBackListener mCallBackListener) { MyHandler handler = new MyHandler(); handler.setCallBackListener(mCallBackListener, pathList.size()); ExecutorService singlePool = Executors.newSingleThreadExecutor(); for (int i = 0; i < pathList.size(); i++) { singlePool.execute(new UploadNetTask(url, fileKey, pathList.get(i), isNet.get(i), params, handler)); } } /** * 图片上传线程 */ public class UploadTask implements Runnable { private String fileKey; private String filePath; private String url; private Map<String, String> header; private Map<String, Object> params; private String[] values; private MyHandler mMyHandler; private UploadTask(String url, String fileKey, String filepath, Map<String, String> header, Map<String, Object> params, MyHandler handler) { this.fileKey = fileKey; this.filePath = filepath; this.url = url; this.header = header; this.params = params; mMyHandler = handler; } //发送返回的数据 @Override public void run() { String[] values = upload(url, fileKey, filePath, header, params); if ("1".equals(values[0])) { Message message = Message.obtain(); Bundle bundle = new Bundle(); bundle.putString("info", values[1]); message.setData(bundle); message.what = 0; mMyHandler.sendMessage(message); } else { Message message = Message.obtain(); Bundle bundle = new Bundle(); bundle.putString("info", values[1]); message.setData(bundle); message.what = 1; mMyHandler.sendMessage(message); } } //上传图片并返回服务器返回的数据 private String[] upload(String url, String fileKey, String filepath, Map<String, String> header, Map<String, Object> params) { try { OkHttpClient okHttpClient = new OkHttpClient(); Headers headers = Headers.of(header); // form 表单形式上传 MultipartBody.Builder requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM); if (filepath != null) { // MediaType.parse() 里面是上传的文件类型。 File file = new File(filepath); RequestBody body = RequestBody.create(file, MediaType.parse("image/*")); String filename = file.getName(); // 参数分别为, 请求key ,文件名称 , RequestBody requestBody.addFormDataPart(fileKey, filename, body); } if (params != null) { // map 里面是请求中所需要的 key 和 value for (Map.Entry entry : params.entrySet()) { requestBody.addFormDataPart(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); } } //创建RequestBody RequestBody body = requestBody.build(); //创建Request final Request request = new Request.Builder().url(url).headers(headers).post(body).build(); try (Response response = okHttpClient.newCall(request).execute()) { if (response.body() != null && response.isSuccessful()) { String jsonData = Objects.requireNonNull(response.body()).string(); values = new String[]{"1", jsonData}; } else { values = new String[]{"0", "网络请求失败: " + response.toString()}; } } } catch (Exception e) { e.printStackTrace(); values = new String[]{"0", "网络请求失败:" + e.toString()}; } return values; } } /** * 网络图片上传线程 */ public class UploadNetTask implements Runnable { private String fileKey; private String filePath; private String url; private Map<String, Object> params; private boolean isNetPic; private String[] values; private MyHandler mMyHandler; private UploadNetTask(String url, String fileKey, String filepath, boolean isNetPic, Map<String, Object> params, MyHandler handler) { this.fileKey = fileKey; this.filePath = filepath; this.isNetPic = isNetPic; this.url = url; this.params = params; mMyHandler = handler; } //发送返回的数据 @Override public void run() { String[] values = upload(url, fileKey, filePath, isNetPic, params); if ("1".equals(values[0])) { Message message = Message.obtain(); Bundle bundle = new Bundle(); bundle.putString("info", values[1]); message.setData(bundle); message.what = 0; mMyHandler.sendMessage(message); } else { Message message = Message.obtain(); Bundle bundle = new Bundle(); bundle.putString("info", values[1]); message.setData(bundle); message.what = 1; mMyHandler.sendMessage(message); } } //上传图片并返回服务器返回的数据 public String[] upload(String url, String fileKey, String filepath, boolean isNetPic, Map<String, Object> params) {// try {// final StringRequest request = new StringRequest(url, RequestMethod.POST);// File glidePath = new File(filepath);// if (isNetPic) {// glidePath = Glide.with(mContext).load(filepath).downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).get();// }// File tempPath = new Compressor(mContext).compressToFile(glidePath);// request.add(params).add(fileKey, tempPath);//// Response<String> response = SyncRequestExecutor.INSTANCE.execute(request);// Log.e("返回值", response.toString());// if (response.isSucceed()) {// Log.e("返回值", response.get());// values = new String[]{"1", response.get()};// } else {// // 请求失败,拿到错误:// Exception e = response.getException();// values = new String[]{"0", "网络请求失败:" + e.toString()};// }// } catch (Exception e) {// Log.e("try:", e.toString());// values = new String[]{"0", "图片上传失败"};// } return values; } } /** * 回调接口 */ public interface CallBackListener { void onFinish(List<String> values); void onError(String e); } /** * Handler传递消息 */ private class MyHandler extends Handler { private CallBackListener mCallBackListener; private int temp; private int total; private List<String> values; public void setCallBackListener(CallBackListener callBackListener, int size) { mCallBackListener = callBackListener; temp = 0; values = new ArrayList<>(); total = size; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); String info = msg.getData().getString("info"); temp++; switch (msg.what) { case 0: values.add(info); if (temp == total) { mCallBackListener.onFinish(values); } break; case 1: Log.e("图片上传失败", "第" + temp + "张照片上传失败" + info); mCallBackListener.onError(info); break; } } }} 权限申请 点我展开 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849import android.app.Activityimport android.content.pm.PackageManagerimport androidx.core.app.ActivityCompatimport androidx.core.content.ContextCompatimport java.util.*/** * @author kavan jiangtao on 2019/8/6 */object PermissionUtils { const val REQUEST_CODE = 10 @JvmStatic fun applyPermission(activity: Activity, permissions: Array<String>) { val permissionList: MutableList<String> = ArrayList() for (permission in permissions) { if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) { permissionList.add(permission) } } val permissionArray = permissionList.toTypedArray() if (permissionArray.isNotEmpty()) { ActivityCompat.requestPermissions(activity, permissionArray, REQUEST_CODE) } } @JvmStatic fun hasPermission(activity: Activity, permissions: Array<String>): Boolean { for (permission in permissions) { if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) { return false } } return true } @JvmStatic fun isGrantedAll(grantResults: IntArray): Boolean { var isGranted = false for (grantResult in grantResults) { if (grantResult == PackageManager.PERMISSION_GRANTED) { isGranted = true } else { isGranted = false break } } return isGranted }} implementation ‘com.afollestad.assent:core:3.0.0-RC4’ 123456789101112131415161718192021222324252627import android.app.Activityimport com.afollestad.assent.Permissionimport com.afollestad.assent.askForPermissionsimport com.afollestad.assent.isAllGrantedimport com.afollestad.assent.runWithPermissionstypealias PermissionListener = () -> Unitfun Activity.checkPermissions( vararg permissions: Permission, permissionListener: PermissionListener? = null) { if (isAllGranted(*permissions)) { runWithPermissions(*permissions) { permissionListener?.invoke() } } else { askForPermissions(*permissions) { result -> if (result.isAllGranted()) { permissionListener?.invoke() } else { checkPermissions(*permissions, permissionListener = permissionListener) } } }} content映射到文件路径 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144import android.annotation.SuppressLint;import android.content.ContentUris;import android.content.Context;import android.database.Cursor;import android.net.Uri;import android.os.Build;import android.os.Environment;import android.provider.DocumentsContract;import android.provider.MediaStore;/** * Created by Admin on 2018/11/27 * Describe: */public class FileUriUtils { public static String getRealPathFromURI(Context context, Uri contentUri) { String res = null; String[] proj = {MediaStore.Images.Media.DATA}; Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null); if (null != cursor && cursor.moveToFirst()) { ; int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); res = cursor.getString(column_index); cursor.close(); } return res; } /** * 专为Android4.4设计的从Uri获取文件绝对路径,以前的方法已不好使 */ @SuppressLint("NewApi") public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{split[1]}; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @param selection (Optional) Filter used in the query. * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); }} 圆角图片 点我展开 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.RectF;import android.graphics.Shader;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.ColorDrawable;import android.graphics.drawable.Drawable;import android.net.Uri;import android.util.AttributeSet;import android.widget.ImageView;/** * Created by Administrator on 2016/10/19. */public class CircleImageView extends ImageView { private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP; private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; private static final int COLORDRAWABLE_DIMENSION = 1; private static final int DEFAULT_BORDER_WIDTH = 0; private static final int DEFAULT_BORDER_COLOR = Color.BLACK; private final RectF mDrawableRect = new RectF(); private final RectF mBorderRect = new RectF(); private final Matrix mShaderMatrix = new Matrix(); private final Paint mBitmapPaint = new Paint(); private final Paint mBorderPaint = new Paint(); private int mBorderColor = DEFAULT_BORDER_COLOR; private int mBorderWidth = DEFAULT_BORDER_WIDTH; private Bitmap mBitmap; private BitmapShader mBitmapShader; private int mBitmapWidth; private int mBitmapHeight; private float mDrawableRadius; private float mBorderRadius; private boolean mReady; private boolean mSetupPending; public CircleImageView(Context context) { super(context); init(); } public CircleImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);// TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);//// mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);// mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);//// a.recycle(); init(); } private void init() { super.setScaleType(SCALE_TYPE); mReady = true; if (mSetupPending) { setup(); mSetupPending = false; } } @Override public ScaleType getScaleType() { return SCALE_TYPE; } @Override public void setScaleType(ScaleType scaleType) { if (scaleType != SCALE_TYPE) { throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType)); } } @Override protected void onDraw(Canvas canvas) { if (getDrawable() == null) { return; } canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint); if (mBorderWidth != 0) { canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); setup(); } public int getBorderColor() { return mBorderColor; } public void setBorderColor(int borderColor) { if (borderColor == mBorderColor) { return; } mBorderColor = borderColor; mBorderPaint.setColor(mBorderColor); invalidate(); } public int getBorderWidth() { return mBorderWidth; } public void setBorderWidth(int borderWidth) { if (borderWidth == mBorderWidth) { return; } mBorderWidth = borderWidth; setup(); } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); mBitmap = bm; setup(); } @Override public void setImageDrawable(Drawable drawable) { super.setImageDrawable(drawable); mBitmap = getBitmapFromDrawable(drawable); setup(); } @Override public void setImageResource(int resId) { super.setImageResource(resId); mBitmap = getBitmapFromDrawable(getDrawable()); setup(); } @Override public void setImageURI(Uri uri) { super.setImageURI(uri); mBitmap = getBitmapFromDrawable(getDrawable()); setup(); } private Bitmap getBitmapFromDrawable(Drawable drawable) { if (drawable == null) { return null; } if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } try { Bitmap bitmap; if (drawable instanceof ColorDrawable) { bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG); } else { bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG); } Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } catch (OutOfMemoryError e) { return null; } } private void setup() { if (!mReady) { mSetupPending = true; return; } if (mBitmap == null) { return; } mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mBitmapPaint.setAntiAlias(true); mBitmapPaint.setShader(mBitmapShader); mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setAntiAlias(true); mBorderPaint.setColor(mBorderColor); mBorderPaint.setStrokeWidth(mBorderWidth); mBitmapHeight = mBitmap.getHeight(); mBitmapWidth = mBitmap.getWidth(); mBorderRect.set(0, 0, getWidth(), getHeight()); mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2); mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth); mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2); updateShaderMatrix(); invalidate(); } private void updateShaderMatrix() { float scale; float dx = 0; float dy = 0; mShaderMatrix.set(null); if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) { scale = mDrawableRect.height() / (float) mBitmapHeight; dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; } else { scale = mDrawableRect.width() / (float) mBitmapWidth; dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; } mShaderMatrix.setScale(scale, scale); mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth); mBitmapShader.setLocalMatrix(mShaderMatrix); }} 解决ScrollView滑动粘滞 点我展开 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.ViewConfiguration;import android.widget.ScrollView;/** * Created by jiangtao on 2017/2/12 16:10 * E-mail:3305727299@qq.com */public class SlideScrollview extends ScrollView { private int downX; private int downY; private int mTouchSlop; public SlideScrollview(Context context) { super(context); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } public SlideScrollview(Context context, AttributeSet attrs) { super(context, attrs); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } public SlideScrollview(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override public boolean onInterceptTouchEvent(MotionEvent e) { int action = e.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: downX = (int) e.getRawX(); downY = (int) e.getRawY(); break; case MotionEvent.ACTION_MOVE: int moveY = (int) e.getRawY(); if (Math.abs(moveY - downY) > mTouchSlop) { return true; } } return super.onInterceptTouchEvent(e); }}","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"Code","slug":"Code","permalink":"https://laujiangtao.github.io/tags/Code/"}]},{"title":"终端命令","slug":"终端命令","date":"2021-01-04T09:17:39.000Z","updated":"2021-05-15T15:53:21.891Z","comments":false,"path":"2021/01/04/terminal-command/","link":"","permalink":"https://laujiangtao.github.io/2021/01/04/terminal-command/","excerpt":"","text":"Java JDK1234// 【获取签名文件信息】// 1. key的路径// 2. 库的密码keytool -list -v -keystore %1 -storepass %2 Android SDK12345678// 【签名】// 1. key的路径// 2. 库的密码// 3. key的密码// 4. 签名后输出的文件名// 5. 需要签名的原文件// 6. key的别名jarsigner -keystore %1 -storepass %2 -keypass %3 -sigfile CERT -signedjar %4 %5 %6 -verbose -certs Git123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960// 配置个人名字和邮箱git config --global user.name "${name}"git config --global user.email "${email}"// 克隆公共仓库代码git clone https://github.com/${username}/xxx.git// 克隆私密仓库代码git clone https://${username}:${password}@github.com/${username}/xxx.git// 查看所有分支git branch -a// 检出分支/切换分支git checkout ${branch_name}// 查看当前Git状态git status// 提交代码git stash git fetchgit rebasegit stash popgit add .git commit -m 'xxx'git push// 查看远程仓库git remote -v// 添加远程仓库git add remote ${remote_name} https://github.com/${username}/xxx.git// 推送到默认仓库git push// 推送到指定仓库git push ${remote_name}// 修改http.postBuffer大小 (error: unable to rewind rpc post data - try increasing http.postBuffer)git config --global http.postBuffer 524288000// 列出taggit tag// 过滤taggit tag -l “v1.0*”// 创建taggit tag v1.0.0// 创建带备注的taggit tag -a v1.0.0 -m “版本1.0.0”// 查看tag信息git show v1.0.0// 给指定的commit加taggit tag -a v1.0.0 908f7b13 -m “版本1.0.0”// 将单个tag推送到远程git push [${remote_name}] v1.0.0// 推送所有taggit push [${remote_name}] --tags// 删除本地taggit tag -d v1.0.0// 删除远程taggit push [${remote_name}] :refs/tags/v1.0.0// 清除缓存git rm -r --cached . VI 命令12345678910111213141516171819202122232425262728293031323334Ctrl+u:向文件首翻半屏;Ctrl+d:向文件尾翻半屏;Ctrl+f:向文件尾翻一屏;Ctrl+b:向文件首翻一屏;Esc:从编辑模式切换到命令模式;ZZ:命令模式下保存当前文件所做的修改后退出vi;:行号:光标跳转到指定行的行首;:$:光标跳转到最后一行的行首;x或X:删除一个字符,x删除光标后的,而X删除光标前的;D:删除从当前光标到光标所在行尾的全部字符;dd:删除光标行正行内容;ndd:删除当前行及其后n-1行;nyy:将当前行及其下n行的内容保存到寄存器?中,其中?为一个字母,n为一个数字;p:粘贴文本操作,用于将缓存区的内容粘贴到当前光标所在位置的下方;P:粘贴文本操作,用于将缓存区的内容粘贴到当前光标所在位置的上方;/字符串:文本查找操作,用于从当前光标所在位置开始向文件尾部查找指定字符串的内容,查找的字符串会被加亮显示;?name:文本查找操作,用于从当前光标所在位置开始向文件头部查找指定字符串的内容,查找的字符串会被加亮显示;a,bs/F/T:替换文本操作,用于在第a行到第b行之间,将F字符串换成T字符串。其中,“s/”表示进行替换操作;a:在当前字符后添加文本;A:在行末添加文本;i:在当前字符前插入文本;I:在行首插入文本;o:在当前行后面插入一空行;O:在当前行前面插入一空行;:wq:在命令模式下,执行存盘退出操作;:w:在命令模式下,执行存盘操作;:w!:在命令模式下,执行强制存盘操作;:q:在命令模式下,执行退出vi操作;:q!:在命令模式下,执行强制退出vi操作;:e文件名:在命令模式下,打开并编辑指定名称的文件;:n:在命令模式下,如果同时打开多个文件,则继续编辑下一个文件;:f:在命令模式下,用于显示当前的文件名、光标所在行的行号以及显示比例;:set number:在命令模式下,用于在最左端显示行号;:set nonumber:在命令模式下,用于在最左端不显示行号;","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"Terminal","slug":"Terminal","permalink":"https://laujiangtao.github.io/tags/Terminal/"}]},{"title":"下载JDK和配置环境变量","slug":"下载JDK和配置环境变量","date":"2020-12-28T02:22:06.000Z","updated":"2021-05-15T15:53:14.783Z","comments":false,"path":"2020/12/28/download-jdk-and-configure-environment-variables/","link":"","permalink":"https://laujiangtao.github.io/2020/12/28/download-jdk-and-configure-environment-variables/","excerpt":"","text":"下载JDK去Oracel官网下载最新版本的JDK并安装 配置环境变量Windows 10环境右键 此电脑 -> 属性 -> 高级系统设置 -> 环境变量 在环境变量弹窗,新建JAVA_HOME变量 变量名:JAVA_HOME变量值:电脑上JDK安装的绝对路径 如果存在 CLASSPATH 变量,选中点击 编辑。 如果没有,点击 新建… 。 变量名:CLASSPATH变量值:.;%JAVA_HOME%\\lib\\dt.jar;%JAVA_HOME%\\lib\\tools.jar; 由于 win10 的不同,当选中 Path 变量的时候,系统会很方便的把所有不同路径都分开了,不会像 win7 或者 win8 那样连在一起。 新建两条路径: %JAVA_HOME%\\bin%JAVA_HOME%\\jre\\bin 打开cmd,输入java -version显示版本号说明配置成功了 Mac环境在~/.bash_profile或~/.zprofile里指定JAVA_HOME 1export JAVA_HOME=`/usr/libexec/java_home -v 15` 然后,把JAVA_HOME的bin目录附加到系统环境变量PATH上。 1export PATH=$JAVA_HOME/bin:$PATH 打开终端,输入java -version显示版本号说明配置成功了","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"Java","slug":"Java","permalink":"https://laujiangtao.github.io/tags/Java/"}]},{"title":"【转载】配置方法数超过 64K 的应用","slug":"配置方法数超过 64K 的应用","date":"2020-12-25T08:26:03.000Z","updated":"2021-05-15T15:51:54.952Z","comments":false,"path":"2020/12/25/configure-apps-with-more-than-64k-methods/","link":"","permalink":"https://laujiangtao.github.io/2020/12/25/configure-apps-with-more-than-64k-methods/","excerpt":"","text":"随着 Android 平台的持续成长,Android 应用的大小也在增加。当您的应用及其引用的库达到特定大小时,您会遇到构建错误,指明您的应用已达到 Android 应用构建架构的极限。早期版本的构建系统按如下方式报告这一错误: 12Conversion to Dalvik format failed:Unable to execute dex: method ID not in [0, 0xffff]: 65536 较新版本的 Android 构建系统虽然显示的错误不同,但指示的是同一问题: 123trouble writing output:Too many field references: 131000; max is 65536.You may try using --multi-dex option. 这些错误状况都会显示下面这个数字:65,536。这个数字很重要,因为它代表的是单个 Dalvik Executable (DEX) 字节码文件内的代码可调用的引用总数。本页介绍如何通过启用被称为 Dalvik 可执行文件分包的应用配置来越过这一限制,使您的应用能够构建并读取 Dalvik 可执行文件分包 DEX 文件。 关于 64K 引用限制Android 应用 (APK) 文件包含 Dalvik Executable (DEX) 文件形式的可执行字节码文件,其中包含用来运行您的应用的已编译代码。Dalvik Executable 规范将可在单个 DEX 文件内可引用的方法总数限制在 65,536,其中包括 Android 框架方法、库方法以及您自己代码中的方法。在计算机科学领域内,术语千(简称 K)表示 1024(或 2^10)。由于 65,536 等于 64 X 1024,因此这一限制也称为“64K 引用限制”。 Android 5.0 之前版本的 Dalvik 可执行文件分包支持Android 5.0(API 级别 21)之前的平台版本使用 Dalvik 运行时来执行应用代码。默认情况下,Dalvik 限制应用的每个 APK 只能使用单个 classes.dex 字节码文件。要想绕过这一限制,您可以使用 Dalvik 可执行文件分包支持库,它会成为您的应用主要 DEX 文件的一部分,然后管理对其他 DEX 文件及其所包含代码的访问。 注:如果您的项目配置时所面向的 Dalvik 可执行文件分包使用的是 minSdkVersion 20 或更低版本,并且您将其部署到运行 Android 4.4(API 级别 20)或更低版本的目标设备上,则 Android Studio 会停用 Instant Run。 Android 5.0 及更高版本的 Dalvik 可执行文件分包支持Android 5.0(API 级别 21)及更高版本使用名为 ART 的运行时,后者原生支持从 APK 文件加载多个 DEX 文件。ART 在应用安装时执行预编译,扫描 classesN.dex 文件,并将它们编译成单个 .oat 文件,供 Android 设备执行。因此,如果您的 minSdkVersion 为 21 或更高值,则不需要 Dalvik 可执行文件分包支持库。 如需了解有关 Android 5.0 运行时的详细信息,请参阅 ART 和 Dalvik。 注:如果将应用的 minSdkVersion 设置为 21 或更高值,使用 Instant Run 时,Android Studio 会自动将应用配置为进行 Dalvik 可执行文件分包。由于 Instant Run 仅适用于调试版本的应用,您仍需配置发布构建进行 Dalvik 可执行文件分包,以规避 64K 限制。 规避 64K 限制在将您的应用配置为支持使用 64K 或更多方法引用之前,您应该采取措施减少应用代码调用的引用总数,包括由您的应用代码或包含的库定义的方法。下列策略可帮助您避免达到 DEX 引用限制: 检查您的应用的直接和传递依赖项 - 确保您在应用中使用任何庞大依赖库所带来的好处大于为应用添加大量代码所带来的弊端。一种常见的反面模式是,仅仅为了使用几个实用方法就在应用中加入非常庞大的库。减少您的应用代码依赖项往往能够帮助您规避 dex 引用限制。 通过 ProGuard 移除未使用的代码 - 为您的版本构建启用代码压缩以运行 ProGuard。启用压缩可确保您交付的 APK 不含有未使用的代码。 使用这些技巧使您不必在应用中启用 Dalvik 可执行文件分包,同时还会减小 APK 的总体大小。 配置您的应用进行 Dalvik 可执行文件分包将您的应用项目设置为使用 Dalvik 可执行文件分包配置需要对您的应用项目进行以下修改,具体取决于应用支持的最低 Android 版本。 如果您的 minSdkVersion 设置为 21 或更高值,您只需在模块级 build.gradle 文件中将 multiDexEnabled 设置为 true,如此处所示: 123456789android { defaultConfig { ... minSdkVersion 21 targetSdkVersion 28 multiDexEnabled true } ...} 但是,如果您的 minSdkVersion 设置为 20 或更低值,则您必须按如下方式使用 Dalvik 可执行文件分包支持库: 修改模块级 build.gradle 文件以启用 Dalvik 可执行文件分包,并将 Dalvik 可执行文件分包库添加为依赖项,如此处所示: 12345678910111213android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 28 multiDexEnabled true } ...}dependencies { compile 'com.android.support:multidex:1.0.3'} 根据是否要替换 Application 类,执行以下操作之一: 如果您没有替换 Application 类,请编辑清单文件,按如下方式设置 <application> 标记中的 android:name: 12345678<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <application android:name="android.support.multidex.MultiDexApplication" > ... </application></manifest> 如果您替换了 Application 类,请按如下方式对其进行更改以扩展 MultiDexApplication(如果可能): 1public class MyApplication extends MultiDexApplication { ... } 或者,如果您替换了 Application 类,但无法更改基本类,则可以改为替换 attachBaseContext() 方法并调用 MultiDex.install(this) 来启用 Dalvik 可执行文件分包: 1234567public class MyApplication extends SomeOtherApplication { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); }} 构建应用后,Android 构建工具会根据需要构建主 DEX 文件 (classes.dex) 和辅助 DEX 文件(classes2.dex 和 classes3.dex 等)。然后,构建系统会将所有 DEX 文件打包到您的 APK 中。 运行时,Dalvik 可执行文件分包 API 使用特殊的类加载器来搜索适用于您的方法的所有 DEX 文件(而不是仅在主 classes.dex 文件中搜索)。 Dalvik 可执行文件分包支持库的局限性Dalvik 可执行文件分包支持库具有一些已知的局限性,将其纳入您的应用构建配置之中时,您应该注意这些局限性并进行针对性的测试: 启动期间在设备数据分区中安装 DEX 文件的过程相当复杂,如果辅助 DEX 文件较大,可能会导致应用无响应 (ANR) 错误。在此情况下,您应该通过 ProGuard 应用代码压缩以尽量减小 DEX 文件的大小,并移除未使用的那部分代码。 由于存在 Dalvik linearAlloc 错误(问题 22586),使用 Dalvik 可执行文件分包的应用可能无法在运行的平台版本早于 Android 4.0(API 级别 14)的设备上启动。如果您的目标 API 级别低于 14,请务必针对这些版本的平台进行测试,因为您的应用可能会在启动时或加载特定类群时出现问题。代码压缩可以减少甚至有可能消除这些潜在问题。 由于存在 Dalvik linearAlloc 限制(问题 78035),因此,如果使用 Dalvik 可执行文件分包配置的应用发出非常庞大的内存分配请求,则可能会在运行期间发生崩溃。尽管 Android 4.0(API 级别 14)提高了分配限制,但在 Android 5.0(API 级别 21)之前的 Android 版本上,应用仍有可能遭遇这一限制。 声明主 DEX 文件中需要的类为 Dalvik 可执行文件分包构建每个 DEX 文件时,构建工具会执行复杂的决策制定来确定主要 DEX 文件中需要的类,以便应用能够成功启动。如果启动期间需要的任何类未在主 DEX 文件中提供,那么您的应用将崩溃并出现错误 java.lang.NoClassDefFoundError。 该情况不应出现在直接从应用代码访问的代码上,因为构建工具能识别这些代码路径,但可能在代码路径可见性较低(如使用的库具有复杂的依赖项)时出现。例如,如果代码使用自检机制或从原生代码调用 Java 方法,那么这些类可能不会被识别为主 DEX 文件中的必需项。 因此,如果您收到 java.lang.NoClassDefFoundError,则必须使用构建类型中的 multiDexKeepFile 或 multiDexKeepProguard 属性声明它们,以手动将这些其他类指定为主 DEX 文件中的必需项。如果类在 multiDexKeepFile 或 multiDexKeepProguard 文件中匹配,则该类会添加至主 DEX 文件。 multiDexKeepFile 属性您在 multiDexKeepFile 中指定的文件应该每行包含一个类,并且采用 com/example/MyClass.class 的格式。例如,您可以创建一个名为 multidex-config.txt 的文件,如下所示: 12com/example/MyClass.classcom/example/MyOtherClass.class 然后,您可以按以下方式针对构建类型声明该文件: 12345678android { buildTypes { release { multiDexKeepFile file('multidex-config.txt') ... } }} 请记住,Gradle 会读取相对于 build.gradle 文件的路径,因此如果 multidex-config.txt 与 build.gradle 文件在同一目录中,以上示例将有效。 multiDexKeepProguard 属性multiDexKeepProguard 文件使用与 Proguard 相同的格式,并且支持整个 Proguard 语法。如需了解有关 Proguard 格式和语法的详细信息,请参阅 Proguard 手册中的 Keep Options 一节。 您在 multiDexKeepProguard 中指定的文件应该在任何有效的 ProGuard 语法中包含 -keep 选项。例如,-keep com.example.MyClass.class。您可以创建一个名为 multidex-config.pro 的文件,如下所示: 12-keep class com.example.MyClass-keep class com.example.MyClassToo 如果您想要指定包中的所有类,文件将如下所示: 1-keep class com.example.** { *; } // All classes in the com.example package 然后,您可以按以下方式针对构建类型声明该文件: 12345678android { buildTypes { release { multiDexKeepProguard('multidex-config.pro') ... } }} 优化开发构建中的 Dalvik 可执行文件分包Dalvik 可执行文件分包配置会大幅增加构建处理时间,因为构建系统必须就哪些类必须包括在主 DEX 文件中以及哪些类可以包括在辅助 DEX 文件中作出复杂的决策。这意味着使用 Dalvik 可执行文件分包的增量式构建通常耗时更长,可能会拖慢您的开发进度。 为了缩短耗时更长的 Dalvik 可执行文件分包输出构建时间,请利用 productFlavors(一个开发定制和一个发布定制,具有不同的 minSdkVersion 值)创建两个构建变型。 对于开发定制,将 minSdkVersion 设置为 21。该设置将启用一个名为 pre-dexing 的构建功能,此功能使用仅适用于 Android 5.0(API 级别 21)和更高版本的 ART 格式更快生成 Dalvik 可执行文件分包输出。对于发布定制,将 minSdkVersion 设置为适于您的实际最低支持级别。此设置生成的 Dalvik 可执行文件分包 APK 可兼容更多设备,但构建时间更长。 以下构建配置示例展示了如何在 Gradle 构建文件中设置这些定制: 123456789101112131415161718192021222324252627android { defaultConfig { ... multiDexEnabled true } productFlavors { dev { // Enable pre-dexing to produce an APK that can be tested on // Android 5.0+ without the time-consuming DEX build processes. minSdkVersion 21 } prod { // The actual minSdkVersion for the production version. minSdkVersion 14 } } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }}dependencies { compile 'com.android.support:multidex:1.0.3'} 您完成此配置变更后,可以为增量式构建使用应用的 devDebug 变体,后者集 dev 产品定制与 debug 构建类型的属性于一身。这将创建已启用 Dalvik 可执行文件分包且禁用 proguard 的可调试应用(因为 minifyEnabled 默认为 false)。这些设置会使适用于 Gradle 的 Android 插件执行以下操作: 执行 pre-dexing:将每个应用模块和每个依赖项构建为单独的 DEX 文件。 将每个 DEX 文件加入 APK,并且不做任何修改(不执行代码压缩)。 最重要的是,模块 DEX 文件不执行合并操作,因此可以避免为确定主 DEX 文件的内容而进行长时间的计算。 这些设置的好处是,可以进行快速的增量式构建,因为只有修改过的模块的 DEX 文件才会在后续构建期间重新计算并重新打包。但是,这些构建的 APK 只能用于在 Android 5.0 设备上进行测试。不过,由于是以定制形式实现配置,您保留了使用与发布相适的最低 API 级别和 ProGuard 代码压缩执行正常构建的能力。 您还可以构建其他变体,包括 prodDebug 变体构建,该变体虽然构建时间更长,但可用于开发以外的测试。在所示配置内,prodRelease 变体将是最终测试和发布版本。如需了解有关使用构建变体的详细信息,请参阅配置构建变体。 提示:由于您有适用于不同 Dalvik 可执行文件分包需求的不同构建变体,因此也可以为不同变体提供不同清单文件(这样,只有适用于 API 级别 20 和更低版本的清单文件会更改 <application> 标记名称),或者为每个变体创建不同的 Application 子类(这样,只有适用于 API 级别 20 和更低版本的清单文件会扩展 MultiDexApplication 类或调用 MultiDex.install(this))。 测试 Dalvik 可执行文件分包应用编写面向 Dalvik 可执行文件分包应用的仪器测试时,无需进行其他配置。AndroidJUnitRunner 直接支持 Dalvik 可执行文件分包,前提是您使用 MultiDexApplication 或替换您的自定义 Application 对象中的 attachBaseContext() 方法,并调用 MultiDex.install(this) 以启用 Dalvik 可执行文件分包。或者,您可以替换 AndroidJUnitRunner 中的 onCreate() 方法: 12345public void onCreate(Bundle arguments) { MultiDex.install(getTargetContext()); super.onCreate(arguments); ...} 注:目前不支持使用 Dalvik 可执行文件分包来创建测试 APK。","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"Android","slug":"Android","permalink":"https://laujiangtao.github.io/tags/Android/"}]},{"title":"Android抢红包","slug":"Android抢红包","date":"2020-12-25T07:08:27.000Z","updated":"2021-05-15T15:52:59.017Z","comments":false,"path":"2020/12/25/android-grabs-red-envelopes/","link":"","permalink":"https://laujiangtao.github.io/2020/12/25/android-grabs-red-envelopes/","excerpt":"","text":"本文原理是利用Android无障碍服务实现的自动抢红包功能 自动抢红包功能是一个服务实现的,继承自AccessibilityService,然后在服务里面做一些相应的操作。 首先 AndroidManifest.xml 文件中,把所有的<activity></activity>节点全部删除掉,然后创建一个<service></service>节点,配置如下: 12345678910111213<service android:name=".RobMoneyService" android:enabled="true" android:exported="true" android:label="@string/app_name" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility" /></service> 我们看到,其中引用了xml文件夹下的accessibility文件,这个文件是配置你的后台服务是注册了那个应用,比如你想抢微信红包,你就可以根据下面的样式写自己的accessibility.xml文件了 123456789<?xml version="1.0" encoding="utf-8"?><accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" android:description="@string/desc" android:notificationTimeout="100" android:packageNames="com.tencent.mm" /> 里面有许多属性,那么accessibilityEventTypes是设置事件相应类型的,accessibilityFeedbackType是设置回馈给用户的方式的,有震动和语音播报等。canRetrieveWindowContent表示你的服务能否获取到窗口中的内容,参数是boolean值,另外一些就见名知意了,不再赘述。 接下来就要重写我们的服务类了,在继承AccessibilityService之后,需要重写onAccessibilityEvent(AccessibilityEvent accessibilityEvent) 和onInterrupt()两个方法,由名字我们就可以知道,后一个是服务中断时使用,这里我们用不到,主要看第一个方法,参数列表里面就一个参数,是一个AccessibilityEvent 类型的参数,那么我们可以通过这一个对象获得事件类型,再由事件类型判断接下来需要做什么样的工作。 我么知道,在锁屏的时候,有红包发过来,通知栏会有提示,那么我们就通过通知栏的提示来打开抢红包界面,我们可以这样写: 1234int eventType = accessibilityEvent.getEventType();switch (eventType) { case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:break; 通过switch过滤不同的事件,这里我们就监听了通知栏改变时的事件。 那么微信发来红包之后,在通知栏都会有**[微信红包]**这个字样,我们就利用他判断是不是微信红包,如果有这个字样,我们就打开通知进入抢红包界面,实现代码如下: 1234567891011121314151617181920212223case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: //检测通知栏是否有 [微信红包] 字样 List<CharSequence> texts = accessibilityEvent.getText(); if (!texts.isEmpty()) { for (CharSequence text : texts) { String content = text.toString(); Log.i("demo", "text:" + content); if (content.contains("[微信红包]")) { //模拟打开通知栏消息 if (accessibilityEvent.getParcelableData() != null && accessibilityEvent.getParcelableData() instanceof Notification) { Notification notification = (Notification) accessibilityEvent.getParcelableData(); PendingIntent pendingIntent = notification.contentIntent; try { pendingIntent.send(); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } } } } }break; 通过pendingIntent的send()方法,打开界面。 接下来就开始打开红包了。但是,控件的内容和描述并不是一定存在的,这时我们可以通过id来获得控件,并添加点击。 123456789private void openPacket() { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if (nodeInfo != null) { AccessibilityNodeInfo nodeInfosById = findNodeInfosById(nodeInfo, "com.tencent.mm:id/ba_"); if (nodeInfosById !=null) { nodeInfosById.performAction(AccessibilityNodeInfo.ACTION_CLICK); } }} 这样,红包就打开了。","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"Android","slug":"Android","permalink":"https://laujiangtao.github.io/tags/Android/"}]},{"title":"FFmpeg笔记","slug":"FFmpeg笔记","date":"2020-12-25T03:17:18.000Z","updated":"2022-07-10T12:05:51.965Z","comments":false,"path":"2020/12/25/ffmpeg-note/","link":"","permalink":"https://laujiangtao.github.io/2020/12/25/ffmpeg-note/","excerpt":"","text":"Mac/Linux下编译ffmpeg./configure --prefix=/usr/local/ffmpeg --enable-debug=3 --disable-static --enable-shared --enable-libfdk-aac --enable-gpl --enable-nonfree参数含义: –prefix 指定安装路径 –enable-debug=3 打开调试模式,级别是3 –disable-static 关闭静态库 –enable-share 打开动态库 执行完会产生编译脚本make file 12make -j 4make install 参数含义: -j 表示同时有多个CPU并发进行最后安装到 –prefix指定的路径 HelloWorld12345#include<stdio.h>int main(int argc, char* atgv[]){ printf("Hello World!\\n"); return 0;} 编译命令: gcc/clang -g -o HelloWorld HelloWorld.c参数含义: gcc Linux下的命令clang Mac下的命令-g debug模式-o 要输出文件的名字,这里是HelloWorldxxx.c 源代码执行命令: ./HelloWorld 命令方式采集音频Mac系统 ffmpeg -f avfoundation -i :0 out.wavLinux系统 ffmpeg -f asla -i hw:0 out.wav 播放音频 ffplay -ar 44100 -ac 2 -f f32le audio.pcm 使用ffmpeg生成aac ffmpeg -i xxx.mp4 -vn -c:a libfdk_aac -ar 44100 -channels 2 -profile:a aac_he_v2 xxx.aac参数含义: -i 输入的文件-vn v代表video,n代表没有video,vn可以将视频过滤掉-c:a c代表编码器,a代表音频编码器-ar 音频采样率-channels 代表通道数-profile:a 对编码器libfdk_aac设置参数,:a是对音频的编码 缺少库安装: brew reinstall ffmpeg --with-sdl2 --with-fdk-aac --with-fontconfig brew install --with-sdl2 --with-fdk-aac --with-fontconfig --with-frei0r --with-game-music-emu --with-libass --with-libbs2b --with-libcaca --with-libgsm --with-libmodplug --with-librsvg --with-libsoxr --with-libssh --with-libvidstab --with-libvorbis --with-libvpx --with-opencore-amr --with-openh264 --with-openjpeg --with-openssl --with-opus --with-rtmpdump --with-rubberband --with-sdl2 --with-snapp --with-speex --with-srt --with-tesseract --with-theora --with-tools --with-two-lame --with-wavpack --with-webp --with-x265 --with-xz --with-zeromq --with-zimg --with-chromaprint --with-libbluray --with-snappy --with-freetype 剪切视频(长度) ffmpeg -ss 990 -i input.mp4 -t 50 output.mp4 -ss 开始的秒数-t 截取时长-s 调整分辨率 剪切视频 (尺寸、长度)1ffmpeg -i input -vf crop=w:h:x:y -ss start_time -t duration output Syntax crop=ow[:oh[:x[:y[:keep_aspect]]]] Avaliable variables in expressions for ow and oh parameters x, y computed values for x (number of pixels from top left corner horizontally) and y (number of pixels vertically), they are evaluated for every frame, default values of x is (iw -ow)/2, default value of y is (ih - oh)/2 in_w, iw input width in_h, ih input height out_w, ow output(cropped) width, default value = iw out_h, oh output(cropped) heigth, default value = ih a aspect ratio, same as iw/ih sar input same aspect ratio dar input display aspect ratio, equals to the expression a*sar hsub, vsub horizontal and vertical chroma subsample values, for the pixel format yuv422p the value of hsub is 2 and vsub is 1 n number of input frame, starting from 0 pos position in the file of the input frame, NAN if unknown t timestamp expressed in seconds, NAN if the input timestamp is unknown Example: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485PS E:\\Download> ffmpeg -i .\\权力的游戏.第一季.第1集.Game.of.Thrones.S01E01.HK.BD-1080p.X264.AAC.CHS.ENG-UUMp4.mp4 -vf crop=600:800:iw/6:ih/6 -ss 300 -t 60 out.mp4ffmpeg version 4.3.2-2021-02-27-full_build-www.gyan.dev Copyright (c) 2000-2021 the FFmpeg developers built with gcc 10.2.0 (Rev6, Built by MSYS2 project) configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-lzma --enable-libsnappy --enable-zlib --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libdav1d --enable-libzvbi --enable-librav1e --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint libavutil 56. 51.100 / 56. 51.100 libavcodec 58. 91.100 / 58. 91.100 libavformat 58. 45.100 / 58. 45.100 libavdevice 58. 10.100 / 58. 10.100 libavfilter 7. 85.100 / 7. 85.100 libswscale 5. 7.100 / 5. 7.100 libswresample 3. 7.100 / 3. 7.100 libpostproc 55. 7.100 / 55. 7.100Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '.\\权力的游戏.第一季.第1集.Game.of.Thrones.S01E01.HK.BD-1080p.X264.AAC.CHS.ENG-UUMp4.mp4': Metadata: major_brand : mp42 minor_version : 1 compatible_brands: isommp423gp5 creation_time : 2019-05-13T16:22:44.000000Z encoder : My MP4Box GUI 0.6.0.6 <http://my-mp4box-gui.zymichost.com> Duration: 01:01:37.17, start: 0.000000, bitrate: 2586 kb/s Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 2485 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default) Metadata: creation_time : 2019-05-08T18:52:18.000000Z handler_name : ȨfµÄÓÎϷ.µÚһ�¾.µÚ1�¯.Game.of.Thrones.S01E01.HK.BD-1080p.X264.AAC.CHS.ENG-UUMp4.mp4 encoder : AVC Coding Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 97 kb/s (default) Metadata: creation_time : 2019-05-13T16:23:09.000000Z handler_name : Game.of.Thrones.S01E01.HK.Blu-ray.1080p.Remux.H264.DTS-HD.MA.5.1.aac Stream #0:2(und): Data: none (mp4s / 0x7334706D), 0 kb/s (default) Metadata: creation_time : 2019-05-13T16:23:11.000000Z handler_name : GPAC MPEG-4 OD Handler Stream #0:3(und): Data: none (mp4s / 0x7334706D), 0 kb/s (default) Metadata: creation_time : 2019-05-13T16:23:11.000000Z handler_name : GPAC MPEG-4 Scene Description HandlerFile 'out.mp4' already exists. Overwrite? [y/N] yStream mapping: Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264)) Stream #0:1 -> #0:1 (aac (native) -> aac (native))Press [q] to stop, [?] for help[libx264 @ 00000155b6ac5980] using SAR=1/1[libx264 @ 00000155b6ac5980] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2[libx264 @ 00000155b6ac5980] profile High, level 3.1, 4:2:0, 8-bit[libx264 @ 00000155b6ac5980] 264 - core 161 r3048 b86ae3c - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=23 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00Output #0, mp4, to 'out.mp4': Metadata: major_brand : mp42 minor_version : 1 compatible_brands: isommp423gp5 encoder : Lavf58.45.100 Stream #0:0(und): Video: h264 (libx264) (avc1 / 0x31637661), yuv420p, 600x800 [SAR 1:1 DAR 3:4], q=-1--1, 23.98 fps, 24k tbn, 23.98 tbc (default) Metadata: creation_time : 2019-05-08T18:52:18.000000Z handler_name : ȨfµÄÓÎϷ.µÚһ�¾.µÚ1�¯.Game.of.Thrones.S01E01.HK.BD-1080p.X264.AAC.CHS.ENG-UUMp4.mp4 encoder : Lavc58.91.100 libx264 Side data: cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default) Metadata: creation_time : 2019-05-13T16:23:09.000000Z handler_name : Game.of.Thrones.S01E01.HK.Blu-ray.1080p.Remux.H264.DTS-HD.MA.5.1.aac encoder : Lavc58.91.100 aacframe= 1439 fps= 48 q=-1.0 Lsize= 5577kB time=00:01:00.01 bitrate= 761.3kbits/s speed=1.99xvideo:4594kB audio:942kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.744073%[libx264 @ 00000155b6ac5980] frame I:19 Avg QP:18.65 size: 26467[libx264 @ 00000155b6ac5980] frame P:480 Avg QP:20.83 size: 5503[libx264 @ 00000155b6ac5980] frame B:940 Avg QP:22.86 size: 1658[libx264 @ 00000155b6ac5980] consecutive B-frames: 9.5% 8.6% 4.6% 77.3%[libx264 @ 00000155b6ac5980] mb I I16..4: 13.6% 77.6% 8.8%[libx264 @ 00000155b6ac5980] mb P I16..4: 6.2% 12.2% 0.4% P16..4: 41.1% 8.5% 5.4% 0.0% 0.0% skip:26.2%[libx264 @ 00000155b6ac5980] mb B I16..4: 0.7% 0.7% 0.0% B16..8: 36.9% 1.7% 0.2% direct: 1.0% skip:58.7% L0:45.0% L1:53.6% BI: 1.4%[libx264 @ 00000155b6ac5980] 8x8 transform intra:65.0% inter:88.4%[libx264 @ 00000155b6ac5980] coded y,uvDC,uvAC intra: 35.4% 42.5% 4.1% inter: 9.1% 13.9% 0.0%[libx264 @ 00000155b6ac5980] i16 v,h,dc,p: 38% 23% 16% 24%[libx264 @ 00000155b6ac5980] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 22% 18% 37% 4% 4% 4% 4% 4% 4%[libx264 @ 00000155b6ac5980] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 22% 19% 18% 6% 9% 7% 8% 5% 5%[libx264 @ 00000155b6ac5980] i8c dc,h,v,p: 64% 16% 19% 1%[libx264 @ 00000155b6ac5980] Weighted P-Frames: Y:0.4% UV:0.4%[libx264 @ 00000155b6ac5980] ref P L0: 69.3% 9.9% 15.6% 5.2% 0.0%[libx264 @ 00000155b6ac5980] ref B L0: 88.0% 9.7% 2.2%[libx264 @ 00000155b6ac5980] ref B L1: 96.9% 3.1%[libx264 @ 00000155b6ac5980] kb/s:626.89[aac @ 00000155b6abfb40] Qavg: 163.868","categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"}],"tags":[{"name":"FFmpeg","slug":"FFmpeg","permalink":"https://laujiangtao.github.io/tags/FFmpeg/"}]}],"categories":[{"name":"技术","slug":"技术","permalink":"https://laujiangtao.github.io/categories/%E6%8A%80%E6%9C%AF/"},{"name":"生活","slug":"生活","permalink":"https://laujiangtao.github.io/categories/%E7%94%9F%E6%B4%BB/"},{"name":"Question","slug":"Question","permalink":"https://laujiangtao.github.io/categories/Question/"}],"tags":[{"name":"MQTT","slug":"MQTT","permalink":"https://laujiangtao.github.io/tags/MQTT/"},{"name":"java","slug":"java","permalink":"https://laujiangtao.github.io/tags/java/"},{"name":"C","slug":"C","permalink":"https://laujiangtao.github.io/tags/C/"},{"name":"Android","slug":"Android","permalink":"https://laujiangtao.github.io/tags/Android/"},{"name":"自定义View","slug":"自定义View","permalink":"https://laujiangtao.github.io/tags/%E8%87%AA%E5%AE%9A%E4%B9%89View/"},{"name":"jekins","slug":"jekins","permalink":"https://laujiangtao.github.io/tags/jekins/"},{"name":"flutter","slug":"flutter","permalink":"https://laujiangtao.github.io/tags/flutter/"},{"name":"mysql","slug":"mysql","permalink":"https://laujiangtao.github.io/tags/mysql/"},{"name":"C++","slug":"C","permalink":"https://laujiangtao.github.io/tags/C/"},{"name":"audio","slug":"audio","permalink":"https://laujiangtao.github.io/tags/audio/"},{"name":"Kotlin","slug":"Kotlin","permalink":"https://laujiangtao.github.io/tags/Kotlin/"},{"name":"Code","slug":"Code","permalink":"https://laujiangtao.github.io/tags/Code/"},{"name":"Terminal","slug":"Terminal","permalink":"https://laujiangtao.github.io/tags/Terminal/"},{"name":"Java","slug":"Java","permalink":"https://laujiangtao.github.io/tags/Java/"},{"name":"FFmpeg","slug":"FFmpeg","permalink":"https://laujiangtao.github.io/tags/FFmpeg/"}]}