AndroidStudio的几个个性的配置和个人笔记

本文围绕Android开发展开,涵盖AS个人设置、context使用、集合排序等多方面内容。介绍了升级AndroidStudio、屏幕适配、美颜SDK使用等问题的解决办法,还提及定位ANR、视频播放调试、Gradle依赖问题等,为Android开发者提供实用参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.AS的个人设置

2.在很多的工具类或者获取系统的一些组件时总是要用到context的

3.利用Collections.sort排序集合

4.升级到AndroidStudio4.0后,调试程序有时安装成功并启动了,但安装完成后想再次安装时就不会启动程序了(好像是安装成功了但没有启动)。

5.使用ScreenMatch适配,最近适配公司买的主板的机器,按照默认的去适配没有找到1080P的适配。所以只能看看有没有其他的方法了。

6.最近公司用到一个美颜的SDK,主要接触到OPENEGL不怎么多。但公司的需求是要一边预览实况,另一边在拍照后要在预览的情况下在后台对图片进行美颜。

7.使用DialogFragment显示时,虚拟按钮显示的情况。查找资料都说在Dialog在显示前隐藏虚拟按钮的。

8.接口里的方法可选择重写或者不重写:

9.定位ANR

10.调试视频播放时,在网上找到的两个视频播放链接:

11.Gradle's dependency cache may be corrupt

12.使用Glide加载本地图片的问题:(问题出现在android7.1系统的设备)

13.获取手机卡序列号(iccid1):

14.关于DB模式

15.TextToSpeech根据文字读出声音(好像之前的系统都不支持中文的,我现在用的Android10的系统是可以读出中文的)

16.Kotlin实体类的构造方法

17.将As4.0版本升级到AS4.1.3版本提示JRE没有此方法。

18.使用SpannableStringBuilder对TextView显示的字符串设置不同的颜色。

19.Kotiln中使用到懒加载后,判断是否已初始化。

20.使用TextView显示扑克牌的Unicode花色时,花色会按黑红黑红的颜色显示了。

21.AndroidStudio依赖的包文件报红(程序可以运行,但就是想通过点击代码到这个类就不行)

22.将Assets里的数据库转存到程序本地数据库文件夹,再将其数据转移到相应的表。(LitePal数据库)

23.根据包名获取第三方应用的启动页。

24混淆Library和jni

25.AndroidStudio中的Gradle打包中没有相关的Tasks任务。

26.以某点为圆点旋转,得到新的坐标点位置。

27.混淆处理过程中,不混淆aar第三方库。

28.使用Cursor扫描指定后缀名的文件。

29.使用Socket在局域网WIFI情况TCP传输h264数据引出的BUG。 

30.BottomNavigationView挡住了Fragment的底部的显示1).在每个Fragement的最外层添加如下


1.AS的个人设置

Appearance 设置主题Darcula(黑色)
Editor->General->Code Completion->选择none模糊匹配。(AndroidStudio4.0.1版本直接不点击match case 即为模糊匹配)
Editor->Color Scheme->Console Font         设置控制台的大小字体
Editor->Color Scheme->Color Scheme Font 设置代码的大小字体

点击打开项目文件时,左侧树形会跳转到相应的位置:在项目右侧的设置图标中勾选“Always Select Opened File”即可。

keymap设置快捷键

公司使用SVN来保存和管理代码:但忽悠文件又没写好,如.gradle和.idea里的文件会经常变动的。所以在AS中主动设置忽略:Editor--》File Types  -->Ignored Files And Folders里添加即可。

2.在很多的工具类或者获取系统的一些组件时总是要用到context的

考虑对象在内存的使用问题,一般来说都会首先使用getApplicationContext()或者getContext()。

但如果可能的话最后看看后面是否会调用Context去跳转页面或者其他有关UI的(如Dialog

的使用之类的),最好在Activity中传的参数是:*Activity.this。

因为在项目中遇到传getApplicationContext()或者getContext()这两个参数的时候,部分机型也是没问题,但用户不可能会使用指点的机型的。所以就会出现问题了。(遇见的情况就是使用有盾云慧眼的人脸识别时,封装成一个工具来使用时,传的参数不是Activity对象时,有些机型会出问题会不能跳转到人脸识别的页面。)

集合元素交换位置

