Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper ‘-jvm-target’ option

在fragment中使用如下代码,报错

import androidx.fragment.app.viewModels

private val levelMapViewModel: LevelMapViewModel by viewModels()

1.添加依赖

implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'

2.添加设置

kotlinOptions {
    // work-runtime-ktx 2.1.0 and above now requires Java 8
    jvmTarget = "1.8"
}

3.查看IDE

更新Gradle,解决!

Processing did not complete. See error above for details

在使用HiltAndroidApp时报错 Processing did not complete. See error above for details 解决方法

报错如图
   Task :app:kaptDebugKotlin FAILED
   /Users/luchanglong/work/android/Pinpintu/app/build/tmp/kapt3/stubs/debug/com/ppt/pinpintu/PptApp.java:19: 错误: [Hilt]
   public final class PptApp extends android.app.Application {
                ^
     Expected @HiltAndroidApp to have a value. Did you forget to apply the Gradle Plugin? (dagger.hilt.android.plugin)
     See https://dagger.dev/hilt/gradle-setup.html
     [Hilt] Processing did not complete. See error above for details.警告: Binding adapter AK(android.widget.ImageView, java.lang.String) already exists for imageShowUrl! Overriding com.ppt.pinpintu.db.entity.RecordEntity#imageShowUrl with com.ppt.pinpintu.db.entity.ImageEntity#imageShowUrl警告: Binding adapter AK(android.widget.ImageView, java.lang.String) already exists for imageShowUrl! Overriding com.ppt.pinpintu.db.entity.RecordEntity#imageShowUrl with com.ppt.pinpintu.db.entity.ImageEntity#imageShowUrl警告: Binding adapter AK(android.widget.ImageView, java.lang.String) already exists for imageShowUrl! Overriding com.ppt.pinpintu.db.entity.ImageEntity#imageShowUrl with com.ppt.pinpintu.db.entity.Report#imageShowUrl警告: Binding adapter AK(android.widget.ImageView, java.lang.String) already exists for imageShowUrl! Overriding com.ppt.pinpintu.db.entity.ImageEntity#imageShowUrl with com.ppt.pinpintu.db.entity.Report#imageShowUrl[WARN] Incremental annotation processing requested, but support is disabled because the following processors are not incremental: androidx.room.RoomProcessor (DYNAMIC). 
 Execution failed for task ':app:kaptDebugKotlin'.
   A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
   java.lang.reflect.InvocationTargetException (no error message) 

解决:在app/build.gradle 中将有arguments =xxxx 改为 arguments +=

如:
javaCompileOptions {
    annotationProcessorOptions {
        arguments += ["room.schemaLocation":
                             "$projectDir/schemas".toString()]
    }
}

Android Java 使用SoundPool 播放各种音效


import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import android.util.SparseArray;


/**
 * 点击声音类
 */
public class SoundManager {

    private static SoundManager mSoundManager;
    private SoundPool mSoundPool;
    private AudioManager mAudioManager;
    private SparseArray<Integer> mSoundPoolMap;
    public static final int maxSounds = 4;

    public static SoundManager getInstance(Context context) {
        if (mSoundManager == null) {
            mSoundManager = new SoundManager(context);
        }

        return mSoundManager;
    }

    public SoundManager(Context mContext) {
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        mSoundPool = new SoundPool(maxSounds, AudioManager.STREAM_MUSIC, 0);

        mSoundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
            public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
                boolean loaded = true;
            }
        });

        mSoundPoolMap = new SparseArray<Integer>();
        mSoundPoolMap.put(0, mSoundPool.load(mContext, R.raw.click, 0));
        mSoundPoolMap.put(1, mSoundPool.load(mContext, R.raw.success, 1));
        mSoundPoolMap.put(2, mSoundPool.load(mContext, R.raw.change, 2));
        mSoundPoolMap.put(3, mSoundPool.load(mContext, R.raw.click_up, 3));
        mSoundPoolMap.put(4, mSoundPool.load(mContext, R.raw.click_down, 4));
    }

    public void playSound(int index) {
        int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume,
                1, 0, 1f);
    }

    public static void clear() {
        if (mSoundManager != null) {
            mSoundManager.mSoundPool = null;
            mSoundManager.mAudioManager = null;
            mSoundManager.mSoundPoolMap = null;
        }
        mSoundManager = null;
    }
}



//使用

SoundManager soundManagerr = new SoundManager(getContext());
soundManager.playSound(index);

android java 通过MusicService 播放背景音乐

1.在manifests.xml中添加
<service android:name=".utils.MusicService"  android:enabled="true"/>


2.新建一个音乐类

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;


