StudentAgency项目开发遇到的问题

2020-02-27  本文已影响0人  LongSh1z

本文收集了项目开发中遇到的大大小小的问题,主要目的也是方便以后的开发能够别踩那么多的坑、提高开发效率。

一、Glide裁剪成圆形图片

使用Glide自带的功能即可

RequestOptions requestOptions = RequestOptions.circleCropTransform();
Glide.with(getActivity())
         .load(resId)
         .placeholder(R.drawable.placeholder_pic)
         .apply(requestOptions)//重点是这一句
         .into(iv_avatar);

二、Resources中的getColor(int)已过时

替换成ContextCompat.getColor(Context,R.color.colorAccent);

//例如
iv_avatar_bg.setBackgroundColor(ContextCompat.getColor(getActivity(),R.color.themeColor));

三、动态申请权限时,onRequestPermissionsResult未被回调

动态申请权限时,我们要注意动态申请权限的地方在哪里。这里的地方指的是Activity或者是Fragment
如果是Activity:申请的方法应该用ActivityCompat.requestPermissions
如果是Fragment:申请的方法应该用requestPermissions

四、拍照或者从相册中选择的图片被旋转

整体思路就是获取图片被旋转的角度之后,再旋转相应的角度即可

4.1 拍照:ContentUri转文件路径(通过反射)