//                // 要遍历的次数
//                for (int i = 0; i < bluetoothRssis.size() - 1; i++) {
//                    // 从后向前依次的比较相邻两个数的大小,遍历一次后,把数组中第i小的数放在第i个位置上
//                    for (int j = bluetoothRssis.size() - 1; j > i; j--) {
//                        // 比较相邻的元素,如果前面的数小于后面的数,则交换
//                        if (bluetoothRssis.get(j - 1).getRssi() < bluetoothRssis.get(j).getRssi()) {
//                            Collections.swap(bluetoothRssis, (j - 1), j);
//                        }
//                    }
//                }

3.利用Collections.sort排序集合

return 0不处理,return -1表示向前移一位,return表示向后移一位。

4.升级到AndroidStudio4.0后,调试程序有时安装成功并启动了,但安装完成后想再次安装时就不会启动程序了(好像是安装成功了但没有启动)。

上网找原因以为是Install Run的原因。

但在AndroidStudio3.5之后Install Run就已经取消了。

网上也有说是那个类似于热修复的问题安装(apply Changes)。

最终在另一个博客的评论找到答案,记录一下:3.5版以后的应该都可以在Editor-->Inspections里面可以找到,不过名字变成了Thread issues了,点击打钩,后面再搞搞问题还是没解决这好像与手机系统有点问题的。在模拟器和小米手机中都没出现这种现象,在华为手机上出现此问题的。

5.使用ScreenMatch适配,最近适配公司买的主板的机器,按照默认的去适配没有找到1080P的适配。所以只能看看有没有其他的方法了。

最后看到Android屏幕适配(一):ScreenMatch官方屏幕适配方案_feng海涛-CSDN博客

里的方法就行了。

 

DisplayMetrics d = getResources().getDisplayMetrics();
Log.d(Const.TAG, "数据dpi d.density:" + d.density);
Log.d(Const.TAG, "数据dpi densityDpi:" + d.densityDpi);
Log.d(Const.TAG, "数据dpi heightPixels:" + d.heightPixels);
Log.d(Const.TAG, "数据dpi scaledDensity:" + d.scaledDensity);

Log.d(Const.TAG, "数据dpi widthPixels:" + d.widthPixels);//我在项目中看到用这个分辨率的,所以加上1080的

Log.d(Const.TAG, "数据dpi xdpi:" + d.xdpi);
Log.d(Const.TAG, "数据dpi ydpi:" + d.ydpi);

Configuration config = getResources().getConfiguration();
int  smallestScreenWidth = config.smallestScreenWidthDp;//最小宽度DP值(表明使用哪个SW)
Log.d("Const.TAG", "数据dpi smallestScreenWidth:" + smallestScreenWidth);


å¨è¿éæå¥å¾çæè¿°

按提示加上或者去除不需要的分辨率即可。

 StringBuilder swdef = new StringBuilder();
//        PrintWriter out;

        try {

            swdef.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                    "<resources>");

            double value;
            for (int i = 0; i < 500; i++) {
                //这里控制对应转换的值,如果是标准尺寸就一对一转换
                value = (i + 1) * 1;
                value = Math.round(value * 100) / 100;
                swdef.append("<dimen name=\"dp" + (i + 1) + "\">" + value + "dp</dimen>\r\n");
            }
            swdef.append("</resources>");

            //这里是文件名,注意修改 sw 后面的值,和转换值一一对应
            String filedef = "./app/src/main/res/values-sw380dp/dimens.xml";
        System.out.println(swdef.toString());

//            out = new PrintWriter(new BufferedWriter(new FileWriter(filedef)));
//
//            out.println(swdef.toString());
//
//
//            out.close();

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

        }

6.最近公司用到一个美颜的SDK,主要接触到OPENEGL不怎么多。但公司的需求是要一边预览实况,另一边在拍照后要在预览的情况下在后台对图片进行美颜。

但SDK里只有单独对图片,或者单独实况预览的。

(1)刚开始的思路是建立一个glsurfaceview在前台(实况控件在这个控件之下,但这个控件只有1像素)。

但因为在渲染第二张图片时,他不会再重新走onSurfaceCreated(图片在这个方法传入的,暂时没想到有什么方法就用第二种了)。

(2)所以想着用代码加控件的,但因为公司安装程序的机器是安卓7.0系统的。网上都说用view.bringToFront()是放在前台的,手机上测试也是这样。但放到机器上安装时,就死活看不到。

只能再找找办法了。后来找到说是什么glsurfaceview的遮盖之类的,要用到glsurfaceview.setZOrderOnTop(true)才行。

最后图片是美颜成功了,但这个时间不好把控。只能SDK的技术支持了。

(3)新开一个渲染线程,再在线程中轮循一下(SDK的画帧的方法)。