/**
 * MusicService 控制后台音乐播放
 * 提供控制方法,在Activity中调用
 */
/*

public class MusicService extends Service implements MediaPlayer.OnErrorListener {
    private final IBinder mBinder = new ServiceBinder(); //在Activity 使用Ibinder 对象与 MusicService 进行交互
    MediaPlayer mediaPlayer;
    private int length = 0;

    public MusicService() {
        //这里可以执行一些初始化
    }

    public class ServiceBinder extends Binder {
        public MusicService getService() {
            return MusicService.this;
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        mediaPlayer = MediaPlayer.create(this, R.raw.bg_main);
        mediaPlayer.setOnErrorListener(this);

        if (mediaPlayer != null) {
            mediaPlayer.setLooping(true);
            mediaPlayer.setVolume(100, 100);
        }


        mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {

            public boolean onError(MediaPlayer mp, int what, int
                    extra) {

                onError(mediaPlayer, what, extra);
                return true;
            }
        });
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mediaPlayer.start();
        return START_STICKY;
    }

    public void pauseMusic() {
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
            length = mediaPlayer.getCurrentPosition();

        }
    }

    public void resumeMusic() {
        if (mediaPlayer.isPlaying() == false) {
            mediaPlayer.seekTo(length);
            mediaPlayer.start();
        }
    }

    public void stopMusic() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    @Override
    public boolean stopService(Intent name) {
        mediaPlayer.stop();
        mediaPlayer.release();
        return super.stopService(name);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mediaPlayer != null) {
            try {
                mediaPlayer.stop();
                mediaPlayer.release();
            } finally {
                mediaPlayer = null;
            }
        }
    }

    public boolean onError(MediaPlayer mp, int what, int extra) {
        Common.showShort(this,String.valueOf(R.string.music_player_fail));
        if (mediaPlayer != null) {
            try {
                mediaPlayer.stop();
                mediaPlayer.release();
            } finally {
                mediaPlayer = null;
            }
        }
        return false;
    }


/*调用说明:
开始、暂停、继续播放、停止 音乐:
step 1:在 activity 的onCreate 中调用 doBindService 来将 service 绑定到 activity
Step 2: 通过Intent 启动service
        Intent music = new Intent();
        music.setClass(this,MusicService.class);
        startService(music);
Step 3: 在 activity中调用 pause, resume 或者 stop 来控制音乐:
        mServ.pauseMusic();
        mServ.resumeMusic();
        mServ.stopMusic();
Step 4: 在Activity 的onDestroy 中调用 doUnbindService

  以下内容写在Activity中:
*/

    private boolean mIsBound = false;
    private MusicService mServ;
    private ServiceConnection Scon =new ServiceConnection(){

        public void onServiceConnected(ComponentName name, IBinder
                binder) {
            mServ = ((MusicService.ServiceBinder)binder).getService();
        }

        public void onServiceDisconnected(ComponentName name) {
            mServ = null;
        }
    };
    void doBindService(){
        bindService(new Intent(this,MusicService.class),
                Scon,Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }
    void doUnbindService()
    {
        if(mIsBound)
        {
            unbindService(Scon);
            mIsBound = false;
        }
    }

andorid studio / phpstrom copyright 模板

打开设置面板,在copyright中添加内容:

$file.className
Last modified $file.lastModified
Author Mrluchanglong@163.com
Copyright (c) $today.year. All rights reserved. 版权所有

新建文件就会自动生成声明信息,或在文件中就可以使用「右键」->gnerate->Copyright:

go的表单验证

必填字段

Go有一个内置函数len可以获取字符串的长度,这样我们就可以通过len来获取数据的长度,例如:

if len(r.Form["username"][0])==0{
	//为空的处理
}

r.Form对不同类型的表单元素的留空有不同的处理, 对于空文本框、空文本区域以及文件上传,元素的值为空值,而如果是未选中的复选框和单选按钮,则根本不会在r.Form中产生相应条目,如果我们用上面例子中的方式去获取数据时程序就会报错。所以我们需要通过r.Form.Get()来获取值,因为如果字段不存在,通过该方式获取的是空值。但是通过r.Form.Get()只能获取单个的值,如果是map的值,必须通过上面的方式来获取。

数字

你想要确保一个表单输入框中获取的只能是数字,例如,你想通过表单获取某个人的具体年龄是50岁还是10岁,而不是像“一把年纪了”或“年轻着呢”这种描述

如果我们是判断正整数,那么我们先转化成int类型,然后进行处理

getint,err:=strconv.Atoi(r.Form.Get("age"))
if err!=nil{
	//数字转化出错了,那么可能就不是数字
}

//接下来就可以判断这个数字的大小范围了
if getint >100 {
	//太大了
}

还有一种方式就是正则匹配的方式

if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
	return false
}