//获取content开头的Uri文件在文件系统中的路径
    public static String getFPUriToPath(Context context, Uri uri) {
        try {
            List<PackageInfo> packs = context.getPackageManager().getInstalledPackages(PackageManager.GET_PROVIDERS);
            if (packs != null) {
                String fileProviderClassName = FileProvider.class.getName();
                for (PackageInfo pack : packs) {
                    ProviderInfo[] providers = pack.providers;
                    if (providers != null) {
                        for (ProviderInfo provider : providers) {
                            if (uri.getAuthority().equals(provider.authority)) {
                                if (provider.name.equalsIgnoreCase(fileProviderClassName)) {
                                    Class<FileProvider> fileProviderClass = FileProvider.class;
                                    try {
                                        Method getPathStrategy = fileProviderClass.getDeclaredMethod("getPathStrategy", Context.class, String.class);
                                        getPathStrategy.setAccessible(true);
                                        Object invoke = getPathStrategy.invoke(null, context, uri.getAuthority());
                                        if (invoke != null) {
                                            String PathStrategyStringClass = FileProvider.class.getName() + "$PathStrategy";
                                            Class<?> PathStrategy = Class.forName(PathStrategyStringClass);
                                            Method getFileForUri = PathStrategy.getDeclaredMethod("getFileForUri", Uri.class);
                                            getFileForUri.setAccessible(true);
                                            Object invoke1 = getFileForUri.invoke(invoke, uri);
                                            if (invoke1 instanceof File) {
                                                String filePath = ((File) invoke1).getAbsolutePath();
                                                return filePath;
                                            }
                                        }
                                    } catch (NoSuchMethodException e) {
                                        e.printStackTrace();
                                    } catch (InvocationTargetException e) {
                                        e.printStackTrace();
                                    } catch (IllegalAccessException e) {
                                        e.printStackTrace();
                                    } catch (ClassNotFoundException e) {
                                        e.printStackTrace();
                                    }
                                    break;
                                }
                                break;
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

4.2 从相册中选择:ContentUri转文件路径

public static String getRealPathFromURI(Context context, Uri uri) {

        int sdkVersion = Build.VERSION.SDK_INT;
        if (sdkVersion >= 19) { // api >= 19
            return getRealPathFromUriAboveApi19(context, uri);
        } else { // api < 19
            return getRealPathFromUriBelowAPI19(context, uri);
        }
    }

    private static String getRealPathFromUriBelowAPI19(Context context, Uri uri) {
        String[] proj = {MediaStore.Audio.Media.DATA};
        Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
        cursor.moveToFirst();
        int columnIndex = cursor.getColumnIndex(proj[0]);
        String picturePath = cursor.getString(columnIndex);
        cursor.close();
        return picturePath;
    }

    /**
     * 适配api19及以上,根据uri获取图片的绝对路径
     *
     * @param uri     图片的Uri
     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
     */
    @SuppressLint("NewApi")
    private static String getRealPathFromUriAboveApi19(Context context, Uri uri) {
        String filePath = null;
        if ("content".equalsIgnoreCase(uri.getScheme())){
            // 如果是 content 类型的 Uri
            filePath = getDataColumn(context, uri, null, null);
        } else if ("file".equals(uri.getScheme())) {
            // 如果是 file 类型的 Uri,直接获取图片对应的路径
            filePath = uri.getPath();
        }
        Log.e(TAG,"real path = "+filePath) ;
        return filePath;
    }

    /**
     * 获取数据库表中的 _data 列,即返回Uri对应的文件路径
     * @return
     */
    private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
        String path = null;

        String[] projection = new String[]{MediaStore.Images.Media.DATA};
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                int columnIndex = cursor.getColumnIndexOrThrow(projection[0]);
                path = cursor.getString(columnIndex);
            }
        } catch (Exception e) {
            if (cursor != null) {
                cursor.close();
            }
        }
        return path;
    }

4.3 获取被旋转的角度以及旋转图片

//读取图片旋转角度
    public static int readPictureDegree(String path) {
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            Log.i(TAG,"readPictureDegree : orientation = " + orientation);
            if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
                degree = 90;
            } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
                degree = 180;
            } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
                degree = 270;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }

    //旋转图片
    public static Bitmap rotateBitmap(int angle, Bitmap bitmap) {
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        Bitmap rotation = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                matrix, true);
        return rotation;
    }

五、没有使用音频却出现AudioTrack错误

在logcat中的报错信息如下,项目中没有使用任何音频,却出现了AudioTrack错误:

图片.png

此时如果定位不到任何地方的代码时,可以尝试在Run项中查看有没有其他报错信息。该次的错误其实是textView.setText时里面的参数放进了int类型的数据,所以才导致了错误。

图片.png

六、recyclerview.setLayoutManager空指针错误

androidx.recyclerview.widget.RecyclerView.setLayoutManager on a null object reference

在排除了recyclerview包名相同,语法正确之后,依然出现空指针错误。这时可以改一下xml中recyclerview的id,然后再重新运行即可。

七、toast自带包名

//toast日常使用方式改为:
Toast toast = Toast.makeText(context, "", Toast.LENGTH_SHORT);
toast.setText("请填写相关信息!");
toast.show();

八、启动页白屏

白屏产生的原因我就不讲了,无非是application在初始化时耗时较久之类的,这里我采用的是修改style方式

//1.首先在styles文件中添加启动时的默认图片
<style name="SloganTheme" parent="AppTheme">
        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowBackground">@drawable/slogan_style</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowTranslucentNavigation">true</item>
</style>

//2.然后在AndroidManifest文件中设置theme
<activity android:name=".ui.activity.SloganActivity"
            android:theme="@style/SloganTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
</activity>
//最后,如果你的启动页的背景和style中的几乎一样的话,还可以在启动页的activity中取消setContentView

九、Retrofit同时发送两次请求以及后台正常返回数据,客户端却回调失败onError closed信息

9.1 Retrofit同时发送两次请求

原因:
一般我们使用retrofit的时候,都会去使用拦截器。这时,如果多个拦截器里面都会调用chain.proceed()方法,则会同时发起多次网络请求,每调用一次chain.proceed()方法,都会发起一次请求。
解决方案:
可以将拦截器合成一个,保证只调用一次chain.proceed()方法。

9.2 后台正常返回数据,客户端却回调失败onError closed信息

原因:
如题目所示,虽然后台能够正常返回数据,但是客户端回调的却是onError,并且错误信息为closed。原因是在拦截器里面已经调用过response.body().string()方法,向上返回的response的responseBody已经被close掉了,所以错误信息显示的是closed。
解决方法:
在return的时候重新构建一个response。

@Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request()
                    .newBuilder()
                    .addHeader("X-APP-TYPE","android")
                    .build();
            Response response = chain.proceed(request);

            //打印请求的具体信息
            String url = request.url().toString();
            String params = requestBodyToString(request.body());
            String responseString = response.body().string();//JsonHandleUtils.jsonHandle(response)
            String time = DateUtils.getCurrentDateByFormat("yyyy-MM-dd HH:mm:ss");
            String log =
                    "\n\n******请求时间*****:\n" + time +
                            "\n*****路径*****:\n" + url +
                            "\n*******参数******:\n" + params +
                            "\n*****报文******:\n" + responseString+"\n \n";
            Log.d(TAG, log);

            return response.newBuilder().body(ResponseBody.create(response.body().contentType(), responseString)).build();
        }

参考链接:
Retrofit2的一些坑

十、Retrofit上传图片时Multipart注解和FormUrlEncoded注解冲突

问题:
当我们需要上传图片时,需要Multipart注解;当我们还有其他参数需要用POST方法传递时,需要FormUrlEncoded注解。当我们在同一个请求方法上同时加上这两个注解时,就会报冲突异常。
解决方案:
将需要传递的其他参数的Field换成Part即可

//错误写法
@FormUrlEncoded
@Multipart
@POST("api-user/user/uploadAvatar")
Observable<ResponseBean> uploadAvatar(
                                      @Part MultipartBody.Part avatar, 
                                      @Field("userId") int userId);

//正确写法
@Multipart
@POST("api-user/user/uploadAvatar")
Observable<ResponseBean> uploadAvatar(
                                      @Part MultipartBody.Part avatar, 
                                      @Part("userId") int userId);
上一篇下一篇

猜你喜欢

热点阅读