除了自定义一个渲染线程外,其实也可以用新建的glsurfaceview中的渲染线程去处理的。这样的情况就不用怎么关心退出页面时的内存泄漏问题(因为这个控件我只是单纯使用他的渲染线程)。

不好的地方可能得放在前台去处理。

下面是自定义的渲染线程:

public class CustomRenderThread extends HandlerThread {
    private static final String TAG = "GLThread";
    private Handler mHandler;
    private EGLConfig eglConfig = null;
    private EGLDisplay eglDisplay = EGL14.EGL_NO_DISPLAY;
    private EGLContext eglContext = EGL14.EGL_NO_CONTEXT;
    private EGLSurface eglSurface = null;

    public CustomRenderThread() {
        super("GLRenderer");
    }

    /**
     * 创建OpenGL环境
     */
    private void createGL(){
        // 获取显示设备(默认的显示设备)
        eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        // 初始化
        int []version = new int[2];
        if (!EGL14.eglInitialize(eglDisplay, version,0,version,1)) {
            throw new RuntimeException("EGL error "+ EGL14.eglGetError());
        }
        // 获取FrameBuffer格式和能力
        int []configAttribs = {
                EGL14.EGL_BUFFER_SIZE, 32,
                EGL14.EGL_ALPHA_SIZE, 8,
                EGL14.EGL_BLUE_SIZE, 8,
                EGL14.EGL_GREEN_SIZE, 8,
                EGL14.EGL_RED_SIZE, 8,
                EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
                EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
                EGL14.EGL_NONE
        };
        int []numConfigs = new int[1];
        EGLConfig[]configs = new EGLConfig[1];
        if (!EGL14.eglChooseConfig(eglDisplay, configAttribs,0, configs, 0,configs.length, numConfigs,0)) {
            throw new RuntimeException("EGL error "+ EGL14.eglGetError());
        }
        eglConfig = configs[0];
        // 创建OpenGL上下文
        int []contextAttribs = {
                EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL14.EGL_NONE
        };
        eglContext = EGL14.eglCreateContext(eglDisplay, eglConfig, EGL14.EGL_NO_CONTEXT, contextAttribs,0);
        if(eglContext== EGL14.EGL_NO_CONTEXT) {
            throw new RuntimeException("EGL error "+ EGL14.eglGetError());
        }
        eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, eglConfig, configAttribs, 0);
        EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
    }

    /**
     * 销毁OpenGL环境
     */
    private void destroyGL(){
        EGL14.eglDestroySurface(eglDisplay, eglSurface);
        EGL14.eglDestroyContext(eglDisplay, eglContext);
        EGL14.eglReleaseThread();
        eglContext = EGL14.EGL_NO_CONTEXT;
        eglDisplay = EGL14.EGL_NO_DISPLAY;
        eglSurface=null;
        eglConfig=null;

    }

    @Override
    public synchronized void start() {
        super.start();
        mHandler = new Handler(getLooper());
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                createGL();
            }
        });
    }

    public void release(){
        if (null ==  mHandler)return;
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                
                destroyGL();
                quit();
                mHandler.removeCallbacksAndMessages(null);
            }
        });

    }


    public void post(Runnable runnable){
        if (null ==  mHandler)return;
        mHandler.post(runnable);
    }
}

建立对象,然后start,在需要使用的地方使用post方法即可。退出页面就执行release()。

7.使用DialogFragment显示时,虚拟按钮显示的情况。查找资料都说在Dialog在显示前隐藏虚拟按钮的。

(重点:原本隐藏的虚拟按钮为什么会显示呢,查找资料说是window的焦点从Activity变成Dialog了)
但我直接去隐藏虚拟按钮时,虚拟按钮的位置直接变黑了。
最后参考了:Android dialog 弹出时,保持 隐藏 navigation bar(虚拟栏)_大于弱智的博客-CSDN博客
在Dialog显示前设置

Window().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

显示后先隐藏虚拟按钮,再clearFlags。

因为DialogFragment相当于就是一个Fragment上加载一个Dialog似的。

所以去DialogFragment中查看一下源码。查看Dialog显示是在OnStart的,所以:

@Override
public void onStart() {
    if (getDialog()!=null){
        getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
    }
    super.onStart();
}

在显示后(可以监听OnShowListener获取显示的监听)

@Override
public void onShow(DialogInterface dialog) {
    DialogUtils.hideNavigationBar(getDialog().getWindow());
    getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
}
/**
 * 隐藏虚拟栏 ,显示的时候再隐藏掉
 * @param window
 */