对于性能要求很高的用户来说,这是一个老生常谈的问题了,他们认为应该尽量避免使用正则表达式,因为使用正则表达式的速度会比较慢。但是在目前机器性能那么强劲的情况下,对于这种简单的正则表达式效率和类型转换函数是没有什么差别的。如果你对正则表达式很熟悉,而且你在其它语言中也在使用它,那么在Go里面使用正则表达式将是一个便利的方式。

Go实现的正则是RE2,所有的字符都是UTF-8编码的。

中文

有时候我们想通过表单元素获取一个用户的中文名字,但是又为了保证获取的是正确的中文,我们需要进行验证,而不是用户随便的一些输入。对于中文我们目前有两种方式来验证,可以使用 unicode 包提供的 func Is(rangeTab *RangeTable, r rune) bool 来验证,也可以使用正则方式来验证,这里使用最简单的正则方式,如下代码所示

if m, _ := regexp.MatchString("^\\p{Han}+$", r.Form.Get("realname")); !m {
	return false
}

英文

我们期望通过表单元素获取一个英文值,例如我们想知道一个用户的英文名,应该是astaxie,而不是asta谢。

我们可以很简单的通过正则验证数据:

if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
	return false
}

电子邮件地址

你想知道用户输入的一个Email地址是否正确,通过如下这个方式可以验证:

if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m {
	fmt.Println("no")
}else{
	fmt.Println("yes")
}

手机号码

你想要判断用户输入的手机号码是否正确,通过正则也可以验证:

if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, r.Form.Get("mobile")); !m {
	return false
}

下拉菜单

如果我们想要判断表单里面<select>元素生成的下拉菜单中是否有被选中的项目。有些时候黑客可能会伪造这个下拉菜单不存在的值发送给你,那么如何判断这个值是否是我们预设的值呢?

我们的select可能是这样的一些元素

<select name="fruit">
<option value="apple">apple</option>
<option value="pear">pear</option>
<option value="banana">banana</option>
</select>

那么我们可以这样来验证

slice:=[]string{"apple","pear","banana"}

v := r.Form.Get("fruit")
for _, item := range slice {
	if item == v {
		return true
	}
}

return false

单选按钮

如果我们想要判断radio按钮是否有一个被选中了,我们页面的输出可能就是一个男、女性别的选择,但是也可能一个15岁大的无聊小孩,一手拿着http协议的书,另一只手通过telnet客户端向你的程序在发送请求呢,你设定的性别男值是1,女是2,他给你发送一个3,你的程序会出现异常吗?因此我们也需要像下拉菜单的判断方式类似,判断我们获取的值是我们预设的值,而不是额外的值。

<input type="radio" name="gender" value="1">男
<input type="radio" name="gender" value="2">女

那我们也可以类似下拉菜单的做法一样

slice:=[]string{"1","2"}

for _, v := range slice {
	if v == r.Form.Get("gender") {
		return true
	}
}
return false

复选框

有一项选择兴趣的复选框,你想确定用户选中的和你提供给用户选择的是同一个类型的数据。

<input type="checkbox" name="interest" value="football">足球
<input type="checkbox" name="interest" value="basketball">篮球
<input type="checkbox" name="interest" value="tennis">网球

对于复选框我们的验证和单选有点不一样,因为接收到的数据是一个slice

slice:=[]string{"football","basketball","tennis"}
a:=Slice_diff(r.Form["interest"],slice)
if a == nil{
	return true
}

return false

上面这个函数Slice_diff包含在我开源的一个库里面(操作slice和map的库),https://github.com/astaxie/beeku

日期和时间

你想确定用户填写的日期或时间是否有效。例如 ,用户在日程表中安排8月份的第45天开会,或者提供未来的某个时间作为生日。

Go里面提供了一个time的处理包,我们可以把用户的输入年月日转化成相应的时间,然后进行逻辑判断

t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
fmt.Printf("Go launched at %s\n", t.Local())

获取time之后我们就可以进行很多时间函数的操作。具体的判断就根据自己的需求调整。

身份证号码

如果我们想验证表单输入的是否是身份证,通过正则也可以方便的验证,但是身份证有15位和18位,我们两个都需要验证

//验证15位身份证,15位的是全部数字
if m, _ := regexp.MatchString(`^(\d{15})$`, r.Form.Get("usercard")); !m {
	return false
}

//验证18位身份证,18位前17位为数字,最后一位是校验位,可能为数字或字符X。
if m, _ := regexp.MatchString(`^(\d{17})([0-9]|X)$`, r.Form.Get("usercard")); !m {
	return false
}