static public void hideNavigationBar(Window window) {
    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    window.getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
        @Override
        public void onSystemUiVisibilityChange(int visibility) {
            int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                    //布局位于状态栏下方
                    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                    //全屏
                    View.SYSTEM_UI_FLAG_FULLSCREEN |
                    //隐藏导航栏
                    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
            if (Build.VERSION.SDK_INT >= 19) {
                uiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
            } else {
                uiOptions |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
            }
            window.getDecorView().setSystemUiVisibility(uiOptions);
        }
    });
}





8.接口里的方法可选择重写或者不重写:

因为default是在java8中引入的关键字,也可称为Virtual 
所以在接口方法加上这个关键词后,可以选择是否重写这个方法的。

public interface Test {
    void aa();
    default void bb(){
        Log.e("TAG", "bb: " );
    }
}

当普通类以Test为接口时,aa()为必须重写的方法。bb为可以选择重写也可以不重写的。

9.定位ANR

使用adb pull data/anr/traces.txt显示日志

搜索group="main"确定为主线程出问题的点(好像是搜索自己的包名和group="main"定位的,我遇到的ANR不知道是不是系统差造成的)。

10.调试视频播放时,在网上找到的两个视频播放链接:

http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4(1分钟)
http://vfx.mtime.cn/Video/2019/03/09/mp4/190309153658147087.mp4(1分34秒)

11.Gradle's dependency cache may be corrupt

遇到这个错误是打开旧项目,下载旧项目的gradle版本出问题了。(例如打开项目,然后下载。这个gradle只下载了一部分会提示解压失败的)

解决:1.在网上找这个项目相对应的gradle版本下载放到相应的位置。

2.将项目的gradle更改为最新的gradle。

3.最后都不想的话就去C:\Users\Administrator\.gradle\wrapper\dists自己电脑上的.gradle路径下删除gradle-4.6-all,再重新加载项目让AS慢慢下载了。

12.使用Glide加载本地图片的问题:(问题出现在android7.1系统的设备)

刚刚开始为了方便就直接

Glide.with(mContext).load(path).into(imageView);
这样的情况加载网络图片,看到完全没问题的,而在下载完图片到本地就加载本地的图片。

正常情况下在测试时都没看到出现问题,但同一个图片,有些设备在显示本地图片时可能会出现其他情况。

我这里当前的情况是下载的图片是完整的。但在显示的时候只显示了图片的四分之一,剩余部分就用灰色去填充了。

解决方法:

Glide.with(mContext).load(new File(path)).into(imageView);当要显示本地图片就得new file才能有点点保证没出现这个错。

13.获取手机卡序列号(iccid1):

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {

SubscriptionManager sm = SubscriptionManager.from(context);

@SuppressLint("MissingPermission") List sis = sm.getActiveSubscriptionInfoList();

SubscriptionInfo si = sis.get(0); String iccid = si.getIccId();

}else {

//获取sim卡信息

TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);

// @SuppressLint("MissingPermission") String deviceid = tm.getDeviceId();

@SuppressLint("MissingPermission") String tel = tm.getLine1Number();

@SuppressLint("MissingPermission") String iccid1 = tm.getSimSerialNumber();

@SuppressLint("MissingPermission") String imsi = tm.getSubscriberId();

int simState = tm.getSimState(); }

14.关于DB模式

createDeviceProtectedStorageContext()方法
Context deviceContext = context.createDeviceProtectedStorageContext();

因为Android7..0之后就有这个DB模式问题,最近搞华为SDK要用到这个的。

正常模式是可以访问到data/data/包名/share_pre的SharePerfener文件。但当手机重启且手机设置密码(没开锁)的情况,就访问不到这个文件夹,这样就会使程序无法访问相应的SP文件。

在相应的四大组件中加入

android:directBootAware="true"且监听开机广播。

这样的情况可以使程序可以在开机后运行

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
    Context deviceContext = mContext.createDeviceProtectedStorageContext();
    deviceContext.moveSharedPreferencesFrom(mContext, spName);
    sp = PreferenceManager.getDefaultSharedPreferences(deviceContext);
}else {
    sp = mContext.getSharedPreferences(spName, mode);
}

(只是针对自己的项目,做的笔记)

15.TextToSpeech根据文字读出声音(好像之前的系统都不支持中文的,我现在用的Android10的系统是可以读出中文的)

lateinit var textSpeect: TextToSpeech
textSpeect= TextToSpeech(this, TextToSpeech.OnInitListener {
    Log.e("TAG", "onCreate:$it " )
    if (it==TextToSpeech.SUCCESS){
        // 设置音调,值越大声音越尖(女生),值越小则变成男声,1.0是常规
        textSpeect.setPitch(1f)
        // 设置语速
        textSpeect.setSpeechRate(0.2f)
        var supported=textSpeect.setLanguage(Locale.CHINA)
        if ((supported!=TextToSpeech.LANG_AVAILABLE)&&(supported!=TextToSpeech.LANG_COUNTRY_AVAILABLE)) {
            Toast.makeText(applicationContext, "不支持当前语言!", Toast.LENGTH_SHORT).show();
        }
    }
})
textSpeect.speak("文字转语音    不支持当前语言",TextToSpeech.QUEUE_FLUSH,null)
读出声音(只有textSpeect初始化成功才可以读出这些文字的,比如在Android7中使用这代码,初始化是不成功的提示不支持当前语言的)

最后在OnStop中关闭

textSpeect.stop()
textSpeect.shutdown()

-》转载自:https://blog.csdn.net/qq_29449697/article/details/112466697
因为Android11 软件包可见性问题导致TextToSpeech初始化失败,只需要在AndroidManifest.xml文件中增加标签即可

转载自:TextToSpeech 因为Android11应用读取权限问题初始化失败_爱玩不变的博客-CSDN博客   《-

<queries>
    <intent>
        <action android:name="android.intent.action.TTS_SERVICE" />
    </intent>
</queries>

监听说的话是否完成:
 

textToSpeech.speak(lightBean?.title,TextToSpeech.QUEUE_FLUSH,null,"1")

textToSpeech.setOnUtteranceProgressListener(object : UtteranceProgressListener(){
    override fun onDone(utteranceId: String?) {
        Log.e("TAG", "onDone: $utteranceId" )
        when(utteranceId){
           "1","2"->{
              //todo 此处不能刷新UI,要更新UI得切换回UI线程
            }
        }
    }

    override fun onError(utteranceId: String?) {
        Log.e("TAG", "onError: $utteranceId" )
    }

    override fun onStart(utteranceId: String?) {
      Log.e("TAG", "onStart: $utteranceId" )
    }
})

speak时要加上utteranceId参数,若不加上则不会进入这个监听方法的。

如设置utteranceId为1,则在ondone中监听说完后的操作。

16.Kotlin实体类的构造方法

data class ResultLiveData(
    val testResult: TestResult?,
    val responseThrowable: ResponseThrowable?
){
    constructor(testResult: TestResult) : this(testResult,null)
    constructor(responseThrowable: ResponseThrowable) : this(null,responseThrowable)
}

放在类名之后的构造方法为主构造方法,之后定义的都是次构造方法。

如果要建次构造方法就得在之后加this :(name1,name2),调用主构造方法将其他的未在次构造方法的数据赋初始值。

17.将As4.0版本升级到AS4.1.3版本提示JRE没有此方法。

将C:\Users\ASUS\AppData\Roaming\Google里的AndroidStudio4.1删除即可,然后重新打开AS就行。

18.使用SpannableStringBuilder对TextView显示的字符串设置不同的颜色。

String timeStr=(timeCount-aLong)+"S";
binding.tvPrintPrintTimeText.setText(
        setSpannableText(
                getResources().getString(R.string.print_print_time_text,timeStr),
                timeStr,
                getResources().getColor(R.color._FF9600)));
/**
     * 在Textiview中对特定字符串进行改变颜色处理
     * @param str  要显示的字符串
     * @param changColorStr  显示中要改变颜色的字符串
     * @param colorId   显示的颜色ID
     * @return
     */
    private SpannableStringBuilder setSpannableText(String str,String changColorStr,int colorId){
        SpannableStringBuilder style = new SpannableStringBuilder(str);
        int start=str.indexOf(changColorStr);
        int end=start+changColorStr.length();
//        style.setSpan(new ForegroundColorSpan(Color.RED), 0, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        //设置颜色
        style.setSpan(new ForegroundColorSpan(colorId), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        //设置大小
//        style.setSpan(new RelativeSizeSpan(0.75f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        return style;
    }

当注释的两行也开启的效果图为:

RelativeSizeSpan设置的字体大小的比例(相对于TextView设置了TextSize的,比如我这个Textview设置了16sp,当设置为0.75f的比例时就相当将字体设置为12sp了)

19.Kotiln中使用到懒加载后,判断是否已初始化。

this::adapter.isInitialized

一定要加上::不然无法写出此方法。(在Kt中使用Bind的方法使用Service是经常使用到,所以记录下。)

20.使用TextView显示扑克牌的Unicode花色时,花色会按黑红黑红的颜色显示了。

符号UNICODE符号UNICODE
HTMLJSCSSHTMLJSCSS
&#9824\u2660\2660&#9827\u2663\2663
&#9829\u2665\2665&#9830\u2666\2666
&#9828\u2664\2664&#9831\u2667\2667
&#9825\u2661\2661&#9826\u2662\2662

显示前面四个时系统自己改变了颜色了。后面四个就会根据TextView的颜色改变而改变。

后来看了如何在TextView中更改unicode字符的颜色? - VoidCC

这个帖子看到在\u2665后面加上 \ uFE0E就会相当于随TextView的颜色改变而改变了,当然他也给出了另一个解决方案就是改变字体,使用一种显示花色而不会改变颜色的字体(没找着具体字体包下载就不搞了)

21.AndroidStudio依赖的包文件报红(程序可以运行,但就是想通过点击代码到这个类就不行)

4.1以下版本就是删除C盘中的用户文件夹下的.AndroidStudio3.6(对应版本)里的除config文件夹外的文件。

4.1及以上版本就删除C:\Users\Administrator\AppData\Local\Google中对应版本的文件夹即可。

22.将Assets里的数据库转存到程序本地数据库文件夹,再将其数据转移到相应的表。(LitePal数据库)
 

File dbFile=new File("/data/data/com.bwzn.demo/databases/","ToolCar.db");
boolean saveFile=false;
if (!dbFile.exists()){//复制数据
    saveFile= FileUtils.copyAssetsFile(getApplicationContext(),dbFile.getName(),dbFile.getParent());
}
if (dbFile.exists()){
    LitePalDB litePalDB = new LitePalDB("ToolCar", 4);
    litePalDB.addClassName(ToolEt.class.getName());
    LitePal.use(litePalDB);
    List<ToolEt> inventoryTool=LitePal.findAll(ToolEt.class);
    LogUtils.i(ToolEt.class.getName()+"===== "+inventoryTool.size());
    if (inventoryTool.size()>0){//todo 数据库有相应的表数据就将其转移
        SPUtils.save(ConstSpKeyData.FIRST_SAVE_DB,false);

        List<ConsumableDbBean> consumableBeanList=new ArrayList<>();
        for (ToolEt toolEt: inventoryTool){
            ConsumableDbBean bean=new ConsumableDbBean();
            bean.setLocation(toolEt.getLocation());
            bean.setName(toolEt.getDesp());
            bean.setEpc(toolEt.getBarcode());
            consumableBeanList.add(bean);
        }
       

        LitePal.useDefault();
        LitePal.saveAll(consumableBeanList);
    }
}

23.根据包名获取第三方应用的启动页。

(1)

Intent temp1= getActivity().getPackageManager().getLaunchIntentForPackage(packageName);
若没有启动页temp1为null的,不为null时可以打印temp1.getComponent()查看

(2)

Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> mAllApps=getActivity().getPackageManager().queryIntentActivities(mainIntent, 0);
//按包名排序
Collections.sort(mAllApps, new ResolveInfo.DisplayNameComparator(getActivity().getPackageManager()));
//遍历
for(ResolveInfo res : mAllApps) {
    if (res.activityInfo.packageName.equals(packageName)){
        LogUtils.e(" === "+res.activityInfo.name);
    }
}

24混淆Library和jni

混淆Library参考Android多模块混淆、多module混淆、多library混淆的正确姿势_逆境中徘徊的博客-CSDN博客_android library 混淆

自己在此记录一下:(1)混淆规则与app包基本一样。只是相应的依赖库或者内容不一样而已。

(2)app包开启混淆,Library默认与app包一致(app包开启,Library也开启)

配置:

在Library中build.gradle添加

buildTypes {
    debug{
        consumerProguardFiles "proguard-rules.pro"
    }
    release {
        consumerProguardFiles "proguard-rules.pro"
    }
}

因为我当前的Library很多都可以混淆,所以只是对实体类不混淆就行了。

jni的混淆:(网上一个个找的,反正后面就没发生问题就没简化了)

-keepclassmembers class com.*.*.HelloJni{*;}
-keep class com.*.*.HelloJni{*;}
-keepnames class com.*.*.HelloJni{*;}
-keepclassmembernames class com.*.*.HelloJni{*;}

# 设置所有 native 方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}
#不混淆类
-keepclasseswithmembernames class com.*.*.HelloJni {*;}
-keep class com.*.*.HelloJni  {
    native <methods>;
 }

==================

HelloJni 里包含

System.loadLibrary这个方法和相应的 native方法。

注意:如果使用SharedPreferences保存数据的话,在getSharedPreferences(name,Context.MODE_PRIVATE)中的name如果使用类名称作为sp保存的文件命名。直接使用字符串来代码,不要用使用SpUtils.class.getSimpleName,因为在混淆后Sputils的名称不一定是这样的了,有可能是X,所以混淆后与混淆前读取的sp数据会不一样(读取的文件就不一样,所以会有问题)。

25.AndroidStudio中的Gradle打包中没有相关的Tasks任务。


打开File---》Experimental----》看Gradle 将“Do not build gradle task list during gradle sync”的勾
去除,然后在File中点击“Sync Project  with Gradle Files”即可。

26.以某点为圆点旋转,得到新的坐标点位置。

Matrix matrix=new Matrix();
matrix.setRotate(90,getWidth() / 2.0f, getHeight() / 2.0f);
float point[] = {cx, cy};
matrix.mapPoints(point);

其中这里是以控件中心旋转的,如果以原点旋转可以写0或者不写也行。

cx,cy为旋转前的坐标,旋转后的坐标为point[0]和point[1]

27.混淆处理过程中,不混淆aar第三方库。

(直接使用-keep class 包名就可以不混淆aar第三方库了,以下的提示“key pair generate err” 只是因为引入的库接入时要依赖一个加密的库,在做混淆处理时,没有加这个混淆规则所以才会提示这个错误。下面的只是自己混淆过程中多加码的东西,不删除是让自己记住这个错而已)

因为引用的第三方库还没完善(一直开发中),而公司又要版本升级而且还要混淆。但第三方库没有混淆规则,故网上查了一堆都说-keep class包名是可以。加入这个包名的混淆规则,打包然后分析安装包,看着确实有第三方库的内容。但因为这个库是有一个包名对应的appid的(个人理解当这个是密钥),在混淆做一些操作失败后的提示为:“key pair generate err”。

所以这样混淆还是不行,只能再找找了。
说使用-libraryjars的,使用了没有作用。

说使用

#-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,Annotation,EnclosingMethod,MethodParametersclass
#
#-keep class **.R$* {*;}

的也没有作用。

最后看一下别的第三方的规则了:

看见除了-keey class 包名外。还有一个-dontwarn 包名的(

不提示警告 dontwarn是一个和keep可以说是形影不离,尤其是处理引入的library时.

如腾讯文档库的:(类似这样添加就不混淆第三方库的)

-keep class com.tencent.smtt.**

-keep class com.tencent.smtt.**{*;}
-dontwarn com.tencent.smtt.**

-keep class com.tencent.tbs.**{*;}

28.使用Cursor扫描指定后缀名的文件。

参考文章链接:Android查询扫描SD卡里指定后缀名称的文件_黄油奥特曼的博客-CSDN博客

String[] queryFileTypes={"%.pdf","%.txt","%.doc","%.docx","%.xlsx","%.xls","%.ppt","%.pptx","%.epub"};
List<String> files = new ArrayList<>();
for (String queryfileType : queryFileTypes){
     //每扫描一次得到相应后缀名的结果
    files.addAll(queryFiles(context,queryfileType)) ;
}
private List<String> queryFiles(Context context,String queryFileType){
    List<String> files = new ArrayList<>();
    String[] projection = new String[] { MediaStore.Files.FileColumns._ID,
            MediaStore.Files.FileColumns.DATA,
            MediaStore.Files.FileColumns.SIZE
    };
    //使用 MediaStore.Files.getContentUri("external")获取的URI同样是content://media/external/file
    Cursor cursor = context.getContentResolver().query(
            Uri.parse("content://media/external/file"),
            projection,
            MediaStore.Files.FileColumns.DATA + " like ?",
            new String[]{queryFileType},
            null);
    if (cursor != null) {
        if (cursor.moveToFirst()) {

            int idindex = cursor.getColumnIndex(MediaStore.Files.FileColumns._ID);
            int dataindex = cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA);
            int sizeindex = cursor.getColumnIndex(MediaStore.Files.FileColumns.SIZE);
            do {
                String id = cursor.getString(idindex);
                String path = cursor.getString(dataindex);
                String size = cursor.getString(sizeindex);
                File file=new File(path);
                //因为根据参考的文章的评论和实际结果:被删除的也可能被扫描到,还有一个情况是
//如果文件夹的命名也是类似于文件的命名,这里同样会扫描到的,所以过滤不相关的得到想要的结果
                if (file.exists() && file.isFile()){   
                    files.add(path);
                }
            } while (cursor.moveToNext());
        }
        cursor.close();
    }
    return files;
}

29.使用Socket在局域网WIFI情况TCP传输h264数据引出的BUG。 


使用场景是线程采集到数据,然后进行H264编码,相应的sps和pps进行静态保存到同一个数组中。因为预定不是只有一个端接收的,当另一个后面接入的端也得有sps和pps才能渲染出相应的画面。(项目还集成了封装rtmp数据的,我处理的方式是编码后的数据,SOCKET的拿到数据做相关处理,RTMP也是拿这个编码后数据进行处理,相应是将数据COPY为两份去处理)

出现问题:是先推送数组给其他方,再用另一个局域网手机进行连接播放页面出现bug的。

(1)接收端使用DataInputStream接收数据,在使用ReadInt方法要么接收到的数值为负数,要么就是数值很大,因为在接收后还创建了byte数组进行接收数据的,负数过滤好像是这样,但数值过大的创建数组就会造成OOM了,所以相关头痛治头,脚疼医脚的道理。加一个过滤条件吧。

条件为:接收的int大于0 且 小于等于Socket最大接收的值(在连接Socket时设置的了)

设置条件后的现象:程序不崩,也不OOM了。但画面严重卡顿,因为很多不符合过滤条件的数据都被过滤了,所以画面卡。

(2)继续在上面的条件上查找解决方法:有些方案是使用skipBytes跳过几个字节,但又说每个设备跳过的字节都不一样。这坑让我无法跨过啊。直接写跳过8个字节,现象是画面更卡了。

(3)既然接收端无法解决就看一下发送端了:DataOutputStream先是writeInt 然后flush再写write(byte[]) flush,改一下为write(byte[] ,0,date.length)。结果还是那样。

(4)再看一下发送的数据(int type=data[4] &0x1f 根据这个type来判断是正式数据或者sps的数组或者 是其他数据):sps和Pps的数组(类型7或者类型8)与视频正常数据(类型为5)每次都是先发sps的数组再发正式数据的(出现接收错乱的是这里造成的),想着改一下,将sps数组与正式数据合并为一个数组再发送,然后上面修改我还保留着(不想因为数值的问题造成SOCKET断开连接之类的,所以保留下来而已)。现象正常接收和播放了。

30.BottomNavigationView挡住了Fragment的底部的显示

1).在每个Fragement的最外层添加如下

android:paddingBottom="?attr/actionBarSize"
android:clipToPadding="false"

参考:[Android] [解决]Bottom Navigation Views Activity工程带来的fragment底部遮盖的问题_android bottomnavigationview 遮挡内容-CSDN博客

2).在Activity中设置高度问题。
控件fragment的高度直接设置为0db,而不是match_parent.
参考:BottomNavigationView底部导航遮挡fragment解决方法_bottomnavigationview 遮挡-CSDN博客

31.运行测试代码
1.环境配置
testImplementation 'androidx.test:core-ktx:1.5.0'
testImplementation 'org.robolectric:robolectric:4.10.3'
类的顶部添加环境
@RunWith( RobolectricTestRunner::class)
@Config(manifest = Config.NONE ,sdk = [Build.VERSION_CODES.P])
class CustomConfigTest{

@Test
fun test(){
    val context: Context = ApplicationProvider.getApplicationContext()
    //设置获取资源的语言环境
    val resources: Resources = RuntimeEnvironment.application.resources
    val config = Configuration(resources.getConfiguration())
    config.setLocale(Locale.SIMPLIFIED_CHINESE) // 设置为简体

    resources.updateConfiguration(config, resources.getDisplayMetrics())

    AppConfigUtil.dealGenJson(context)
}
}

2.将相应内容生成文件
@Throws(IOException::class)
fun generateFileInProjectDir(jsonStr: String, jsonFileName: String) {
    // 获取项目根目录
    val projectDir = System.getProperty("user.dir")

    // 创建测试输出目录
    val outputDir = File("$projectDir/test-output")
    if (!outputDir.exists()) {
        outputDir.mkdirs()
    }

    // 创建文件并写入内容
    val outputFile = File(outputDir, jsonFileName)
    FileWriter(outputFile).use { writer ->
        writer.write(jsonStr)
    }
    println("文件已生成: " + outputFile.absolutePath)
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值